Grim Finance Token and Vault - Smart Contract Audit Report
Summary
Update December 19th, 2021: Grim Finance has been subject to an attack resulting in a loss of funds from numerous vault contracts. The cause of this issue was the ability of users to input arbitrary addresses and have them called within the depositFor function. Via reentrancy, the issue allowed users to falsely increase their shares in Grim's vaults and subsequently withdraw more than they had deposited. In an effort to be transparent, we have posted a thread documenting the attack and the measures we have taken as a response to this incident which can be viewed here.
Everything below this line remains unchanged from the original report.
Grim Finance intends to build a yield aggregator on Fantom's Opera network.
For this audit we reviewed the project's Token and Vault contracts. We reviewed the contracts at the following addresses:
Notes of the Contracts:
GrimToken:GrimVault:
- The current supply of GrimToken (GRIM) is 10,000,000.
- 40% of GrimTokens are held by the GrimPool contract.
- 28% of the token is held by an unlocked address.
- 10.5% of the token is held by a liquidity pool, with 78% of that liquidity staked in a MasterChef staking contract.
- There is a 1% burn fee and a 1% liquidity fee taken on transfers. These fees can be changed by the owner at any time.
- The owner and contract are excluded from these fees, and the owner has the power to exclude other addresses at any time.
- Once the number of tokens collected by the LP fee has reached a certain threshold (beginning at 1000), it will add liquidity through a swap and liquify function. This threshold can be changed by the owner at any time. In addition, the owner can change the destination wallet that the LP tokens are sent to at any time.
- The swap and liquify function can also be directly called at any time as it is public.
- Some functions could be declared external instead of public.
Audit Findings Summary
- Users can stake various tokens into the Vault contract in order to earn rewards in the project's native token.
- User's deposited tokens will be forwarded to a Strategy contract to earn yield while the user earns rewards.
- The owner can initiate a change of the strategy in use by the vault at any time. The delay to upgrade to a new strategy after a new strategy is proposed is set by the team upon deployment. This delay cannot be changed after deployment.
- Some functions could be declared external instead of public.
- ReentrancyGuard is used in relevant locations to preent reentrancy attacks.
- Utilization of SafeMath (or similarily safe functions) to prevent overflow issues.
- The owner can also recover tokens erroneously sent to the contract. Staked tokens cannot be moved unless the strategy token is changed, in which case the owner has the ability to withdraw the previous strategy's tokens.
- Note that the Strategy contract, not reviewed by our team, will have significant control of deposited funds.
- No issues from external attackers were identified.
- Ensure trust in the team as the owner has the ability to change the Strategy contract, the burn fee, and the LP fee at any time.
- Date: August 24th, 2021.
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: Grim Finance Vault Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Context
- [Int] _msgSender
- [Int] _msgData
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Lib] SafeMath
- [Int] add
- [Int] sub
- [Int] sub
- [Int] mul
- [Int] div
- [Int] div
- [Int] mod
- [Int] mod
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Prv] _functionCallWithValue #
+ ERC20 (Context, IERC20)
- [Pub] #
- [Pub] name
- [Pub] symbol
- [Pub] decimals
- [Pub] totalSupply
- [Pub] balanceOf
- [Pub] transfer #
- [Pub] allowance
- [Pub] approve #
- [Pub] transferFrom #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
- [Int] _transfer #
- [Int] _mint #
- [Int] _burn #
- [Int] _approve #
- [Int] _setupDecimals #
- [Int] _beforeTokenTransfer #
+ [Lib] SafeERC20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Prv] _callOptionalReturn #
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ ReentrancyGuard
+ [Int] IStrategy
- [Ext] vault
- [Ext] want
- [Ext] beforeDeposit #
- [Ext] deposit #
- [Ext] withdraw #
- [Ext] balanceOf
- [Ext] harvest #
- [Ext] retireStrat #
- [Ext] panic #
- [Ext] pause #
- [Ext] unpause #
- [Ext] paused
+ GrimVault (ERC20, Ownable, ReentrancyGuard)
- [Pub] #
- modifiers: ERC20
- [Pub] want
- [Pub] balance
- [Pub] available
- [Pub] getPricePerFullShare
- [Ext] depositAll #
- [Pub] deposit #
- modifiers: nonReentrant
- [Pub] earn #
- [Ext] withdrawAll #
- [Pub] withdraw #
- [Pub] proposeStrat #
- modifiers: onlyOwner
- [Pub] upgradeStrat #
- modifiers: onlyOwner
- [Ext] inCaseTokensGetStuck #
- modifiers: onlyOwner
- [Pub] depositFor #
Details: GrimToken Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] SafeMath
- [Int] add
- [Int] sub
- [Int] sub
- [Int] mul
- [Int] div
- [Int] div
- [Int] mod
- [Int] mod
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IERC20Metadata (IERC20)
- [Ext] name
- [Ext] symbol
- [Ext] decimals
+ Context
- [Int] _msgSender
- [Int] _msgData
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Prv] _functionCallWithValue #
+ [Lib] SafeERC20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Prv] _callOptionalReturn #
+ Ownable (Context)
- [Pub] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ [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 #
+ Grim (Ownable)
- [Pub] #
- [Pub] setFee #
- modifiers: onlyOwner
- [Pub] changeLpReceiverAddress #
- modifiers: onlyOwner
- [Pub] exclude #
- modifiers: onlyOwner
- [Pub] setMaxLPCap #
- modifiers: onlyOwner
- [Prv] calculateBurnFee
- [Pub] setSwapAndLiquify #
- modifiers: onlyOwner
- [Prv] calculateLiquidityFee
- [Pub] name
- [Pub] symbol
- [Pub] decimals
- [Pub] totalSupply
- [Pub] balanceOf
- [Pub] allowance
- [Pub] approve #
- [Pub] transfer #
- [Pub] transferFrom #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
- [Int] _mint #
- [Int] _approve #
- [Prv] _transfer #
- [Ext] ($)
- [Pub] swapAndLiquify #
- [Prv] swapTokensForEth #
- [Prv] addLiquidity #
- [Pub] setRouter #
- modifiers: onlyOwner