Skip to content

Commit 0391ad7

Browse files
committed
fix claim quantity limit
1 parent 41cc5ee commit 0391ad7

File tree

10 files changed

+247
-8
lines changed

10 files changed

+247
-8
lines changed

contracts/drop/DropERC1155.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,12 @@ contract DropERC1155 is
290290
_proofMaxQuantityPerTransaction
291291
);
292292

293+
// ClaimCondition memory currentClaimPhase = claimCondition[_tokenId].phases[activeConditionId];
294+
293295
// Verify claim validity. If not valid, revert.
294-
bool toVerifyMaxQuantityPerTransaction = _proofMaxQuantityPerTransaction == 0;
296+
// when there's allowlist present --> verifyClaimMerkleProof will verify the _proofMaxQuantityPerTransaction value with hashed leaf in the allowlist
297+
// when there's no allowlist, this check is true --> verifyClaim will check for _quantity being less/equal than the limit
298+
bool toVerifyMaxQuantityPerTransaction = _proofMaxQuantityPerTransaction == 0 || claimCondition[_tokenId].phases[activeConditionId].merkleRoot == bytes32(0);
295299
verifyClaim(
296300
activeConditionId,
297301
_msgSender(),

contracts/drop/DropERC20.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,12 @@ contract DropERC20 is
209209
_proofMaxQuantityPerTransaction
210210
);
211211

212+
// ClaimCondition memory currentClaimPhase = claimCondition.phases[activeConditionId];
213+
212214
// Verify claim validity. If not valid, revert.
213-
bool toVerifyMaxQuantityPerTransaction = _proofMaxQuantityPerTransaction == 0;
215+
// when there's allowlist present --> verifyClaimMerkleProof will verify the _proofMaxQuantityPerTransaction value with hashed leaf in the allowlist
216+
// when there's no allowlist, this check is true --> verifyClaim will check for _quantity being less/equal than the limit
217+
bool toVerifyMaxQuantityPerTransaction = _proofMaxQuantityPerTransaction == 0 || claimCondition.phases[activeConditionId].merkleRoot == bytes32(0);
214218
verifyClaim(
215219
activeConditionId,
216220
_msgSender(),

contracts/drop/DropERC721.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,12 @@ contract DropERC721 is
353353
_proofMaxQuantityPerTransaction
354354
);
355355

356+
// ClaimCondition memory currentClaimPhase = claimCondition.phases[activeConditionId];
357+
356358
// Verify claim validity. If not valid, revert.
357-
bool toVerifyMaxQuantityPerTransaction = _proofMaxQuantityPerTransaction == 0;
359+
// when there's allowlist present --> verifyClaimMerkleProof will verify the _proofMaxQuantityPerTransaction value with hashed leaf in the allowlist
360+
// when there's no allowlist, this check is true --> verifyClaim will check for _quantity being less/equal than the limit
361+
bool toVerifyMaxQuantityPerTransaction = _proofMaxQuantityPerTransaction == 0 || claimCondition.phases[activeConditionId].merkleRoot == bytes32(0);
358362
verifyClaim(
359363
activeConditionId,
360364
_msgSender(),

foundry.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ src = 'contracts'
2323
test = 'src/test'
2424
verbosity = 0
2525
#ignored_error_codes = []
26-
#fuzz_runs = 256
26+
fuzz_runs = 100
2727
#ffi = false
2828
#sender = '0x00a329c0648769a73afac7f9381e08fb43dbea72'
2929
#tx_origin = '0x00a329c0648769a73afac7f9381e08fb43dbea72'

lib/forge-std

src/test/drop/DropERC1155.t.sol

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "contracts/drop/DropERC1155.sol";
5+
6+
// Test imports
7+
import "../utils/BaseTest.sol";
8+
9+
contract DropERC1155Test is BaseTest {
10+
DropERC1155 public drop;
11+
12+
function setUp() public override {
13+
super.setUp();
14+
drop = DropERC1155(getContract("DropERC1155"));
15+
}
16+
17+
/*///////////////////////////////////////////////////////////////
18+
Miscellaneous
19+
//////////////////////////////////////////////////////////////*/
20+
21+
function test_revert_claim_claimQty() public {
22+
vm.warp(1);
23+
24+
address receiver = getActor(0);
25+
bytes32[] memory proofs = new bytes32[](0);
26+
27+
DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1);
28+
conditions[0].maxClaimableSupply = 500;
29+
conditions[0].quantityLimitPerTransaction = 100;
30+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
31+
32+
vm.prank(deployer);
33+
drop.lazyMint(500, "ipfs://");
34+
35+
vm.prank(deployer);
36+
drop.setClaimConditions(0, conditions, false);
37+
38+
vm.prank(getActor(5), getActor(5));
39+
vm.expectRevert("invalid quantity claimed.");
40+
drop.claim(receiver, 0, 200, address(0), 0, proofs, 2);
41+
42+
vm.prank(deployer);
43+
drop.setClaimConditions(0, conditions, true);
44+
45+
vm.prank(getActor(5), getActor(5));
46+
vm.expectRevert("invalid quantity claimed.");
47+
drop.claim(receiver, 0, 200, address(0), 0, proofs, 1);
48+
}
49+
}

src/test/drop/DropERC20.t.sol

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "contracts/drop/DropERC20.sol";
5+
6+
// Test imports
7+
import "../utils/BaseTest.sol";
8+
9+
contract DropERC20Test is BaseTest {
10+
DropERC20 public drop;
11+
12+
function setUp() public override {
13+
super.setUp();
14+
drop = DropERC20(getContract("DropERC20"));
15+
}
16+
17+
/*///////////////////////////////////////////////////////////////
18+
Miscellaneous
19+
//////////////////////////////////////////////////////////////*/
20+
21+
function test_revert_claim_claimQty() public {
22+
vm.warp(1);
23+
24+
address receiver = getActor(0);
25+
bytes32[] memory proofs = new bytes32[](0);
26+
27+
DropERC20.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1);
28+
conditions[0].maxClaimableSupply = 500;
29+
conditions[0].quantityLimitPerTransaction = 100;
30+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
31+
32+
// vm.prank(deployer);
33+
// drop.setMaxTotalSupply(10_000);
34+
35+
vm.prank(deployer);
36+
drop.setClaimConditions(conditions, false);
37+
38+
vm.prank(getActor(5), getActor(5));
39+
vm.expectRevert("invalid quantity claimed.");
40+
drop.claim(receiver, 200, address(0), 0, proofs, 1);
41+
42+
vm.prank(deployer);
43+
drop.setClaimConditions(conditions, true);
44+
45+
vm.prank(getActor(5), getActor(5));
46+
vm.expectRevert("invalid quantity claimed.");
47+
drop.claim(receiver, 200, address(0), 0, proofs, 1);
48+
}
49+
}

src/test/drop/DropERC721.t.sol

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
pragma solidity ^0.8.0;
33

44
import "contracts/drop/DropERC721.sol";
5+
import "@openzeppelin/contracts/utils/Strings.sol";
56

67
// Test imports
78
import "../utils/BaseTest.sol";
89

910
contract SubExploitContract is ERC721Holder, ERC1155Holder {
1011
DropERC721 internal drop;
1112
address payable internal master;
13+
// using Strings for uint256;
1214

1315
constructor(address _drop) {
1416
drop = DropERC721(_drop);
@@ -223,4 +225,123 @@ contract DropERC721Test is BaseTest {
223225
0
224226
);
225227
}
228+
229+
/*///////////////////////////////////////////////////////////////
230+
Miscellaneous
231+
//////////////////////////////////////////////////////////////*/
232+
233+
function test_revert_claim_claimQty(uint256 x) public {
234+
vm.assume(x != 0);
235+
vm.warp(1);
236+
237+
address receiver = getActor(0);
238+
bytes32[] memory proofs = new bytes32[](0);
239+
240+
DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1);
241+
conditions[0].maxClaimableSupply = 500;
242+
conditions[0].quantityLimitPerTransaction = 100;
243+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
244+
245+
vm.prank(deployer);
246+
drop.lazyMint(500, "ipfs://", bytes(""));
247+
248+
vm.prank(deployer);
249+
drop.setClaimConditions(conditions, false);
250+
251+
vm.prank(getActor(5), getActor(5));
252+
vm.expectRevert("invalid quantity.");
253+
drop.claim(receiver, 200, address(0), 0, proofs, x);
254+
255+
vm.prank(deployer);
256+
drop.setClaimConditions(conditions, true);
257+
258+
vm.prank(getActor(5), getActor(5));
259+
vm.expectRevert("invalid quantity.");
260+
drop.claim(receiver, 200, address(0), 0, proofs, x);
261+
}
262+
263+
function test_claimCondition_merkleProof(uint256 x) public {
264+
vm.assume(x != 0 && x < 500);
265+
string[] memory inputs = new string[](3);
266+
267+
inputs[0] = "node";
268+
inputs[1] = "src/test/scripts/generateRoot.ts";
269+
inputs[2] = Strings.toString(x);
270+
271+
bytes memory result = vm.ffi(inputs);
272+
// revert();
273+
bytes32 root = abi.decode(result, (bytes32));
274+
275+
inputs[1] = "src/test/scripts/getProof.ts";
276+
result = vm.ffi(inputs);
277+
bytes32[] memory proofs = abi.decode(result, (bytes32[]));
278+
279+
vm.warp(1);
280+
281+
address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3);
282+
283+
// bytes32[] memory proofs = new bytes32[](0);
284+
285+
DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1);
286+
conditions[0].maxClaimableSupply = x;
287+
conditions[0].quantityLimitPerTransaction = 100;
288+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
289+
conditions[0].merkleRoot = root;
290+
291+
vm.prank(deployer);
292+
drop.lazyMint(x, "ipfs://", "");
293+
vm.prank(deployer);
294+
drop.setClaimConditions(conditions, false);
295+
296+
// vm.prank(getActor(5), getActor(5));
297+
vm.prank(receiver, receiver);
298+
drop.claim(receiver, x, address(0), 0, proofs, x);
299+
300+
vm.prank(address(4), address(4));
301+
vm.expectRevert("not in whitelist.");
302+
drop.claim(receiver, x, address(0), 0, proofs, x);
303+
}
304+
305+
function testFail_claimCondition_merkleProof(uint256 x, uint256 y) public {
306+
vm.assume(x != 0 && x < 500);
307+
vm.assume(x != y);
308+
string[] memory inputs = new string[](3);
309+
310+
inputs[0] = "node";
311+
inputs[1] = "src/test/scripts/generateRoot.ts";
312+
inputs[2] = Strings.toString(x);
313+
314+
bytes memory result = vm.ffi(inputs);
315+
// revert();
316+
bytes32 root = abi.decode(result, (bytes32));
317+
318+
inputs[1] = "src/test/scripts/getProof.ts";
319+
result = vm.ffi(inputs);
320+
bytes32[] memory proofs = abi.decode(result, (bytes32[]));
321+
322+
vm.warp(1);
323+
324+
address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3);
325+
326+
// bytes32[] memory proofs = new bytes32[](0);
327+
328+
DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1);
329+
conditions[0].maxClaimableSupply = x;
330+
conditions[0].quantityLimitPerTransaction = 100;
331+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
332+
conditions[0].merkleRoot = root;
333+
334+
vm.prank(deployer);
335+
drop.lazyMint(x, "ipfs://", "");
336+
vm.prank(deployer);
337+
drop.setClaimConditions(conditions, false);
338+
339+
// vm.prank(getActor(5), getActor(5));
340+
vm.prank(receiver, receiver);
341+
drop.claim(receiver, x, address(0), 0, proofs, y);
342+
343+
vm.prank(address(4), address(4));
344+
vm.expectRevert("not in whitelist.");
345+
drop.claim(receiver, x, address(0), 0, proofs, y);
346+
}
226347
}

src/test/scripts/generateRoot.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ const { MerkleTree } = require("merkletreejs");
22
const keccak256 = require("keccak256");
33
const { ethers } = require("ethers");
44

5+
const process = require('process');
6+
57
const members = [
68
"0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3",
79
"0xD0d82c095d184e6E2c8B72689c9171DE59FFd28d",
810
"0xFD78F7E2dF2B8c3D5bff0413c96f3237500898B3",
911
];
1012

11-
const hashedLeafs = members.map(l => ethers.utils.solidityKeccak256(["address", "uint256"], [l, 0]));
13+
let val = process.argv[2];
14+
15+
const hashedLeafs = members.map(l => ethers.utils.solidityKeccak256(["address", "uint256"], [l, val]));
1216

1317
const tree = new MerkleTree(hashedLeafs, keccak256, {
1418
sort: true,

src/test/scripts/getProof.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@ const { MerkleTree } = require("merkletreejs");
22
const keccak256 = require("keccak256");
33
const { ethers } = require("ethers");
44

5+
const process = require('process');
6+
57
const members = [
68
"0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3",
79
"0xD0d82c095d184e6E2c8B72689c9171DE59FFd28d",
810
"0xFD78F7E2dF2B8c3D5bff0413c96f3237500898B3",
911
];
1012

11-
const hashedLeafs = members.map(l => ethers.utils.solidityKeccak256(["address", "uint256"], [l, 0]));
13+
let val = process.argv[2];
14+
15+
const hashedLeafs = members.map(l => ethers.utils.solidityKeccak256(["address", "uint256"], [l, val]));
1216

1317
const tree = new MerkleTree(hashedLeafs, keccak256, {
1418
sort: true,
1519
sortLeaves: true,
1620
sortPairs: true,
1721
});
1822

19-
const expectedProof = tree.getHexProof(ethers.utils.solidityKeccak256(["address", "uint256"], [members[0], 0]));
23+
const expectedProof = tree.getHexProof(ethers.utils.solidityKeccak256(["address", "uint256"], [members[0], val]));
2024

2125
process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32[]"], [expectedProof]));

0 commit comments

Comments
 (0)