Immortal Farm - Audit Report
Summary
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:Audit Findings Summary
- 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.
- 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 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 Thief | N/A | PASS |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Flash Loans | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Multiple Sends | N/A | PASS |
Oracles | 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 | PASS |
Details: MultiVault Contract
($) = 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 #