Immortal Farm - Audit Report

Summary

ImmortalFarm Audit Report Immortal Farm is developing a yield farming strategy on the Fantom Blockchain in which users deposit FTM and earn rewards from an LP yield farming platform.

For this audit we reviewed Immortal Farm's CustomERC20PresetMinterPauser and MultiVault contracts at commit 5461fe7a62215ccae01d31d9685356fa788869b7 on the team's GitHub repository.

Notes on the Contracts:
  • Anyone can deposit FTM into the MultiVault contract at any time; in return, the user will receive an amount of shares proportional to the total value they are contributing to the pool.
  • There is a cooldown time of 22 minutes between transferring, minting, and burning shares.
  • The protocol uses time-weighted averages provided by the Tarot Price Oracle for accurate and tamper-resistant pricing; the Oracle calculates average prices over a period of 20 minutes.
  • The FTM deposited will be split and allocated towards several different liquidity pools, which are set on initialization.
  • Half of the FTM allocated towards each pool is sold for the secondary pool token. The secondary token is paired with the remaining FTM, and added as liquidity to the FTM pair. The newly created LP tokens are deposited into the appropriate pool in the SpiritMasterChef to earn rewards.
  • Anyone can use the contract to harvest the rewards from the SpiritMasterChef at any time.
  • The harvested reward tokens are sold for FTM, and 95.5% of the FTM received is invested back into each of the pools of interest in the SpiritMasterChef.
  • The remaining 4.5% is transferred to a fee wallet controlled by the team.
  • Users can liquidate their shares only after the 22 minute cooldown time has passed since the latest deposit.
  • On withdrawals, an amount of LP tokens proportional to the shares the user is burning is withdrawn from each pool in the SpiritMasterChef.
  • The LP tokens are decoupled and converted to FTM.
  • 99.9% of the resulting FTM is delivered to the user, and the remaining 0.1% is delivered to the fee wallet.
  • The owner can set the address of the share token contract only once after deployment.
  • The owner can adjust the slippage used while swapping users' FTM deposits for secondary tokens at any time.
  • The owner can set the cooldown time for withdrawing in the MultiVault to any value to up 24 hours.
  • The owner can set the cooldown time for minting, burning, and transferring the share token to any value to up 12 hours.
  • The owner can pause the system at any time, preventing deposits, withdrawals, and transfers.

  • The team worked with us to implement changes related to gas optimization and to make the contracts resistant to price manipulation.
  • Excellent structuring of logic to prevent re-entrancy attacks.
  • The contract utilizes 112x112 fixed point number representation for some arithmetic operations, which promotes gas efficiency in calculations, but sacrifices range and precision that the standard floating point number representation offers.
  • As the contract utilizes SafeMath, it is protected from any overflows.
Audit Findings Summary
  • No external threats were detected.
  • Ensure trust in the team as they have some control in the ecosystem.
  • Date: October 21th, 2021
  • Updated: October 31th, 2021 to add cooldown logic for transferring, minting, burning, and withdrawing shares.
  • Updated: December 2nd, 2021 to use price oracle within the compound function, and to restrict setting the share address.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Delegate Call to Untrusted ContractN/APASS
Dependence on Predictable VariablesN/APASS
Deprecated OpcodesN/APASS
Ether ThiefN/APASS
ExceptionsN/APASS
External CallsN/APASS
Flash LoansN/APASS
Integer Over/UnderflowN/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

Details: MultiVault Contract

MatrixVault Graph

MatrixVault


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

 + [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 #
    - [Ext] mint #

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

 + [Lib] Babylonian2 
    - [Int] sqrt

 + [Lib] HomoraMath 
    - [Int] divCeil
    - [Int] fmul
    - [Int] fdiv
    - [Int] sqrt

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

 + [Int] IPancakeFactory 
    - [Ext] feeTo
    - [Ext] feeToSetter
    - [Ext] getPair
    - [Ext] allPairs
    - [Ext] allPairsLength
    - [Ext] createPair #
    - [Ext] setFeeTo #
    - [Ext] setFeeToSetter #
    - [Ext] locked
    - [Ext] setLocked #

 + [Lib] TransferHelper 
    - [Int] safeApprove #
    - [Int] safeTransfer #
    - [Int] safeTransferFrom #
    - [Int] safeTransferETH #

 + [Int] IPancakeRouter01 
    - [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] IPancakeRouter02 (IPancakeRouter01)
    - [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
    - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
    - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
    - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
    - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #

 + [Int] IPancakePair 
    - [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 #

 + [Lib] PancakeLibrary 
    - [Int] sortTokens
    - [Int] pairFor
    - [Int] getReserves
    - [Int] quote
    - [Int] getAmountOut
    - [Int] getAmountIn
    - [Int] getAmountsOut
    - [Int] getAmountsIn

 + [Int] IWETH 
    - [Ext] deposit ($)
    - [Ext] transfer #
    - [Ext] withdraw #

 +  SpiritRouter (IPancakeRouter02)
    - [Pub]  #
    - [Ext]  ($)
    - [Int] _addLiquidity #
    - [Ext] addLiquidity #
       - modifiers: ensure
    - [Ext] addLiquidityETH ($)
       - modifiers: ensure
    - [Pub] removeLiquidity #
       - modifiers: ensure
    - [Pub] removeLiquidityETH #
       - modifiers: ensure
    - [Ext] removeLiquidityWithPermit #
    - [Ext] removeLiquidityETHWithPermit #
    - [Pub] removeLiquidityETHSupportingFeeOnTransferTokens #
       - modifiers: ensure
    - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
    - [Int] _swap #
    - [Ext] swapExactTokensForTokens #
       - modifiers: ensure
    - [Ext] swapTokensForExactTokens #
       - modifiers: ensure
    - [Ext] swapExactETHForTokens ($)
       - modifiers: ensure
    - [Ext] swapTokensForExactETH #
       - modifiers: ensure
    - [Ext] swapExactTokensForETH #
       - modifiers: ensure
    - [Ext] swapETHForExactTokens ($)
       - modifiers: ensure
    - [Int] _swapSupportingFeeOnTransferTokens #
    - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
       - modifiers: ensure
    - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
       - modifiers: ensure
    - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
       - modifiers: ensure
    - [Pub] quote
    - [Pub] getAmountOut
    - [Pub] getAmountIn
    - [Pub] getAmountsOut
    - [Pub] getAmountsIn

 + [Int] IBEP20 
    - [Ext] totalSupply
    - [Ext] decimals
    - [Ext] symbol
    - [Ext] name
    - [Ext] getOwner
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

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

 +  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] symbol
    - [Pub] decimals
    - [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 #

 +  SpiritToken (BEP20)
    - [Pub] mint #
       - modifiers: onlyOwner
    - [Ext] delegates
    - [Ext] delegate #
    - [Ext] delegateBySig #
    - [Ext] getCurrentVotes
    - [Ext] getPriorVotes
    - [Int] _delegate #
    - [Int] _moveDelegates #
    - [Int] _writeCheckpoint #
    - [Int] safe32
    - [Int] getChainId

 +  SpiritMasterChef (Ownable)
    - [Pub]  #
    - [Ext] poolLength
    - [Pub] add #
       - modifiers: onlyOwner
    - [Pub] set #
       - modifiers: onlyOwner
    - [Pub] getMultiplier
    - [Ext] pendingSpirit
    - [Pub] massUpdatePools #
    - [Pub] updatePool #
    - [Pub] deposit #
    - [Pub] withdraw #
    - [Pub] emergencyWithdraw #
    - [Int] safeSpiritTransfer #
    - [Pub] dev #
    - [Pub] setFeeAddress #
    - [Pub] updateEmissionRate #
       - modifiers: onlyOwner

 + [Int] ITarotPriceOracle 
    - [Ext] MIN_T
    - [Ext] getPair
    - [Ext] initialize #
    - [Ext] getResult #
    - [Ext] getBlockTimestamp

 + [Lib] UQFixed 
    - [Int] encode
    - [Int] uqdiv
    - [Int] decode
    - [Int] reciprocal

 + [Int] AggregatorV3Interface 
    - [Ext] decimals
    - [Ext] description
    - [Ext] version
    - [Ext] getRoundData
    - [Ext] latestRoundData

 + [Lib] FullMath 
    - [Int] fullMul
    - [Prv] fullDiv
    - [Int] mulDiv

 + [Lib] Babylonian 
    - [Int] sqrt

 + [Lib] BitMath 
    - [Int] mostSignificantBit
    - [Int] leastSignificantBit

 + [Lib] FixedPoint 
    - [Int] encode
    - [Int] encode144
    - [Int] decode
    - [Int] decode144
    - [Int] mul
    - [Int] muli
    - [Int] muluq
    - [Int] divuq
    - [Int] fraction
    - [Int] reciprocal
    - [Int] sqrt

 + [Int] ERC20Interface 
    - [Ext] balanceOf
    - [Ext] burnFrom #

 + [Lib] SafeToken 
    - [Int] myBalance
    - [Int] balanceOf
    - [Int] safeApprove #
    - [Int] safeTransfer #
    - [Int] safeTransferFrom #

 +  MultiVault 
    - [Pub]  #
    - [Ext]  ($)
    - [Pub] calcTVL #
    - [Pub] calcShare #
    - [Pub] declareShareAddress #
       - modifiers: onlyOwner
    - [Pub] withdraw #
    - [Pub] withdraw #
    - [Prv] approveRouter #
    - [Prv] swapExactTokensForTokensSupportingFeeOnTransferTokens #
       - modifiers: onlyOwner
    - [Prv] addLiquidity #
    - [Prv] swapExactETHForTokensSupportingFeeOnTransferTokens #
    - [Prv] swapExactTokensForETHSupportingFeeOnTransferTokens #
    - [Prv] addLiquidityETH #
    - [Prv] removeLiquidity #
    - [Prv] removeLiquidityETHSupportingFeeOnTransferTokens #
    - [Pub] compound #
    - [Prv] getLPPriceFTM #
    - [Prv] allocate #