Decubate - Smart Contract Audit Report

Audit Summary

Starstream Audit Report Decubate intends to build a yield aggregator in which users deposit various tokens to earn rewards using a MasterChef contract.

For this audit, we reviewed the project team's DCBVault, DecubateMasterChef, and DecubateStaking contracts at commit e271a71bde88d12f11e2edc41b44c5e4caa9a703 on the team's private GitHub repository.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: February 14th, 2022.

Finding #1 - DecubateStaking - Informational

Description: The staking and reward token prices are determined using a moving price average with no designated period of time which is not very resistant to manipulation.
Risk/Impact: The moving price average can be manipulated using flash loans on consecutive blocks in order to inflate the pricing of the tokens. The reward rate would then be inflated to an inaccurate rate as well. The attackers can then withdraw an inaccurate amount of tokens.
Recommendation: We recommend using a fixed window price average with a designated period of time in order to mitigate the impact of flash loan attacks on the pricing mechanism.

Finding #2 - DecubateMasterChef, DecubateStaking - Informational

Description: Several functions are declared public, but are never called internally.
- DecubateStaking: claim, claimAll, updateRatioAll, getPools
- DecubateMasterChef: claim, reinvestAll, claimAll, unStake
Recommendation: We recommend declaring these functions external for additional gas savings on each call.

Contracts Overview

  • As the contracts are implemented using Solidity v0.8.x, they are safe from any possible overflow/underflow attacks.
  • Note that the NFT contract, not reviewed by our team, is used in the DecubateMasterChef and DecubateStaking contracts to determine rewards.
DCBVault Contract:
  • This contract allows users to deposit funds and earn rewards based on a designated MasterChef contract.
  • While the contract is not paused, users can deposit tokens to a specified staking pool within the MasterChef contract to earn yield. Users are credited a proportional amount of shares for each deposit.
  • Users are able to withdraw staked funds and rewards out of the MasterChef contract through the Vault once the lock period within the MasterChef contract has elapsed.
  • Once the funds are transferred back to the user, a proportional amount of Vault shares are deducted from the user.
  • When the contract is not paused, any address that isn't a contract can trigger the function to harvest and re-invest the total rewards of a specified staking pool from the MasterChef contract. The user who called the harvest function also gets a portion of the total rewards as well; by default set to 0.25%.
  • The contract utilizes logic to protect against re-entrancy attacks in applicable functions.
  • The owner is able to withdraw any token in the contract at any time.
  • The owner can set the harvest reward amount to any value at any time.
  • The owner can pause and unpause depositing and reward harvesting functionality in the contract at any time.
DecubateMasterChef Contract:
  • This contract serves as a valid MasterChef contract for the DCBVault contract by allowing users to stake tokens into various staking pools to earn rewards until the pool's maximum limit or end date has been reached. Staked funds are held in the contract.
  • Each staking pool has a minimum stake amount limit as well as a maximum total staked amount limit.
  • Users can unstake their funds when the lock period has elapsed.
  • On deposits, any pending rewards are re-invested. On withdrawals, any pending rewards are calculated and transferred to the user unless the total reward supply has been depleted.
  • Users can re-invest their pending rewards into the pool, ignoring the lock duration at any time.
  • A fee amount, set to 0.5% by default, is deducted from claimed rewards and sent to the fee address.
  • The Vault contract serves as the Compounder contract address and can claim rewards, ignoring the lock duration at any time.
  • The Compounder contract can also stake any amount, ignoring all pools' minimum stake amount and maximum contribution amount at any time.
  • Rewards are calculated using the time staked, pool APY rate, and pool NFT multiplier.
  • While the pool's NFT status is active, the NFT contract is used to determine the NFT multiplier value. If the user owns a specified NFT, the corresponding multiplier value is applied.
  • While the pool's NFT status is inactive or the user does not own a qualifying NFT, the NFT multiplier is set to 10.
  • The NFT contract was out of scope for the purpose of this audit, so we are unable to verify its security at this time.
  • The owner can add a new staking pool at any time.
  • The owner can update all properties of any pool at any time.
  • The owner can transfer any tokens in the contract to any address at any time.
  • The owner can set the Compounder address at any time.
  • The owner can set the fee amount to any value at any time.
  • The owner can change the fee address at any time.
  • The team must exercise caution when setting the staking and reward tokens and must avoid using any fee-on-transfer token or ERC777-compliant tokens.
DecubateStaking Contract:
  • This contract allows users to stake designated tokens into the contract in order to earn rewards in the form of reward tokens determined by the project team.
  • Tokens can be staked until the pool's end date has been reached.
  • The user can withdraw their funds once the lock period has elapsed.
  • On deposits and withdrawals, any pending rewards are calculated and transferred to the user unless the total reward supply has been depleted.
  • Rewards are calculated using the time staked, pool APY rate, pool ratio, and pool NFT multiplier.
  • When rewards are claimed, a fee amount is deducted and sent to the fee address which is by default set to 5%.
  • While the pool's NFT status is active, the NFT contract is used to determine the NFT multiplier value. If the user owns a specified NFT, the corresponding multiplier value is applied. The NFT contract address is not in the scope of this audit.
  • While the pool's NFT status is inactive or the user does not own a qualifying NFT, the NFT multiplier is set to 10.
  • The NFT contract address was out of scope for the purpose of this audit.
  • The pools' APY rate is used to determine the accrued compounded interest for the staked amount.
  • The pool ratio is determined by the value of the pools' staking and reward token. The lower value token is used as the denominator for the pool ratio.
  • When rewards are claimed, the calculated value of both the staking and reward tokens are updated. The staking token value is calculated using a Uniswap liquidity pair of the staking token and another team defined token. The reward token value is calculated using a Uniswap liquidity pair of the reward token and the same team defined token.
  • A moving price average is used to calculate the values to prevent flash loan price manipulation attacks. However, we recommend utilizing a fixed window average which uses a specified period of time and ensures one full period passes before calculating the average again.
  • The owner can withdraw any tokens from the contract at any time.
  • The owner can add a new staking pool at any time.
  • The owner can update all properties of any staking pool at any time.
  • The owner can change the fee amounts to any value at any time.
  • The owner can change the fee address at any time.
  • The owner can transfer ownership at any time.
  • The team must exercise caution when setting the staking and reward tokens and must avoid using any fee-on-transfer token or ERC777-compliant tokens.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Centralization of ControlThe team can set the fees to any value at any time.WARNING
Delegate Call to Untrusted ContractN/APASS
Dependence on Predictable VariablesN/APASS
Deprecated OpcodesN/APASS
Ether ThiefN/APASS
ExceptionsN/APASS
External CallsN/APASS
Flash LoansWe recommend using a fixed window average instead of a moving average to calculate token prices.PASS
Integer Over/UnderflowN/APASS
Logical IssuesN/APASS
Multiple SendsN/APASS
OraclesN/APASS
SuicideN/APASS
State Change External CallsN/APASS
Unchecked RetvalN/APASS
User Supplied AssertionN/APASS
Critical Solidity CompilerN/APASS
Overall Contract Safety PASS

DCBVault Contract

 Token Graph

Multi-file Token

												
($) = payable function
 # = non-constant function

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Int] _transferOwnership #

 + [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] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Lib] Address 
    - [Int] isContract
    - [Int] sendValue #
    - [Int] functionCall #
    - [Int] functionCall #
    - [Int] functionCallWithValue #
    - [Int] functionCallWithValue #
    - [Int] functionStaticCall
    - [Int] functionStaticCall
    - [Int] functionDelegateCall #
    - [Int] functionDelegateCall #
    - [Int] verifyCallResult

 + [Lib] SafeERC20 
    - [Int] safeTransfer #
    - [Int] safeTransferFrom #
    - [Int] safeApprove #
    - [Int] safeIncreaseAllowance #
    - [Int] safeDecreaseAllowance #
    - [Prv] _callOptionalReturn #

 +  Pausable (Context)
    - [Pub]  #
    - [Pub] paused
    - [Int] _pause #
       - modifiers: whenNotPaused
    - [Int] _unpause #
       - modifiers: whenPaused

 + [Int] IDecubateMasterChef 
    - [Ext] add #
    - [Ext] set #
    - [Ext] stake #
    - [Ext] claim #
    - [Ext] reinvest #
    - [Ext] reinvestAll #
    - [Ext] claimAll #
    - [Ext] handleNFTMultiplier #
    - [Ext] unStake #
    - [Ext] updateCompounder #
    - [Ext] canClaim
    - [Ext] calcMultiplier
    - [Ext] payout
    - [Ext] poolInfo
    - [Ext] users
    - [Ext] poolLength

 +  DCBVault (Ownable, Pausable)
    - [Pub]  #
    - [Ext] deposit #
       - modifiers: whenNotPaused,notContract
    - [Ext] withdrawAll #
       - modifiers: notContract
    - [Ext] harvestAll #
       - modifiers: notContract,whenNotPaused
    - [Ext] setCallFee #
       - modifiers: onlyOwner
    - [Ext] pause #
       - modifiers: onlyOwner,whenNotPaused
    - [Ext] unpause #
       - modifiers: onlyOwner,whenPaused
    - [Ext] transferToken #
       - modifiers: onlyOwner
    - [Ext] calculateTotalPendingRewards
    - [Ext] calculateHarvestDcbRewards
    - [Ext] getRewardOfUser
    - [Pub] withdraw #
       - modifiers: notContract
    - [Pub] harvest #
       - modifiers: notContract,whenNotPaused
    - [Pub] getPricePerFullShare
    - [Pub] canUnstake
    - [Pub] available
    - [Pub] balanceOf
    - [Int] _earn #
    - [Int] getTokenOfPool

DecubateMasterChef Contract

BEP20 Token Graph

Multi-file Token

												
($) = payable function
 # = non-constant function
 
  + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Int] _transferOwnership #

 + [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

 +  DSMath 
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] min
    - [Int] max
    - [Int] imin
    - [Int] imax
    - [Int] wmul
    - [Int] rmul
    - [Int] wdiv
    - [Int] rdiv
    - [Int] rpow

 +  InterestHelper (DSMath)
    - [Pub] accrueInterest
    - [Pub] yearlyRateToRay
    - [Int] wadToRay
    - [Int] weiToRay

 + [Int] IDecubateMasterChef 
    - [Ext] add #
    - [Ext] set #
    - [Ext] stake #
    - [Ext] claim #
    - [Ext] reinvest #
    - [Ext] reinvestAll #
    - [Ext] claimAll #
    - [Ext] handleNFTMultiplier #
    - [Ext] unStake #
    - [Ext] updateCompounder #
    - [Ext] canClaim
    - [Ext] calcMultiplier
    - [Ext] payout
    - [Ext] poolInfo
    - [Ext] users
    - [Ext] poolLength

 + [Int] IDecubateNFT 
    - [Ext] walletOfOwner

 +  DecubateMasterChef (Ownable, InterestHelper, IDecubateMasterChef)
    - [Pub]  #
    - [Ext]  ($)
    - [Ext] updateFeeValues #
       - modifiers: onlyOwner
    - [Ext] updateCompounder #
       - modifiers: onlyOwner
    - [Ext] transferToken #
       - modifiers: onlyOwner
    - [Ext] add #
       - modifiers: onlyOwner
    - [Ext] set #
       - modifiers: onlyOwner
    - [Ext] stake #
    - [Ext] handleNFTMultiplier #
    - [Pub] claim #
    - [Pub] reinvest #
    - [Pub] reinvestAll #
    - [Pub] claimAll #
    - [Pub] unStake #
    - [Pub] canClaim
    - [Pub] calcMultiplier
    - [Pub] ownsCorrectNFT
    - [Pub] payout
    - [Pub] poolLength
    - [Pub] getPools
    - [Int] safeTOKENTransfer #
    - [Int] _claim #
    - [Int] _stake #

DecubateStaking Contract

BEP20 Token Graph

Multi-file Token

												
($) = payable function
 # = non-constant function
 
 + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Int] _transferOwnership #

 + [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] IERC165 
    - [Ext] supportsInterface

 + [Int] IERC721 (IERC165)
    - [Ext] balanceOf
    - [Ext] ownerOf
    - [Ext] safeTransferFrom #
    - [Ext] transferFrom #
    - [Ext] approve #
    - [Ext] getApproved
    - [Ext] setApprovalForAll #
    - [Ext] isApprovedForAll
    - [Ext] safeTransferFrom #

 + [Lib] FixedPoint 
    - [Int] encode
    - [Int] encode144
    - [Int] div
    - [Int] mul
    - [Int] fraction
    - [Int] decode
    - [Int] decode144

 + [Lib] UniswapV2OracleLibrary 
    - [Int] currentBlockTimestamp
    - [Int] currentCumulativePrices

 + [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 #

 +  DSMath 
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] min
    - [Int] max
    - [Int] imin
    - [Int] imax
    - [Int] wmul
    - [Int] rmul
    - [Int] wdiv
    - [Int] rdiv
    - [Int] rpow

 +  InterestHelper (DSMath)
    - [Pub] accrueInterest
    - [Pub] yearlyRateToRay
    - [Int] wadToRay
    - [Int] weiToRay

 + [Int] IDecubateStaking 
    - [Ext] add #
    - [Ext] set #
    - [Ext] setTokens #
    - [Ext] stake #
    - [Ext] unStake #
    - [Ext] updateFeeValues #
    - [Ext] updateTimeGap #
    - [Ext] claim #
    - [Ext] claimAll #
    - [Ext] updateRatio #
    - [Ext] updateRatioAll #
    - [Ext] poolInfo
    - [Ext] users
    - [Ext] canUnstake
    - [Ext] calcMultiplier
    - [Ext] poolLength
    - [Ext] payout

 + [Int] IDecubateNFT 
    - [Ext] walletOfOwner

 +  DecubateStaking (Ownable, InterestHelper, IDecubateStaking)
    - [Pub]  #
    - [Ext]  ($)
    - [Ext] add #
       - modifiers: onlyOwner
    - [Ext] set #
       - modifiers: onlyOwner
    - [Ext] setTokens #
       - modifiers: onlyOwner
    - [Ext] transferToken #
       - modifiers: onlyOwner
    - [Ext] stake #
    - [Ext] unStake #
    - [Ext] updateFeeValues #
       - modifiers: onlyOwner
    - [Ext] updateTimeGap #
       - modifiers: onlyOwner
    - [Pub] claim #
    - [Pub] claimAll #
    - [Pub] updateRatio #
    - [Pub] updateRatioAll #
    - [Pub] canUnstake
    - [Pub] calcMultiplier
    - [Pub] poolLength
    - [Pub] getPools
    - [Pub] payout
    - [Pub] ownsCorrectNFT
    - [Int] _claim #
    - [Int] _stake #
    - [Int] safeTransfer #
    - [Int] _updateRatio #
    - [Int] getPrices
    - [Int] getTokenPrice