Skip to content

Commit a7327bf

Browse files
authored
Merge pull request #155 from thirdweb-dev/drop-bot-check
Drop bot check
2 parents 668f312 + 5c4abdf commit a7327bf

File tree

7 files changed

+98
-41
lines changed

7 files changed

+98
-41
lines changed

contracts/drop/DropERC1155.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ contract DropERC1155 is
5959
uint256 private constant MAX_BPS = 10_000;
6060

6161
/// @dev The thirdweb contract with fee related information.
62-
ITWFee public immutable thirdwebFee;
62+
ITWFee private immutable thirdwebFee;
6363

6464
/// @dev Owner of the contract (purpose: OpenSea compatibility)
6565
address private _owner;
@@ -254,6 +254,8 @@ contract DropERC1155 is
254254
bytes32[] calldata _proofs,
255255
uint256 _proofMaxQuantityPerTransaction
256256
) external payable nonReentrant {
257+
require(isTrustedForwarder(msg.sender) || _msgSender() == tx.origin, "BOT");
258+
257259
// Get the active claim condition index.
258260
uint256 activeConditionId = getActiveClaimConditionId(_tokenId);
259261

contracts/drop/DropERC20.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ contract DropERC20 is
4747
bytes32 private constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
4848

4949
/// @dev The thirdweb contract with fee related information.
50-
ITWFee internal immutable thirdwebFee;
50+
ITWFee private immutable thirdwebFee;
5151

5252
/// @dev Contract level metadata.
5353
string public contractURI;
@@ -178,6 +178,8 @@ contract DropERC20 is
178178
bytes32[] calldata _proofs,
179179
uint256 _proofMaxQuantityPerTransaction
180180
) external payable nonReentrant {
181+
require(isTrustedForwarder(msg.sender) || _msgSender() == tx.origin, "BOT");
182+
181183
// Get the claim conditions.
182184
uint256 activeConditionId = getActiveClaimConditionId();
183185

contracts/drop/DropERC721.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ contract DropERC721 is
5252
uint256 private constant MAX_BPS = 10_000;
5353

5454
/// @dev The thirdweb contract with fee related information.
55-
ITWFee public immutable thirdwebFee;
55+
ITWFee private immutable thirdwebFee;
5656

5757
/// @dev Owner of the contract (purpose: OpenSea compatibility)
5858
address private _owner;
@@ -317,6 +317,8 @@ contract DropERC721 is
317317
bytes32[] calldata _proofs,
318318
uint256 _proofMaxQuantityPerTransaction
319319
) external payable nonReentrant {
320+
require(isTrustedForwarder(msg.sender) || _msgSender() == tx.origin, "BOT");
321+
320322
uint256 tokenIdToClaim = nextTokenIdToClaim;
321323

322324
// Get the claim conditions.

docs/DropERC1155.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,23 +1008,6 @@ function symbol() external view returns (string)
10081008
|---|---|---|
10091009
| _0 | string | undefined
10101010

1011-
### thirdwebFee
1012-
1013-
```solidity
1014-
function thirdwebFee() external view returns (contract ITWFee)
1015-
```
1016-
1017-
1018-
1019-
*The thirdweb contract with fee related information.*
1020-
1021-
1022-
#### Returns
1023-
1024-
| Name | Type | Description |
1025-
|---|---|---|
1026-
| _0 | contract ITWFee | undefined
1027-
10281011
### totalSupply
10291012

10301013
```solidity

docs/DropERC721.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,23 +1080,6 @@ function symbol() external view returns (string)
10801080
|---|---|---|
10811081
| _0 | string | undefined
10821082

1083-
### thirdwebFee
1084-
1085-
```solidity
1086-
function thirdwebFee() external view returns (contract ITWFee)
1087-
```
1088-
1089-
1090-
1091-
*The thirdweb contract with fee related information.*
1092-
1093-
1094-
#### Returns
1095-
1096-
| Name | Type | Description |
1097-
|---|---|---|
1098-
| _0 | contract ITWFee | undefined
1099-
11001083
### tokenByIndex
11011084

11021085
```solidity

lib/forge-std

src/test/drop/DropERC721.t.sol

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,54 @@ import "contracts/drop/DropERC721.sol";
66
// Test imports
77
import "../utils/BaseTest.sol";
88

9-
contract BaseDropERC721Test is BaseTest {
9+
contract SubExploitContract is ERC721Holder, ERC1155Holder {
10+
DropERC721 internal drop;
11+
address payable internal master;
12+
13+
constructor(address _drop) {
14+
drop = DropERC721(_drop);
15+
master = payable(msg.sender);
16+
}
17+
18+
/// @dev Lets an account claim NFTs.
19+
function claimDrop(
20+
address _receiver,
21+
uint256 _quantity,
22+
address _currency,
23+
uint256 _pricePerToken,
24+
bytes32[] calldata _proofs,
25+
uint256 _proofMaxQuantityPerTransaction
26+
) external {
27+
drop.claim(_receiver, _quantity, _currency, _pricePerToken, _proofs, _proofMaxQuantityPerTransaction);
28+
29+
selfdestruct(master);
30+
}
31+
}
32+
33+
contract MasterExploitContract is ERC721Holder, ERC1155Holder {
34+
address internal drop;
35+
36+
constructor(address _drop) {
37+
drop = _drop;
38+
}
39+
40+
/// @dev Lets an account claim NFTs.
41+
function performExploit(
42+
address _receiver,
43+
uint256 _quantity,
44+
address _currency,
45+
uint256 _pricePerToken,
46+
bytes32[] calldata _proofs,
47+
uint256 _proofMaxQuantityPerTransaction
48+
) external {
49+
for (uint256 i = 0; i < 100; i++) {
50+
SubExploitContract sub = new SubExploitContract(address(drop));
51+
sub.claimDrop(_receiver, _quantity, _currency, _pricePerToken, _proofs, _proofMaxQuantityPerTransaction);
52+
}
53+
}
54+
}
55+
56+
contract DropERC721Test is BaseTest {
1057
DropERC721 public drop;
1158

1259
function setUp() public override {
@@ -99,7 +146,6 @@ contract BaseDropERC721Test is BaseTest {
99146
}
100147

101148
function test_claimCondition_waitTimeInSecondsBetweenClaims() public {
102-
vm.startPrank(deployer);
103149
vm.warp(1);
104150

105151
address receiver = getActor(0);
@@ -110,16 +156,20 @@ contract BaseDropERC721Test is BaseTest {
110156
conditions[0].quantityLimitPerTransaction = 100;
111157
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
112158

159+
vm.prank(deployer);
113160
drop.lazyMint(100, "ipfs://", bytes(""));
161+
vm.prank(deployer);
114162
drop.setClaimConditions(conditions, false);
163+
164+
vm.prank(getActor(5), getActor(5));
115165
drop.claim(receiver, 1, address(0), 0, proofs, 0);
116166

117167
vm.expectRevert("cannot claim.");
168+
vm.prank(getActor(5), getActor(5));
118169
drop.claim(receiver, 1, address(0), 0, proofs, 0);
119170
}
120171

121172
function test_claimCondition_resetEligibility_waitTimeInSecondsBetweenClaims() public {
122-
vm.startPrank(deployer);
123173
vm.warp(1);
124174

125175
address receiver = getActor(0);
@@ -130,12 +180,47 @@ contract BaseDropERC721Test is BaseTest {
130180
conditions[0].quantityLimitPerTransaction = 100;
131181
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
132182

183+
vm.prank(deployer);
133184
drop.lazyMint(100, "ipfs://", bytes(""));
134185

186+
vm.prank(deployer);
135187
drop.setClaimConditions(conditions, false);
188+
189+
vm.prank(getActor(5), getActor(5));
136190
drop.claim(receiver, 1, address(0), 0, proofs, 0);
137191

192+
vm.prank(deployer);
138193
drop.setClaimConditions(conditions, true);
194+
195+
vm.prank(getActor(5), getActor(5));
139196
drop.claim(receiver, 1, address(0), 0, proofs, 0);
140197
}
198+
199+
function test_multiple_claim_exploit() public {
200+
MasterExploitContract masterExploit = new MasterExploitContract(address(drop));
201+
202+
DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1);
203+
conditions[0].maxClaimableSupply = 100;
204+
conditions[0].quantityLimitPerTransaction = 1;
205+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
206+
207+
vm.prank(deployer);
208+
drop.lazyMint(100, "ipfs://", bytes(""));
209+
210+
vm.prank(deployer);
211+
drop.setClaimConditions(conditions, false);
212+
213+
bytes32[] memory proofs = new bytes32[](0);
214+
215+
vm.startPrank(getActor(5));
216+
vm.expectRevert(bytes("BOT"));
217+
masterExploit.performExploit(
218+
address(masterExploit),
219+
conditions[0].quantityLimitPerTransaction,
220+
conditions[0].currency,
221+
conditions[0].pricePerToken,
222+
proofs,
223+
0
224+
);
225+
}
141226
}

0 commit comments

Comments
 (0)