GMX V2 Vault
Smart Contract Audit Report
Audit Summary
GMX V2 Vault is a new platform which allows users to stake USDC and WETH in order to earn interest over time.
For this audit, we reviewed the project team's LPTokenFactory, LPToken, Vaultv2, and DataRetrievalContract contracts at commit ab874ecbc8ac740c62bd4c3fee03a317d066ffeb on the team's private GitHub repository.
We previously reviewed the project team's GND platform here.
Audit Findings
A High finding was identified that has been acknowledged by the team. In addition, centralized aspects are present.
Date: November 29th, 2023.
Updated: November 30th, 2023 to include a response from the team regarding Finding #1.Finding #1 - Vaultv2 - High (Resolved)
Description: Upon depositing into the Vault, users are minted shares based on the pool's "total staked amount". This total staked value is incremented over time based on the pool's APR to increase the value of user shares, regardless of whether the contract is actually accumulating rewards for distribution. When redeeming shares from the Vault, the user is transferred a portion of the total staked value in the form of their original deposit token, regardless of whether this total staked value represents the pool's actual value.
Risk/Impact: If a pool's APR is larger than the rate that the Vault is actually accumulating rewards at, rewards will come from other user's deposited tokens. As a result, there may not be enough funds to allow all users to redeem their shares.
Recommendation: The project team should calculate share value as the sum of a pool's current staking token balance and the value of GMarket tokens purchased using the pool's staking tokens.
Update: The team has stated that a Reserve address exists to ensure that user's deposits are collateralized. Users must exercise caution as they must rely on the project team to ensure that their funds are not lost.
Finding #2 - Vaultv2 - Informational
Description: ThecheckDuplicate()
function is declared but never used.
Recommendation: As this function is internal, it serves no purpose can be removed for gas savings on deployment and on various function calls. Alternatively, the project team could alter the its functionality and use it upon construction to ensure that the USDC and WETH pools do not use the same share token.
Finding #3 - Vaultv2 - Informational
Description: The following variables are not declared constant but are never updated:
Recommendation: These variables can be declared constant for gas savings on various function calls.GMarketAddress, USDC, WETH, wntReceiver, wntReceiverWithdraw
Finding #4 - Vaultv2 - Informational
Description: TheUserWithdrawAmount
struct'sburnEthGDAmount
andburnEthGDAmount
fields are declared but never used.
Recommendation: These fields can be removed for gas savings on various function calls.
Contracts Overview
LPTokenFactory Contract:
- Users must exercise caution as this contract transfers user funds to out of scope contracts in order to generate rewards.
- Users must also exercise caution and must rely on the team to ensure that the associated Reserve address holds sufficient funds to collateralize user deposits and along with any generated rewards in the event that accumulated APR interest is larger than a pool's token balance.
- The contracts utilize ReentrancyGuard to prevent against reentrancy attacks in applicable functions.
- As the contracts are implemented with Solidity v0.8.0, they are safe from any possible overflows/underflows.
LPToken Contract:
- This contract allows its owner to create new "LPTokens" at any time.
- When an LP token is created, its ownership is transferred to this contract's "Manager" address, intended to be the Vault contract.
- The owner can update the Manager address at any time.
Vaultv2 Contract:
- LPTokens are intended to be used as "share" tokens for pools in the Vault contract.
- Each token is created through the LPTokenFactory by its owner address.
- The owner, intended to be the Vault, can burn or mint tokens from any address at any time.
- This contract complies with the ERC-20 standard.
DataRetrievalContract Contract:
- This contract allows users to deposit USDC or WETH in order to earn rewards through the GMX platform.
- Upon depositing, users are minted GDWETH or GDUSDC tokens representing their share of the Vault's total funds of the associated asset type.
- A deposit fee is taken based on the pools current deposit fee percentage.
- Rewards are accumulated in the form of the pool's deposit token based on the pool's APR.
- A user can redeem their shares at any time, burning them and adding their redeemed WETH or USDC value to their pending withdrawable amount.
- The user can then withdraw their pending USDC or WETH at any time.
- A withdrawal fee is taken based on the pools current withdrawal fee percentage.
- The Rebalancer address can "buy GM tokens" at any time by depositing any amount of the Vault's WETH, USDC, and ETH to the associated ExchangeRouter address, intending to receive GMTokens in exchange.
- The Rebalancer can then redeem these tokens through the ExchangeRouter at any time.
- The owner can withdraw any ETH or tokens from the contract apart from GMarket tokens at any time, including users' staked USDC and WETH.
- The owner can update the Oracle and Router addresses at any time.
- The owner can update a pool's deposit and withdrawal fees to up to 1% each at any time.
- The owner can update the Rebalancer address at any time.
- The owner can update a pool's maximum total staked amount at any time.
- The owner can update a pool's APR to any value between 2% and 40% at any time.
- The owner can enable or disable deposits or withdrawals from any pool at any time.
- The owner can pause reward accumulation from any pool at any time.
- As the ExchangeRouter, DepositVault, and WithdrawalVault contracts used to interact with Vault funds were not included within the scope of this audit, we are unable to provide an assessment with regards to their security or functionality.
- This contract is used to retrieve GMETH, GMBT, WETH, and USDC asset prices.
- The Vault uses this contract in order to retrieve USDC/ETH and GMETH prices in order to calculate the total USDC and ETH values of the Vault, where an associated Chainlink Oracle address is used to fetch prices.
- USDC/ETH Prices can also be fetched through this contract using an associated ReaderContract address.
- GMETH and GMBT prices are manually set by the updater address and can be updated by them at any time.
- The owner can update the Updater address at any time.
- As the associated ReaderContract, marketToken, indexToken, longToken, and shortToken contracts used for price fetching were not included within the scope of this audit, we are unable to provide an assessment with regards to their security or functionality.
Audit Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Jump/Storage Write | N/A | PASS |
Centralization of Control |
|
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 | WARNING | |
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 |
Sybil Attack | N/A | PASS |
Unbounded Loops | N/A | PASS |
Unused Code | N/A | PASS |
Overall Contract Safety | WARNING |
LPTokenFactory & LPToken Contracts
($) = 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 #
+ Ownable (Context)
- [Pub] #
- [Pub] owner
- [Int] _checkOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ LPToken (ERC20, Ownable)
- [Pub] #
- modifiers: Ownable,ERC20
- [Ext] burn #
- modifiers: onlyOwner
- [Ext] mint #
- modifiers: onlyOwner
+ LPTokenFactory (Ownable)
- [Pub] #
- modifiers: Ownable
- [Ext] createLPToken #
- modifiers: onlyOwner
- [Ext] setManager #
- modifiers: onlyOwner
Vaultv2 Contract
($) = 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] verifyCallResultFromTarget
- [Int] verifyCallResult
- [Prv] _revert
+ [Lib] SafeERC20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Int] forceApprove #
- [Int] safePermit #
- [Prv] _callOptionalReturn #
- [Prv] _callOptionalReturnBool #
+ [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] #
- [Prv] _nonReentrantBefore #
- [Prv] _nonReentrantAfter #
- [Int] _reentrancyGuardEntered
+ [Int] GDtoken (IERC20)
- [Ext] mint #
- [Ext] burn #
+ [Int] IERC20Extented (IERC20)
- [Ext] decimals
+ [Int] oracle
- [Ext] getGMETHprice
- [Ext] getGMBTCprice
- [Ext] getAssetPrice
- [Ext] getStableAssetPrice
+ [Int] IExchangeRouter
- [Ext] sendWnt ($)
- [Ext] sendTokens ($)
- [Ext] createDeposit ($)
- [Ext] createWithdrawal ($)
+ vaultv2 (ReentrancyGuard, Ownable)
- [Pub] #
- modifiers: Ownable
- [Ext] ($)
- [Ext] updateOracle #
- modifiers: onlyOwner
- [Ext] updateRebalancer #
- modifiers: onlyOwner
- [Ext] Router #
- modifiers: onlyOwner
- [Ext] getWithdrawETHneeded
- [Ext] getWithdrawUSDCneeded
- [Ext] getFreeWETH
- [Ext] getFreeUSDC
- [Ext] setFees #
- modifiers: onlyOwner
- [Ext] setWithdrawFees #
- modifiers: onlyOwner
- [Int] updatePool #
- [Pub] currentPoolTotal
- [Pub] displayStakedBalance
- [Int] updatePoolRate #
- [Ext] setPoolCap #
- modifiers: onlyOwner
- [Ext] setAPR #
- modifiers: onlyOwner
- [Ext] setOpenVault #
- modifiers: onlyOwner
- [Ext] setOpenAllVault #
- modifiers: onlyOwner
- [Ext] startReward #
- modifiers: onlyOwner
- [Ext] pauseReward #
- modifiers: onlyOwner
- [Ext] openWithdraw #
- modifiers: onlyOwner
- [Ext] openAllWithdraw #
- modifiers: onlyOwner
- [Int] checkDuplicate
- [Pub] GDpriceToStakedtoken
- [Pub] enter #
- modifiers: nonReentrant
- [Pub] leave #
- modifiers: nonReentrant
- [Int] resetUserWithdraw #
- [Pub] withdraw #
- modifiers: nonReentrant
- [Pub] withdrawable
- [Pub] totalUSDvault
- [Pub] totalGMUSD
- [Ext] withdrawProfit #
- modifiers: onlyOwner
- [Ext] withdrawARB #
- modifiers: onlyOwner
- [Ext] withdrawETH #
- modifiers: onlyOwner
- [Ext] BuyGMtokens ($)
- [Int] _buyGMtokens #
- [Ext] SwapToAssets ($)
- [Int] _swapToAssets #
DataRetrievalContract Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Pub] #
- [Pub] owner
- [Int] _checkOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ [Int] AggregatorV3Interface
- [Ext] decimals
- [Ext] description
- [Ext] version
- [Ext] getRoundData
- [Ext] latestRoundData
+ [Int] IReaderContract
- [Ext] getWithdrawalAmountOut
+ DataRetrievalContract (Ownable)
- [Pub] #
- modifiers: Ownable
- [Pub] getAnswerFromPriceContract
- [Ext] setUpdater #
- modifiers: onlyOwner
- [Ext] updateGMETHprice #
- [Ext] updateGMBTCprice #
- [Pub] getGMETHprice
- [Pub] getGMBTCprice
- [Pub] getAssetPrice
- [Pub] getStableAssetPrice
- [Ext] retrieveETHUSDCAmount
- [Ext] retrieveETHUSDCAmount
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 1800+ 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!
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.