Skip to content

Commit

Permalink
Merge pull request #126 from ourzora/v2-mitigations-only-use-merkle-c…
Browse files Browse the repository at this point in the history
…laim-once

Restrict merkle claims to only be used once
  • Loading branch information
neokry authored Jan 2, 2024
2 parents 9686e8f + d46bac3 commit 5bd5dcc
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/minters/MerkleReserveMinter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ contract MerkleReserveMinter {
/// @param merkleRoot Merkle root for collection
error INVALID_MERKLE_PROOF(address mintTo, bytes32[] merkleProof, bytes32 merkleRoot);

/// @dev Claim has already been used
error CLAIM_ALREADY_USED();

/// ///
/// STRUCTS ///
/// ///
Expand Down Expand Up @@ -96,6 +99,9 @@ contract MerkleReserveMinter {
/// @notice Mapping of DAO token contract to merkle settings
mapping(address => MerkleMinterSettings) public allowedMerkles;

/// @notice Mapping of token contract to used claims
mapping(address => mapping(uint256 => bool)) public usedClaims;

/// ///
/// MODIFIERS ///
/// ///
Expand Down Expand Up @@ -161,6 +167,14 @@ contract MerkleReserveMinter {
revert INVALID_MERKLE_PROOF(claim.mintTo, claim.merkleProof, settings.merkleRoot);
}

// Check if claim has already been used
if (usedClaims[tokenContract][claim.tokenId]) {
revert CLAIM_ALREADY_USED();
}

// Mark claim as used
usedClaims[tokenContract][claim.tokenId] = true;

// Only allowing reserved tokens to be minted for this strategy
IToken(tokenContract).mintFromReserveTo(claim.mintTo, claim.tokenId);
}
Expand Down
41 changes: 41 additions & 0 deletions test/MerkleReserveMinter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,45 @@ contract MerkleReserveMinterTest is NounsBuilderTest {
assertEq(pricePerToken, 0);
assertEq(merkleRoot, bytes32(0));
}

function testRevert_CannotReuseClaim() public {
deployAltMock(20);

bytes32 root = bytes32(0x5e0da80989496579de029b8ad2f9c234e8de75f5487035210bfb7676e386af8b);

MerkleReserveMinter.MerkleMinterSettings memory settings = MerkleReserveMinter.MerkleMinterSettings({
mintStart: 0,
mintEnd: uint64(block.timestamp + 1000),
pricePerToken: 0 ether,
merkleRoot: root
});

vm.prank(address(founder));
minter.setMintSettings(address(token), settings);

(uint64 mintStart, uint64 mintEnd, uint64 pricePerToken, bytes32 merkleRoot) = minter.allowedMerkles(address(token));
assertEq(mintStart, settings.mintStart);
assertEq(mintEnd, settings.mintEnd);
assertEq(pricePerToken, settings.pricePerToken);
assertEq(merkleRoot, settings.merkleRoot);

TokenTypesV2.MinterParams memory params = TokenTypesV2.MinterParams({ minter: address(minter), allowed: true });
TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1);
minters[0] = params;
vm.prank(address(founder));
token.updateMinters(minters);

bytes32[] memory proof = new bytes32[](1);
proof[0] = bytes32(0xd77d6d8eeae66a03ce8ecdba82c6a0ce9cff76f7a4a6bc2bdc670680d3714273);

MerkleReserveMinter.MerkleClaim[] memory claims = new MerkleReserveMinter.MerkleClaim[](1);
claims[0] = MerkleReserveMinter.MerkleClaim({ mintTo: claimer1, tokenId: 5, merkleProof: proof });

minter.mintFromReserve(address(token), claims);

assertEq(token.ownerOf(5), claimer1);

vm.expectRevert(abi.encodeWithSignature("CLAIM_ALREADY_USED()"));
minter.mintFromReserve(address(token), claims);
}
}

0 comments on commit 5bd5dcc

Please sign in to comment.