Skip to content

Commit e217a9e

Browse files
nkrishangkumaryash90Krishang NadgaudaKrishang Nadgaudajoaquim-verges
authored
SignatureDrop: using DropSinglePhase (#169)
* back to DropSinglePhase * access control for setClaimConditions * updated unit tests * eliminate SigMint; IDrop to IDropSinglePhase * delete prev commented code * delete unused DropSinglePhase * delete getters for size * remove reentrancy upgradeable from SignatureDrop for sizing * reentrancy tests outline * reentrancy tests: claim and mintWithSignature * use erc721a _burn; remove duplication * run prettier * delete unused SigMint * silence forge build warnings * merkle tree, allowlist for tests * use _msgSender() * code cleanup; refactored tests for mintWithSignature * run prettier * scenario based tests * run prettier * yaml edit forge tests * yaml edit forge tests * yaml edit forge tests * package update * getClaimTimestamp conditionId * benchmark tests for claim and mintWithSignature * update docs * dev release * Remove claimConditionIndex from TokensClaimed in IDropSinglePhase * code comment about mintWithSignature frontrun attack * use internal _setup instead of public setters in initialize * yarn: use erc721a-upgradeable 3.3 * code comment for ERC721A's _burn * run prettier * run prettier Co-authored-by: yash <kumaryashcse@gmail.com> Co-authored-by: Krishang Nadgauda <nkrishang@Krishangs-MBP.lan> Co-authored-by: Krishang Nadgauda <nkrishang@Krishangs-MacBook-Pro.local> Co-authored-by: Joaquim Verges <joaquim.verges@gmail.com>
1 parent aa6ea50 commit e217a9e

28 files changed

+658
-907
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ jobs:
3232
with:
3333
version: nightly
3434
- name: Run tests
35-
run: forge test
35+
run: |
36+
forge test --no-match-contract SignatureDrop
37+
forge test --match-contract SignatureDrop --ffi

contracts/feature/ContractMetadata.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ abstract contract ContractMetadata is IContractMetadata {
88
string public override contractURI;
99

1010
/// @dev Lets a contract admin set the URI for contract-level metadata.
11-
function setContractURI(string memory _uri) public override {
11+
function setContractURI(string memory _uri) external override {
1212
require(_canSetContractURI(), "Not authorized");
13+
_setupContractURI(_uri);
14+
}
15+
16+
/// @dev Lets a contract admin set the URI for contract-level metadata.
17+
function _setupContractURI(string memory _uri) internal {
1318
string memory prevURI = contractURI;
1419
contractURI = _uri;
1520

contracts/feature/DropSinglePhase.sol

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,15 @@ abstract contract DropSinglePhase is IDropSinglePhase {
8888
// Mint the relevant NFTs to claimer.
8989
uint256 startTokenId = transferTokensOnClaim(_receiver, _quantity);
9090

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

9393
_afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
9494
}
9595

9696
/// @dev Lets a contract admin set claim conditions.
97-
function setClaimConditions(
98-
ClaimCondition calldata _condition,
99-
bool _resetClaimEligibility,
100-
bytes memory
101-
) external virtual override {
97+
function setClaimConditions(ClaimCondition calldata _condition, bool _resetClaimEligibility) external override {
98+
require(_canSetClaimConditions(), "Not authorized");
99+
102100
bytes32 targetConditionId = conditionId;
103101
uint256 supplyClaimedAlready = claimCondition.supplyClaimed;
104102

@@ -110,10 +108,10 @@ abstract contract DropSinglePhase is IDropSinglePhase {
110108
require(supplyClaimedAlready <= _condition.maxClaimableSupply, "max supply claimed already");
111109

112110
claimCondition = ClaimCondition({
113-
startTimestamp: block.timestamp,
111+
startTimestamp: _condition.startTimestamp,
114112
maxClaimableSupply: _condition.maxClaimableSupply,
115113
supplyClaimed: supplyClaimedAlready,
116-
quantityLimitPerTransaction: _condition.supplyClaimed,
114+
quantityLimitPerTransaction: _condition.quantityLimitPerTransaction,
117115
waitTimeInSecondsBetweenClaims: _condition.waitTimeInSecondsBetweenClaims,
118116
merkleRoot: _condition.merkleRoot,
119117
pricePerToken: _condition.pricePerToken,
@@ -150,12 +148,8 @@ abstract contract DropSinglePhase is IDropSinglePhase {
150148
"exceed max claimable supply."
151149
);
152150

153-
uint256 timestampOfLastClaim = lastClaimTimestamp[conditionId][_claimer];
154-
require(
155-
timestampOfLastClaim == 0 ||
156-
block.timestamp >= timestampOfLastClaim + currentClaimPhase.waitTimeInSecondsBetweenClaims,
157-
"cannot claim."
158-
);
151+
(uint256 lastClaimedAt, uint256 nextValidClaimTimestamp) = getClaimTimestamp(_claimer);
152+
require(lastClaimedAt == 0 || block.timestamp >= nextValidClaimTimestamp, "cannot claim.");
159153
}
160154

161155
/// @dev Checks whether a claimer meets the claim condition's allowlist criteria.
@@ -181,6 +175,23 @@ abstract contract DropSinglePhase is IDropSinglePhase {
181175
}
182176
}
183177

178+
/// @dev Returns the timestamp for when a claimer is eligible for claiming NFTs again.
179+
function getClaimTimestamp(address _claimer)
180+
public
181+
view
182+
returns (uint256 lastClaimedAt, uint256 nextValidClaimTimestamp)
183+
{
184+
lastClaimedAt = lastClaimTimestamp[conditionId][_claimer];
185+
186+
unchecked {
187+
nextValidClaimTimestamp = lastClaimedAt + claimCondition.waitTimeInSecondsBetweenClaims;
188+
189+
if (nextValidClaimTimestamp < lastClaimedAt) {
190+
nextValidClaimTimestamp = type(uint256).max;
191+
}
192+
}
193+
}
194+
184195
/*////////////////////////////////////////////////////////////////////
185196
Optional hooks that can be implemented in the derived contract
186197
///////////////////////////////////////////////////////////////////*/
@@ -210,10 +221,6 @@ abstract contract DropSinglePhase is IDropSinglePhase {
210221
bytes memory _data
211222
) internal virtual {}
212223

213-
/*///////////////////////////////////////////////////////////////
214-
Virtual functions: to be implemented in derived contract
215-
//////////////////////////////////////////////////////////////*/
216-
217224
/// @dev Collects and distributes the primary sale value of NFTs being claimed.
218225
function collectPriceOnClaim(
219226
uint256 _quantityToClaim,
@@ -226,4 +233,6 @@ abstract contract DropSinglePhase is IDropSinglePhase {
226233
internal
227234
virtual
228235
returns (uint256 startTokenId);
236+
237+
function _canSetClaimConditions() internal virtual returns (bool);
229238
}

contracts/feature/Ownable.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ abstract contract Ownable is IOwnable {
88
address public override owner;
99

1010
/// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
11-
function setOwner(address _newOwner) public override {
11+
function setOwner(address _newOwner) external override {
1212
require(_canSetOwner(), "Not authorized");
13+
_setupOwner(_newOwner);
14+
}
1315

16+
/// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
17+
function _setupOwner(address _newOwner) internal {
1418
address _prevOwner = owner;
1519
owner = _newOwner;
1620

contracts/feature/PlatformFee.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ abstract contract PlatformFee is IPlatformFee {
1616
}
1717

1818
/// @dev Lets a contract admin update the platform fee recipient and bps
19-
function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) public override {
19+
function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
2020
require(_canSetPlatformFeeInfo(), "Not authorized");
21+
_setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
22+
}
23+
24+
/// @dev Lets a contract admin update the platform fee recipient and bps
25+
function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
2126
require(_platformFeeBps <= 10_000, "Exceeds max bps");
2227

2328
platformFeeBps = uint16(_platformFeeBps);

contracts/feature/PrimarySale.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ abstract contract PrimarySale is IPrimarySale {
1212
}
1313

1414
/// @dev Lets a contract admin set the recipient for all primary sales.
15-
function setPrimarySaleRecipient(address _saleRecipient) public override {
15+
function setPrimarySaleRecipient(address _saleRecipient) external override {
1616
require(_canSetPrimarySaleRecipient(), "Not authorized");
17+
_setupPrimarySaleRecipient(_saleRecipient);
18+
}
1719

20+
/// @dev Lets a contract admin set the recipient for all primary sales.
21+
function _setupPrimarySaleRecipient(address _saleRecipient) internal {
1822
recipient = _saleRecipient;
1923
emit PrimarySaleRecipientUpdated(_saleRecipient);
2024
}

contracts/feature/Royalty.sol

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,13 @@ abstract contract Royalty is IRoyalty {
4242
}
4343

4444
/// @dev Lets a contract admin update the default royalty recipient and bps.
45-
function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) public override {
45+
function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
4646
require(_canSetRoyaltyInfo(), "Not authorized");
47+
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
48+
}
49+
50+
/// @dev Lets a contract admin update the default royalty recipient and bps.
51+
function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
4752
require(_royaltyBps <= 10_000, "Exceeds max bps");
4853

4954
royaltyRecipient = _royaltyRecipient;
@@ -59,6 +64,15 @@ abstract contract Royalty is IRoyalty {
5964
uint256 _bps
6065
) external override {
6166
require(_canSetRoyaltyInfo(), "Not authorized");
67+
_setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
68+
}
69+
70+
/// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
71+
function _setupRoyaltyInfoForToken(
72+
uint256 _tokenId,
73+
address _recipient,
74+
uint256 _bps
75+
) internal {
6276
require(_bps <= 10_000, "Exceeds max bps");
6377

6478
royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });

contracts/feature/interface/IDropSinglePhase.sol

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ interface IDropSinglePhase is IClaimCondition {
1010
}
1111

1212
event TokensClaimed(
13-
ClaimCondition condition,
1413
address indexed claimer,
1514
address indexed receiver,
16-
uint256 quantityClaimed,
17-
uint256 indexed aux
15+
uint256 startTokenId,
16+
uint256 quantityClaimed
1817
);
1918

2019
event ClaimConditionUpdated(ClaimCondition condition, bool resetEligibility);
@@ -46,12 +45,6 @@ interface IDropSinglePhase is IClaimCondition {
4645
*
4746
* @param resetClaimEligibility Whether to reset `limitLastClaimTimestamp` and `limitMerkleProofClaim` values when setting new
4847
* claim conditions.
49-
*
50-
* @param data Arbitrary bytes data that can be leveraged in the implementation of this interface.
5148
*/
52-
function setClaimConditions(
53-
ClaimCondition calldata phase,
54-
bool resetClaimEligibility,
55-
bytes memory data
56-
) external;
49+
function setClaimConditions(ClaimCondition calldata phase, bool resetClaimEligibility) external;
5750
}

0 commit comments

Comments
 (0)