Mizar Staking

Smart Contract Audit Report

Audit Summary

Mizar is building a new platform where users can stake tokens and earn rewards.

For this audit, we reviewed the project team's Staking contract at commit 42320856f14d63d3b8e1d65d8eb0c155f55ee321 and Farming contract at commit a81704e2a4b0ec2095bceaf31e2b534f8bc432ef on the team's private GitHub repository.

Audit Findings

All findings have been resolved.
Date: February 22nd, 2023.
Updated: February 24th, 2023 to reflect updates made to the Staking contract that resolves all Findings.

Finding #1 - Staking - High (Resolved)

Description: If a user requests a withdrawal and makes an additional deposit into the contract before withdrawing the requested tokens, the requested withdrawal amount will no longer be available for the user to withdraw.
Risk/Impact: The user's requested withdrawal amount will be forfeited and permanently locked in the contract.
Recommendation: The team should modify the deposit() function to either disallow users from depositing if they have any pending withdrawals or remove the following line from the deposit() function:
user.withdrawableAmount = 0;
Resolution: The team has removed the above line from the deposit() function.

Finding #2 - Staking - High (Resolved)

Description: If the user requests multiple withdrawals in a row via the requestWithdrawal() function, all of their previous withdrawal amounts will be forfeited.
function requestWithdrawal(uint256 _amount) public override whenNotPaused {

    UserInfo storage user = userInfo[msg.sender];
    require(user.stackedAmount <= _amount, "You cannot withdraw more than what you stacked");
    user.stackedAmount -= _amount;    
    user.withdrawableAmount = _amount;
    user.requestedWithdrawalTimestamp = block.timestamp;

}
Risk/Impact: The user's previously requested withdrawal amounts will be forfeited and permanently locked in the contract.
Recommendation: The team should either modify the requestWithdrawal() function to disallow users from making another request if they currently have any pending withdrawals or modify the requestWithdrawal() function to always increase user.withdrawableAmount by _amount.
Resolution: The team has modifed the requestWithdrawal() function to always increase user.withdrawableAmount by _amount.

Finding #3 - Staking - Low (Resolved)

Description: The following require statement in the requestWithdrawal() function allows users to input a withdraw amount for more tokens than they've staked, however the function will only execute if the user inputs their full staked amount.
require(user.stackedAmount <= _amount, "You cannot withdraw more than what you stacked");
Risk/Impact: If the user inputs a withdrawal amount for more tokens than they've staked, the above require statement will pass, although the transaction will later revert due to an arithmetic underflow.
Recommendation: The above require statement should ensure that the user's total staked amount is greater than or equal to their requested withdrawal amount, as shown below:
require(user.stackedAmount >= _amount, "You cannot withdraw more than what you staked");
Resolution: The team has implemented the above recommendation.

Contracts Overview

  • The contracts can be upgraded by the team at any time.
  • As the contracts are implemented with Solidity v0.8.0, they are safe from any possible overflows/underflows.
Staking Contract:
  • Any user can initiate a deposit by specifying a number of MZR tokens that will be transferred to the contract.
  • The user must grant the contract a sufficient allowance in order for the deposit to successfully occur.
  • Users that have deposited tokens can request a withdrawal at any time by specifying a total number of tokens to withdraw.
  • Users that have requested a withdrawal can finalize their withdrawal after the withdraw lock period set by the team has passed by specifying a number of tokens to withdraw up to the requested amount.
  • The withdrawn tokens are transferred from the contract to the user's wallet address.
  • The team must exercise caution when assigning the deposit token to avoid using a fee-on-transfer token. If a fee-on-transfer token is used, the contract must be excluded from the tokens fee mechanism.
  • The Admin can set the withdraw lock period to any value at any time.
  • The Admin can pause/unpause the contract at any time which disables all deposits and withdrawals.
Farming Contract:
  • Any user can initiate a deposit into a staking pool by specifying the pool ID and a token amount that will be transferred to the contract.
  • The user must grant the contract a sufficient allowance in order for the deposit to successfully occur.
  • Users can initiate deposits on behalf of other users.
  • Users that have staked tokens can request a withdrawal from a specified pool at any time.
  • If the caller has been set as an "Investor" by the team, the contract's Investor unlock time must have passed in order to successfully request a withdrawal.
  • Users can finalize a withdrawal after at least 7 days have passed since initiating a request by specifying a number of tokens to withdraw and the pool ID.
  • Each withdrawal request expires after 14 days.
  • Users can claim any pending rewards once the contract's claim time (set by the team) has passed.
  • Users can initiate an "exit" transaction which will withdraw any staked tokens and claim any pending rewards.
  • Users can trigger an emergency withdrawal which will withdraw their staked from the contract and forfeit any rewards.
  • The team must ensure a sufficient number of reward tokens are in the contract to support claims.
  • The Manager role can add a new staking pool to the contract at any time.
  • The team should not assign the staking token as the reward token when adding a pool as rewards could be funded with users' staked funds.
  • The team should not add the same token twice for staking.
  • The Manager role can update the allocation point, start time, and end time of an existing pool at any time.
  • The Manager role can set the rewards per block to any value at any time.
  • The Manager role can add/remove any address as an Investor address at any time.
  • The Admin can withdraw any number of reward tokens from the contract at any time.
  • The Admin can pause/unpause the contract at any time which disables all deposits, withdrawals, and claims.

Audit Results

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control
  • The contracts can be upgraded by the team at any time.
  • The team can pause all withdrawing and claiming at any time.
WARNING
Compiler Issues N/A PASS
Delegate Call to Untrusted Contract N/A PASS
Dependence on Predictable Variables N/A PASS
Ether/Token Theft N/A PASS
Flash Loans N/A PASS
Front Running N/A PASS
Improper Events N/A PASS
Improper Authorization Scheme N/A PASS
Integer Over/Underflow N/A PASS
Logical Issues N/A PASS
Oracle Issues N/A PASS
Outdated Compiler Version N/A PASS
Race Conditions N/A PASS
Reentrancy N/A PASS
Signature Issues N/A PASS
Sybil Attack N/A PASS
Unbounded Loops N/A PASS
Unused Code N/A PASS
Overall Contract Safety   PASS

Staking Contract

Smart Contract Audit - Inheritance

Smart Contract Audit - Graph


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] IERC20Upgradeable 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Int] IERC20PermitUpgradeable 
    - [Ext] permit #
    - [Ext] nonces
    - [Ext] DOMAIN_SEPARATOR

 + [Lib] AddressUpgradeable 
    - [Int] isContract
    - [Int] sendValue #
    - [Int] functionCall #
    - [Int] functionCall #
    - [Int] functionCallWithValue #
    - [Int] functionCallWithValue #
    - [Int] functionStaticCall
    - [Int] functionStaticCall
    - [Int] verifyCallResultFromTarget
    - [Int] verifyCallResult
    - [Prv] _revert

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

 + [Int] IAccessControlUpgradeable 
    - [Ext] hasRole
    - [Ext] getRoleAdmin
    - [Ext] grantRole #
    - [Ext] revokeRole #
    - [Ext] renounceRole #

 +  Initializable 
    - [Int] _disableInitializers #
    - [Int] _getInitializedVersion
    - [Int] _isInitializing

 +  ContextUpgradeable (Initializable)
    - [Int] __Context_init #
       - modifiers: onlyInitializing
    - [Int] __Context_init_unchained #
       - modifiers: onlyInitializing
    - [Int] _msgSender
    - [Int] _msgData

 + [Lib] MathUpgradeable 
    - [Int] max
    - [Int] min
    - [Int] average
    - [Int] ceilDiv
    - [Int] mulDiv
    - [Int] mulDiv
    - [Int] sqrt
    - [Int] sqrt
    - [Int] log2
    - [Int] log2
    - [Int] log10
    - [Int] log10
    - [Int] log256
    - [Int] log256

 + [Lib] SignedMathUpgradeable 
    - [Int] max
    - [Int] min
    - [Int] average
    - [Int] abs

 + [Lib] StringsUpgradeable 
    - [Int] toString
    - [Int] toString
    - [Int] toHexString
    - [Int] toHexString
    - [Int] toHexString
    - [Int] equal

 + [Int] IERC165Upgradeable 
    - [Ext] supportsInterface

 +  ERC165Upgradeable (Initializable, IERC165Upgradeable)
    - [Int] __ERC165_init #
       - modifiers: onlyInitializing
    - [Int] __ERC165_init_unchained #
       - modifiers: onlyInitializing
    - [Pub] supportsInterface

 +  AccessControlUpgradeable (Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable)
    - [Int] __AccessControl_init #
       - modifiers: onlyInitializing
    - [Int] __AccessControl_init_unchained #
       - modifiers: onlyInitializing
    - [Pub] supportsInterface
    - [Pub] hasRole
    - [Int] _checkRole
    - [Int] _checkRole
    - [Pub] getRoleAdmin
    - [Pub] grantRole #
       - modifiers: onlyRole
    - [Pub] revokeRole #
       - modifiers: onlyRole
    - [Pub] renounceRole #
    - [Int] _setupRole #
    - [Int] _setRoleAdmin #
    - [Int] _grantRole #
    - [Int] _revokeRole #

 +  PausableUpgradeable (Initializable, ContextUpgradeable)
    - [Int] __Pausable_init #
       - modifiers: onlyInitializing
    - [Int] __Pausable_init_unchained #
       - modifiers: onlyInitializing
    - [Pub] paused
    - [Int] _requireNotPaused
    - [Int] _requirePaused
    - [Int] _pause #
       - modifiers: whenNotPaused
    - [Int] _unpause #
       - modifiers: whenPaused

 + [Int] IStaking 
    - [Ext] deposit #
    - [Ext] withdraw #
    - [Ext] requestWithdrawal #
    - [Ext] getPool
    - [Ext] getUserInfo

 +  Staking (Initializable, AccessControlUpgradeable, PausableUpgradeable, IStaking)
    - [Pub] initialize #
       - modifiers: initializer
    - [Pub] version
    - [Pub] pause #
       - modifiers: onlyAdmin
    - [Pub] unpause #
       - modifiers: onlyAdmin
    - [Pub] setwithdrawalLockPeriod #
       - modifiers: onlyAdmin
    - [Pub] deposit #
       - modifiers: whenNotPaused
    - [Pub] requestWithdrawal #
       - modifiers: whenNotPaused
    - [Pub] withdraw #
       - modifiers: whenNotPaused
    - [Ext] getPool
    - [Ext] getUserInfo

Farming Contract

Smart Contract Audit - Inheritance

Smart Contract Audit - Graph


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] IERC20Upgradeable 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Int] IERC20PermitUpgradeable 
    - [Ext] permit #
    - [Ext] nonces
    - [Ext] DOMAIN_SEPARATOR

 + [Lib] AddressUpgradeable 
    - [Int] isContract
    - [Int] sendValue #
    - [Int] functionCall #
    - [Int] functionCall #
    - [Int] functionCallWithValue #
    - [Int] functionCallWithValue #
    - [Int] functionStaticCall
    - [Int] functionStaticCall
    - [Int] verifyCallResultFromTarget
    - [Int] verifyCallResult
    - [Prv] _revert

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

 + [Int] IAccessControlUpgradeable 
    - [Ext] hasRole
    - [Ext] getRoleAdmin
    - [Ext] grantRole #
    - [Ext] revokeRole #
    - [Ext] renounceRole #

 +  Initializable 
    - [Int] _disableInitializers #
    - [Int] _getInitializedVersion
    - [Int] _isInitializing

 +  ContextUpgradeable (Initializable)
    - [Int] __Context_init #
       - modifiers: onlyInitializing
    - [Int] __Context_init_unchained #
       - modifiers: onlyInitializing
    - [Int] _msgSender
    - [Int] _msgData

 + [Lib] MathUpgradeable 
    - [Int] max
    - [Int] min
    - [Int] average
    - [Int] ceilDiv
    - [Int] mulDiv
    - [Int] mulDiv
    - [Int] sqrt
    - [Int] sqrt
    - [Int] log2
    - [Int] log2
    - [Int] log10
    - [Int] log10
    - [Int] log256
    - [Int] log256

 + [Lib] SignedMathUpgradeable 
    - [Int] max
    - [Int] min
    - [Int] average
    - [Int] abs

 + [Lib] StringsUpgradeable 
    - [Int] toString
    - [Int] toString
    - [Int] toHexString
    - [Int] toHexString
    - [Int] toHexString
    - [Int] equal

 + [Int] IERC165Upgradeable 
    - [Ext] supportsInterface

 +  ERC165Upgradeable (Initializable, IERC165Upgradeable)
    - [Int] __ERC165_init #
       - modifiers: onlyInitializing
    - [Int] __ERC165_init_unchained #
       - modifiers: onlyInitializing
    - [Pub] supportsInterface

 +  AccessControlUpgradeable (Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable)
    - [Int] __AccessControl_init #
       - modifiers: onlyInitializing
    - [Int] __AccessControl_init_unchained #
       - modifiers: onlyInitializing
    - [Pub] supportsInterface
    - [Pub] hasRole
    - [Int] _checkRole
    - [Int] _checkRole
    - [Pub] getRoleAdmin
    - [Pub] grantRole #
       - modifiers: onlyRole
    - [Pub] revokeRole #
       - modifiers: onlyRole
    - [Pub] renounceRole #
    - [Int] _setupRole #
    - [Int] _setRoleAdmin #
    - [Int] _grantRole #
    - [Int] _revokeRole #

 +  PausableUpgradeable (Initializable, ContextUpgradeable)
    - [Int] __Pausable_init #
       - modifiers: onlyInitializing
    - [Int] __Pausable_init_unchained #
       - modifiers: onlyInitializing
    - [Pub] paused
    - [Int] _requireNotPaused
    - [Int] _requirePaused
    - [Int] _pause #
       - modifiers: whenNotPaused
    - [Int] _unpause #
       - modifiers: whenPaused

 + [Int] IFarming 
    - [Ext] depositTo #
    - [Ext] deposit #
    - [Ext] withdraw #
    - [Ext] requestWithdrawal #
    - [Ext] claimReward #
    - [Ext] exit #
    - [Ext] emergencyWithdraw #
    - [Ext] getContractData
    - [Ext] getPoolLength
    - [Ext] getPool
    - [Ext] getUserInfo

 +  Farming (Initializable, AccessControlUpgradeable, PausableUpgradeable, IFarming)
    - [Pub] initialize #
       - modifiers: initializer
    - [Pub] version
    - [Pub] setTokenPerBlock #
       - modifiers: onlyManager
    - [Pub] setInvestorsUnlockTimestamp #
       - modifiers: onlyManager
    - [Pub] setNoRewardClaimsUntil #
       - modifiers: onlyManager
    - [Pub] addPool #
       - modifiers: onlyManager
    - [Pub] addInvestorAddress #
       - modifiers: onlyManager
    - [Pub] removeInvestorAddress #
       - modifiers: onlyManager
    - [Pub] updatePoolData #
       - modifiers: onlyManager
    - [Pub] withdrawRewardTokens #
       - modifiers: onlyAdmin
    - [Pub] pause #
       - modifiers: onlyAdmin
    - [Pub] unpause #
       - modifiers: onlyAdmin
    - [Pub] massUpdatePools #
       - modifiers: whenNotPaused
    - [Pub] updatePool #
       - modifiers: whenNotPaused
    - [Pub] deposit #
       - modifiers: whenNotPaused
    - [Pub] depositTo #
       - modifiers: whenNotPaused
    - [Pub] requestWithdrawal #
       - modifiers: whenNotPaused
    - [Pub] withdraw #
       - modifiers: whenNotPaused
    - [Pub] claimReward #
       - modifiers: whenNotPaused
    - [Pub] exit #
       - modifiers: whenNotPaused
    - [Pub] emergencyWithdraw #
       - modifiers: whenNotPaused
    - [Ext] getContractData
    - [Ext] getPoolLength
    - [Ext] getPool
    - [Pub] getMultiplier
    - [Ext] getUserInfo
    - [Int] _safeTokenTransfer #
    - [Int] _massUpdatePools #
    - [Int] _updatePool #

About SourceHat

SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

Contact us today to get a free quote for a smart contract audit of your project!

What is a SourceHat Audit?

Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

How Do I Interpret the Findings?

Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

  • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
  • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
  • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
  • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.