GMX engaged Guardian to review the security of its RewardRouterV2 and Governance updates. From the 5th of December to the 12th of December a team of 3 auditors reviewed the source code in scope.
- Published
- Review window
- December 5 to 12, 2023
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Governance
- 0 Critical
- 1 High
- 3 Medium
- 5 Low
- 0 Informational
Scope
Overview
GMX engaged Guardian to review the security of its RewardRouterV2 and Governance updates. From the 5th of December to the 12th of December a team of 3 auditors reviewed the source code in scope.
Findings 9
-
RROU-1 High Burning bnGMX Could Be Avoided Logical Error Acknowledged
Description
Proof of concept: PoC
When unstaking GMX and
_shouldReduceBnGmx = true, a proportionate amount of the user's bnGMX wallet balance is burnt.A user can avoid the loss of their bnGMX (any amount beyond what is claimed in
_unstakeGmx) by transferring the token to another account they control.After unstaking, the sent bnGMX could be returned. Consequently, a user will have more multiplier points than intended, which will lead to more voting power.
Recommendation
Consider restricting the transfer of bnGMX for non-handler accounts. Furthermore, clearly document the intended behavior regarding bnGMX slashing upon unstaking GMX.
Resolution
GMX Team: bnGMX.inPrivateTransferMode is not currently set to true, agree that it should be set to true.
-
GLOBAL-1 Medium Payload Attack Enables Griefing Of Keepers Griefing Resolved
Description
The
_transferOutETHWithGasLimitFallbackToWethand_transferOutETHfunctions in GMX V1 are used to transfer ether.Neither of these functions utilizes assembly to avoid copying return data into memory.
This allows any contract interacting with GMX V1 to maliciously return a large amount of data, which will end up costing more gas than anticipated and could force the keepers to run a deficit.
Recommendation
Use a low-level call to avoid loading the return data into memory:
assembly { success := call(gasLimit, receiver, amount, 0, 0, 0, 0) }.Resolution
GMX Team: The resolution was implemented.
-
GLOBAL-2 Medium Incorrect block.timestamp Used Logical Error Resolved
Description
In the
GovTokenandProtocolGovernancecontracts, theCLOCK_MODEfunction validates that the clock function returns theblock.timestamp, however it should be validated against theChain.currentTimestampto avoid failures due to L2 timestamp drift.Recommendation
Validate the result of the clock function against the
Chain.currentTimestamp().Resolution
GMX Team: The resolution was implemented.
-
RROU-2 Medium Infinite Voting Power Logical Error Acknowledged
Description
Proof of concept: PoC
When syncing an account's voting power, the user's staked amounts are compared against their current governance token wallet holdings. If the governance holdings are less, the appropriate amount is minted.
This can be exploited if a user transfers their governance tokens to another account, triggers a sync, and then those governance tokens are minted again. This way, a user can generate infinite votes and move forward malicious proposals.
Recommendation
Ensure that the governance token used cannot be freely transferred.
Resolution
GMX Team: GovToken.sol would be used, only contracts with the role GOV_TOKEN_CONTROLLER would be able to make transfers.
-
RROU-3 Low Unnecessary Vote Syncing In _compound Optimization Acknowledged
Description
In the
acceptTransferfunction, the_compoundinvocation will always trigger a syncing of the voting power for the_sender. However, the voting is once again synced after stake balances are transferred at the end of theacceptTransferfunction.Recommendation
Consider adding a boolean parameter to the
_compoundfunction to specify whether the voting power should be synced. In the case of an account transfer this boolean would be false to avoid unnecessary syncs to save gas. In all other cases the boolean value would be true.Resolution
GMX Team: Acknowledged.
-
RROU-4 Low Early Return Misses Voting Sync Logical Error Resolved
Description
In the
_stakeBnGmxfunction the_syncVotingPowerinvocation will be missed if thecurrentBnGmxAmountis greater than themaxAllowedBnGmxAmount.There is no net effect as the
_syncVotingPowerfunction will always be called for the_accountlater for every instance where the_stakeBnGmxfunction is used.However, this poses a risk if the
_stakeBnGmxfunction were to be used without syncing the voting power of the_accountafterwards.Recommendation
Consider removing the
_syncVotingPowerinvocation from the_stakeBnGmxfunction as it is redundant for all cases where the_stakeBnGmxfunction is currently used.Otherwise be sure to sync the voting power in the case where the
currentBnGmxAmountis greater than themaxAllowedBnGmxAmountand the_stakeBnGmxfunction early returns.Resolution
GMX Team: The recommendation was implemented.
-
RROU-5 Low Unnecessary Voting Sync In _stakeGmx Optimization Resolved
Description
In the
_stakeGmxfunction it is unnecessary to sync the voting power for the funding account as the funding account will provide unstaked tokens, while the voting power relies on staked tokens.Therefore the voting power of the funding account cannot be affected by the
_stakeGmxfunction.Additionally, in the only case where the
_stakeGmxfunction is used with a_fundingAccountthat is different from the account is in theacceptTransferfunction where the voting power is redundantly synced for the_senderat the end.Recommendation
Remove the syncing logic for the
_fundingAccountin the_stakeGmxfunction as it is unnecessary.Resolution
GMX Team: The recommendation was implemented.
-
RROU-6 Low inStrictTransferMode Does Not Ensure Successful acceptTransfer Logic Error Acknowledged
Description
Calling the function
signalTransferrequires thatmsg.senderhas given the_receiverallowance equivalent to or more than the balance ofmsg.senderwheninStrictTransferModeis set to true.The original
msg.sendercan decrease the approval prior to the receiver accepting the transfer, causing the functionacceptTransferto revert in the case where the transfer is not being performed by a handler.When the transfer is performed by the handler,
transferFromdoes not check the allowance on theBaseToken.Recommendation
Consider removing the
inStrictTransferModecheck since the transfers are being done by handlers, or clearly document its intended behavior.Resolution
GMX Team: The reason for inStrictTransferMode is mainly to prevent phishing, we have observed phishing scams that utilize signalTransfer, because no approval is needed, there is less warning / less alerting of the user from their wallet
-
DEPLOY-1 Low Miniscule proposalThreshold Configured Logical Error Resolved
Description
In the deploy file for the
ProtocolGovernorcontract, 30_000 is used as aproposalThreshold. However, this is a minuscule amount of governance tokens, as the governance token is configured with 18 decimals.Recommendation
Be sure to configure a reasonable minimum threshold with 18 decimals for proposals in production as the
proposalThresholdis in a governance token amount, which has 18 decimals of precision.Resolution
GMX Team: The recommendation was implemented.
No findings match.
More from GMX
All 44 reportsPut your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
