Skip to content

Commit 77395d7

Browse files
nkrishangkumaryash90Krishang Nadgauda
authored
Audit fixes: DropERC20.sol (#215)
* [M-3] Users can claim small amounts of tokens for free * [Q-2] Missing Events: DropERC20 * [Q-4] Unused import * [G-4] Reduce SLOADs in verifyClaim() * [Q-5] Inaccurate getClaimTimestamp() return value * forge update * forge update * run prettier Co-authored-by: Yash <kumaryashcse@gmail.com> Co-authored-by: Krishang Nadgauda <nkrishang@Krishangs-MacBook-Pro.local>
1 parent 177e75d commit 77395d7

File tree

5 files changed

+77
-17
lines changed

5 files changed

+77
-17
lines changed

contracts/drop/DropERC20.sol

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ pragma solidity ^0.8.11;
44
// ========== External imports ==========
55

66
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
7-
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PausableUpgradeable.sol";
87
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
98

109
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
@@ -314,6 +313,8 @@ contract DropERC20 is
314313

315314
// `_pricePerToken` is interpreted as price per 1 ether unit of the ERC20 tokens.
316315
uint256 totalPrice = (_quantityToClaim * _pricePerToken) / 1 ether;
316+
require(totalPrice > 0, "quantity too low");
317+
317318
uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
318319

319320
if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
@@ -366,14 +367,17 @@ contract DropERC20 is
366367
currentClaimPhase.supplyClaimed + _quantity <= currentClaimPhase.maxClaimableSupply,
367368
"exceed max mint supply."
368369
);
369-
require(maxTotalSupply == 0 || totalSupply() + _quantity <= maxTotalSupply, "exceed max total supply.");
370+
371+
uint256 _maxTotalSupply = maxTotalSupply;
372+
uint256 _maxWalletClaimCount = maxWalletClaimCount;
373+
require(_maxTotalSupply == 0 || totalSupply() + _quantity <= _maxTotalSupply, "exceed max total supply.");
370374
require(
371-
maxWalletClaimCount == 0 || walletClaimCount[_claimer] + _quantity <= maxWalletClaimCount,
375+
_maxWalletClaimCount == 0 || walletClaimCount[_claimer] + _quantity <= _maxWalletClaimCount,
372376
"exceed claim limit for wallet"
373377
);
374378

375-
(uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) = getClaimTimestamp(_conditionId, _claimer);
376-
require(lastClaimTimestamp == 0 || block.timestamp >= nextValidClaimTimestamp, "cannot claim yet.");
379+
(, uint256 nextValidClaimTimestamp) = getClaimTimestamp(_conditionId, _claimer);
380+
require(block.timestamp >= nextValidClaimTimestamp, "cannot claim yet.");
377381
}
378382

379383
/// @dev Checks whether a claimer meets the claim condition's allowlist criteria.
@@ -424,13 +428,15 @@ contract DropERC20 is
424428
{
425429
lastClaimTimestamp = claimCondition.limitLastClaimTimestamp[_conditionId][_claimer];
426430

427-
unchecked {
428-
nextValidClaimTimestamp =
429-
lastClaimTimestamp +
430-
claimCondition.phases[_conditionId].waitTimeInSecondsBetweenClaims;
431+
if (lastClaimTimestamp != 0) {
432+
unchecked {
433+
nextValidClaimTimestamp =
434+
lastClaimTimestamp +
435+
claimCondition.phases[_conditionId].waitTimeInSecondsBetweenClaims;
431436

432-
if (nextValidClaimTimestamp < lastClaimTimestamp) {
433-
nextValidClaimTimestamp = type(uint256).max;
437+
if (nextValidClaimTimestamp < lastClaimTimestamp) {
438+
nextValidClaimTimestamp = type(uint256).max;
439+
}
434440
}
435441
}
436442
}
@@ -488,7 +494,10 @@ contract DropERC20 is
488494

489495
/// @dev Lets a contract admin set the URI for contract-level metadata.
490496
function setContractURI(string calldata _uri) external onlyRole(DEFAULT_ADMIN_ROLE) {
497+
string memory prevURI = contractURI;
491498
contractURI = _uri;
499+
500+
emit ContractURIUpdated(prevURI, _uri);
492501
}
493502

494503
/*///////////////////////////////////////////////////////////////

contracts/interfaces/drop/IDropERC20.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ interface IDropERC20 is IERC20Upgradeable, IDropClaimCondition {
3434
/// @dev Emitted when the global max wallet claim count is updated.
3535
event MaxWalletClaimCountUpdated(uint256 count);
3636

37+
/// @dev Emitted when the contract URI is updated.
38+
event ContractURIUpdated(string prevURI, string newURI);
39+
3740
/**
3841
* @notice Lets an account claim a given quantity of tokens.
3942
*

lib/forge-std

src/test/drop/DropERC20.t.sol

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ contract DropERC20Test is BaseTest {
2424
address receiver = getActor(0);
2525
bytes32[] memory proofs = new bytes32[](0);
2626

27-
DropERC20.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1);
27+
DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1);
2828
conditions[0].maxClaimableSupply = 500;
2929
conditions[0].quantityLimitPerTransaction = 100;
3030
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
@@ -46,4 +46,52 @@ contract DropERC20Test is BaseTest {
4646
vm.expectRevert("invalid quantity claimed.");
4747
drop.claim(receiver, 200, address(0), 0, proofs, 1);
4848
}
49+
50+
function test_state_claim_timestamps() public {
51+
vm.warp(100);
52+
53+
address receiver = getActor(0);
54+
bytes32[] memory proofs = new bytes32[](0);
55+
56+
DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1);
57+
conditions[0].maxClaimableSupply = 500;
58+
conditions[0].quantityLimitPerTransaction = 100;
59+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
60+
61+
vm.prank(deployer);
62+
drop.setClaimConditions(conditions, false);
63+
64+
(uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) = drop.getClaimTimestamp(0, getActor(5));
65+
assertEq(lastClaimTimestamp, 0);
66+
assertEq(nextValidClaimTimestamp, 0);
67+
68+
vm.prank(getActor(5), getActor(5));
69+
drop.claim(receiver, 50, address(0), 0, proofs, 1);
70+
71+
(lastClaimTimestamp, nextValidClaimTimestamp) = drop.getClaimTimestamp(0, getActor(5));
72+
assertEq(lastClaimTimestamp, 100);
73+
assertEq(nextValidClaimTimestamp, type(uint256).max);
74+
}
75+
76+
function test_revert_claim_timestamps() public {
77+
vm.warp(1);
78+
79+
address receiver = getActor(0);
80+
bytes32[] memory proofs = new bytes32[](0);
81+
82+
DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1);
83+
conditions[0].maxClaimableSupply = 500;
84+
conditions[0].quantityLimitPerTransaction = 100;
85+
conditions[0].waitTimeInSecondsBetweenClaims = type(uint256).max;
86+
87+
vm.prank(deployer);
88+
drop.setClaimConditions(conditions, false);
89+
90+
vm.prank(getActor(5), getActor(5));
91+
drop.claim(receiver, 50, address(0), 0, proofs, 1);
92+
93+
vm.prank(getActor(5), getActor(5));
94+
vm.expectRevert("cannot claim yet.");
95+
drop.claim(receiver, 50, address(0), 0, proofs, 1);
96+
}
4997
}

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -650,10 +650,10 @@
650650
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.0.tgz#3092d70ea60e3d1835466266b1d68ad47035a2d5"
651651
integrity sha512-52Qb+A1DdOss8QvJrijYYPSf32GUg2pGaG/yCxtaA3cu4jduouTdg4XZSMLW9op54m1jH7J8hoajhHKOPsoJFw==
652652

653-
"@primitivefi/hardhat-dodoc@^0.1.3":
654-
version "0.1.3"
655-
resolved "https://registry.npmjs.org/@primitivefi/hardhat-dodoc/-/hardhat-dodoc-0.1.3.tgz"
656-
integrity sha512-IM2rwyk9SHxnifHnoCKmB1K1su/d1BvF5C0zspCWH8rVrrNpS1NzLTjisDNJmbM69/cWcEX0vfk449LuTsQVaw==
653+
"@primitivefi/hardhat-dodoc@^0.2.0":
654+
version "0.2.3"
655+
resolved "https://registry.yarnpkg.com/@primitivefi/hardhat-dodoc/-/hardhat-dodoc-0.2.3.tgz#76aebbfa70de2d6454af29e166b1430583b54c5c"
656+
integrity sha512-ver9uHa79LTDTeebOKZ/eOVRL/FP1k0s0x/5Bo/8ZaDdLWFVClKqZyZYVjjW4CJqTPCt8uU9b9p71P2vzH4O9A==
657657
dependencies:
658658
squirrelly "^8.0.8"
659659

0 commit comments

Comments
 (0)