
nFraction Vesting - Smart Contract Audit Report
Summary
nFraction Vesting is a set of new contracts that can be used to vest ERC20 tokens in a variety of different ways.
Notes on the VestingSeed, VestingPrivate, and VestingStrategic Contracts:Notes on the VestingTreasury, VestingCommunityIncentives, VestingMarketing and VestingTeam 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 VestingAdvisors Contract:
- 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 TestERC20 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.
- 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 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: TestERC20 Contract


($) = 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


($) = 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


($) = 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


($) = 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


($) = 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


($) = 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


($) = 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


($) = 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


($) = 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