Fantom Frens Staking - Smart Contract Audit Report

Audit Summary

BrainiacBondingAudit Report Fantom Frens is building two new staking contracts for users to deposit tokens and earn yield.

For this audit, we reviewed the FFSStaking and FPADStaking contracts provided to us by the project team.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: February 17th, 2022.
Updated: February 18th, 2022 to address changes made by the team.

Finding #1 - FPADStaking - Medium (Resolved)

Description: There is an issue within the contract's reward functions in which if there are not enough reward tokens in the contract for rewards, rewards would be funded with users' staked funds.
Risk/Impact: If there are not enough reward tokens in the contract for rewards, rewards would be funded with users' staked funds.
Recommendation: The project team should differentiate between staked and reward funds in the contract so users are always able to withdraw their original staked amount.
Resolution: The deposited funds are now stored in the contract, while the reward funds are still transferred from the vault address. Since rewards are transferred only from the vault address, the users deposited tokens will not be used to fund rewards.

Finding #2 - FFSStaking - Medium (Resolved)

Description: The _claim_out(), _claim_referral(), and _withdraw_principal() functions can only be called by the Manager address or the owner but transfer their calculated funds to the caller of the function instead of the corresponding user address.
Risk/Impact: The Manager address or owner can withdraw all staked and reward tokens from any user at any time.
Recommendation: We recommend changing the address being transferred the funds in the functions from the caller to the user address parameter.
Resolution: The user address used to calculated the rewards is now transferred their corresponding funds instead of the caller of the function.

Finding #3 - FPADStaking - Informational (Resolved)

Description: The updateManager() function and manager address variable are not used and should be removed.
Recommendation: We recommend removing this extra logic to reduce contract size and in turn enjoy additional gas savings on deployment.
Resolution: The team has removed the extra logic from the code.

Finding #4 - FFSStaking, FPADStaking - Informational (Resolved)

Description: The deposit() function's conditional check on the value of the caller's userAddress value is a contradiction. The _refPayout() function is rendered as dead code.
Recommendation: We recommend moving the conditional check and _refPayout() function call above the _deposit() function call in order to have the referral functionality work effectively. Example provided below.
if(refAddress != address(0) && users[msg.sender].userAddress == address(0)){
            _refPayout(_userAddress, refAddress, _amount);
}

_deposit(_userAddress, _amount);
Resolution: The team has changed the ordering of the function calls so that the _refPayout() function is no longer dead code.

Finding #5 - FFSStaking, FPADStaking - Informational (Resolved)

Description: The total_withdraw variable is returned in the InfoTotals() function but is never used and will always return 0.
Recommendation: We recommend replacing the total_withdraw variable with the total_compounded variable as it is utilized in the contracts.
Resolution: The team has updated the code so that the total_withdraw variable is incremented every time rewards are transferred to users.

Finding #6 - FFSStaking, FPADStaking - Informational (Resolved)

Description: The following functions are declared public, but are never called internally.
- FFSStaking: updatePayoutRate, updateRefDepth, updateRefBonus, updateInitialDeposit, updateFFSToken, updateFPADToken, updateVaultAddress, updateManager, updateMinStakeTime, compound, isNetPositive
- FPADStaking: updatePayoutRate, updateRefDepth, updateRefBonus, updateInitialDeposit, updateFPADToken, updateVaultAddress, updateManager, updateMinStakeTime, compound, isNetPositive
Recommendation: We recommend declaring these functions external for additional gas savings on each call.
Resolution: The team has declared these functions external.

Finding #7 - FFSStaking, FPADStaking - Informational (Resolved)

Description: Several state variables can never be modified, but are not declared constant.
- FFSStaking: minimumAmount, total_withdraw
- FPADStaking: minimumAmount, total_withdraw
Recommendation: We recommend declaring these state variables constant for additional gas savings on each call.
Resolution: The state variables can be changed; therefore, do not need to be declared constant.

Contracts Overview

  • As the contracts are implemented using Solidity v0.8.x, they are safe from any possible overflow/underflow attacks.
  • The team must exercise caution when setting the staking/reward tokens and must avoid using any fee-on-transfer or ERC777-compliant tokens; if a fee-on-transfer token is used as the staking token then this contract should be excluded from the staking/reward token's fee mechanism.
FFSStaking Contract:
  • This contract allows users to stake a designated token into the contract in order to earn rewards in the form of a reward token determined by the team until the total reward supply has been depleted.
  • On deposits, users can specify a referral address to be gifted referral rewards. Any pending payout amounts for the user are also automatically compounded with the users deposited staked amount.
  • The referral address must have at least the minimum referral deposit amount at the time of the referral to be eligible for referral rewards.
  • The referral reward token amount is calculated based on the referral bonus amount and conversion rate and can only be claimed for the user addresses by the Manager address or owner. The Manager or owner provides the conversion rate at the time of the function call.
  • Referral bonus amounts are calculated based on the referral deposit amount and the contracts current referral bonus rate.
  • Users can amass unlimited referrals from deposits unless the referral limit is set.
  • Users can compound their pending payout amount at any time.

  • Users reward token amounts can only be claimed and transferred to them by the Manager address and owner when the minimum stake time has elapsed.
  • The reward token amount is calculated based on the users' total payout amount and the conversion rate. The Manager or owner provides the conversion rate at the time of the function call.
  • Users payout amounts are based on the users' total balance, payout rate, and time staked; the total balance is the sum of their deposited staked amount and their compounded rewards amount.
  • Only the Manager address or owner can call the withdrawal function for user addresses. Once done, the referral reward tokens and staking reward tokens are both calculated using the conversion rate and are transferred to the user along with the deposited staking tokens. The conversion rate is provided by the Manager or owner at the time of the function call.
  • The Manager address or Owner must claim
  • The owner can grant the Manager role to any single address, at any time.
  • The owner can withdraw any ETH in the contract at any time.
  • The owner can change the staking/reward token addresses at any time.
  • The owner can update the initial and regular minimum deposit amount at any time.
  • The owner can update the minimum referral address deposit amount at any time.
  • The owner can update the reward rate at any time.
  • The owner can change the referral bonus to any value at any time.
  • The owner can update the maximum number of referrals at any time.
  • The owner can change the vault address at any time.
FPADStaking Contract:
  • This contract allows users to stake a designated token into the contract in order to earn rewards in the form of the same token until the total reward supply has been depleted.
  • On deposits, users can specify a referral address to be gifted referral rewards. Any pending payout amounts for the user are also automatically compounded into the users' deposited staked amount.
  • Users can also choose to compound their pending payout amount at any time without depositing.
  • The referral address must have at least the minimum referral deposit amount at the time of the referral to be eligible for referral rewards.
  • Users' referral rewards can be claimed at any time and are calculated based on the referral deposit amount and the contract's current referral bonus rate.
  • Users can amass unlimited referrals from deposits unless the referral limit is set.

  • Users can claim their reward tokens once the minimum stake time has elapsed.
  • The reward amount is calculated based on the users' total payout amount in addition to the users' compounded rewards amount.
  • Users' payout amounts are based on the users' total balance, payout rate, and time staked; the total balance is the sum of their deposited staked amount and their compounded rewards amount.
  • On withdrawals, referral and staking reward tokens are automatically transferred to the user in addition to the staking tokens they've deposited.

  • The owner can withdraw any ETH in the contract at any time.
  • The owner can change the contract token address at any time.
  • The owner can update the initial and regular minimum deposit amount at any time.
  • The owner can update the reward rate at any time.
  • The owner can change the referral bonus to any value at any time.
  • The owner can update the minimum referral address deposit amount at any time.
  • The owner can update the maximum number of referrals at any time.
  • The owner can change the vault address at any time.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Centralization of ControlThe team has the ownership controls described above.WARNING
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
Logical IssuesN/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

FFSStaking Contract

 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
    - [Int] _transferOwnership #

 + [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 #

 + [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 #

 +  FFSStaking (Ownable)
    - [Pub] updatePayoutRate #
       - modifiers: onlyOwner
    - [Pub] updateRefDepth #
       - modifiers: onlyOwner
    - [Pub] updateRefBonus #
       - modifiers: onlyOwner
    - [Pub] updateInitialDeposit #
       - modifiers: onlyOwner
    - [Pub] updateFFSToken #
       - modifiers: onlyOwner
    - [Pub] updateFPADToken #
       - modifiers: onlyOwner
    - [Pub] updateVaultAddress #
       - modifiers: onlyOwner
    - [Pub] updateManager #
       - modifiers: onlyOwner
    - [Pub] updateMinStakeTime #
       - modifiers: onlyOwner
    - [Ext] withdrawETH #
       - modifiers: onlyOwner
    - [Ext] deposit #
    - [Ext] claim #
    - [Ext] claimReferral #
    - [Ext] withdrawPrincipal #
    - [Pub] compound #
    - [Int] _deposit #
    - [Int] _refPayout #
    - [Int] _compound #
    - [Int] _claim_out #
    - [Int] _claim_referral #
    - [Int] _withdraw_principal #
    - [Pub] isNetPositive
    - [Pub] creditsAndDebits
    - [Pub] claimsAvailable
    - [Pub] canUserClaim
    - [Pub] payoutOf
    - [Ext] userInfo
    - [Ext] InfoTotals
    - [Ext] stakeInfo

FPADStaking 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
    - [Int] _transferOwnership #

 + [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 #

 + [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 #

 +  FPADStaking (Ownable)
    - [Pub] updatePayoutRate #
       - modifiers: onlyOwner
    - [Pub] updateRefDepth #
       - modifiers: onlyOwner
    - [Pub] updateRefBonus #
       - modifiers: onlyOwner
    - [Pub] updateInitialDeposit #
       - modifiers: onlyOwner
    - [Pub] updateFPADToken #
       - modifiers: onlyOwner
    - [Pub] updateVaultAddress #
       - modifiers: onlyOwner
    - [Pub] updateManager #
       - modifiers: onlyOwner
    - [Pub] updateMinStakeTime #
       - modifiers: onlyOwner
    - [Ext] withdrawETH #
       - modifiers: onlyOwner
    - [Ext] deposit #
    - [Ext] claim #
    - [Ext] claimReferral #
    - [Ext] withdrawPrincipal #
    - [Pub] compound #
    - [Int] _deposit #
    - [Int] _refPayout #
    - [Int] _compound #
    - [Int] _claim_out #
    - [Int] _claim_referral #
    - [Int] _withdraw_principal #
    - [Pub] isNetPositive
    - [Pub] creditsAndDebits
    - [Pub] claimsAvailable
    - [Pub] canUserClaim
    - [Pub] payoutOf
    - [Ext] userInfo
    - [Ext] InfoTotals
    - [Ext] stakeInfo