Platform: Code4rena
Start Date: 21/08/2023
Pot Size: $125,000 USDC
Total HM: 26
Participants: 189
Period: 16 days
Judge: GalloDaSballo
Total Solo HM: 3
Id: 278
League: ETH
Rank: 36/189
Findings: 3
Award: $537.51
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xWagmi
Also found by: 836541, Bauchibred, GangsOfBrahmin, Hama, IceBear, Inspecktor, Matin, MohammedRizwan, catellatech, erebus, lsaudit, niki, okolicodes, ravikiranweb3, tapir, vangrim, zaevlad
46.2486 USDC - $46.25
Inflation share price can be done by depositing as soon as the vault is created.
Impact:
The problem exists because the exchange rate is calculated as the ratio between shares totalSupply and totalAssets(). When an attacker transfers assets, totalAssets() incrementally increases and hence the exchange rate also increases.
ERC4626 vaults are subject to a share price manipulation attack that allows an attacker to steal the underlying tokens from other contributors (this is a known issue with the Solmate ERC4626 implementation (https://github.com/transmissions11/solmate/issues/178)).
Alice - the first custodian of the PerpetualAtlanticVaultLP;
Alice contributes 1 wei tokens;
In the deposit() (https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L118-L135) function, the number of shares is calculated using previewDeposit () functions: function deposit( uint256 assets, address receiver ) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");
perpetualAtlanticVault.updateFunding();
// Need to transfer before minting or ERC777s could reenter. collateral.transferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
_totalCollateral += assets;
emit Deposit(msg.sender, receiver, assets, shares); }
Since Alice is the first contributor (totalSupply is 0), she gets 1 share (1 wei);
Then Alice sends 9999999999999999999 tokens (10e18 - 1) to the vault;
The price for 1 share is now 10 tokens: Alice is the only depositor in the vault, she holds 1 wei of shares, and the pool balance is 10 tokens;
Bob contributes 19 tokens and only gets 1 share due to rounding in the convertToShares function: 10e18 * 1 / 10e18 == 1;
Alice redeems her share and receives half of the deposited assets, 14.5 tokens (minus the withdrawal fee);
Bob redeems his share and receives only 14.5 tokens (minus withdrawal fees) instead of the 19 tokens he deposited.
Manual review
Consider any of these options:
ERC4626
#0 - c4-pre-sort
2023-09-07T13:34:02Z
bytes032 marked the issue as duplicate of #863
#1 - c4-pre-sort
2023-09-11T09:10:54Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-18T12:41:47Z
GalloDaSballo changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-10-18T12:50:24Z
GalloDaSballo marked the issue as satisfactory
491.258 USDC - $491.26
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1016-L1042
The RdpxV2Core.sol contract implements the pause functionality with the ability to check whether the contract is in pause mode (function _whenNotPaused()). _whenNotPaused() is present in almost all public functions. However, the redeem() function (https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1016-L1042) does not have it.
Manual review
Add _whenNotPaused() to the redeem() function.
Access Control
#0 - c4-pre-sort
2023-09-10T08:06:33Z
bytes032 marked the issue as duplicate of #1809
#1 - c4-pre-sort
2023-09-11T12:11:31Z
bytes032 marked the issue as duplicate of #6
#2 - c4-pre-sort
2023-09-11T12:12:21Z
bytes032 marked the issue as sufficient quality report
#3 - c4-judge
2023-10-20T19:59:51Z
GalloDaSballo marked the issue as satisfactory