Final Report
Copyright © 2022 by Verilog Solutions. All rights reserved.
January 4, 2022
by Verilog Solutions
This report presents our engineering engagement with YuzuSwap, one of the first DEX projects for the Emerald paratime on the Oasis Network. YuzuSwap is an AMM DEX with innovative trading incentive designs, such as the trading pool share token (TPST). YuzuSwap's TPST design keeps track of DEX users' swap operations and provides rewards for these users.
YuzuSwap is a decentralized exchange on the Oasis Emerald paratime that includes incentives such as liquidity mining and trade mining. YuzuSwap follows a non-custodial, peer-to-peer, automated-market-maker model and aims to provide a safe, swift, and low-cost tool to discover and swap tokens within the Oasis ecosystem. The YuzuSwap platform itself is fully open to developers and members of the Yuzu DAO.
Our review focused on the main branch, specifically, commit hash 12b06a57c53ec9eed3eadaded2afd93362d44d8b.
Our auditing service for YuzuSwap includes the following two stages:
Pre-Audit Consulting Service
As a part of the pre-audit service, the Verilog team worked closely with the YuzuSwap development to discuss potential vulnerability and smart contract development best practices in a timely fashion. Verilog team is very appreciative of establishing an efficient and effective communication channel with the YuzuSwap team, as new findings are often exchanged promptly and fixes were deployed quickly, during the preliminary report stage.
Audit Service
The Verilog team conducted a thorough study of the YuzuSwap code, with the YuzuSwap architecture graph and UML graph presented below in the YuzuSwap Architecture section. The list of findings, along with the severity and solution, is available under section Findings & Improvement Suggestions.
These are the major smart contracts in YuzuSwap:
YuzuToken.sol
YuzuKeeper.sol
YuzuPark.sol
YuzuParkExt.sol
YuzuRouter.sol
YuzuSwapMining.sol
YuzuZap.sol
YuzuKeeper.sol
YuzuKeeper
has the privilege to mint yuzu tokens to the registered applications and dev
address.YuzuToken.sol
YuzuToken
is a standard ERC20 token contract, that can be minted by owner
.YuzuPark.sol
YuzuPark
is the contract where it distributes yuzu tokens half-attenuationally as rewards for staking eligible tokens.YuzuParkExt.sol
YuzuParkExt
is a extenstion for YuzuPark
. Multiple tokens can be set as reward tokens besides the yuzu token.YuzuRouter.sol
YuzuSwapMining.sol
YuzuRouter
to swap tokens of eligible pair.YuzuZap.sol
YuzuZap
, users can get lp tokens by supplying a single token. Users can also swap back to a single token with the lp token.YUZUTokenBlackHole.sol
@ address 0x00b9dCA177aa3DB6F344A455d9E0511a6Aa7ad8DThere is one main privileged role owner
.
It can:
YuzuToken.sol
applications
in YuzuKeeper.sol
YuzuPark.sol
, YuzuParkExt.sol
, YuzuSwapMining.sol
, YuzuRouter.sol
, StandardReward.sol
.InformationalMinorMediumMajorCritical
Total | Acknowledged | Resolved | |
---|---|---|---|
Critical | 3 | 3 | 3 |
Major | 0 | 0 | 0 |
Medium | 2 | 1 | 1 |
Minor | 9 | 7 | 5 |
Informational | 7 | 3 | 3 |
Router address should be a variable set by owner (YuzuZap.sol
: L48, L93, L98, L112, L144, 165, L171).Critical
Description: Router address is being passed in as argument and token allowance for router is set to maximum. Attackers can deploy a set of swap and router contracts with the same interface to drain contracts' funds.
Recommendation: Router address should be a variable set by owner to prevent exploits.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
<address>.call()
should be used instead of <address>.transfer()
to transfer native tokens (YuzuZap.sol
:L448). Critical
Description: <address>.call()
should be used instead of <address>.transfer
to transfer native tokens.
Recommendation: Use uniswap's TransferHelper.safeTransferETH()
to transfer native token.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
Reset amount of rewarder contract at emergencyWithdraw
(YuzuParkExt.sol
: L313). Critical
Description: User amounts of rewarder contracts are not reset to zero.
Recommendation: Reset User amounts of rewarder contracts to zero.
Result: Resolved in commit 6867b4b00980862cefc5feb8487e9bb98ae4e31c.
No major issue was found.
Add nonRentrant modifier for external functions:zapInToken()
, zapIn()
, zapAcross()
, zapOut()
, zapOutToken()
, swapToken()
, swapToNative()
(YuzuZap.sol
:L48, L93, L98, L112, L144, 165, L171). Medium
Description: No nonReentrant modifier for those functions.
Recommendation: Add nonReentrant modifier.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
Approve actual amount instead of maximum amount (YuzuZap.sol
:L180). Medium
Description: Maximum amount is used for approval here.
Recommendation: Modify the functions to approve actual amount instead of maximum amount.
Result: This suggestion is not adopted.
Require only receive native tokens from router address (YuzuZap.sol
:L46). Minor
Description: Anyone can send native tokens to the contract
Recommendation: Require only router can send native tokens to current contract.
Result: This suggestion is not adopted.
Change public
to external
for external call only functions(YuzuParkExt.sol
:L123, L149, L242, L280, L313, YuzuPark.sol
:L96, L120, L190, L217, L234). Minor
Description: public
and external
differs in gas usage. Also public
allows calls made inside the contract.
Recommendation: Change public
to external
for external call only functions.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923 and in commit 0d0caf24d4898dcd13ef803d8f29b1e6f2022eaf.
Add check for pool Id to make sure the pool does not exist at addPool()
and add()
(StandardReward.sol
:L70, YuzuPark.sol
:L96). Minor
Description: Pool might already exist.
Recommendation: For example, add the following requirement to check if pool id is valid.
Result: Not Resolved.
Add check for pool Id to make sure pool exist at updatePool()
and setPool()
(YuzuParkExt.sol
:L219, YuzuPark.sol
:L167, StandardReward.sol
:L89, L99). Minor
Description: Pool might not exist.
Recommendation: For example, add the following requirement to check if pool already added.
Result: Not Resolved.
Some varibales can be immutable. (YuzuZap.sol
:L35, HalfAttenuationYuzuReward.sol
:L13-L17). Minor
Description: WNATIVE
at YuzuZap
and startBlock
, blockNumberOfHalfAttenuationCycle
and yuzuPerBlock
at HalfAttenuationYuzuReward
can be immutable.
Recommendation: add immutable
keyword to WNATIVE
.
Result: Resolved in commit 0d0caf24d4898dcd13ef803d8f29b1e6f2022eaf.
The visibility of functiongetYuzuFromStartblock()
can be pure
(L41). Minor
Description: Function visibility can be restricted to pure
.
Recommendation: Change function visibility to pure
.
Result: Resolved in commit 0d0caf24d4898dcd13ef803d8f29b1e6f2022eaf.
Use div
from SafeMath
(StandardReward.sol
:L107, L109, L152, L177, L178, L182). Minor
Description: The /
is used for division instead of div
.
Recommendation: Use div
from SafeMath
.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
Add Solidity pragma version. Minor
Description: There is no soldity version been added to the YuzuSwapMining.sol.
Recommendation: Add pragma solidity 0.6.12;
as other smart contracts.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
Multiply before dividing (YuzuKeeper
: L117). Minor
Description: Use div
before mul
.
Recommendation: Use mul
before div
to prevent precision lost.
Result: This suggestion is not adopted.
Seperate interface
. Informational
Description: Currently, interfaces is inside the same file with contract.
Recommendation: Seperate interface
.
Result: This suggestion is not adopted.
Call massUpdatePools()
directly at functions add
and set
(YuzuParkExt.sol
:L130, L157, YuzuPark.sol
:L102, L126). Informational
Description: The _withUpdate
is always true. massUpdatePools()
can be called directly.
Recommendation: Remove if
else
check for _withUpdate
. massUpdatePools()
can be called directly.
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
Modifies the logic of function duplicatedTokenDetect()
. (YuzuPark.sol
:L254, YuzuParkExt.sol
:L345). Informational
Description: the check for dulipcate tokens detection can be optimized.
Recommendation: For example, use a mapping to track added tokens.
Result: Resolved in commit 0d0caf24d4898dcd13ef803d8f29b1e6f2022eaf.
Input Multicheck and Limitation (YuzuKeeper
: L62). Informational
Description: As one of the most important functions, input _totalValue
should be limited into a certain range (less than 10% of the total token supply), input _perBlockLimit
should be limited into a certain range, input _startBlock
should be limited after certain date.
Recommendation: Add requirement for each of the input.
Result: This suggestion is not adopted. We suggest that the owner of the smart contract must be very careful when calling this function.
Logic Improvement for Function RequestForYUZU()
(YuzuKeeper
: L79). Informational
Description: When 1 $YUZU is mint, 0.1 for investor, 0.1 for foundation, 0.1 for dev. The current logic is to send the above 0.3 $YUZU first before sending the leftAmount to the user. This design is a bit unfair for the unlucky users who mint the last bit of Yuzu.
Recommendation:
step 1: check how many $YUZU is left.
step 2: adjust the mint amount to the leftover amount of $YUZU if the mint amount exceeds the leftover amount.
step 3: mint based on the distribution.
*external changes required: YuzuToken.sol needs to add a circulatingSupply()
external view function to feed YuzuKeeper how many tokens have been mint out.
Result: This suggestion is not adopted.
Add public view functions for poolInfo.length
at StandardReward.sol
.Informational
Description: A view function can be added to check the poolInfo.length
for the convenience of adding pools in the future.
Recommendation: Add a public view function for poolInfo.length
Result: Resolved in commit 7333cda08cf6afabeb633e03cbc07143e8fb6923.
Unused variable _pendingYUZU
at onYUZUReward()
and pendingToken()
(StandardReward.sol
:L145, L168). Informational
Description: Unused variable _pendingYUZU
.
Recommendation: Change the code to followings to remove compiler warnings for unused variables.
Result: This suggestion is not adopted.
Verilog receives compensation from one or more clients for performing the smart contract and auditing analysis contained in these reports. The report created is solely for Clients and published with their consent. As such, the scope of our audit is limited to a review of code, and only the code we note as being within the scope of our audit detailed in this report. It is important to note that the Solidity code itself presents unique and unquantifiable risks since the Solidity language itself remains under current development and is subject to unknown risks and flaws. Our sole goal is to help reduce the attack vectors and the high level of variance associated with utilizing new and consistently changing technologies. Thus, Verilog in no way claims any guarantee of security or functionality of the technology we agree to analyze.
In addition, Verilog reports do not provide any indication of the technologies proprietors, business, business model, or legal compliance. As such, reports do not provide investment advice and should not be used to make decisions about investment or involvement with any particular project. Verilog has the right to distribute the Report through other means, including via Verilog publications and other distributions. Verilog makes the reports available to parties other than the Clients (i.e., “third parties”) – on its website in hopes that it can help the blockchain ecosystem develop technical best practices in this rapidly evolving area of innovation.