Platform: Code4rena
Start Date: 13/12/2023
Pot Size: $36,500 USDC
Total HM: 18
Participants: 110
Period: 8 days
Judge: 0xTheC0der
Id: 311
League: ETH
Rank: 11/110
Findings: 8
Award: $815.17
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: osmanozdemir1
Also found by: 0xG0P1, King_, SpicyMeatball, hals, ktg, rvierdiiev
494.8735 USDC - $494.87
When piece is created then creationBlock
is set to block.number
. So all votes will be fetched for that block only during the piece voting. Also quorumVotes
is calculated for the piece as percentage from current total supply of voting tokens which are erc20 token and erc721 verbs token. To pass voting, piece should have more than quorumVotes
.
AuctionHouse is selling verbs tokens one by one. For each new auction verb token is minted, which increases total supply of erc721 voting token. In case if no one is interested in purchasing, then the token will be burnt.
So as i said, new auction mints new token and increases total voting power. In case if new piece will be created during the auction, then this increased total voting power will be used to calculate quorum. And in case if then this token will be burnt(for example in same block), then actual total supply will be smaller than was calculated for the quorum.
Another thing that i want to say is that in case if piece was created at the moment, when auction was in progress, then even when someone will purchase that token, purchaser will never be able to participate in voting for that token(only if he will settle auction is same block when piece was created), because of creationBlock
. This means that usually, ongoing auction's nft will never participate in the piece voting, however it's weight is used in the total supply of erc721.
Exactly same problem exists for the DAO voting. The minted verb token increases totalSupply and thus increases proposalThreshold
. But again purchaser will not be able to use that nft for voting on the proposals that were created during the auction.
The quorum calculation is not correct.
VsCode
So first of all creationBlock
should be block.number - 1
(then even if settling was in same block with piece creating, then this nft doesn't count).
And i think, that createPiece
should not take into account auctioned nft and reduce newPiece.totalVotesSupply
with voting power for that nft. The best option will be to not increase totalSupply, when verb is minted for the auction, but only when it's sold.
Error
#0 - c4-pre-sort
2023-12-21T21:37:47Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-21T21:37:51Z
raymondfam marked the issue as primary issue
#2 - rocketman-21
2024-01-03T02:07:18Z
The quorum calculation is not incorrect, this is how Nouns functions and the expected behavior of our contracts.
#3 - c4-sponsor
2024-01-03T02:07:22Z
rocketman-21 (sponsor) disputed
#4 - c4-judge
2024-01-08T21:43:08Z
MarioPoneder marked the issue as duplicate of #409
#5 - c4-judge
2024-01-08T21:43:17Z
MarioPoneder marked the issue as satisfactory
#6 - c4-judge
2024-01-10T18:27:48Z
MarioPoneder marked the issue as not a duplicate
#7 - c4-judge
2024-01-10T18:28:16Z
MarioPoneder marked the issue as duplicate of #168
#8 - c4-judge
2024-01-10T18:32:36Z
MarioPoneder changed the severity to 3 (High Risk)
🌟 Selected for report: deth
Also found by: 00xSEV, 0xCiphky, 0xHelium, ABAIKUNANBAEV, Aamir, AkshaySrivastav, King_, Pechenite, SpicyMeatball, Tricko, ast3ros, ayden, bart1e, deth, fnanni, ke1caM, mahdirostami, peanuts, pep7siup, pontifex, ptsanev, roland, rvierdiiev, y4y
7.2215 USDC - $7.22
When new piece is created, then it is inserted in the heap with 0 votes. Also creationBlock
is set to block.number for that piece. It means that maximum amount that this piece can get is limited to the total supply of voting tokens in the creationBlock
.
When someone cast votes for the piece, then votes from creationBlock
are fetched as the amount. Then this amount is used to update piece in the heap.
So here is the problem. Suppose that 1 piece was created when total voting supply was 100 and another one is created when total voting supply was 1000. Second piece needs just 10% of total supply to beat first piece. Let's imagine that in some moment first piece gets all 100%, but second piece has 110 votes(just 11%). As result second piece will be at the top of the heap. Now let's also imagine that quorum is 60%, which means that first piece passes it, however second one don't and as result token minting is failing.
Voting system is not good enough.
VsCode
I think that instead of storing total votes in the heap, you need to store percentage of total supply at the creation time. So then in mine example, first piece will have 100, while second will have 11 and as result first piece will be selected.
Error
#0 - c4-pre-sort
2023-12-21T21:32:42Z
raymondfam marked the issue as primary issue
#1 - c4-pre-sort
2023-12-21T21:32:46Z
raymondfam marked the issue as sufficient quality report
#2 - raymondfam
2023-12-24T05:18:04Z
Duplicated submissions come with various/opposing scenarios originating from the same root cause.
#3 - c4-pre-sort
2023-12-24T15:11:33Z
raymondfam marked the issue as duplicate of #449
#4 - c4-judge
2024-01-06T16:03:57Z
MarioPoneder marked the issue as not a duplicate
#5 - c4-judge
2024-01-06T16:04:20Z
MarioPoneder marked the issue as duplicate of #449
#6 - c4-judge
2024-01-06T16:04:25Z
MarioPoneder marked the issue as satisfactory
🌟 Selected for report: ast3ros
Also found by: 0xG0P1, KupiaSec, Pechenite, cccz, deth, dimulski, mojito_auditor, osmanozdemir1, peanuts, rvierdiiev, zhaojie
51.1381 USDC - $51.14
When new piece is created, then block.numer
is stored as newPiece.creationBlock
. This means that during the voting all votes amounts will be fetched for the creationBlock
and maximum voting power should be limited to the supply at the moment of creation.
We can also see, that newPiece.quorumVotes
amount is also calculated for the piece and is based on the total supply of voting tokens at the current moment
. newPiece.quorumVotes
will be used to determine if piece can be minted.
The problem is that when createPiece
is called, then block is not finished yet and total supply for voting tokens is not finalized for that block as well. This allows user to manipulate quorumVotes
amount. They can create piece and increase total supply of voting tokens to be able to pass piece voting even with smaller percentage than quorum.
Users can make it easier to mint piece and get fees
VsCode
Use previous block for the voting.
Error
#0 - c4-pre-sort
2023-12-21T21:34:36Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-21T21:34:40Z
raymondfam marked the issue as primary issue
#2 - c4-pre-sort
2023-12-22T05:11:16Z
raymondfam marked the issue as duplicate of #260
#3 - c4-pre-sort
2023-12-24T05:39:50Z
raymondfam marked the issue as duplicate of #409
#4 - c4-judge
2024-01-05T22:37:10Z
MarioPoneder marked the issue as satisfactory
🌟 Selected for report: deepplus
Also found by: 0xDING99YA, 0xmystery, Aymen0909, DanielArmstrong, Inference, KupiaSec, SadeeqXmosh, SpicyMeatball, Tricko, adeolu, jnforja, passteque, rvierdiiev, wangxx2026, zhaojie
25.1638 USDC - $25.16
Using ERC20TokenEmitter.buyToken
function anyone can buy erc20 voting tokens. buyToken
function uses vrgda approach to calculate token's price. The goal of vrgda is to sell fixed amount of tokens per unit of time. Depending on the current sold amount in this unit vrgda can decrease or increase price to attract buyers or vice versa.
Because of the described behavior of vrgda it means that price of erc20 token is not stable and changes after each call and time. Thus the function needs slippage protection, so buyer can get at least minimum amount that he is agreed and revert otherwise and not cause loses for the caller.
No slippage control for the caller.
VsCode
Add additional param, like minErc20Amount
, as slippage protection. Also you can add expire check as well.
Error
#0 - c4-pre-sort
2023-12-21T22:12:21Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-21T22:12:25Z
raymondfam marked the issue as primary issue
#2 - c4-pre-sort
2023-12-24T06:00:59Z
raymondfam marked the issue as duplicate of #397
#3 - c4-judge
2024-01-06T16:31:56Z
MarioPoneder marked the issue as satisfactory
🌟 Selected for report: osmanozdemir1
Also found by: 0xDING99YA, BARW, Brenzee, DanielArmstrong, KupiaSec, SpicyMeatball, bart1e, deepplus, haxatron, rouhsamad, rvierdiiev
51.1381 USDC - $51.14
Using ERC20TokenEmitter.buyToken
function anyone can buy erc20 voting tokens. buyToken
function uses vrgda approach to calculate tokens price. The goal of vrgda is to sell fixed amount of tokens per unit of time. Depending on the current sold amount in this the unit vrgda can decrease or increase price to attract buyers or vice versa.
buyToken
function is payable and msg.value
is the amount of eth that buyer is willing to spend for erc20 tokens. Part of this amount is considered as creator payment and entropyRateBps
variable determines how much of the creator's payment should be provided in the form of erc20.
Thus totalTokensForCreators
param is amount of erc20 tokens that creator will receive. To calculate that amount function calls getTokenQuoteForEther((msgValueRemaining - toPayTreasury) - creatorDirectPayment)
, which is vrgda call.
Only after creator's tokens were calculated using vrgda, then purchasers tokens amount is calculated. And only after 2 calculations emittedTokenWad
is increased.
Note, that emittedTokenWad
is the total sold amount that is used by vrgda algorithm. Thus after totalTokensForCreators
is calculated, then emittedTokenWad
is not increased, which means that vrgda calculation of totalTokensForBuyers
will be incorrect.
erc20 voting tokens are calculated and distributed incorrectly
VsCode
I think that you need to call getTokenQuoteForEther
for the whole amount: treasury funds + entropy of creator and then distribute them.
Error
#0 - c4-pre-sort
2023-12-21T22:07:25Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-12-21T22:07:29Z
raymondfam marked the issue as primary issue
#2 - c4-pre-sort
2023-12-22T03:28:34Z
raymondfam marked the issue as sufficient quality report
#3 - c4-pre-sort
2023-12-22T03:30:55Z
raymondfam marked the issue as duplicate of #194
#4 - c4-judge
2024-01-06T13:53:50Z
MarioPoneder marked the issue as satisfactory
🌟 Selected for report: ayden
Also found by: 0xCiphky, AS, Brenzee, Inference, KupiaSec, SpicyMeatball, ast3ros, ayden, fnanni, hals, ktg, mahdirostami, nmirchev8, rvierdiiev, wangxx2026
29.8238 USDC - $29.82
When auction is settled, then creators of piece receive rewards. Part of this rewards are sent in eth and other part of eth is used to buy voting tokens for creators. Amount that should be sent directly depends on entropyRateBps
. In case if entropyRateBps
is 0, then all rewards should be sent in form of erc20 voting token.
The problem that in this case vrgdaSplits
and vrgdaReceivers
arrays will not be initialized and as result erc20TokenEmitter.buyToken
function will revert, because of this check. As result settling will stuck, until entropyRateBps
will be set to bigger value.
Settling will stuck.
VsCode
Initialize vrgdaSplits
and vrgdaReceivers
arrays before if
block.
Error
#0 - c4-pre-sort
2023-12-21T21:57:22Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-12-21T21:57:27Z
raymondfam marked the issue as primary issue
#2 - raymondfam
2023-12-21T21:58:33Z
They have been verified here:
uint256 creatorArrayLength = validateCreatorsArray(creatorArray); // Validate the media type and associated data validateMediaType(metadata);
#3 - c4-pre-sort
2023-12-23T02:19:56Z
raymondfam marked the issue as sufficient quality report
#4 - c4-pre-sort
2023-12-23T02:48:37Z
raymondfam marked the issue as duplicate of #335
#5 - c4-judge
2024-01-06T01:25:32Z
MarioPoneder changed the severity to QA (Quality Assurance)
#6 - c4-judge
2024-01-06T15:11:22Z
This previously downgraded issue has been upgraded by MarioPoneder
#7 - c4-judge
2024-01-06T15:12:20Z
MarioPoneder marked the issue as duplicate of #160
#8 - c4-judge
2024-01-06T15:12:46Z
MarioPoneder marked the issue as satisfactory
🌟 Selected for report: Aamir
Also found by: 0xCiphky, SovaSlava, bart1e, osmanozdemir1, rvierdiiev, shaka
148.462 USDC - $148.46
CultureIndex contract allows users to sign cast votes messages, so then can be executed by third party using voteForManyWithSig
or batchVoteForManyWithSig
functions.
Validation of the signed message is done in the _verifyVoteSignature
function. This is how message is encoded.
voteHash = keccak256(abi.encode(VOTE_TYPEHASH, from, pieceIds, nonces[from]++, deadline));
pieceIds
is an array and according to the eip712:
https://eips.ethereum.org/EIPS/eip-712#definition-of-encodedata
The array values are encoded as the keccak256 hash of the concatenated encodeData of their contents (i.e. the encoding of SomeType[5] is identical to that of a struct containing five members of type SomeType). The struct values are encoded recursively as hashStruct(value). This is undefined for cyclical data.
This means that arrays should be additionally encoded which is not done in the contract. As result contract is not eip712 compatible, which means that it will fail to integrate with third party signing solutions.
Contract is not eip712 compatible.
VsCode
Pls, check this answer it has links to the correct implementation https://ethereum.stackexchange.com/questions/125105/signing-an-array-whit-eth-signtypeddata-v4
Error
#0 - c4-pre-sort
2023-12-21T21:40:26Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-21T21:40:30Z
raymondfam marked the issue as primary issue
#2 - c4-sponsor
2024-01-03T02:18:41Z
rocketman-21 (sponsor) confirmed
#3 - c4-judge
2024-01-06T15:18:37Z
MarioPoneder marked the issue as satisfactory
#4 - c4-judge
2024-01-06T15:21:42Z
MarioPoneder marked issue #77 as primary and marked this issue as a duplicate of 77
🌟 Selected for report: 0xmystery
Also found by: 00xSEV, 0xCiphky, 0xDING99YA, 0xhitman, ABAIKUNANBAEV, Aamir, BARW, IllIllI, King_, MrPotatoMagic, Pechenite, SovaSlava, SpicyMeatball, Topmark, Ward, ZanyBonzy, ast3ros, bart1e, cheatc0d3, deth, developerjordy, hals, imare, kaveyjoe, ktg, leegh, osmanozdemir1, peanuts, roland, rvierdiiev, shaka, sivanesh_808, spacelord47
7.359 USDC - $7.36
AuctionHouse is configured with reservePrice
and minBidIncrementPercentage
params adn both of them can be 0. While reservePrice
is unlikely will be 0, minBidIncrementPercentage
can be for sure, as minBidIncrementPercentage == 0
means that auction owner doesn't care what is the min price increment, he only wants each bid to be bigger than previous.
createBid
function is used to provide your bid. Several criterias should be met. Bid should be not smaller than reservePrice
and bid should be not less
than increment previous bid.
In case if minBidIncrementPercentage
is set to 0, then it will be enough for next bidder to just send same amount of funds that was sent by previous bidder and he will get the nft.
Bidder can provide same amount and get the nft.
VsCode
Make sure that current bid is bigger than previous one.
Error
#0 - c4-pre-sort
2023-12-21T21:52:08Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-21T21:52:19Z
raymondfam marked the issue as primary issue
#2 - c4-pre-sort
2023-12-24T05:51:55Z
raymondfam marked the issue as insufficient quality report
#3 - c4-pre-sort
2023-12-24T05:52:10Z
raymondfam marked the issue as duplicate of #197
#4 - c4-judge
2024-01-06T20:28:37Z
MarioPoneder changed the severity to QA (Quality Assurance)
#5 - c4-judge
2024-01-06T20:30:07Z
MarioPoneder marked the issue as grade-c
#6 - rvierdiyev
2024-01-10T09:04:51Z
I would like the judge to review this issue one more time.
While root issue #197 is talking about malicious owner, this report doesn't talk about any malicious owner, but it says about using minBidIncrementPercentage == 0
as normal param for the auction. In the report i have described why it's possible that minBidIncrementPercentage == 0
will be used and i think it's realistic that such situation will occur.
#7 - MarioPoneder
2024-01-11T14:59:33Z
Thank you for your comment!
Although I appreciate the validity of this concern, it requires the owner/DAO to misconfigure the parameter to minBidIncrementPercentage = 0
. There is no incentive to do that, but it's a valid QA suggestion to enforce limits in the setter method to avoid accidental misconfiguration.
#8 - c4-judge
2024-01-11T14:59:37Z
MarioPoneder marked the issue as grade-b