Platform: Code4rena
Start Date: 07/07/2022
Pot Size: $75,000 USDC
Total HM: 32
Participants: 141
Period: 7 days
Judge: HardlyDifficult
Total Solo HM: 4
Id: 144
League: ETH
Rank: 54/141
Findings: 2
Award: $143.24
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: dipp
Also found by: 0x52, Lambda, PwnedNoMore, Ruhum, Treasure-Seeker, ak1, auditor0517, hansfriese, jonatascm, kenzo, panprog, smiling_heretic, xiaoming90
The caller can claim more than his/her shares of fractional tokens. Since the caller can claim more than his/her tokens, I would put this under high.
function migrateFractions(address _vault, uint256 _proposalId) external { // Reverts if address is not a registered vault (, uint256 id) = IVaultRegistry(registry).vaultToToken(_vault); if (id == 0) revert NotVault(_vault); // Reverts if buyout state is not successful (, address proposer, State current, , , ) = IBuyout(buyout).buyoutInfo( _vault ); State required = State.SUCCESS; if (current != required) revert IBuyout.InvalidState(required, current); // Reverts if proposer of buyout is not this contract if (proposer != address(this)) revert NotProposalBuyout(); // Gets the last total supply of fractions for the vault (, , , , , uint256 lastTotalSupply) = IBuyout(buyout).buyoutInfo( _vault ); // Calculates the total ether amount of a successful proposal uint256 totalInEth = _calculateTotal( 1 ether, lastTotalSupply, migrationInfo[_vault][_proposalId].totalEth, migrationInfo[_vault][_proposalId].totalFractions ); // Calculates balance of caller based on ether contribution uint256 balanceContributedInEth = _calculateContribution( totalInEth, lastTotalSupply, userProposalEth[_proposalId][msg.sender], userProposalFractions[_proposalId][msg.sender] ); // Gets the token and fraction ID of the new vault address newVault = migrationInfo[_vault][_proposalId].newVault; (address token, uint256 newFractionId) = IVaultRegistry(registry) .vaultToToken(newVault); // Calculates share amount of fractions for the new vault based on the new total supply uint256 newTotalSupply = IVaultRegistry(registry).totalSupply(newVault); uint256 shareAmount = (balanceContributedInEth * newTotalSupply) / totalInEth; // Transfers fractional tokens to caller based on share amount IFERC1155(token).safeTransferFrom( address(this), msg.sender, newFractionId, shareAmount, "" ); }
The caller can call this function even after claiming his/her tokens. This allows the caller to claim more tokens. There are no stop checks to avoid this.
Manual code review
There are more than one solution to avoid this. I have listed few of them below.
#0 - mehtaculous
2022-07-20T16:56:51Z
Duplicate of #460
#1 - HardlyDifficult
2022-08-11T17:18:40Z
🌟 Selected for report: xiaoming90
Also found by: 0x1f8b, 0x29A, 0x52, 0xA5DF, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xsanson, 0xsolstars, 242, 8olidity, Amithuddar, Aymen0909, Bnke0x0, BowTiedWardens, David_, Deivitto, ElKu, Funen, Hawkeye, IllIllI, JC, Kaiziron, Keen_Sheen, Kthere, Kulk0, Kumpa, Lambda, MEP, ReyAdmirado, Rohan16, Ruhum, Sm4rty, TomJ, Tomio, Treasure-Seeker, TrungOre, Tutturu, Viksaa39, Waze, _Adam, __141345__, ak1, apostle0x01, asutorufos, async, ayeslick, aysha, bbrho, benbaessler, berndartmueller, c3phas, cccz, chatch, cloudjunky, codexploder, cryptphi, delfin454000, dipp, durianSausage, dy, exd0tpy, fatherOfBlocks, hake, hansfriese, horsefacts, hubble, joestakey, jonatascm, kebabsec, kenzo, kyteg, mektigboy, neumo, oyc_109, pashov, pedr02b2, peritoflores, rajatbeladiya, rbserver, robee, rokinot, s3cunda, sach1r0, sahar, sashik_eth, scaraven, shenwilly, simon135, sorrynotsorry, sseefried, svskaushik, unforgiven, z3s, zzzitron
61.9379 USDC - $61.94
The current implementation allows for vault creation without any modules. This can be misused by the bad actors for creating vault without any modules. This could be impact the credibility of the protocol.
Suggestion/solution : We would enforce the front end to display vaults with a known combination of modules that we know about. A vault with no modules wouldn’t be displayed in that case.