GMDAO Vault

Smart Contract Audit Report

Audit Summary

GMDAO Audit Report GMDAO is building a new Vault contract in which users can deposit various tokens in order to earn rewards in the same tokens through GLP staking.

For this audit, we reviewed GMDAO's Vault contract at commit 991a51efb08ae86912cf8bdc48c2e13fd500093f on the team's private GitHub repository.

Audit Findings

Informational findings were identified and the team may want to review them. In addition, centralized aspects are present.
Date: November 3rd, 2022.

Finding #1 - Vault - Informational

Description: A pool is intended to disallow deposits if it results in its total staked amount exceeding its maximum, but the pool is updated after this check occurs. As a pool update increases its total staked amount, users may be permitted to deposit despite exceeding the pool's maximum stake amount. In addition, when a pool is updated upon a withdrawal, its total staked amount can exceed its intended maximum permitted staked amount.
require((poolInfo[_pid].totalStaked + _amount) <= poolInfo[_pid].vaultcap, "cant deposit more than vault cap");

if (poolInfo[_pid].rewardStart){
	updatePool(_pid);
}
Risk/Impact: A pool may exceed its maximum intended stake or reward amount.
Recommendation: The require statement should occur after the pool is updated in both the enter() and enterETH() functions. In addition, a similar conditional block can be added to the updatePoolRate() function to limit the pool's total stake amount to its maximum amount.
Resolution: The team has not yet addressed this issue.

Finding #2 - Vault - Informational

Description: The poolGLP variable is not declared constant, but is never updated.
Recommendation: This variable can be declared constant for gas savings on each swaptoGLP() call.

Contract Overview

  • The owner must ensure that the APR set for a pool is not larger than the GLP rewards earned by the pool's tokens minus any GLP used for slippage purposes, otherwise some users will not be able to withdraw their tokens/rewards.
  • As the GLPPriceFeed and GLPRouter contracts, used for pricing, swapping, and staking, were not included in the scope of this audit, we are unable to provide an assessment with regards to security or functionality.
  • Users should exercise caution before investing as a fall in the fsGLP price could cause users to be unable to withdraw their originally staked tokens or due rewards.
  • The team must exercise caution when assigning the staking token to avoid using fee-on-transfer tokens.
  • As the contracts are implemented with Solidity v0.8.0, they are safe from any possible overflows/underflows.
  • This contract allows users to stake various tokens in exchange for rewards in the same token.
  • Rewards are earned by swapping the deposited tokens for GLP and staking them for rewards using the GLPRouter contract; the contract is transferred fsGLP as a result.
  • When withdrawing staked tokens, GLP is unstaked and swapped back for the original staking token using the GLPRouter contract before being transferred back to the user.
  • Users can deposit into a pool with staking enabled at any time as long as the pool's resulting total staked amount does not exceed its maximum stake amount.
  • A fee is taken upon deposit based on the pool's specified fee percentage.
  • The pool's share tokens are then minted to the user based on the remaining amount, intended to represent their portion of the pool's total staked amount.
  • The deposited staking tokens are immediately swapped for GLP using the GLPRouter contract.
  • Users must deposit ETH if the staking token is WETH; the ETH is first exchanged for WETH before being swapped for GLP.
  • The "total staked balance" of a pool will increase over time based on the pool's APR.
  • This will increase the share token value over time, resulting in the user receiving a larger amount of tokens than deposited as their reward.
  • Once a pool has been made withdrawable, users can exchange their share tokens for their due rewards. This will swap the appropriate amount of fsGLP through the GLPRouter contract in exchange for the pool's staking token.
  • A slightly larger amount of fsGLP is swapped to account for slippage, determined by the slippage factor.
  • Users should exercise caution when staking as any token can be used as a share token. If a share token can be minted outside of this contract, rewards may be unfairly claimed by token holders.
  • The owner can update slippage to any amount at any time.
  • The owner can "cycle ETH rewards" at any time, which will claim WETH rewards earned by the GLPRouter contract, then stake a portion of the rewards using the GLPRouter contract. This portion is determined by the contract's "compound percentage".
  • The remaining WETH is transferred to the owner.
  • The owner can update the compound percentage to any value between 0 and 90 percent at any time.
  • The owner can "cycle ETH and EsGMX rewards" at any time, which will claim EsGMX rewards and stake them using the GLPRouter contract, and subsequently cycle ETH rewards.
  • The owner can unstake the contract's GLP through the GLPRouter contract for any token as long as the contract's resulting fsGLP value remains greater than the total amount owed to users across all pools.
  • The owner can then withdraw these tokens to their own address or stake them through the GLPRouter contract.
  • The owner can update a pool's maximum stake amount to any value at any time.
  • The owner can update the APR of a pool to any amount between 5% and 16% at any time.
  • The owner can open a pool at any time. This will allow funds to be staked, but will disable withdrawals from it.
  • The owner can allow withdrawals for a pool at any time; this will also disable staking for the pool.
  • The owner can start or stop reward generation for a pool at any time. While a pool is stopped, staked funds will not accrue rewards.
  • The owner can withdraw any tokens from the contract apart from fsGLP.

Audit Results

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control
  • The owner has the permissions described above.
  • The owner can disable staking or withdrawals for a pool at any time.
  • The owner can update the APR for any pool at any time, or disable rewards entirely.
  • The owner can withdraw any funds not allocated for rewards at any time.
  • The owner can update the compound percentage to any amount between 0 and 90 percent at any time.
  • The owner can specify any address as a share token as long as it adheres to the GDtoken interface.
  • WARNING
    Compiler Issues N/A PASS
    Delegate Call to Untrusted Contract N/A PASS
    Dependence on Predictable Variables N/A PASS
    Ether/Token Theft N/A PASS
    Flash Loans N/A PASS
    Front Running N/A PASS
    Improper Events N/A PASS
    Improper Authorization Scheme N/A PASS
    Integer Over/Underflow N/A PASS
    Logical Issues N/A PASS
    Oracle Issues N/A PASS
    Outdated Compiler Version N/A PASS
    Race Conditions N/A PASS
    Reentrancy N/A PASS
    Signature Issues N/A PASS
    Unbounded Loops N/A PASS
    Unused Code N/A PASS
    Overall Contract Safety   PASS

    Inheritance Chart

    Smart Contract Audit - Inheritance

    Function Graph

    Smart Contract Audit - Graph

    Functions Overview

    
     ($) = 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] _spendAllowance #
        - [Int] _beforeTokenTransfer #
        - [Int] _afterTokenTransfer #
    
     + [Int] IERC20Permit 
        - [Ext] permit #
        - [Ext] nonces
        - [Ext] DOMAIN_SEPARATOR
    
     + [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 #
        - [Int] safePermit #
        - [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
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Int] _checkOwner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     +  ReentrancyGuard 
        - [Pub]  #
    
     + [Int] GLPRouter 
        - [Ext] unstakeAndRedeemGlp #
        - [Ext] mintAndStakeGlp #
        - [Ext] claimFees #
        - [Ext] claimEsGmx #
        - [Ext] stakeEsGmx #
    
     + [Int] GDtoken (IERC20)
        - [Ext] mint #
        - [Ext] burn #
    
     + [Int] GLPPriceFeed 
        - [Ext] getGLPprice
        - [Ext] getPrice
    
     + [Int] IWETH (IERC20)
        - [Ext] deposit ($)
        - [Ext] safeTransfer #
        - [Ext] withdraw #
    
     +  vault (Ownable, ReentrancyGuard)
        - [Pub]  #
        - [Prv] swapGLPto #
        - [Ext] swapGLPout #
           - modifiers: onlyOwner
        - [Prv] swaptoGLP #
        - [Pub] treasuryMint #
           - modifiers: onlyOwner
        - [Ext] cycleRewardsETHandEsGMX #
           - modifiers: onlyOwner
        - [Ext] cycleRewardsETH #
           - modifiers: onlyOwner
        - [Prv] _cycleRewardsETH #
        - [Ext] setCompoundPercentage #
           - modifiers: onlyOwner
        - [Int] updatePool #
        - [Pub] currentPoolTotal
        - [Int] updatePoolRate #
        - [Ext] setPoolCap #
           - modifiers: onlyOwner
        - [Ext] setAPR #
           - modifiers: onlyOwner
        - [Ext] openVault #
           - modifiers: onlyOwner
        - [Ext] openAllVault #
           - modifiers: onlyOwner
        - [Ext] startReward #
           - modifiers: onlyOwner
        - [Ext] pauseReward #
           - modifiers: onlyOwner
        - [Ext] openWithdraw #
           - modifiers: onlyOwner
        - [Ext] openAllWithdraw #
           - modifiers: onlyOwner
        - [Ext] setSlippage #
           - modifiers: onlyOwner
        - [Int] checkDuplicate
        - [Ext] addPool #
           - modifiers: onlyOwner
        - [Ext] enterETH ($)
           - modifiers: nonReentrant
        - [Ext] leaveETH ($)
           - modifiers: nonReentrant
        - [Ext]  ($)
        - [Pub] enter #
           - modifiers: nonReentrant
        - [Pub] leave #
           - modifiers: nonReentrant
        - [Pub] displayStakedBalance
        - [Pub] GDpriceToStakedtoken
        - [Ext] convertDust #
           - modifiers: onlyOwner
        - [Ext] recoverTreasuryTokensFromGLP #
           - modifiers: onlyOwner
        - [Ext] recoverTreasuryTokens #
           - modifiers: onlyOwner
        - [Pub] totalUSDvault
        - [Pub] totalUSDvaults
        - [Pub] GLPbackingNeeded
        - [Pub] GLPinVault
    
    

    About SourceHat

    SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1300+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value across 1500 projects!.
    Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

    Contact us today to get a free quote for a smart contract audit of your project!

    What is a SourceHat Audit?

    Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

    How Do I Interpret the Findings?

    Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

    • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
    • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
    • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
    • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.