From bec211868ed394ee161720b2cc842fe37da7fadb Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 13 May 2024 20:21:01 +0530 Subject: [PATCH 1/2] Fix claim condition state for supply claimed by wallet --- .gitignore | 4 ++++ contracts/extension/Drop.sol | 18 ++++++++++++++--- contracts/extension/Drop1155.sol | 18 ++++++++++++++--- .../interface/IClaimConditionMultiPhase.sol | 3 ++- contracts/extension/upgradeable/Drop.sol | 20 ++++++++++++++++--- .../drop/verify-claim/verifyClaim.t.sol | 3 ++- .../drop/verify-claim/verifyClaim.t.sol | 3 ++- 7 files changed, 57 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 52199fb52..0883449af 100644 --- a/.gitignore +++ b/.gitignore @@ -8,16 +8,20 @@ artifacts/build-info build/ scripts/reference-scripts cache/ +cache_hardhat/ +cache_hardhat-zk/ coverage/ dist/ node_modules/ typechain/ +typechain-types/ .parcel-cache/ abi/ contracts/abi/ contracts/README.md artifacts/ +artifacts-zk/ artifacts_forge/ contract_artifacts/ diff --git a/contracts/extension/Drop.sol b/contracts/extension/Drop.sol index 5a1ee1a62..bd173da2e 100644 --- a/contracts/extension/Drop.sol +++ b/contracts/extension/Drop.sol @@ -60,8 +60,9 @@ abstract contract Drop is IDrop { verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof); // Update contract state. + bytes32 activeConditionHash = claimCondition.conditionHash[activeConditionId]; claimCondition.conditions[activeConditionId].supplyClaimed += _quantity; - claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity; + claimCondition.supplyClaimedByWallet[activeConditionHash][_dropMsgSender()] += _quantity; // If there's a price, collect price. _collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken); @@ -113,6 +114,13 @@ abstract contract Drop is IDrop { claimCondition.conditions[newStartIndex + i] = _conditions[i]; claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready; + bytes32 _conditionHash = claimCondition.conditionHash[newStartIndex + i]; + if (_resetClaimEligibility || _conditionHash == bytes32(0)) { + claimCondition.conditionHash[newStartIndex + i] = keccak256( + abi.encodePacked((newStartIndex + i), block.number) + ); + } + lastConditionStartTimestamp = _conditions[i].startTimestamp; } @@ -129,11 +137,13 @@ abstract contract Drop is IDrop { if (_resetClaimEligibility) { for (uint256 i = existingStartIndex; i < newStartIndex; i++) { delete claimCondition.conditions[i]; + delete claimCondition.conditionHash[i]; } } else { if (existingPhaseCount > _conditions.length) { for (uint256 i = _conditions.length; i < existingPhaseCount; i++) { delete claimCondition.conditions[newStartIndex + i]; + delete claimCondition.conditionHash[newStartIndex + i]; } } } @@ -151,6 +161,7 @@ abstract contract Drop is IDrop { AllowlistProof calldata _allowlistProof ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId]; + bytes32 activeConditionHash = claimCondition.conditionHash[_conditionId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; @@ -186,7 +197,7 @@ abstract contract Drop is IDrop { : claimCurrency; } - uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer]; + uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[activeConditionHash][_claimer]; if (_currency != claimCurrency || _pricePerToken != claimPrice) { revert DropClaimInvalidTokenPrice(_currency, _pricePerToken, claimCurrency, claimPrice); @@ -229,7 +240,8 @@ abstract contract Drop is IDrop { uint256 _conditionId, address _claimer ) public view returns (uint256 supplyClaimedByWallet) { - supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer]; + bytes32 _conditionHash = claimCondition.conditionHash[_conditionId]; + supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionHash][_claimer]; } /*//////////////////////////////////////////////////////////////////// diff --git a/contracts/extension/Drop1155.sol b/contracts/extension/Drop1155.sol index 07631e32d..5806a0bea 100644 --- a/contracts/extension/Drop1155.sol +++ b/contracts/extension/Drop1155.sol @@ -69,8 +69,9 @@ abstract contract Drop1155 is IDrop1155 { ); // Update contract state. + bytes32 activeConditionHash = claimCondition[_tokenId].conditionHash[activeConditionId]; claimCondition[_tokenId].conditions[activeConditionId].supplyClaimed += _quantity; - claimCondition[_tokenId].supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity; + claimCondition[_tokenId].supplyClaimedByWallet[activeConditionHash][_dropMsgSender()] += _quantity; // If there's a price, collect price. collectPriceOnClaim(_tokenId, address(0), _quantity, _currency, _pricePerToken); @@ -123,6 +124,13 @@ abstract contract Drop1155 is IDrop1155 { conditionList.conditions[newStartIndex + i] = _conditions[i]; conditionList.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready; + bytes32 _conditionHash = conditionList.conditionHash[newStartIndex + i]; + if (_resetClaimEligibility || _conditionHash == bytes32(0)) { + conditionList.conditionHash[newStartIndex + i] = keccak256( + abi.encodePacked((newStartIndex + i), block.number) + ); + } + lastConditionStartTimestamp = _conditions[i].startTimestamp; } @@ -139,11 +147,13 @@ abstract contract Drop1155 is IDrop1155 { if (_resetClaimEligibility) { for (uint256 i = existingStartIndex; i < newStartIndex; i++) { delete conditionList.conditions[i]; + delete conditionList.conditionHash[i]; } } else { if (existingPhaseCount > _conditions.length) { for (uint256 i = _conditions.length; i < existingPhaseCount; i++) { delete conditionList.conditions[newStartIndex + i]; + delete conditionList.conditionHash[newStartIndex + i]; } } } @@ -162,6 +172,7 @@ abstract contract Drop1155 is IDrop1155 { AllowlistProof calldata _allowlistProof ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = claimCondition[_tokenId].conditions[_conditionId]; + bytes32 activeConditionHash = claimCondition[_tokenId].conditionHash[_conditionId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; @@ -197,7 +208,7 @@ abstract contract Drop1155 is IDrop1155 { : claimCurrency; } - uint256 supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer]; + uint256 supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[activeConditionHash][_claimer]; if (_currency != claimCurrency || _pricePerToken != claimPrice) { revert DropClaimInvalidTokenPrice(_currency, _pricePerToken, claimCurrency, claimPrice); @@ -245,7 +256,8 @@ abstract contract Drop1155 is IDrop1155 { uint256 _conditionId, address _claimer ) public view returns (uint256 supplyClaimedByWallet) { - supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionId][_claimer]; + bytes32 _conditionHash = claimCondition[_tokenId].conditionHash[_conditionId]; + supplyClaimedByWallet = claimCondition[_tokenId].supplyClaimedByWallet[_conditionHash][_claimer]; } /*//////////////////////////////////////////////////////////////////// diff --git a/contracts/extension/interface/IClaimConditionMultiPhase.sol b/contracts/extension/interface/IClaimConditionMultiPhase.sol index df0d424e3..3023ea57e 100644 --- a/contracts/extension/interface/IClaimConditionMultiPhase.sol +++ b/contracts/extension/interface/IClaimConditionMultiPhase.sol @@ -33,7 +33,8 @@ interface IClaimConditionMultiPhase is IClaimCondition { struct ClaimConditionList { uint256 currentStartId; uint256 count; + mapping(uint256 => bytes32) conditionHash; mapping(uint256 => ClaimCondition) conditions; - mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet; + mapping(bytes32 => mapping(address => uint256)) supplyClaimedByWallet; } } diff --git a/contracts/extension/upgradeable/Drop.sol b/contracts/extension/upgradeable/Drop.sol index 4d701a204..3fdcc2898 100644 --- a/contracts/extension/upgradeable/Drop.sol +++ b/contracts/extension/upgradeable/Drop.sol @@ -49,8 +49,9 @@ abstract contract Drop is IDrop { verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof); // Update contract state. + bytes32 activeConditionHash = _dropStorage().claimCondition.conditionHash[activeConditionId]; _dropStorage().claimCondition.conditions[activeConditionId].supplyClaimed += _quantity; - _dropStorage().claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity; + _dropStorage().claimCondition.supplyClaimedByWallet[activeConditionHash][_dropMsgSender()] += _quantity; // If there's a price, collect price. _collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken); @@ -102,6 +103,13 @@ abstract contract Drop is IDrop { _dropStorage().claimCondition.conditions[newStartIndex + i] = _conditions[i]; _dropStorage().claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready; + bytes32 _conditionHash = _dropStorage().claimCondition.conditionHash[newStartIndex + i]; + if (_resetClaimEligibility || _conditionHash == bytes32(0)) { + _dropStorage().claimCondition.conditionHash[newStartIndex + i] = keccak256( + abi.encodePacked((newStartIndex + i), block.number) + ); + } + lastConditionStartTimestamp = _conditions[i].startTimestamp; } @@ -118,11 +126,13 @@ abstract contract Drop is IDrop { if (_resetClaimEligibility) { for (uint256 i = existingStartIndex; i < newStartIndex; i++) { delete _dropStorage().claimCondition.conditions[i]; + delete _dropStorage().claimCondition.conditionHash[i]; } } else { if (existingPhaseCount > _conditions.length) { for (uint256 i = _conditions.length; i < existingPhaseCount; i++) { delete _dropStorage().claimCondition.conditions[newStartIndex + i]; + delete _dropStorage().claimCondition.conditionHash[newStartIndex + i]; } } } @@ -140,6 +150,7 @@ abstract contract Drop is IDrop { AllowlistProof calldata _allowlistProof ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = _dropStorage().claimCondition.conditions[_conditionId]; + bytes32 activeConditionHash = _dropStorage().claimCondition.conditionHash[_conditionId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; @@ -175,7 +186,9 @@ abstract contract Drop is IDrop { : claimCurrency; } - uint256 supplyClaimedByWallet = _dropStorage().claimCondition.supplyClaimedByWallet[_conditionId][_claimer]; + uint256 supplyClaimedByWallet = _dropStorage().claimCondition.supplyClaimedByWallet[activeConditionHash][ + _claimer + ]; if (_currency != claimCurrency || _pricePerToken != claimPrice) { revert("!PriceOrCurrency"); @@ -218,7 +231,8 @@ abstract contract Drop is IDrop { uint256 _conditionId, address _claimer ) public view returns (uint256 supplyClaimedByWallet) { - supplyClaimedByWallet = _dropStorage().claimCondition.supplyClaimedByWallet[_conditionId][_claimer]; + bytes32 _conditionHash = _dropStorage().claimCondition.conditionHash[_conditionId]; + supplyClaimedByWallet = _dropStorage().claimCondition.supplyClaimedByWallet[_conditionHash][_claimer]; } /*//////////////////////////////////////////////////////////////////// diff --git a/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol b/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol index 0057f6220..9a75cb589 100644 --- a/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol +++ b/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol @@ -34,7 +34,8 @@ contract MyDrop is Drop { } function setSupplyClaimedByWallet(uint256 _conditionId, address _wallet, uint256 _supplyClaimed) public { - claimCondition.supplyClaimedByWallet[_conditionId][_wallet] = _supplyClaimed; + bytes32 _conditionHash = keccak256(abi.encodePacked(_conditionId, block.number)); + claimCondition.supplyClaimedByWallet[_conditionHash][_wallet] = _supplyClaimed; } } diff --git a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol index 3cd3372a1..381705795 100644 --- a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol +++ b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol @@ -34,7 +34,8 @@ contract MyDropUpg is Drop { } function setSupplyClaimedByWallet(uint256 _conditionId, address _wallet, uint256 _supplyClaimed) public { - _dropStorage().claimCondition.supplyClaimedByWallet[_conditionId][_wallet] = _supplyClaimed; + bytes32 _conditionHash = keccak256(abi.encodePacked(_conditionId, block.number)); + _dropStorage().claimCondition.supplyClaimedByWallet[_conditionHash][_wallet] = _supplyClaimed; } } From 5345ca4761231f9d9d4d5b07f9807cff72444d89 Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 13 May 2024 21:14:32 +0530 Subject: [PATCH 2/2] fix tests --- contracts/extension/interface/IClaimConditionMultiPhase.sol | 5 ++++- src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol | 2 ++ .../upgradeable/drop/verify-claim/verifyClaim.t.sol | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/contracts/extension/interface/IClaimConditionMultiPhase.sol b/contracts/extension/interface/IClaimConditionMultiPhase.sol index 3023ea57e..c15f7b49b 100644 --- a/contracts/extension/interface/IClaimConditionMultiPhase.sol +++ b/contracts/extension/interface/IClaimConditionMultiPhase.sol @@ -25,10 +25,13 @@ interface IClaimConditionMultiPhase is IClaimCondition { * @param count The total number of phases / claim conditions in the list * of claim conditions. * + * @param conditionHash Mapping from numeric claim condition ID to a unique hash. + * Used in supplyClaimedByWallet mapping below. + * * @param conditions The claim conditions at a given uid. Claim conditions * are ordered in an ascending order by their `startTimestamp`. * - * @param supplyClaimedByWallet Map from a claim condition uid and account to supply claimed by account. + * @param supplyClaimedByWallet Map from a claim condition uid/hash and account to supply claimed by account. */ struct ClaimConditionList { uint256 currentStartId; diff --git a/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol b/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol index 9a75cb589..d79c30040 100644 --- a/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol +++ b/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol @@ -31,6 +31,7 @@ contract MyDrop is Drop { function setCondition(ClaimCondition calldata condition, uint256 _conditionId) public { claimCondition.conditions[_conditionId] = condition; + claimCondition.conditionHash[_conditionId] = keccak256(abi.encodePacked(_conditionId, block.number)); } function setSupplyClaimedByWallet(uint256 _conditionId, address _wallet, uint256 _supplyClaimed) public { @@ -407,6 +408,7 @@ contract Drop_VerifyClaim is ExtensionUtilTest { _currency = address(weth); _pricePerToken = 2; _quantity = 1; + vm.expectRevert( abi.encodeWithSelector( Drop.DropClaimExceedLimit.selector, diff --git a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol index 381705795..0cbc92ffc 100644 --- a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol +++ b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol @@ -31,6 +31,9 @@ contract MyDropUpg is Drop { function setCondition(ClaimCondition calldata condition, uint256 _conditionId) public { _dropStorage().claimCondition.conditions[_conditionId] = condition; + _dropStorage().claimCondition.conditionHash[_conditionId] = keccak256( + abi.encodePacked(_conditionId, block.number) + ); } function setSupplyClaimedByWallet(uint256 _conditionId, address _wallet, uint256 _supplyClaimed) public {