Unity Ventures - Smart Contract Audit Report
Summary
Unity Ventures is building a new DeFi ecosystem on the Binance Smart Chain.
For this audit, we analyzed the Unity Ventures Token, SyrupBar, and MasterChef contracts. We reviewed the team's code at the following deployments to the Binance Smart Chain mainnet: UnityVenturesToken | SyrupBar | MasterChefNotes on the UnityVenturesToken Contract:At the time of writing this report, the current supply of $UV Token is 164,237.488375. 41.53% of the total $UV token supply is held in the MasterChef contract. 16.85% of the total supply is held in PancakeSwap V2 Liquidity. The owner of the contract has been set to be the MasterChef contract. As ownership is not in the team's control, the UnityVenturesToken Contract instead utilizes an "operator" role to retain control of certain aspects of the $UV Token contract. The $UV token is designed to be a governance token where 1 token = 1 vote. Token holders can delegate their voting rights to any address. To save gas, users can also do so using an EIP-712 signature. The transfer function of the token does not properly call _moveDelegates. As a result, when tokens (that are currently delegated) are transferred, the voting power is not being moved. Therefore the governance related features will not function as intended. There is a "Transfer Tax" that is charged on transactions which is allocated to the 'Burn Fee', and 'Liquidity Fee'. The operator role has the ability to modify the tax and allocations to any percentage (up to a maximum percentage that is also determined by the operator role) at any time. The tokens that are taxed as part of the burn fee are instantly burned at the time of the transaction. The tokens that are taxed for the liquidity fee during transfers are stored in the contract address balance. Once the threshold value (also determined by the operator) is met, a swap will occur for the purpose of funding Pancakeswap liquidity. This functionality can be disabled by the operator at any time. Liquidity-adds are funded by selling a portion of the tokens collected as fees (after the threshold as determined by the operator is met), then pairing the received BNB with the token, and adding it as liquidity to the BNB pair. The recipient of the newly created LP tokens is the operator. The stability of the liquidity pool can be at risk if these LP tokens are not locked. The contract includes a maximum transfer amount that can be set and updated by the operator at any time. The maximum transfer amount will restrict the number of $UV tokens that can be sent during a single transaction. This maximum transfer amount also applies to the contract address only when swapping tokens (in the event that the contract token balance and the threshold for swapping exceeds the maximum transfer amount). Utilization of SafeMath (or similarily safe functions) to prevent overflows. The contract complies with the BEP20 Token Standard.
Notes on the SyrupBar Contract:At the time of writing this report, the current supply is 69,348.91022 $SYRUP. 97.50% of the total $SYRUP supply is held in the UVVault contract. This contract was not in scope for the purpose of this audit. The other 2.5% of the total supply is distributed across the other 7 $SYRUP Holders. The $SyrupBar token is designed to be a governance token where 1 token = 1 vote. Token holders can delegate their voting rights to any address. To save gas, users can also do so using an EIP-712 signature. The transfer function of the token does not properly call _moveDelegates. As a result, when tokens (that are currently delegated) are transferred, the voting power is not being moved. Therefore the governance related features will not function as intended. The SyrupBar token represents a "staking" position with the UnityVentures MasterChef contract. Users who stake $UV tokens into the contract will be minted an equal amount of $Syrup which is later burned by the MasterChef contract when the user unstakes. Utilization of SafeMath (or similarily safe functions) to prevent overflows. The contract complies with the BEP20 Token Standard.
Notes on the MasterChef Contract:Users can stake various tokens into the MasterChef contract to earn rewards in the form of the project's native $UV token. $UV Tokens can be minted through generated rewards for stakeholders, where an additional 10% of minted $UV Tokens are minted to the SyrupBar Token Contract. The owner of the token contract has been properly set to the MasterChef staking contract for the provision of staking rewards. There is a fee associated with making a deposit to the contract, set by the team upon adding the pool. The fee is directed to the team and its percentage can be updated to any amount at any time. The share of rewards that are generated by a given pool can be changed by the owner at any time. The team must also be careful not to add the same token twice for staking. The MasterChef staking contract should not be used with deflationary, fee-on-transfer, or ERC-777 tokens. If a fee-on-transfer token is added as a staking asset, then the contract must be exempt from transfer fees in order to avoid exploitation from an outside attacker that could mint an extremely large amount of reward tokens that would make it possible to drain the liquidity pool. Given that the UV Token has fees on transfers, holders that have staked the UV token must unstake their position as soon as possible as the current state of the UV Pool is ripe for exploitation. We recommend that the team sets the alloc points for that pool to 0 (immediately) in order to prevent abuse of this exploit. The contract includes referral logic which allows users to earn additional $UV token rewards in the form of a commission rate that can range anywhere from 1% - 10%. The referral contract that is referenced is out of scope for the purpose of this audit and was not reviewed by our team. Referral commission appears to only be paid if users (that have been referred) opt to claim their rewards by depositing more tokens. In the event that a holder decides to either withdraw, or stake to the $UV Pool, then commission is not accounted for and will not be paid to the "referrer". The referral process appears to be easily abused as a savvy user can refer themself and use the additional address to harvest the referral benefits. A 'Migrator' function is present, allowing the team to migrate all of user's staked funds to a new contract without restriction. Some gas optimizations can be achieved through marking functions external instead of public. There are also some public variables that could be declared constant. This is merely informational as the contract has already been deployed. Utilization of SafeMath (or similarily safe functions) to prevent overflows.
Audit Findings Summary:
- The team added a 'fee-on-transfer' token to the MasterChef. As a result, an external attacker can abuse the rewards mechanism to mint an extremely large amount of $UV tokens.
- Ensure trust in the team as they have substantial control within the ecosystem as they have the ability to migrate user's staked funds.
- Date: September 27th, 2021
Combined External Threat Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Storage Write | N/A | PASS |
Arbitrary Jump | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Deprecated Opcodes | N/A | PASS |
Ether/Token Thief | N/A | FAIL |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Multiple Sends | N/A | PASS |
Suicide | N/A | PASS |
State Change External Calls | N/A | PASS |
Unchecked Retval | N/A | PASS |
User Supplied Assertion | N/A | PASS |
Critical Solidity Compiler | N/A | PASS |
Overall Contract Safety | FAIL |
Function Graph
Inheritence Chart
Functions Overview
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] SafeBEP20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Prv] _callOptionalReturn #
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Int] functionDelegateCall #
- [Int] functionDelegateCall #
- [Prv] _verifyCallResult
+ [Lib] SafeMath
- [Int] tryAdd
- [Int] trySub
- [Int] tryMul
- [Int] tryDiv
- [Int] tryMod
- [Int] add
- [Int] sub
- [Int] mul
- [Int] div
- [Int] mod
- [Int] sub
- [Int] div
- [Int] mod
+ [Int] IBEP20
- [Ext] totalSupply
- [Ext] decimals
- [Ext] symbol
- [Ext] name
- [Ext] getOwner
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ BEP20 (Context, IBEP20, Ownable)
- [Pub] #
- [Ext] getOwner
- [Pub] name
- [Pub] decimals
- [Pub] symbol
- [Pub] totalSupply
- [Pub] balanceOf
- [Pub] transfer #
- [Pub] allowance
- [Pub] approve #
- [Pub] transferFrom #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
- [Pub] mint #
- modifiers: onlyOwner
- [Int] _transfer #
- [Int] _mint #
- [Int] _burn #
- [Int] _approve #
- [Int] _burnFrom #
+ [Int] IUniswapV2Factory
- [Ext] feeTo
- [Ext] feeToSetter
- [Ext] getPair
- [Ext] allPairs
- [Ext] allPairsLength
- [Ext] createPair #
- [Ext] setFeeTo #
- [Ext] setFeeToSetter #
+ [Int] IUniswapV2Pair
- [Ext] name
- [Ext] symbol
- [Ext] decimals
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] allowance
- [Ext] approve #
- [Ext] transfer #
- [Ext] transferFrom #
- [Ext] DOMAIN_SEPARATOR
- [Ext] PERMIT_TYPEHASH
- [Ext] nonces
- [Ext] permit #
- [Ext] MINIMUM_LIQUIDITY
- [Ext] factory
- [Ext] token0
- [Ext] token1
- [Ext] getReserves
- [Ext] price0CumulativeLast
- [Ext] price1CumulativeLast
- [Ext] kLast
- [Ext] mint #
- [Ext] burn #
- [Ext] swap #
- [Ext] skim #
- [Ext] sync #
- [Ext] initialize #
+ [Int] IUniswapV2Router01
- [Ext] factory
- [Ext] WETH
- [Ext] addLiquidity #
- [Ext] addLiquidityETH ($)
- [Ext] removeLiquidity #
- [Ext] removeLiquidityETH #
- [Ext] removeLiquidityWithPermit #
- [Ext] removeLiquidityETHWithPermit #
- [Ext] swapExactTokensForTokens #
- [Ext] swapTokensForExactTokens #
- [Ext] swapExactETHForTokens ($)
- [Ext] swapTokensForExactETH #
- [Ext] swapExactTokensForETH #
- [Ext] swapETHForExactTokens ($)
- [Ext] quote
- [Ext] getAmountOut
- [Ext] getAmountIn
- [Ext] getAmountsOut
- [Ext] getAmountsIn
+ [Int] IUniswapV2Router02 (IUniswapV2Router01)
- [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
- [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
- [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
- [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
- [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
+ [Int] IUnityVenturesReferral
- [Ext] recordReferral #
- [Ext] recordReferralCommission #
- [Ext] getReferrer
+ [Int] IMigratorChef
- [Ext] migrate #
+ UnityVenturesToken (BEP20)
- [Pub] #
- modifiers: BEP20
- [Pub] mint #
- modifiers: onlyOwner
- [Int] _transfer #
- modifiers: antiWhale
- [Prv] swapAndLiquify #
- modifiers: lockTheSwap,transferTaxFree
- [Prv] swapTokensForEth #
- [Prv] addLiquidity #
- [Pub] maxTransferAmount
- [Pub] isExcludedFromAntiWhale
- [Ext] ($)
- [Pub] updateTransferTaxRate #
- modifiers: onlyOperator
- [Pub] updateBurnRate #
- modifiers: onlyOperator
- [Pub] updateMaxTransferAmountRate #
- modifiers: onlyOperator
- [Pub] updateMinAmountToLiquify #
- modifiers: onlyOperator
- [Pub] setExcludedFromAntiWhale #
- modifiers: onlyOperator
- [Pub] updateSwapAndLiquifyEnabled #
- modifiers: onlyOperator
- [Pub] updateUVSwapRouter #
- modifiers: onlyOperator
- [Pub] operator
- [Pub] transferOperator #
- modifiers: onlyOperator
- [Ext] delegates
- [Ext] delegate #
- [Ext] delegateBySig #
- [Ext] getCurrentVotes
- [Ext] getPriorVotes
- [Int] _delegate #
- [Int] _moveDelegates #
- [Int] _writeCheckpoint #
- [Int] safe32
- [Int] getChainId
+ SyrupBar (BEP20)
- [Pub] mint #
- modifiers: onlyOwner
- [Pub] burn #
- modifiers: onlyOwner
- [Pub] #
- [Pub] safeUVTransfer #
- modifiers: onlyOwner
- [Ext] delegates
- [Ext] delegate #
- [Ext] delegateBySig #
- [Ext] getCurrentVotes
- [Ext] getPriorVotes
- [Int] _delegate #
- [Int] _moveDelegates #
- [Int] _writeCheckpoint #
- [Int] safe32
- [Int] getChainId
+ MasterChef (Ownable)
- [Pub] #
- [Pub] updateMultiplier #
- modifiers: onlyOwner
- [Pub] approve #
- modifiers: onlyOwner
- [Ext] poolLength
- [Pub] add #
- modifiers: onlyOwner
- [Pub] set #
- modifiers: onlyOwner
- [Int] updateStakingPool #
- [Pub] setMigrator #
- modifiers: onlyOwner
- [Pub] migrate #
- [Pub] getMultiplier
- [Ext] pendingUV
- [Pub] massUpdatePools #
- [Pub] updatePool #
- [Pub] deposit #
- [Pub] withdraw #
- [Pub] enterStaking #
- [Pub] leaveStaking #
- [Pub] emergencyWithdraw #
- [Int] safeUVTransfer #
- [Pub] setDevAddress #
- [Pub] setFeeAddress #
- [Pub] updateStartBlock #
- modifiers: onlyOwner
- [Pub] updateUVPerBlock #
- modifiers: onlyOwner
- [Pub] setUnityVenturesReferral #
- modifiers: onlyOwner
- [Pub] setReferralCommissionRate #
- modifiers: onlyOwner
- [Int] payReferralCommission #