Platform: Code4rena
Start Date: 06/09/2022
Pot Size: $90,000 USDC
Total HM: 33
Participants: 168
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 10
Id: 157
League: ETH
Rank: 75/168
Findings: 3
Award: $115.47
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: MEP
Also found by: 0xSky, CertoraInc, MiloTruck, PwnPatrol, R2, Tointer, V_B, __141345__, antonttc, azephiar, cccz, d3e4, datapunk, davidbrai, easy_peasy, hansfriese, minhtrng, neumo, pcarranzav, peritoflores, scaraven, teawaterwire, tonisives, unforgiven, wagmi, zkhorse, zzzitron
5.6134 USDC - $5.61
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/token/Token.sol/#L179
This happens for bigger founder allocation, like 70% and 10% for 2 founders. The multiplier has to be divisible by the same amount, for instance 10.
In other configurations that have the same total owner percentage, the nouns are vested correctly. For instance 40% and 40% for 2 founders allocates correctly, while 70% and 10% (same 80%), doesnโt.
Test results for 70% and 10%(founder 2 gets 2% instead of 10):
forge test -vv -m test_SimilarMultiplier5 total 879 founder 1 630 71% founder 2 25 2% bidder 224 25%
Test results for 40% and 40%
forge test -vv -m test_SimilarMultiplier7 total 1184 founder1 480 40% founder2 480 40% bidder 224 18%
Add to the test folder
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { console2 } from "forge-std/console2.sol"; import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol"; import { TokenTypesV1 } from "../src/token/types/TokenTypesV1.sol"; import { IERC721 } from "../src/lib/interfaces/IERC721.sol"; import { IBaseMetadata } from "../src/token/metadata/interfaces/IBaseMetadata.sol"; contract VestingExpiryTest is NounsBuilderTest, TokenTypesV1 { address internal bidder1; address internal bidder2; function setUp() public virtual override { super.setUp(); bidder1 = vm.addr(0xB1); bidder2 = vm.addr(0xB2); vm.deal(bidder1, 100 ether); vm.deal(bidder2, 100 ether); } function deployWithCustomFounders( address[] memory _wallets, uint256[] memory _percents, uint256[] memory _vestExpirys ) internal virtual { setFounderParams(_wallets, _percents, _vestExpirys); setMockTokenParams(); // setMockAuctionParams(); // every 3 hours auction = (24/3) * 7 = 56 per week setAuctionParams(0.01 ether, 3 hours); setMockGovParams(); deploy(foundersArr, tokenParams, auctionParams, govParams); } // following have same amount of auctions: // every 3 hours = (24/3) * 7 = 56 per week // 2 founders, 70% and 10% โ๏ธ founder 2 gets 3% only function test_SimilarMultiplier5() public { createUsers(2, 1 ether); address[] memory wallets = new address[](2); uint256[] memory percents = new uint256[](2); uint256[] memory vestExpirys = new uint256[](2); wallets[0] = otherUsers[0]; percents[0] = 70; vestExpirys[0] = 4 weeks; wallets[1] = otherUsers[1]; percents[1] = 10; vestExpirys[1] = 4 weeks; deployWithCustomFounders(wallets, percents, vestExpirys); console2.log(auction.duration()); assertEq(token.totalFounders(), 2); vm.prank(wallets[0]); auction.unpause(); uint256 timePass = 4 weeks / 3 hours; uint256 auctionInterval = 1 seconds; vm.startPrank(bidder1); for (uint256 i = 0; i < timePass; ++i) { auction.createBid{ value: 0.01 ether }(token.totalSupply() - 1); auctionInterval += 3 hours; vm.warp(auctionInterval); auction.settleCurrentAndCreateNewAuction(); } vm.stopPrank(); IERC721 nft = IERC721(IBaseMetadata(token.metadataRenderer()).token()); uint256 wallet0Owned = nft.balanceOf(wallets[0]); uint256 wallet1Owned = nft.balanceOf(wallets[1]); uint256 bidderOwned = nft.balanceOf(bidder1); uint256 total = wallet0Owned + wallet1Owned + bidderOwned; console2.log("total %d", total); console2.log("founder 1 %d %d%", wallet0Owned, (wallet0Owned * 100) / total); console2.log("founder 2 %d %d%", wallet1Owned, (wallet1Owned * 100) / total); console2.log("bidder %d %d%", bidderOwned, (bidderOwned * 100) / total); /** results total 879 founder 1 630 71% founder 2 25 2% bidder 224 25% */ } // 2 founders, 40% and 40 % โ function test_SimilarMultiplier7() public { createUsers(2, 1 ether); address[] memory wallets = new address[](2); uint256[] memory percents = new uint256[](2); uint256[] memory vestExpirys = new uint256[](2); wallets[0] = otherUsers[0]; percents[0] = 40; vestExpirys[0] = 4 weeks; wallets[1] = otherUsers[1]; percents[1] = 40; vestExpirys[1] = 4 weeks; deployWithCustomFounders(wallets, percents, vestExpirys); console2.log(auction.duration()); assertEq(token.totalFounders(), 2); vm.prank(wallets[0]); auction.unpause(); uint256 timePass = 4 weeks / 3 hours; uint256 auctionInterval = 1 seconds; vm.startPrank(bidder1); for (uint256 i = 0; i < timePass; ++i) { auction.createBid{ value: 0.01 ether }(token.totalSupply() - 1); auctionInterval += 3 hours; vm.warp(auctionInterval); auction.settleCurrentAndCreateNewAuction(); } vm.stopPrank(); IERC721 nft = IERC721(IBaseMetadata(token.metadataRenderer()).token()); uint256 wallet0Owned = nft.balanceOf(wallets[0]); uint256 wallet1Owned = nft.balanceOf(wallets[1]); uint256 bidderOwned = nft.balanceOf(bidder1); uint256 total = wallet0Owned + wallet1Owned + bidderOwned; console2.log("total %d", total); console2.log("founder1 %d %d%", wallet0Owned, (wallet0Owned * 100) / total); console2.log("founder2 %d %d%", wallet1Owned, (wallet1Owned * 100) / total); console2.log("bidder %d %d%", bidderOwned, (bidderOwned * 100) / total); /** total 1184 founder1 480 40% founder2 480 40% bidder 224 18% */ } }
vscode
I think the problem might be caused by the Founder allocation logic that uses a multiplier of 100% to calculate the founder tokens here. Is it possible that the baseTokenId
multiplier overlaps for both 70% and 10%, and thus misses one founders tokens?
uint256 baseTokenId = _tokenId % 100;
#0 - GalloDaSballo
2022-09-20T19:47:08Z
#1 - GalloDaSballo
2022-09-25T19:45:28Z
Re-evaluating as a separate finding as this one is about the % statement being broken and not the revert due to 100% infinite loop
#2 - GalloDaSballo
2022-09-25T19:58:25Z
Dup of #269
๐ Selected for report: Lambda
Also found by: 0x1337, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 8olidity, Aymen0909, B2, Bnke0x0, CRYP70, Captainkay, CertoraInc, Ch_301, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, ElKu, EthLedger, Franfran, Funen, GimelSec, JansenC, Jeiwan, Jujic, Lead_Belly, MEP, MasterCookie, MiloTruck, Noah3o6, PPrieditis, PaludoX0, Picodes, PwnPatrol, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rolezn, Samatak, Tointer, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ak1, asutorufos, azephiar, ballx, bharg4v, bin2chen, bobirichman, brgltd, bulej93, c3phas, cccz, ch0bu, cloudjunky, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, davidbrai, delfin454000, dharma09, dic0de, dipp, djxploit, eierina, erictee, fatherOfBlocks, gogo, hansfriese, hyh, imare, indijanc, izhuer, jonatascm, ladboy233, leosathya, lucacez, lukris02, m9800, martin, minhtrng, ne0n, neumo, oyc_109, p_crypt0, pashov, pauliax, pcarranzav, pedr02b2, peritoflores, pfapostol, rbserver, ret2basic, robee, rvierdiiev, sach1r0, sahar, scaraven, sikorico, simon135, slowmoses, sorrynotsorry, tnevler, tonisives, volky, yixxas, zkhorse, zzzitron
60.7775 USDC - $60.78
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/manager/Manager.sol/#L80 _owner variable is shadowed by the storage variable. New developer might confuse it with the function parameter.
remove nounsDAO
ivar. it is unused https://github.com/code-423n4/2022-09-nouns-builder/blob/main/test/utils/NounsBuilderTest.sol/#L33
add onlyTreasury modifier. This check is used in 3 functions, and could be refactored into a single modifier.
if (msg.sender != address(this)) revert ONLY_TREASURY();
#0 - GalloDaSballo
2022-09-27T01:11:44Z
L
R
R
1L 2R