Skip to content

Commit 2284362

Browse files
Krishang NadgaudaKrishang Nadgauda
authored andcommitted
Move ExecutionContext dependent features to /meta-tx
1 parent cdd987c commit 2284362

File tree

5 files changed

+236
-17
lines changed

5 files changed

+236
-17
lines changed

contracts/drop/SignatureDrop.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import "../feature/DelayedReveal.sol";
3333
import "../feature/LazyMint.sol";
3434
import "../feature/PermissionsEnumerable.sol";
3535
import "../feature/SignatureMintERC721Upgradeable.sol";
36-
import "../feature/DropSinglePhase.sol";
36+
import "../feature/meta-tx/DropSinglePhase.sol";
3737

3838
contract SignatureDrop is
3939
Initializable,

contracts/feature/DropSinglePhase.sol

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

44
import "./interface/IDropSinglePhase.sol";
55
import "../lib/MerkleProof.sol";
6-
import "./ExecutionContext.sol";
76
import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol";
87

9-
abstract contract DropSinglePhase is IDropSinglePhase, ExecutionContext {
8+
abstract contract DropSinglePhase is IDropSinglePhase {
109
using BitMapsUpgradeable for BitMapsUpgradeable.BitMap;
1110

1211
/*///////////////////////////////////////////////////////////////
@@ -61,15 +60,15 @@ abstract contract DropSinglePhase is IDropSinglePhase, ExecutionContext {
6160

6261
// Verify inclusion in allowlist.
6362
(bool validMerkleProof, uint256 merkleProofIndex) = verifyClaimMerkleProof(
64-
_msgSender(),
63+
msg.sender,
6564
_quantity,
6665
_allowlistProof
6766
);
6867

6968
// Verify claim validity. If not valid, revert.
7069
bool toVerifyMaxQuantityPerTransaction = _allowlistProof.maxQuantityInAllowlist == 0;
7170

72-
verifyClaim(_msgSender(), _quantity, _currency, _pricePerToken, toVerifyMaxQuantityPerTransaction);
71+
verifyClaim(msg.sender, _quantity, _currency, _pricePerToken, toVerifyMaxQuantityPerTransaction);
7372

7473
if (validMerkleProof && _allowlistProof.maxQuantityInAllowlist > 0) {
7574
/**
@@ -81,15 +80,15 @@ abstract contract DropSinglePhase is IDropSinglePhase, ExecutionContext {
8180

8281
// Update contract state.
8382
claimCondition.supplyClaimed += _quantity;
84-
lastClaimTimestamp[activeConditionId][_msgSender()] = block.timestamp;
83+
lastClaimTimestamp[activeConditionId][msg.sender] = block.timestamp;
8584

8685
// If there's a price, collect price.
8786
collectPriceOnClaim(_quantity, _currency, _pricePerToken);
8887

8988
// Mint the relevant NFTs to claimer.
9089
uint256 startTokenId = transferTokensOnClaim(_receiver, _quantity);
9190

92-
emit TokensClaimed(claimCondition, _msgSender(), _receiver, _quantity, startTokenId);
91+
emit TokensClaimed(claimCondition, msg.sender, _receiver, _quantity, startTokenId);
9392

9493
_afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
9594
}

contracts/feature/Permissions.sol

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
pragma solidity ^0.8.0;
33

44
import "./interface/IPermissions.sol";
5-
import "./ExecutionContext.sol";
65
import "../lib/Strings.sol";
76

8-
contract Permissions is IPermissions, ExecutionContext {
7+
contract Permissions is IPermissions {
98
mapping(bytes32 => mapping(address => bool)) private _hasRole;
109
mapping(bytes32 => bytes32) private _getRoleAdmin;
1110

1211
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
1312

1413
modifier onlyRole(bytes32 role) {
15-
_checkRole(role, _msgSender());
14+
_checkRole(role, msg.sender);
1615
_;
1716
}
1817

@@ -25,27 +24,27 @@ contract Permissions is IPermissions, ExecutionContext {
2524
}
2625

2726
function grantRole(bytes32 role, address account) public virtual {
28-
_checkRole(_getRoleAdmin[role], _msgSender());
27+
_checkRole(_getRoleAdmin[role], msg.sender);
2928

3029
_hasRole[role][account] = true;
3130

32-
emit RoleGranted(role, account, _msgSender());
31+
emit RoleGranted(role, account, msg.sender);
3332
}
3433

3534
function revokeRole(bytes32 role, address account) public virtual {
36-
_checkRole(_getRoleAdmin[role], _msgSender());
35+
_checkRole(_getRoleAdmin[role], msg.sender);
3736

3837
delete _hasRole[role][account];
3938

40-
emit RoleRevoked(role, account, _msgSender());
39+
emit RoleRevoked(role, account, msg.sender);
4140
}
4241

4342
function renounceRole(bytes32 role, address account) public virtual {
44-
require(_msgSender() == account, "Can only renounce for self");
43+
require(msg.sender == account, "Can only renounce for self");
4544

4645
delete _hasRole[role][account];
4746

48-
emit RoleRevoked(role, account, _msgSender());
47+
emit RoleRevoked(role, account, msg.sender);
4948
}
5049

5150
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
@@ -56,7 +55,7 @@ contract Permissions is IPermissions, ExecutionContext {
5655

5756
function _setupRole(bytes32 role, address account) internal virtual {
5857
_hasRole[role][account] = true;
59-
emit RoleGranted(role, account, _msgSender());
58+
emit RoleGranted(role, account, msg.sender);
6059
}
6160

6261
function _checkRole(bytes32 role, address account) internal view virtual {
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "../interface/IDropSinglePhase.sol";
5+
import "../../lib/MerkleProof.sol";
6+
import "./ExecutionContext.sol";
7+
import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol";
8+
9+
abstract contract DropSinglePhase is IDropSinglePhase, ExecutionContext {
10+
using BitMapsUpgradeable for BitMapsUpgradeable.BitMap;
11+
12+
/*///////////////////////////////////////////////////////////////
13+
State variables
14+
//////////////////////////////////////////////////////////////*/
15+
16+
/// @dev The active conditions for claiming tokens.
17+
ClaimCondition public claimCondition;
18+
19+
/// @dev The ID for the active claim condition.
20+
bytes32 private conditionId;
21+
22+
/*///////////////////////////////////////////////////////////////
23+
Mappings
24+
//////////////////////////////////////////////////////////////*/
25+
26+
/**
27+
* @dev Map from an account and uid for a claim condition, to the last timestamp
28+
* at which the account claimed tokens under that claim condition.
29+
*/
30+
mapping(bytes32 => mapping(address => uint256)) private lastClaimTimestamp;
31+
32+
/**
33+
* @dev Map from a claim condition uid to whether an address in an allowlist
34+
* has already claimed tokens i.e. used their place in the allowlist.
35+
*/
36+
mapping(bytes32 => BitMapsUpgradeable.BitMap) private usedAllowlistSpot;
37+
38+
/*///////////////////////////////////////////////////////////////
39+
Drop logic
40+
//////////////////////////////////////////////////////////////*/
41+
42+
/// @dev Lets an account claim tokens.
43+
function claim(
44+
address _receiver,
45+
uint256 _quantity,
46+
address _currency,
47+
uint256 _pricePerToken,
48+
AllowlistProof calldata _allowlistProof,
49+
bytes memory _data
50+
) public payable virtual {
51+
_beforeClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
52+
53+
bytes32 activeConditionId = conditionId;
54+
55+
/**
56+
* We make allowlist checks (i.e. verifyClaimMerkleProof) before verifying the claim's general
57+
* validity (i.e. verifyClaim) because we give precedence to the check of allow list quantity
58+
* restriction over the check of the general claim condition's quantityLimitPerTransaction
59+
* restriction.
60+
*/
61+
62+
// Verify inclusion in allowlist.
63+
(bool validMerkleProof, uint256 merkleProofIndex) = verifyClaimMerkleProof(
64+
_msgSender(),
65+
_quantity,
66+
_allowlistProof
67+
);
68+
69+
// Verify claim validity. If not valid, revert.
70+
bool toVerifyMaxQuantityPerTransaction = _allowlistProof.maxQuantityInAllowlist == 0;
71+
72+
verifyClaim(_msgSender(), _quantity, _currency, _pricePerToken, toVerifyMaxQuantityPerTransaction);
73+
74+
if (validMerkleProof && _allowlistProof.maxQuantityInAllowlist > 0) {
75+
/**
76+
* Mark the claimer's use of their position in the allowlist. A spot in an allowlist
77+
* can be used only once.
78+
*/
79+
usedAllowlistSpot[activeConditionId].set(merkleProofIndex);
80+
}
81+
82+
// Update contract state.
83+
claimCondition.supplyClaimed += _quantity;
84+
lastClaimTimestamp[activeConditionId][_msgSender()] = block.timestamp;
85+
86+
// If there's a price, collect price.
87+
collectPriceOnClaim(_quantity, _currency, _pricePerToken);
88+
89+
// Mint the relevant NFTs to claimer.
90+
uint256 startTokenId = transferTokensOnClaim(_receiver, _quantity);
91+
92+
emit TokensClaimed(claimCondition, _msgSender(), _receiver, _quantity, startTokenId);
93+
94+
_afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
95+
}
96+
97+
/// @dev Lets a contract admin set claim conditions.
98+
function setClaimConditions(
99+
ClaimCondition calldata _condition,
100+
bool _resetClaimEligibility,
101+
bytes memory
102+
) external {
103+
bytes32 targetConditionId = conditionId;
104+
uint256 supplyClaimedAlready = claimCondition.supplyClaimed;
105+
106+
if (_resetClaimEligibility) {
107+
supplyClaimedAlready = 0;
108+
targetConditionId = keccak256(abi.encodePacked(msg.sender, block.number));
109+
}
110+
111+
require(supplyClaimedAlready <= _condition.maxClaimableSupply, "max supply claimed already");
112+
113+
claimCondition = ClaimCondition({
114+
startTimestamp: block.timestamp,
115+
maxClaimableSupply: _condition.maxClaimableSupply,
116+
supplyClaimed: supplyClaimedAlready,
117+
quantityLimitPerTransaction: _condition.supplyClaimed,
118+
waitTimeInSecondsBetweenClaims: _condition.waitTimeInSecondsBetweenClaims,
119+
merkleRoot: _condition.merkleRoot,
120+
pricePerToken: _condition.pricePerToken,
121+
currency: _condition.currency
122+
});
123+
conditionId = targetConditionId;
124+
125+
emit ClaimConditionUpdated(_condition, _resetClaimEligibility);
126+
}
127+
128+
/// @dev Checks a request to claim NFTs against the active claim condition's criteria.
129+
function verifyClaim(
130+
address _claimer,
131+
uint256 _quantity,
132+
address _currency,
133+
uint256 _pricePerToken,
134+
bool verifyMaxQuantityPerTransaction
135+
) public view {
136+
ClaimCondition memory currentClaimPhase = claimCondition;
137+
138+
require(
139+
_currency == currentClaimPhase.currency && _pricePerToken == currentClaimPhase.pricePerToken,
140+
"invalid currency or price."
141+
);
142+
143+
// If we're checking for an allowlist quantity restriction, ignore the general quantity restriction.
144+
require(
145+
_quantity > 0 &&
146+
(!verifyMaxQuantityPerTransaction || _quantity <= currentClaimPhase.quantityLimitPerTransaction),
147+
"invalid quantity."
148+
);
149+
require(
150+
currentClaimPhase.supplyClaimed + _quantity <= currentClaimPhase.maxClaimableSupply,
151+
"exceed max claimable supply."
152+
);
153+
154+
uint256 timestampOfLastClaim = lastClaimTimestamp[conditionId][_claimer];
155+
require(
156+
timestampOfLastClaim == 0 ||
157+
block.timestamp >= timestampOfLastClaim + currentClaimPhase.waitTimeInSecondsBetweenClaims,
158+
"cannot claim."
159+
);
160+
}
161+
162+
/// @dev Checks whether a claimer meets the claim condition's allowlist criteria.
163+
function verifyClaimMerkleProof(
164+
address _claimer,
165+
uint256 _quantity,
166+
AllowlistProof calldata _allowlistProof
167+
) public view returns (bool validMerkleProof, uint256 merkleProofIndex) {
168+
ClaimCondition memory currentClaimPhase = claimCondition;
169+
170+
if (currentClaimPhase.merkleRoot != bytes32(0)) {
171+
(validMerkleProof, merkleProofIndex) = MerkleProof.verify(
172+
_allowlistProof.proof,
173+
currentClaimPhase.merkleRoot,
174+
keccak256(abi.encodePacked(_claimer, _allowlistProof.maxQuantityInAllowlist))
175+
);
176+
require(validMerkleProof, "not in whitelist.");
177+
require(!usedAllowlistSpot[conditionId].get(merkleProofIndex), "proof claimed.");
178+
require(
179+
_allowlistProof.maxQuantityInAllowlist == 0 || _quantity <= _allowlistProof.maxQuantityInAllowlist,
180+
"invalid quantity proof."
181+
);
182+
}
183+
}
184+
185+
/*///////////////////////////////////////////////////////////////
186+
Virtual functions: to be implemented in derived contract
187+
//////////////////////////////////////////////////////////////*/
188+
189+
/// @dev Runs before every `claim` function call.
190+
function _beforeClaim(
191+
address _receiver,
192+
uint256 _quantity,
193+
address _currency,
194+
uint256 _pricePerToken,
195+
AllowlistProof calldata _allowlistProof,
196+
bytes memory _data
197+
) internal virtual {}
198+
199+
/// @dev Runs after every `claim` function call.
200+
function _afterClaim(
201+
address _receiver,
202+
uint256 _quantity,
203+
address _currency,
204+
uint256 _pricePerToken,
205+
AllowlistProof calldata _allowlistProof,
206+
bytes memory _data
207+
) internal virtual {}
208+
209+
/// @dev Collects and distributes the primary sale value of NFTs being claimed.
210+
function collectPriceOnClaim(
211+
uint256 _quantityToClaim,
212+
address _currency,
213+
uint256 _pricePerToken
214+
) internal virtual;
215+
216+
/// @dev Transfers the NFTs being claimed.
217+
function transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed)
218+
internal
219+
virtual
220+
returns (uint256 startTokenId);
221+
}

0 commit comments

Comments
 (0)