Ember Token Crowdsale - Smart Contract Audit Report

Summary

Ember ICO Forgotten Chain is building a new MMORPG. The game's native token ("Ember") is claimed to be used for purchasing items in-game, swap for NFTs, repair damaged items, purchase mounts, pets and more. Ember token will be deployed to the Binance Smart Chain. For the purpose of this audit, we reviewed the Ember Token and the Token's Crowdsale and Vesting Contracts.

We reviewed the team's Token, Vesting, and Crowdsale contracts at commit a497af62743155a882a2f87fb9115171e732bc19 on GitHub.

Notes on the Ember Token Contract:
  • The token is not currently deployed to the BSC Mainnet.
  • The Ember token's total supply is determined upon deployment and the tokens are granted to the owner.
  • The Ember Token Crowdsale contract indicates that the total supply should be 5 Billion tokens, so the deployer must be careful to ensure this amount is accurately set.
  • No mint or burn functions are accessible after deployment; though the circulating supply can be reduced by sending tokens to the 0x..dead address, if desired.
  • Although the contract inherits the ownable contract, there are no ownership restricted functions present within the token contract.
  • The contract complies with the BEP20 Token Standard.
  • The contract utilizes SafeMath libraries to prevent overflows.

  • Notes on the Ember Token Crowdsale Contract:
  • This contract allows users to contribute BNB to purchase Ember Tokens that will vest to them at a later date. During this sale, up to 10% of the total token supply can be purchased across all participants.
  • Ember tokens are denominated in BNB, and the exchange rate will be set by the team upon deployment.
  • If the Crowdsale is unpaused, participants can send BNB to the ‘Ember Crowdsale Contract’ which will automatically “buyTokens” on their behalf.
  • When a participant purchases tokens during the crowdsale, they will become beneficiaries of a separate vesting contract that is created at the end of the sale.
  • There are three possible stages of the sale: "PreICO", "ICO", and "PostICO". The owner has the ability to modify the ICO stage (at any time if they desire), and the stage determines the features that are available for crowd sale participants.
  • During the PreICO stage, up to 3% of the total token supply can be purchased by various participants. The stage will automatically increment to the ‘ICO’ stage during the transaction that causes the total tokens purchased to surpass this 3% total token supply. When this occurs, the contract will be “paused”, and must be unpaused by the owner.
  • During the ICO stage, up to 10% of the total token supply can be purchased by various participants. The stage will automatically increment to the ‘PostICO’ stage during the transaction that causes the total tokens purchased to surpass this 10% total token supply. When this occurs, the contract will be “paused”.
  • During the PostICO stage, users cannot buy tokens with this contract, as the crowd sale has ended.
  • When the crowd sale is in the PostICO stage, the owner is able to “Distribute Tokens” from the contract. This will immediately transfer 500,000,000 Ember tokens (10% of the total supply) to the team’s “Liquidity and Marketing Fund”, and then generates the token vesting contracts for various funds set up by the team.
    1. 'FoundationEscrow1' will be vested 50,000,000 tokens (1% total supply) over a 300 day period.
    2. 'FoundationEscrow2' will be vested 450,000,000 tokens (9% total supply) over a 540 day period.
    3. The "Token Vesting Pool" contract is automatically created for those that participated in the crowd sale. The token vesting pool contract will create an individual vesting contract for each beneficiary who purchased tokens during the sale. These tokens will vest to their respective beneficiaries over a period of 600 days.
    4. 'GameEscrow' will be vested the remaining unsold tokens (this will range from 70% to 80% of the total supply) over a 2555 day period.
  • This “Distribute Tokens” function can only be called once, and it must be called by the owner.
  • The contract will be compiled with Solidity version 0.8.0 (or above) which will prevent any possible overflow issues.

  • Ownership Controls:
  • The owner has the ability to pause and unpause the Crowdsale contract at any time. When paused, participants can not buy tokens through the Crowdsale contract.
  • The owner has the ability to manually update the stage of the ICO to any of the 3 stages (PreICO, ICO, and PostICO) at any time.
  • The owner has the ability to update the rate of tokens that are issued per BNB at any time.
  • The owner has the ability to manually add a beneficiary to the vesting pool.
  • The owner has the ability to call the distributeTokens function only one time.
  • The owner has the ability to send the BNB raised during the sale from the contract address to a wallet that is specified upon deployment.

  • Audit Findings Summary
    • No issues from external attackers were identified.
    • As with any presale, ensure trust in the team prior to investing.
    • Further, ensure trust in the team as they have substantial control in the ecosystem.
    • Date: October 2nd, 2021
    • Updated: October 6th, 2021 to correct a critical issue resulting in multiplication of presale participant funds.

    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
    Logical IssuesN/APASS
    Integer Over/UnderflowN/APASS
    Multiple SendsN/APASS
    SuicideN/APASS
    State Change External CallsN/APASS
    Unchecked RetvalN/APASS
    User Supplied AssertionN/APASS
    Critical Solidity CompilerN/APASS
    Overall Contract Safety PASS


    EmbToken Contract - Details

    Contract Graph

    Multi-file Token

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
     
     + [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 #
    
     + [Int] IERC20Metadata (IERC20)
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  ERC20 (Context, IERC20, IERC20Metadata)
        - [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] _beforeTokenTransfer #
        - [Int] _afterTokenTransfer #
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     +  EmbToken (ERC20, Ownable)
        - [Pub]  #
           - modifiers: ERC20


    EmbTokenCrowdsale Contract - Details

    Contract Graph

    Multi-file Token

    
     ($) = 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 #
    
     + [Int] IERC20Metadata (IERC20)
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  ERC20 (Context, IERC20, IERC20Metadata)
        - [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] _beforeTokenTransfer #
        - [Int] _afterTokenTransfer #
    
     + [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
    
     + [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 #
    
     +  ReentrancyGuard 
        - [Pub]  #
    
     +  Pausable (Context)
        - [Pub]  #
        - [Pub] paused
        - [Int] _pause #
           - modifiers: whenNotPaused
        - [Int] _unpause #
           - modifiers: whenPaused
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     +  Claimable (Ownable)
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
    
     +  TokenVesting (Ownable)
        - [Pub]  #
        - [Pub] release #
        - [Pub] revoke #
           - modifiers: onlyOwner
        - [Pub] releasableAmount
        - [Pub] vestedAmount
    
     +  TokenVestingPool (Claimable)
        - [Pub]  #
           - modifiers: validAddress
        - [Pub] addBeneficiary #
           - modifiers: onlyOwner,validAddress
        - [Pub] getDistributionContracts
           - modifiers: validAddress
        - [Int] beneficiaryExists
    
     +  EmbTokenCrowdsale (Ownable, Pausable, ReentrancyGuard)
        - [Pub]  #
        - [Int] _getTokenAmount
        - [Ext]  ($)
        - [Ext]  ($)
        - [Pub] buyTokens ($)
           - modifiers: whenNotPaused,nonReentrant
        - [Pub] pause #
           - modifiers: onlyOwner
        - [Pub] unpause #
           - modifiers: onlyOwner
        - [Pub] getUserContribution
        - [Pub] incrementCrowdsaleStage #
           - modifiers: onlyOwner,nonReentrant
        - [Pub] updateCrowdsaleRate #
           - modifiers: onlyOwner,nonReentrant
        - [Pub] distributeTokens #
           - modifiers: onlyOwner,nonReentrant
        - [Pub] getFoundationVestedContract1
        - [Pub] getFoundationVestedContract2
        - [Pub] getTokenSaleVestedContract
        - [Pub] getGameVestedContract
        - [Pub] getFoundationVestedFunds1
        - [Pub] getFoundationVestedFunds2
        - [Pub] getTokenSaleVestedFunds
        - [Pub] getGameVestedFunds
        - [Pub] releaseFoundationVestedFunds1 #
        - [Pub] releaseFoundationVestedFunds2 #
        - [Pub] releaseTokenSaleVestedFunds #
        - [Pub] releaseGameVestedFunds #
        - [Pub] _forwardFunds #
           - modifiers: onlyOwner