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
Rank: 98/111
Findings: 1
Award: $14.91
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xdeadbeef0x
Also found by: 0xLad, 0xNazgul, 0xSmartContract, 0xbepresent, Arbor-Finance, Breeje, HE1M, IllIllI, Qeew, Rolezn, SEVEN, SamGMK, SmartSek, TomJ, WatchDogs, ak1, btk, ck, datapunk, dic0de, eierina, fs0c, hansfriese, koxuan, ladboy233, peanuts, rvierdiiev, sces60107, tonisives, unforgiven, yongskiws
14.9051 USDC - $14.91
https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol/#L166 https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol/#L121
The attacker can profit from future users' deposits. While the late users will lose part of their funds to the attacker.
A well known attack vector for almost all shares based liquidity pool contracts, where an early user can manipulate the price per share and profit from late users' deposits because of the precision loss caused by the rather large value of price per share.
A malicious early user can depositAVAX()
with 1 wei
of asset
token as the first depositor of the ggAVAX, and get 1 wei
of shares.
Then the attacker can send 10000e18 - 1
of asset
tokens and inflate the price per share from 1.0000 to an extreme value of 1.0000e22 ( from (1 + 10000e18 - 1) / 1
) .
As a result, the future user who deposits 19999e18
will only receive 1 wei
(from 19999e18 * 1 / 10000e18
) of shares token.
They will immediately lose 9999e18
or half of their deposits if they redeem()
right after the deposit()
.
function depositAVAX() public payable returns (uint256 shares) { uint256 assets = msg.value; // Check for rounding error since we round down in previewDeposit. if ((shares = previewDeposit(assets)) == 0) { revert ZeroShares(); }
previewDeposit
finally calls
function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); }
Manual Review
Consider requiring a minimal amount of share tokens to be minted for the first minter, and send a port of the initial mints as a reserve to the DAO so that the previewDeposit
can be more resistant to manipulation.
#0 - c4-judge
2023-01-08T13:11:50Z
GalloDaSballo marked the issue as duplicate of #209
#1 - c4-judge
2023-01-29T18:38:39Z
GalloDaSballo changed the severity to 3 (High Risk)
#2 - c4-judge
2023-02-08T09:44:38Z
GalloDaSballo marked the issue as satisfactory