nFraction Vesting - Smart Contract Audit Report

Summary

nFraction Audit Report nFraction Vesting is a set of new contracts that can be used to vest ERC20 tokens in a variety of different ways.

We audited the nFraction Vesting team's vesting contracts and TestERC20 contract at commit 958ea27e9fe66bc6c984b9a86cbc4fe5a82642e5 on the team's private GitHub.

Notes on the VestingSeed, VestingPrivate, and VestingStrategic Contracts:
  • The purpose of these contracts is to have vesting at an irregular amount based on the period in which the vesting is claimed.
  • The team must manually supply the tokens to the contract after deployment.
  • Every beneficiary has to claim their vested tokens themselves, and can claim once per day.
  • The amount from the last vesting period can be withdrawn at any time after the final period has passed, assuming there are still tokens in the contract.
  • The project team should ensure that the cumulativeAmountToVest array contains increasing values to ensure beneficiaries are being vested increasing amounts of tokens as time passes.
  • Only the owner can add beneficiaries and set the percentages of the tokens allocated to each beneficiary.
  • The start date is set when deploying and cannot be changed later.
  • The cumulativeAmountsToVest array is instantiated to an array of 276 uint256. Then in the constructor a length 270 array is saved into the 276 length array variable, which leaves 6 unused array indices.
  • The following functions could be declared external instead of public to save gas on each function call: setStartDate, addMultipleRecipients, claim, and hasClaim.
Notes on the VestingTreasury, VestingCommunityIncentives, VestingMarketing and VestingTeam Contracts:
  • These contracts are designed to vest tokens to the teams multi-sig wallet for the purpose of provisioning community incentive rewards, marketing expenses, and the team's token allocation.
  • The team must manually supply the tokens to the contract after deployment.
  • The team will have to manually claim tokens from the contract when available.
  • If vesting is not claimed every day, then one day of the vesting will be lost when claiming the next time due to a misscalculation in the claimTokens function when handling a claim for multiple days of vesting.
  • For example, after claiming on epoch 1, the epoch variable will be set to 2. Then when claiming on epoch 3 and not claiming on epoch 2, the beneficiary will only get one epoch's worth of tokens as the multiplier would be 3 - 2 = 1 and thus one only epoch's vesting will be issued.
  • Only an address that is both the owner and beneficiary can change the beneficiary address. We recommend removing the onlyOwner requirement and the onlyOwner requirement and allowing the benficiary alone to change the beneficiary address to reduce deployment costs.
  • The VestingTreasury, VestingMarketing, and VestingTeam contracts differ from VestingCommunity incentives in that the beneficiary is hard-coded, but can still be changed after deployment.
  • VestingTreasury has a claim delay of 90 days, with a total vesting period of 1620 days.
  • VestingCommunityIncentives has a claim delay of 1 day, with a total vesting period of 450 days.
  • VestingMarketing also has a claim delay of 90 days, with a total vesting period of 450 days.
  • VestingTeam again has a claim delay of 90 days, with a total vesting period of 540 days.
  • All of these contracts could declare getClaimAmount external instead of public to save gas on each function call.
Notes on the VestingAdvisors Contract:
  • The purpose of this contract is to vest tokens to multiple advisors based on the proportion of an advisors share of the total shares.
  • Only the team will be able to add beneficiaries and also set the amount of shares that they are entitled.
  • The owner can also add more shares to an existing beneficiary by calling addBeneficiary for a beneficiary that already exists.
  • When adding a beneficiary, we recommend updating the totalShares here rather than using calculateTotalShares in a separate function call. Otherwise the totalShares variable may be inacurate. This function would no longer be needed.
  • The for loop in releaseAllTokens contains external calls. The team should ensure not to add too many beneficiaries, so the for loop does not fail during execution due to hitting the block gas limit.
  • When any advisor claims, vested tokens are distributed to all advisors.
Notes on the TestERC20 Contract:
  • The total supply of the token is 10,800,000.
  • The owner is minted the total supply on deployment.
  • There are no ownership functions present.
  • There are no fees on transfer of the token.
  • The supply variable can be declared constant to save gas on deployment and each reference.
  • Solidity 0.8.X is used across all contracts which has built in overflow protections. This makes the implementation of SafeMath redundant. Therefore, SafeMath can be safely removed to reduce deployment and transaction costs.

  • Audit Findings Summary
    • No external security threats were identified.
    • Some logical issues are present.
    • Ensure trust in the team prior to investing as they have notable control within the ecosystem.
    • Date: September 29th, 2021

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

    Details: TestERC20 Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
      + [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 #
    
     +  ERC20Burnable (Context, ERC20)
        - [Pub] burn #
        - [Pub] burnFrom #
    
     +  ERC20PresetFixedSupply (ERC20Burnable)
        - [Pub]  #
           - modifiers: ERC20
    
     +  TestERC20 (ERC20PresetFixedSupply)
        - [Pub]  #
           - modifiers: ERC20PresetFixedSupply
    	   
    

    Details: VestingAdvisors Contract


    BEP20 Token Graph

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
     
    +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingAdvisors (Ownable)
        - [Pub]  #
        - [Ext] addBeneficiary #
           - modifiers: onlyOwner
        - [Ext] calculateTotalShares #
           - modifiers: onlyOwner
        - [Pub] releaseAllTokens #
           - modifiers: onlyBeneficiaries
        - [Pub] releasableAmount
        - [Pub] calculateShares
        - [Pub] vestedAmount
        - [Prv] release #
    

    Details: VestingCommunityIncentives Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
       +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingCommunityIncentives (Ownable)
        - [Pub]  #
        - [Pub] getCurrentEpoch
        - [Ext] claimTokens #
        - [Pub] getClaimAmount
        - [Pub] claimDust #
        - [Ext] setBeneficiary #
           - modifiers: onlyOwner
    
    	   
    


    Details: VestingMarketing Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
      +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingMarketing (Ownable)
        - [Pub]  #
        - [Pub] getCurrentEpoch
        - [Ext] claimTokens #
        - [Pub] getClaimAmount
        - [Pub] claimDust #
        - [Ext] setBeneficiary #
           - modifiers: onlyOwner
    
    	   
    


    Details: VestingPrivate Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
       +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingPrivate (Ownable)
        - [Pub]  #
        - [Pub] percDiv
        - [Pub] setStartDate #
           - modifiers: onlyOwner
        - [Pub] addRecipient #
           - modifiers: onlyOwner,onlyValidPercentages
        - [Pub] addMultipleRecipients #
           - modifiers: onlyOwner
        - [Pub] claim #
        - [Pub] hasClaim
        - [Int] calculateAmounts
    
    	   
    


    Details: VestingSeed Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
       +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingSeed (Ownable)
        - [Pub]  #
        - [Pub] percDiv
        - [Pub] setStartDate #
           - modifiers: onlyOwner
        - [Pub] addRecipient #
           - modifiers: onlyOwner,onlyValidPercentages
        - [Pub] addMultipleRecipients #
           - modifiers: onlyOwner
        - [Pub] claim #
        - [Pub] hasClaim
        - [Int] calculateAmounts
    
    	   
    


    Details: VestingStrategic Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
       +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingStrategic (Ownable)
        - [Pub]  #
        - [Pub] percDiv
        - [Pub] setStartDate #
           - modifiers: onlyOwner
        - [Pub] addRecipient #
           - modifiers: onlyOwner,onlyValidPercentages
        - [Pub] addMultipleRecipients #
           - modifiers: onlyOwner
        - [Pub] claim #
        - [Pub] hasClaim
        - [Int] calculateAmounts
    
    	   
    


    Details: VestingTeam Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
      +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingTeam (Ownable)
        - [Pub]  #
        - [Pub] getCurrentEpoch
        - [Ext] claimTokens #
        - [Pub] getClaimAmount
        - [Pub] claimDust #
        - [Ext] setBeneficiary #
           - modifiers: onlyOwner
    
    	   
    


    Details: VestingTreasury Contract


    TestERC20-graph.png

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
       +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Prv] _setOwner #
    
     + [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 #
    
     + [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
    
     +  VestingTreasury (Ownable)
        - [Pub]  #
        - [Pub] getCurrentEpoch
        - [Pub] claimFirstAmount #
        - [Ext] claimTokens #
        - [Pub] getClaimAmount
        - [Pub] claimDust #
        - [Ext] setBeneficiary #
           - modifiers: onlyOwner