Skip to content

Commit d331efc

Browse files
Cleanup claim condition extension classes (#171)
* cleanup claim condition extension classes * fix import casing * more cleanup, fix foundry config * rename Bitmaps to TWBitMaps * fix imports
1 parent ad487f2 commit d331efc

File tree

9 files changed

+136
-63
lines changed

9 files changed

+136
-63
lines changed

contracts/feature/meta-tx/Drop.sol renamed to contracts/feature/Drop.sol

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity ^0.8.0;
33

4-
import "../interface/IDrop.sol";
5-
import "../../lib/MerkleProof.sol";
6-
import "./ExecutionContext.sol";
7-
import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol";
4+
import "./interface/IDrop.sol";
5+
import "../lib/MerkleProof.sol";
6+
import "../lib/TWBitMaps.sol";
87

9-
abstract contract Drop is IDrop, ExecutionContext {
10-
using BitMapsUpgradeable for BitMapsUpgradeable.BitMap;
8+
abstract contract Drop is IDrop {
9+
using TWBitMaps for TWBitMaps.BitMap;
1110

1211
/*///////////////////////////////////////////////////////////////
1312
State variables
@@ -44,7 +43,7 @@ abstract contract Drop is IDrop, ExecutionContext {
4443
// Verify inclusion in allowlist.
4544
(bool validMerkleProof, uint256 merkleProofIndex) = verifyClaimMerkleProof(
4645
activeConditionId,
47-
_msgSender(),
46+
_dropMsgSender(),
4847
_quantity,
4948
_allowlistProof
5049
);
@@ -54,7 +53,7 @@ abstract contract Drop is IDrop, ExecutionContext {
5453

5554
verifyClaim(
5655
activeConditionId,
57-
_msgSender(),
56+
_dropMsgSender(),
5857
_quantity,
5958
_currency,
6059
_pricePerToken,
@@ -74,25 +73,27 @@ abstract contract Drop is IDrop, ExecutionContext {
7473
// claimCondition.supplyClaimed += _quantity;
7574
// lastClaimTimestamp[activeConditionId][_msgSender()] = block.timestamp;
7675
claimCondition.conditions[activeConditionId].supplyClaimed += _quantity;
77-
claimCondition.lastClaimTimestamp[activeConditionId][_msgSender()] = block.timestamp;
76+
claimCondition.lastClaimTimestamp[activeConditionId][_dropMsgSender()] = block.timestamp;
7877

7978
// If there's a price, collect price.
8079
collectPriceOnClaim(_quantity, _currency, _pricePerToken);
8180

8281
// Mint the relevant NFTs to claimer.
8382
uint256 startTokenId = transferTokensOnClaim(_receiver, _quantity);
8483

85-
emit TokensClaimed(activeConditionId, _msgSender(), _receiver, startTokenId, _quantity);
84+
emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, startTokenId, _quantity);
8685

8786
_afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
8887
}
8988

9089
/// @dev Lets a contract admin set claim conditions.
91-
function setClaimConditions(
92-
ClaimCondition[] calldata _conditions,
93-
bool _resetClaimEligibility,
94-
bytes memory
95-
) external virtual override {
90+
function setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility)
91+
external
92+
virtual
93+
override
94+
{
95+
require(_canSetClaimConditions(), "Not authorized");
96+
9697
uint256 existingStartIndex = claimCondition.currentStartId;
9798
uint256 existingPhaseCount = claimCondition.count;
9899

@@ -179,13 +180,6 @@ abstract contract Drop is IDrop, ExecutionContext {
179180
"exceed max claimable supply."
180181
);
181182

182-
// uint256 timestampOfLastClaim = lastClaimTimestamp[conditionId][_claimer];
183-
// uint256 timestampOfLastClaim = claimCondition.lastClaimTimestamp[_conditionId][_claimer];
184-
// require(
185-
// timestampOfLastClaim == 0 ||
186-
// block.timestamp >= timestampOfLastClaim + currentClaimPhase.waitTimeInSecondsBetweenClaims,
187-
// "cannot claim."
188-
// );
189183
(uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) = getClaimTimestamp(_conditionId, _claimer);
190184
require(lastClaimTimestamp == 0 || block.timestamp >= nextValidClaimTimestamp, "cannot claim.");
191185
}
@@ -206,7 +200,6 @@ abstract contract Drop is IDrop, ExecutionContext {
206200
keccak256(abi.encodePacked(_claimer, _allowlistProof.maxQuantityInAllowlist))
207201
);
208202
require(validMerkleProof, "not in whitelist.");
209-
// require(!usedAllowlistSpot[conditionId].get(merkleProofIndex), "proof claimed.");
210203
require(!claimCondition.usedAllowlistSpot[_conditionId].get(merkleProofIndex), "proof claimed.");
211204
require(
212205
_allowlistProof.maxQuantityInAllowlist == 0 || _quantity <= _allowlistProof.maxQuantityInAllowlist,
@@ -250,9 +243,14 @@ abstract contract Drop is IDrop, ExecutionContext {
250243
}
251244
}
252245

253-
/*///////////////////////////////////////////////////////////////
254-
Virtual functions: to be implemented in derived contract
255-
//////////////////////////////////////////////////////////////*/
246+
/*////////////////////////////////////////////////////////////////////
247+
Optional hooks that can be implemented in the derived contract
248+
///////////////////////////////////////////////////////////////////*/
249+
250+
/// @dev Exposes the ability to override the msg sender.
251+
function _dropMsgSender() internal virtual returns (address) {
252+
return msg.sender;
253+
}
256254

257255
/// @dev Runs before every `claim` function call.
258256
function _beforeClaim(
@@ -274,6 +272,10 @@ abstract contract Drop is IDrop, ExecutionContext {
274272
bytes memory _data
275273
) internal virtual {}
276274

275+
/*///////////////////////////////////////////////////////////////
276+
Virtual functions: to be implemented in derived contract
277+
//////////////////////////////////////////////////////////////*/
278+
277279
/// @dev Collects and distributes the primary sale value of NFTs being claimed.
278280
function collectPriceOnClaim(
279281
uint256 _quantityToClaim,
@@ -286,4 +288,7 @@ abstract contract Drop is IDrop, ExecutionContext {
286288
internal
287289
virtual
288290
returns (uint256 startTokenId);
291+
292+
/// @dev Determine what wallet can update claim conditions
293+
function _canSetClaimConditions() internal virtual returns (bool);
289294
}

contracts/feature/DropSinglePhase.sol

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ pragma solidity ^0.8.0;
33

44
import "./interface/IDropSinglePhase.sol";
55
import "../lib/MerkleProof.sol";
6-
import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol";
6+
import "../lib/TWBitMaps.sol";
77

88
abstract contract DropSinglePhase is IDropSinglePhase {
9-
using BitMapsUpgradeable for BitMapsUpgradeable.BitMap;
9+
using TWBitMaps for TWBitMaps.BitMap;
1010

1111
/*///////////////////////////////////////////////////////////////
1212
State variables
@@ -32,7 +32,7 @@ abstract contract DropSinglePhase is IDropSinglePhase {
3232
* @dev Map from a claim condition uid to whether an address in an allowlist
3333
* has already claimed tokens i.e. used their place in the allowlist.
3434
*/
35-
mapping(bytes32 => BitMapsUpgradeable.BitMap) private usedAllowlistSpot;
35+
mapping(bytes32 => TWBitMaps.BitMap) private usedAllowlistSpot;
3636

3737
/*///////////////////////////////////////////////////////////////
3838
Drop logic
@@ -60,15 +60,15 @@ abstract contract DropSinglePhase is IDropSinglePhase {
6060

6161
// Verify inclusion in allowlist.
6262
(bool validMerkleProof, uint256 merkleProofIndex) = verifyClaimMerkleProof(
63-
msg.sender,
63+
_dropMsgSender(),
6464
_quantity,
6565
_allowlistProof
6666
);
6767

6868
// Verify claim validity. If not valid, revert.
6969
bool toVerifyMaxQuantityPerTransaction = _allowlistProof.maxQuantityInAllowlist == 0;
7070

71-
verifyClaim(msg.sender, _quantity, _currency, _pricePerToken, toVerifyMaxQuantityPerTransaction);
71+
verifyClaim(_dropMsgSender(), _quantity, _currency, _pricePerToken, toVerifyMaxQuantityPerTransaction);
7272

7373
if (validMerkleProof && _allowlistProof.maxQuantityInAllowlist > 0) {
7474
/**
@@ -80,15 +80,15 @@ abstract contract DropSinglePhase is IDropSinglePhase {
8080

8181
// Update contract state.
8282
claimCondition.supplyClaimed += _quantity;
83-
lastClaimTimestamp[activeConditionId][msg.sender] = block.timestamp;
83+
lastClaimTimestamp[activeConditionId][_dropMsgSender()] = block.timestamp;
8484

8585
// If there's a price, collect price.
8686
collectPriceOnClaim(_quantity, _currency, _pricePerToken);
8787

8888
// Mint the relevant NFTs to claimer.
8989
uint256 startTokenId = transferTokensOnClaim(_receiver, _quantity);
9090

91-
emit TokensClaimed(claimCondition, msg.sender, _receiver, _quantity, startTokenId);
91+
emit TokensClaimed(claimCondition, _dropMsgSender(), _receiver, _quantity, startTokenId);
9292

9393
_afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
9494
}
@@ -104,7 +104,7 @@ abstract contract DropSinglePhase is IDropSinglePhase {
104104

105105
if (_resetClaimEligibility) {
106106
supplyClaimedAlready = 0;
107-
targetConditionId = keccak256(abi.encodePacked(msg.sender, block.number));
107+
targetConditionId = keccak256(abi.encodePacked(_dropMsgSender(), block.number));
108108
}
109109

110110
require(supplyClaimedAlready <= _condition.maxClaimableSupply, "max supply claimed already");
@@ -181,9 +181,14 @@ abstract contract DropSinglePhase is IDropSinglePhase {
181181
}
182182
}
183183

184-
/*///////////////////////////////////////////////////////////////
185-
Virtual functions: to be implemented in derived contract
186-
//////////////////////////////////////////////////////////////*/
184+
/*////////////////////////////////////////////////////////////////////
185+
Optional hooks that can be implemented in the derived contract
186+
///////////////////////////////////////////////////////////////////*/
187+
188+
/// @dev Exposes the ability to override the msg sender.
189+
function _dropMsgSender() internal virtual returns (address) {
190+
return msg.sender;
191+
}
187192

188193
/// @dev Runs before every `claim` function call.
189194
function _beforeClaim(
@@ -205,6 +210,10 @@ abstract contract DropSinglePhase is IDropSinglePhase {
205210
bytes memory _data
206211
) internal virtual {}
207212

213+
/*///////////////////////////////////////////////////////////////
214+
Virtual functions: to be implemented in derived contract
215+
//////////////////////////////////////////////////////////////*/
216+
208217
/// @dev Collects and distributes the primary sale value of NFTs being claimed.
209218
function collectPriceOnClaim(
210219
uint256 _quantityToClaim,

contracts/feature/interface/IClaimCondition.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity ^0.8.0;
33

4-
import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol";
4+
import "../../lib/TWBitMaps.sol";
55

66
/**
77
* Thirdweb's 'Drop' contracts are distribution mechanisms for tokens.
@@ -75,6 +75,6 @@ interface IClaimCondition {
7575
uint256 count;
7676
mapping(uint256 => ClaimCondition) conditions;
7777
mapping(uint256 => mapping(address => uint256)) lastClaimTimestamp;
78-
mapping(uint256 => BitMapsUpgradeable.BitMap) usedAllowlistSpot;
78+
mapping(uint256 => TWBitMaps.BitMap) usedAllowlistSpot;
7979
}
8080
}

contracts/feature/interface/IDrop.sol

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ interface IDrop is IClaimCondition {
4747
* @param resetClaimEligibility Whether to reset `limitLastClaimTimestamp` and `limitMerkleProofClaim` values when setting new
4848
* claim conditions.
4949
*
50-
* @param data Arbitrary bytes data that can be leveraged in the implementation of this interface.
5150
*/
52-
function setClaimConditions(
53-
ClaimCondition[] calldata phases,
54-
bool resetClaimEligibility,
55-
bytes memory data
56-
) external;
51+
function setClaimConditions(ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
5752
}

contracts/feature/interface/ILazyMint.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ interface ILazyMint {
55
function lazyMint(
66
uint256 amount,
77
string calldata baseURIForTokens,
8-
bytes calldata encryptedBaseURI
8+
bytes calldata extraData
99
) external returns (uint256 batchId);
1010
}

contracts/lib/TWBitMaps.sol

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: MIT
2+
// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol)
3+
pragma solidity ^0.8.0;
4+
5+
/**
6+
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
7+
* Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
8+
*/
9+
library TWBitMaps {
10+
struct BitMap {
11+
mapping(uint256 => uint256) _data;
12+
}
13+
14+
/**
15+
* @dev Returns whether the bit at `index` is set.
16+
*/
17+
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
18+
uint256 bucket = index >> 8;
19+
uint256 mask = 1 << (index & 0xff);
20+
return bitmap._data[bucket] & mask != 0;
21+
}
22+
23+
/**
24+
* @dev Sets the bit at `index` to the boolean `value`.
25+
*/
26+
function setTo(
27+
BitMap storage bitmap,
28+
uint256 index,
29+
bool value
30+
) internal {
31+
if (value) {
32+
set(bitmap, index);
33+
} else {
34+
unset(bitmap, index);
35+
}
36+
}
37+
38+
/**
39+
* @dev Sets the bit at `index`.
40+
*/
41+
function set(BitMap storage bitmap, uint256 index) internal {
42+
uint256 bucket = index >> 8;
43+
uint256 mask = 1 << (index & 0xff);
44+
bitmap._data[bucket] |= mask;
45+
}
46+
47+
/**
48+
* @dev Unsets the bit at `index`.
49+
*/
50+
function unset(BitMap storage bitmap, uint256 index) internal {
51+
uint256 bucket = index >> 8;
52+
uint256 mask = 1 << (index & 0xff);
53+
bitmap._data[bucket] &= ~mask;
54+
}
55+
}

contracts/signature-drop/SignatureDrop.sol

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import "../feature/Ownable.sol";
2525
import "../feature/DelayedReveal.sol";
2626
import "../feature/LazyMint.sol";
2727
import "../feature/PermissionsEnumerable.sol";
28-
import "../feature/meta-tx/Drop.sol";
28+
import "../feature/Drop.sol";
2929
import "../feature/interface/ISignatureMintERC721.sol";
3030

3131
contract SignatureDrop is
@@ -325,6 +325,11 @@ contract SignatureDrop is
325325
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
326326
}
327327

328+
/// @dev Returns whether claim conditions can be set in the given execution context.
329+
function _canSetClaimConditions() internal view override returns (bool) {
330+
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
331+
}
332+
328333
/*///////////////////////////////////////////////////////////////
329334
Miscellaneous
330335
//////////////////////////////////////////////////////////////*/
@@ -357,11 +362,15 @@ contract SignatureDrop is
357362
}
358363
}
359364

365+
function _dropMsgSender() internal view virtual override returns (address) {
366+
return _msgSender();
367+
}
368+
360369
function _msgSender()
361370
internal
362371
view
363372
virtual
364-
override(ContextUpgradeable, ERC2771ContextUpgradeable, ExecutionContext)
373+
override(ContextUpgradeable, ERC2771ContextUpgradeable)
365374
returns (address sender)
366375
{
367376
return ERC2771ContextUpgradeable._msgSender();
@@ -371,7 +380,7 @@ contract SignatureDrop is
371380
internal
372381
view
373382
virtual
374-
override(ContextUpgradeable, ERC2771ContextUpgradeable, ExecutionContext)
383+
override(ContextUpgradeable, ERC2771ContextUpgradeable)
375384
returns (bytes calldata)
376385
{
377386
return ERC2771ContextUpgradeable._msgData();

foundry.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ remappings = [
1919
'contracts/=contracts/',
2020
'erc721a-upgradeable/=node_modules/erc721a-upgradeable/',
2121
]
22-
src = 'src'
23-
test = 'test'
22+
src = 'contracts'
23+
test = 'src/test'
2424
verbosity = 0
2525
#ignored_error_codes = []
2626
#fuzz_runs = 256

0 commit comments

Comments
 (0)