Platform: Code4rena
Start Date: 21/11/2022
Pot Size: $90,500 USDC
Total HM: 18
Participants: 101
Period: 7 days
Judge: Picodes
Total Solo HM: 4
Id: 183
League: ETH
Rank: 20/101
Findings: 1
Award: $732.03
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0xAgro, 0xNazgul, 0xPanda, 0xbepresent, 0xfuje, Awesome, B2, Bnke0x0, Deivitto, Diana, Funen, Jeiwan, JohnSmith, Josiah, R2, RaymondFam, Rolezn, Sathish9098, Waze, adriro, aphak5010, brgltd, btk, carrotsmuggler, ch0bu, chaduke, codeislight, codexploder, cryptostellar5, csanuragjain, danyams, datapunk, delfin454000, deliriusz, eierina, erictee, fatherOfBlocks, gz627, gzeon, hansfriese, hihen, jadezti, joestakey, keccak123, martin, nameruse, oyc_109, pedr02b2, perseverancesuccess, rbserver, rotcivegaf, rvierdiiev, sakshamguruji, shark, simon135, subtle77, unforgiven, xiaoming90, yixxas
732.0322 USDC - $732.03
Description
Using balanceOf for reward distribution means accidentally sent reward tokens to the contract will be distributed to all users who are eligible for rewards. In the claimRewards() function of PirexGmx.sol baseRewardBeforeClaim variable is calculated like this: uint256 baseRewardBeforeClaim = gmxBaseReward.balanceOf(address(this));
.
There are other instances of this vulnerability where the user can't get back their tokens if they accidentally send it to contracts listed in codebase references.
Exploit Scenario A user accidentally sent WETH (base reward) to the PirexGmx.sol contract without calling any functions. The contract doesn't have any functions to rescue these funds and will be distributed as rewards when PirexRewards calls the claimRewards() function.
Codebase references
Recommendation
Examine and consider the trade-offs of using internal accounting instead of balanceOf(address(this)
. Implement functions that allow to withdraw the accidentally sent tokens from the contracts.
Description Solmate's safeTransfer doesn't check if the token has code and will return a successful call even if the address doesn't have any code deployed to.
Exploit Scenario If AutoPxGlp.sol or AutoPxGmx.sol contracts have an incorrectly initialized asset to a non-existent address (see PirexERC4626.sol - L53) returning calls from deposit(), mint() withdraw() and redeem() will not revert and return successful calls indicating transfers work correctly. In this case there is nothing else to do (since asset is immutable) except to redeploy said contracts.
Codebase References
Recommendation In the constructor of PirexERC4626.sol check if the ERC20 asset address has code before initializing the asset in the constructor. Long term be mindful when developing that Solmate's transfer library doesn't check if an address it's used on has code deployed to it.
Description The receiver input on all the deposit and redeem functions of PirexGmx.sol can be set to the same contract or other Pirex contracts that can't withdraw. This would mean the user lost the deposited assets. Similar issue to Low Risk #1
Exploit Scenario A user misunderstanding how depositGmx() works (in PirexGmx.sol) wanting to deposit GMX accidentally set the receiver input to the PirexGmx.sol contract address. Now there is no way to recover their funds.
Codebase References
Recommendation
Add a check in the internal _deposit and _redeem functions if (receiver == address(this)) revert WrongAddress();
Description If pirexRewards were set incorrectly to a wrong address when deploying PxErc20, the contract wouldn't have any reward functionality but would still appear as working. In this case the contract has to be redeployed.
Another scenario where this is problematic is when pirexRewards would have a new contract deployed: because pirexRewards is immutable and can't be set to another address PxERC20 has to be redeployed in this case as well.
Codebase References
Recommendation Explore and consider the trade-offs of using a pirexRewards setter function.
Description PirexERC4626 uses a modified version of solmate's ERC4626 implementation, but the documentation does not specify which version or commit is used. This indicates that the protocol does not track upstream changes in contracts used as dependencies. Therefore, contracts may not reflect updates or security fixes implemented in their dependencies, as these updates need to be manually integrated.
Exploit Scenario Third-party contract (Solmate's ERC4626) used in Pirex receives an update with a critical fix for a vulnerability, but the update is not yet manually integrated in the current version of the protocol. An attacker detects the use of vulnerable ERC4626 contract in the protocol and exploits the vulnerability.
Codebase References
Recommendation Review the codebase and document the source and version of each dependency. Include third-party sources as modules in the project to maintain path consistency and ensure dependencies are updated periodically.
Description The constant FEE_DENOMINATOR declared in PirexGmx.sol is 1_000_000, but in AutoPxGlp.sol and AutoPxGmx.sol it is 10000. In PirexFees.sol it's called FEE_PERCENT_DENOMINATOR and is 100.
Codebase References
Recommendation Consider using the same FEE_DENOMINATOR name and value to be more consistent across the codebase.
Description All the enums used in functions and events are represented by a single letter. For example in PirexGmx.sol on L239 RewardTracker enum is used as r. This is bad for clarity and readability.
Codebase references:
Recomendation Instead of using single letters, consider writing more descriptive enums so it's easier to read and make sense of. For example instead of r, the enum inside the function could be named rTracker or rewardTracker or tracker.
Description Events emitting notable changes (for example initiating and completing migration) are not indexed. The negligible gas costs that indexing adds may be a worthwhile trade-off to make tracking these event parameters easier.
Codebase References
Recommendation Consider adding indexed to parameters of events emitting notable changes. See these events above in the references.
Description Return values of functions are inconsistent (named and unnamed through the codebase).
Codebase References
Recommendation Consider naming unnamed return values, especially those that have commented return value descriptions for more clarity
Description In PirexGmx.sol L225 assert is used. Assert is meant for asserting invariants. See the references link below
Recommendation Use revert or require instead of assert().
References SWC-110
Description
A lot of errors in different contracts are the same. For example ZeroAmount() error is in 4 different contracts (PirexGmx.sol, PxERC20.sol, AutoPxGlp.sol, AutoPxGmx.sol).
AutoPxGlp.sol and AutoPxGmx.sol contracts have the most similar errors and events.
Codebase References
Recommendation Consider writing a library and inheriting it in contracts for repeated errors and events so code stays DRY and not repeated.
Description setWithrawalPenalty(), setPlatformFee(), setCompoundIncentive() and setPlatform() are used in both AutoPxGlp.sol and AutoPxGmx.sol with the same functionality.
Codebase references
Recommendation Consider writing an abstract contract that have these functions implemented and inherit it in both contracts.
Description Producer tokens (pxGMX, pxGLP) are different from reward tokens (WETH, WAVAX, etc.), yet setRewardRecipient() allows setting the same address for both producerToken and rewardToken.
Codebase references
Recommendation
Add a check if (address(producerToken) == address(rewardToken)) revert WrongAddress();
to prevent incorrect setting.
Description It is not intuitive that producer refers to the pirexGMX contract.
Recommendation Consider renaming producer to pirexGmx through all the contracts.
Description It is not intuitive that rewardsModule refers to the pirexRewards contract.
Recommendation Consider renaming producer to pirexRewards through all the contracts.
Description newAsset doesn't describe the variable in AutoPxGlp.sol compound() fully.
Codebase References
Recommendation Consider renaming newAssets to a more descriptive name like assetsOut or assetsAmountOut or assetsToBeDistributed.
Description In the contest repository README AutoPxGlp.sol has a description of
Provides a series of permissioned methods that enables the Pirex multisig to configure fees, incentives, and Uniswap V3 pool fee
but there is no method or interaction with a UniswapV3 pools in AutoPxGlp.sol
Recommendation Remove this description or add Uniswap V3 functionality and fee configuration.
Description
In the AutoPxGlp.sol constructor on L87 inside a safeApprove the _platform variable is casted to address: gmxBaseReward.safeApprove(address(_platform), type(uint256).max);
. Since _platform is already an address passed in the constructor it doesn't need any casting.
Codebase References
Recommendation
Remove address() casting, use gmxBaseReward.safeApprove(_platform), type(uint256).max)
; instead.
Description Variables, structs, modifiers and events sometimes lack documentation through the codebase.
Recommendation Consider documenting every variable, struct, modifier, event or at least documenting the critical ones so the reader of the contracts have an easier time understanding everything.
#0 - c4-judge
2022-12-05T08:56:48Z
Picodes marked the issue as grade-a
#1 - c4-sponsor
2022-12-09T07:43:37Z
drahrealm marked the issue as sponsor confirmed