Coinracer Staking - Smart Contract Audit Report
Coinracer is building a new staking contract for users to deposit $CRACE and earn yield.
For this audit, we reviewed the project team's Staking contract at commit 5a711d8c019598669c4058d5ebf1f082b1c82fb7 on the team's GitHub repository.
We previously reviewed the project team's token, crowdsale, and vesting contracts here.
Notes on the Contract:
- This contract allows anyone to stake $CRACE tokens in order to earn rewards in the form of more $CRACE tokens.
- Each staking pool's APY and lock time values are set by the owner to calculate the rewards.
- Users will receive a reward amount based on the amount staked, time in the pool, and APY of the staking pool.
- Users can only claim the rewards when the staking pool lock time has elapsed.
- Alternatively, users can choose to immediately re-invest their rewards back into the staking pool at any time.
- Rewards are not withdrawn when the deposit amount is withdrawn.
- Users can withdraw their staked tokens from a pool given that the pool is not locked.
- The user can pay a withdrawal fee to trigger an emergency withdrawal, which will transfer all the user's deposited $CRACE tokens to their wallet address, forfeiting any owed rewards.
- $CRACE tokens must be supplied to the contract to be distributed as rewards.
- As the contract is implemented with Solidity v0.8.X, it is protected from any underflow/overflow attacks.
- The owner can add a staking pool at any time.
- The owner can change the lock time, APY, and withdraw fee of any staking pool at any time.
- The owner can transfer a specified amount of $CRACE token from the owner address to the contract address at any time.
- The owner can withdraw the entire balance of the contract address and send it to a specified address at any time.
Audit Findings Summary:
- It is possible for users to claim another user's tokens as rewards.
- Ensure trust in the team as they have notable control in the ecosystem.
- Date: December 17th, 2021.
- Updated: December 20th, 2021 to address an issue with reward functions.
- The team addressed an issue within the staking contract's reward functions in which if there was not enough $CRACE tokens in the contract for rewards, rewards would be funded with users' staked funds.
|Arbitrary Storage Write||N/A||PASS|
|Centralization of Control||The owner can withdraw the entire balance of $CRACE tokens from the contract at any time.||WARNING|
|Delegate Call to Untrusted Contract||N/A||PASS|
|Dependence on Predictable Variables||N/A||PASS|
|State Change External Calls||N/A||PASS|
|User Supplied Assertion||N/A||PASS|
|Critical Solidity Compiler||N/A||PASS|
|Overall Contract Safety||PASS|
($) = payable function # = non-constant function Int = Internal Ext = External Pub = Public + [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 # + Context - [Int] _msgSender - [Int] _msgData + Ownable (Context) - [Pub]
# - [Pub] owner - [Pub] renounceOwnership # - modifiers: onlyOwner - [Pub] transferOwnership # - modifiers: onlyOwner - [Int] _transferOwnership # + Staking (Ownable) - [Pub] # - [Ext] poolLength - [Ext] fund # - modifiers: onlyOwner - [Ext] add # - modifiers: onlyOwner - [Ext] updatePool # - modifiers: onlyOwner - [Ext] deposited - [Ext] pending - [Ext] claimReward # - [Ext] compound # - [Ext] deposit # - [Ext] withdraw # - [Ext] emergencyWithdraw # - [Ext] withdrawFunds # - modifiers: onlyOwner