OKLG Rewards Distributor

Smart Contract Audit Report

Audit Summary

Ok.let's.go is creating a new staking contract which allows users to receive shares of deposited rewards.

For this audit, we reviewed the OKLGRewardsDistributor contract at commit 4ec517c2b6d58adbc776b5310fcd90556c361510 on the team's GitHub repository.

We previously reviewed select contracts from OKLG's main platform here.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: February 19th, 2022.
Updated: February 23 to support changes from commit dd3c43d9da0ca1757074ae45e8a47dcec71b35e4 to commit 4ec517c2b6d58adbc776b5310fcd90556c361510.

Finding #1 - Informational (Resolved)

Description: The claimReward(), depositRewards(), stake(), and unstake functions() all contain arbitrary token parameters. which. These passed token addresses have calls delegated to them, allowing arbitrary logic to be executed before state changes are made.
Risk/Impact: Malicious contracts can be passed to execute arbitrary logic when the contract is called. In addition, as these functions do not follow the pattern of checks-effects-interactions, this contract is susceptible to reentrancy attacks.
Recommendation: We recommend adding a token whitelist so only certain token addresses can be passed into these functions. ERC-777 tokens should not be included in this whitelist.
Resolution: The team has removed these parameters and now distributes rewards only in the blockchain's native token.

Finding #2 - Informational (Resolved)

Description: The following line in the depositRewards() function serves no purpose:
(bool sent, ) = payable(address(this)).call{ value: msg.value }('');
Recommendation: This line can be removed for gas saving purposes.
Resolution: The team has removed this line.

Finding #3 - Informational (Resolved)

Description: The shareholderClaims mapping serves no purpose.
Recommendation: This mapping can be removed for gas saving purposes.
Resolution: The team has removed this mapping.

Contract Overview

  • This contract allows users to stake a specified staking token in order to receive a share of deposited rewards in the form of the blockchain's native token.
  • When staking, users can also deposit NFTs from a specified NFT collection, however this contract does not contain any logic for users to earn rewards based on staked NFTs.
  • Users accumulate rewards when they are deposited into this contract.
  • A user's portion of deposited rewards is dependent on their share of total staked tokens.
  • A user can also earn a custom boost to their rewards if they are deemed eligible by associated Booster and BoostRewards contracts.
  • As the Booster and BoostRewardsMultiplier contracts were not included within the scope of this audit, we are unable to provide an assessment with regards to functionality or security.
  • After a minimum stake time has elapsed, a user can unstake any amount of their tokens.
  • Users must input "0" as the unstaking amount in order to have any staked NFTs transferred back to them; this will also withdraw all of their staking tokens.
  • A user's wait time to unstake is reset any time they stake additional tokens.
  • Users accumulate rewards based on their number of staked tokens when rewards are deposited into this contract.
  • When staking or unstaking, users can elect to either claim or forfeit their accumulated rewards.
  • Rewards can also be claimed separately through a claim function.
  • When claiming rewards, the user can elect to have their reward converted into staking tokens and staked back into the platform.
  • As the contract does not follow the checks-effects-interactions pattern, the team should not use ERC-777 tokens as the staking token.

  • The owner has the ability to call a "stake override" function at any time which adds to a list of users' staked amounts without them having to stake any tokens.
  • This will allow these users to unstake tokens that they never deposited, potentially disallowing other users from withdrawing their staked tokens if the contract does not have sufficient funds. When this function is called, the specified users' accumulated rewards will be erased.
  • The stake override function also overrides the users' staked NFT lists with specified NFTs.
  • The owner can update the staking token contract at any time; this could potentially disallow users from withdrawing the token they have staked.
  • The owner can update the minimum staking time at any time.
  • The owner can update the Boost or BoostMultiplier contracts at any time.
  • The owner can withdraw any staked NFTs from the contract at any time.
  • The owner can withdraw any of the blockchain's native token or other tokens, including staking tokens, from this contract at any time.

  • As the contract is implemented with Solidity 0.8.x, it is protected from underflows/overflows.

Audit Results

Vulnerability CategoryNotesResult
Arbitrary Jump/Storage WriteN/APASS
Centralization of Control
  • The owner has the permissions mentioned above.
  • The owner can update the staking token at any time, potentially preventing users from withdrawing their originally staked tokens.
  • The owner can withdraw any of the blockchain's native token or any other token from the contract, including staking tokens and staked NFTs.
  • The owner can grant users artificial staking balances. As a result, they will be able to withdraw other users' staking tokens.
    Compiler IssuesN/APASS
    Delegate Call to Untrusted ContractN/APASS
    Dependence on Predictable VariablesN/APASS
    Ether/Token TheftN/APASS
    Flash LoansN/APASS
    Front RunningN/APASS
    Improper EventsN/APASS
    Improper Authorization SchemeN/APASS
    Integer Over/UnderflowN/APASS
    Logical IssuesN/APASS
    Oracle IssuesN/APASS
    Outdated Compiler VersionN/APASS
    Race ConditionsN/APASS
    Signature IssuesN/APASS
    Unbounded LoopsN/APASS
    Unused CodeN/APASS
    Overall Contract Safety PASS

    Function Graph

    Contract Graph

    Inheritance Chart

    Multi-file Token

    Functions Overview

    ($) = payable function
     # = non-constant function
     + [Int] IERC165 
        - [Ext] supportsInterface
     + [Int] IERC721 (IERC165)
        - [Ext] balanceOf
        - [Ext] ownerOf
        - [Ext] safeTransferFrom #
        - [Ext] transferFrom #
        - [Ext] approve #
        - [Ext] getApproved
        - [Ext] setApprovalForAll #
        - [Ext] isApprovedForAll
        - [Ext] safeTransferFrom #
     + [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] IUniswapV2Router01 
        - [Ext] factory
        - [Ext] WETH
        - [Ext] addLiquidity #
        - [Ext] addLiquidityETH ($)
        - [Ext] removeLiquidity #
        - [Ext] removeLiquidityETH #
        - [Ext] removeLiquidityWithPermit #
        - [Ext] removeLiquidityETHWithPermit #
        - [Ext] swapExactTokensForTokens #
        - [Ext] swapTokensForExactTokens #
        - [Ext] swapExactETHForTokens ($)
        - [Ext] swapTokensForExactETH #
        - [Ext] swapExactTokensForETH #
        - [Ext] swapETHForExactTokens ($)
        - [Ext] quote
        - [Ext] getAmountOut
        - [Ext] getAmountIn
        - [Ext] getAmountsOut
        - [Ext] getAmountsIn
     + [Int] IUniswapV2Router02 (IUniswapV2Router01)
        - [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
        - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
        - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
        - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
        - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
     + [Int] IConditional 
        - [Ext] passesTest
     + [Int] IMultiplier 
        - [Ext] getMultiplier
     + [Int] IOKLGRewardDistributor 
        - [Ext] depositRewards ($)
        - [Ext] getShares
        - [Ext] getBoostNfts
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
     + [Int] IERC20 
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
     +  OKLGWithdrawable (Ownable)
        - [Ext] withdrawTokens #
           - modifiers: onlyOwner
        - [Ext] withdrawETH #
           - modifiers: onlyOwner
     +  OKLGRewardDistributor (IOKLGRewardDistributor, OKLGWithdrawable)
        - [Pub]  #
        - [Ext] stake #
        - [Prv] _stake #
        - [Ext] unstake #
        - [Ext] depositRewards ($)
        - [Int] distributeReward #
        - [Ext] claimReward #
        - [Pub] getAppreciatedShares
        - [Int] getElevatedSharesWithBooster
        - [Pub] getBaseSharesFromBoosted
        - [Pub] getBoostMultiplier
        - [Pub] eligibleForRewardBooster
        - [Pub] getUnpaid
        - [Int] getCumulativeRewards
        - [Ext] getBaseShares
        - [Ext] getShares
        - [Ext] getBoostNfts
        - [Ext] setShareholderToken #
           - modifiers: onlyOwner
        - [Ext] setBoostContract #
           - modifiers: onlyOwner
        - [Ext] setBoostMultiplierContract #
           - modifiers: onlyOwner
        - [Ext] setMinSecondsBeforeUnstake #
           - modifiers: onlyOwner
        - [Ext] stakeOverride #
           - modifiers: onlyOwner
        - [Ext] withdrawNfts #
           - modifiers: onlyOwner
        - [Ext]  ($)