GoGoPool contest - nameruse's results

Liquid staking for Avalanche.

General Information

Platform: Code4rena

Start Date: 15/12/2022

Pot Size: $128,000 USDC

Total HM: 28

Participants: 111

Period: 19 days

Judge: GalloDaSballo

Total Solo HM: 1

Id: 194

League: ETH

GoGoPool

Findings Distribution

Researcher Performance

Rank: 60/111

Findings: 1

Award: $118.88

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: immeas

Also found by: 0x73696d616f, HollaDieWaldfee, cccz, ck, cozzetti, datapunk, koxuan, nameruse, wagmi, yixxas

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-494

Awards

118.8832 USDC - $118.88

External Links

Lines of code

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L380-L440 https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L667-L683

Vulnerability details

Impact

GoGoPool uses staked GGP to compensate users for not receiving the avax rewards in the case where Node operators fails to validate sufficiently and earn validation rewards.

A minimum staked GGP collateral of 10% is required before a minipool can be created which can potentially compensate up to 12 months of a failed validation period as the avax reward rate is estimate at 10%. In which case the 10% GGP can be converted to avax and sent to the users.

The protocol uses the price of GGP in AVAX in order to calculate the amount of GGP required to stake as collateral and at a later phase also the amount of GGP required to slashed to compensate the users.

The problem lies in that price of GGP can vary in such a way where the 10% collateral can become less than 10% by the time the slashing is happening, the minipool would have been created and running for a year afterwhich ending the minipool(Minipool.recordStakingEnd()) would revert because the node operator would not have staked sufficient GGP to cover the slashing due to a price drop of GGP when compared to the price of GGP during the creation of the minipool.

While the users can potentially still receive their rewards and their staked AVAX through manual efforts with the protocol team (also kinda going against the essence of decentralized protocol), there could be a slightly better solution to at least enforcing the slashing.

Proof of Concept

This is how collateral ratio is calculated Link to full code

uint256 ggpStakedInAvax = getGGPStake(stakerAddr).mulWadDown(ggpPriceInAvax); return ggpStakedInAvax.divWadDown(avaxAssigned);

This is how the ggp slashing amount is calculated Link to full code

return avaxRewardAmt.divWadDown(ggpPriceInAvax);

When creating a minipool the 10% minimum collateral ratio is enforced here

So assuming the below at the time a minipool is being created: minipool duration = 1 year avaxAssigned = 1000 ggpPriceInAvax = 2 Therefore min collateral value required = 100 avax required ggpCollateralAmount = 50 ggp

1 year later at validation end with failed validation, slashing should occur although assuming ggpPrice decreased: EstimatedRewardAmount (to users)= 100 avax ggpPriceInAvax = 1 Therfore ggp reqruired = 100 ggp

User's staked amount was only 50 ggp as that was sufficient during the time of minipool creation and due to this slashing will revert because the Node operation would not have enough ggp staked so the Staking.decreaseGGP() function will fail this would lead the Node operator not getting penalilzed

Tools Used

VSCode

Staking.slashGGP() can check if the ggpAmt is greater than the total amount of ggp staked by the staker and if so use the total amount of ggp staked, at least that way slashing can be enforced to still penalize the failed validation. These can used for either additional rewards to the users or possibly just burned

function slashGGP(address stakerAddr, uint256 ggpAmt) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) { Vault vault = Vault(getContractAddress("Vault")); stakedggpAmt = getGGPStake(stakerAddr); ggpAmt = if(ggpAmt>stakedggpAmt)?stakedggpAmt:ggpAmt; decreaseGGPStake(stakerAddr, ggpAmt); vault.transferToken("ProtocolDAO", ggp, ggpAmt); }

#0 - c4-judge

2023-01-09T09:47:04Z

GalloDaSballo marked the issue as duplicate of #136

#1 - c4-judge

2023-02-03T19:37:10Z

GalloDaSballo marked the issue as not a duplicate

#2 - c4-judge

2023-02-03T19:40:43Z

GalloDaSballo changed the severity to 3 (High Risk)

#3 - c4-judge

2023-02-03T19:56:23Z

GalloDaSballo marked the issue as duplicate of #494

#4 - c4-judge

2023-02-03T19:58:50Z

GalloDaSballo changed the severity to 2 (Med Risk)

#5 - c4-judge

2023-02-08T20:16:53Z

GalloDaSballo marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter