Deploy.s.sol
is wrong causing the claiming process to failDeploy.s.sol
script causing eligible user cant claimDeploy.s.sol
is wrong causing the claiming process to failSubmitted by CarlosAlbaWork, aymericrt, heavenz52, f3d0ss, k3ySk1lls, 4rdiii, CryptoKook, lefg, FalseGenius, Vesper, mgnfyview, kostov, paprikrumplikas, vile, 0xanmol, Josh4324, farismaulana, mirkopezo, 0xb0k0, seeu, 0xDimo, Thaddeus19, shikhar229169, naman1729, Coffee, n0kto, Louis, NightHawK, shivam21, Bube, 0xAee, BornTBH. Selected submission by: f3d0ss.
The s_zkSyncUSDC
address in Deploy.s.sol
is incorrectly set, leading to a failure in the claiming process. This error results in funds being stuck in the MerkleAirdrop
contract due to the immutability of the token address.
All funds become permanently trapped in the MerkleAirdrop
contract, rendering them inaccessible for claiming or transfer.
Proof of Concept:
To demonstrate the issue, a test contract can be added and executed using the following command:
forge test --zksync --rpc-url $RPC_ZKSYNC --mt testDeployOnZkSync
Use the RPC URL https://mainnet.era.zksync.io
for testing.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { MerkleAirdrop, IERC20 } from "../src/MerkleAirdrop.sol";
import { Test, console2 } from "forge-std/Test.sol";
contract MerkleAirdropTest is Test {
MerkleAirdrop public s_airdrop;
uint256 s_amountToCollect = (25 * 1e6); // 25.000000
address s_collectorOne = 0x20F41376c713072937eb02Be70ee1eD0D639966C;
bytes32 s_proofOne = 0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838;
bytes32 s_proofTwo = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c;
bytes32[] s_proof = [s_proofOne, s_proofTwo];
address public deployer;
// From Deploy.t.sol
bytes32 public s_merkleRoot = 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd;
address public s_zkSyncUSDC = 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4;
uint256 public s_amountToAirdrop = 4 * (25 * 1e6);
function setUp() public {
deployer = makeAddr("deployer");
deal(0x1D17CbCf0D6d143135be902365d2e5E2a16538d4, deployer, 100 * 1e6);
vm.deal(s_collectorOne, 100 ether);
}
function testDeployOnZkSync() public {
if (block.chainid != 324) {
return;
}
vm.startPrank(deployer);
// From here there is the code from run()
s_airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
// Send USDC -> Merkle Air Dropper
IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(s_airdrop), s_amountToAirdrop);
// end code from run
vm.stopPrank();
vm.startPrank(s_collectorOne);
s_airdrop.claim{ value: s_airdrop.getFee() }(s_collectorOne, s_amountToCollect, s_proof);
vm.stopPrank();
}
function deployMerkleDropper(bytes32 merkleRoot, IERC20 zkSyncUSDC) public returns (MerkleAirdrop) {
return (new MerkleAirdrop(merkleRoot, zkSyncUSDC));
}
}
foundry-zksync
To resolve the issue, update the s_zkSyncUSDC address in Deploy.s.sol to the correct value:
- address public s_zkSyncUSDC = 0x1D17CbCf0D6d143135be902365d2e5E2a16538d4;
+ address public s_zkSyncUSDC = 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4;
Submitted by 0x8840, CarlosAlbaWork, 0xb0k0, heavenz52, f3d0ss, k3ySk1lls, 4rdiii, CryptoKook, FalseGenius, good0000vegetable, lefg, mgnfyview, kostov, lilizhu, mirkopezo, Vesper, vile, imth3AK, paprikrumplikas, shivam21, Josh4324, papalardo, 0xanmol, seeu, Sajjad, Awacs, uba7, peterSR, simon0417, abhishekthakur, shikhar229169, mangocola, 0xDimo, farismaulana, Thaddeus19, Sungyuk1, 0xsalami, takeshikovacs, naman1729, jo13, Audix, Coffee, sakar, n0kto, Louis, NightHawK, 0xAee, Bube, BornTBH. Selected submission by: mgnfyview.
A user eligible for the airdrop can verify themselves as being part of the merkle tree and claim their airdrop amount. However, there is no mechanism enabled to track the users who have already claimed their airdrop, and the merkle tree is still composed of the same user. This allows users to drain the MerkleAirdrop
contract by calling the MerkleAirdrop::claim()
function over and over again.
Severity: High
Likelihood: High
A malicious user can call the MerkleAirdrop::claim()
function over and over again until the contract is drained of all its funds. This also means that other users won't be able to claim their airdrop amounts.
Add the following test to ./test/MerkleAirdrop.t.sol
,
function testClaimAirdropOverAndOverAgain() public {
vm.deal(collectorOne, airdrop.getFee() * 4);
for (uint8 i = 0; i < 4; i++) {
vm.prank(collectorOne);
airdrop.claim{ value: airdrop.getFee() }(collectorOne, amountToCollect, proof);
}
assertEq(token.balanceOf(collectorOne), 100e6);
}
The test passes, and the malicious user has drained the contract of all its funds.
Manual review and Foundry.
Use a mapping to store the addresses that have claimed their airdrop amounts. Check and update this mapping each time a user tries to claim their airdrop amount.
contract MerkleAirdrop is Ownable {
using SafeERC20 for IERC20;
error MerkleAirdrop__InvalidFeeAmount();
error MerkleAirdrop__InvalidProof();
error MerkleAirdrop__TransferFailed();
+ error MerkleAirdrop__AlreadyClaimed();
uint256 private constant FEE = 1e9;
IERC20 private immutable i_airdropToken;
bytes32 private immutable i_merkleRoot;
+ mapping(address user => bool claimed) private s_hasClaimed;
...
function claim(address account, uint256 amount, bytes32[] calldata merkleProof) external payable {
+ if (s_hasClaimed[account]) revert MerkleAirdrop__AlreadyClaimed();
if (msg.value != FEE) {
revert MerkleAirdrop__InvalidFeeAmount();
}
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(account, amount))));
if (!MerkleProof.verify(merkleProof, i_merkleRoot, leaf)) {
revert MerkleAirdrop__InvalidProof();
}
+ s_hasClaimed[account] = true;
emit Claimed(account, amount);
i_airdropToken.safeTransfer(account, amount);
}
Now, let's unit test the changes,
function testCannotClaimAirdropMoreThanOnceAnymore() public {
vm.deal(collectorOne, airdrop.getFee() * 2);
vm.prank(collectorOne);
airdrop.claim{ value: airdrop.getFee() }(collectorOne, amountToCollect, proof);
vm.prank(collectorOne);
airdrop.claim{ value: airdrop.getFee() }(collectorOne, amountToCollect, proof);
}
The test correctly fails, with the following logs,
Failing tests:
Encountered 1 failing test in test/MerkleAirdropTest.t.sol:MerkleAirdropTest
[FAIL. Reason: MerkleAirdrop__AlreadyClaimed()] testCannotClaimAirdropMoreThanOnceAnymore() (gas: 96751)
Deploy.s.sol
script causing eligible user cant claimSubmitted by CarlosAlbaWork, aymericrt, 0xb0k0, f3d0ss, 4rdiii, FalseGenius, paprikrumplikas, vile, Josh4324, 0xanmol, farismaulana, mirkopezo, Vesper, abhishekthakur, shikhar229169, Thaddeus19, shishir, n0kto, Louis, NightHawK, shivam21, blackSquirrel, Bube, BornTBH, naman1729. Selected submission by: farismaulana.
Deploy.s.sol
script provide the wrong value causing eligible address can not claim when the contract are deployed using the script.
There are mismatch in value of merkle tree and merkle proof provided in Deploy.s.sol
and MerkleAirdropTest.t.sol
as the latter provided the correct value but the Deploy.s.sol
script provide the wrong value causing eligible address can not claim when the contract are deployed using the script.
NOTE: this assume the value of s_zkSyncUSDC
used in Deploy.s.sol
are already corrected using 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4
otherwise it would revert and not showing MerkleAirdrop__InvalidProof
error.
If we run yarn run makeMerkle
the following value would be returned:
Merkle Root: 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05
Proof for address: 0x20F41376c713072937eb02Be70ee1eD0D639966C with amount: 25000000000000000000:
[
'0x4fd31fee0e75780cd67704fbc43caee70fddcaa43631e2e1bc9fb233fada2394',
'0xc88d18957ad6849229355580c1bde5de3ae3b78024db2e6c2a9ad674f7b59f84'
]
the Merkle Root value 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05
are used in the Deploy.s.sol
script:
@> bytes32 public s_merkleRoot = 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05;
// 4 users, 25 USDC each
uint256 public s_amountToAirdrop = 4 * (25 * 1e6);
this is problematic because the intended value for every claim is 25 USDC
or 25 * 1e6
because USDC decimal is 6 instead of 18, and the above Merkle Root value are intended for 25 * 1e18
.
the claim
function cannot be called successfully because the contract only held s_amountToAirdrop = 4 * (25 * 1e6)
USDC and user cannot claim 25 * 1e18
as this is wrong value.
Add your zksync rpc url ZKSYNC_MAINNET_RPC_URL
to .env
file.
Add this helper code to Deploy.s.sol
so our test can capture the address of MerkleAirdrop
and IERC20
contract:
- function run() public {
+ function run() public returns (MerkleAirdrop, IERC20) {
vm.startBroadcast();
MerkleAirdrop airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
// Send USDC -> Merkle Air Dropper
IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(airdrop), s_amountToAirdrop);
vm.stopBroadcast();
+ // add helper
+ return (airdrop, IERC20(s_zkSyncUSDC));
}
Add MerkleAirdropDeployScriptTest.t.sol
to test
folder.
MerkleAirdropDeployScriptTest.t.sol
:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { MerkleAirdrop, IERC20 } from "../src/MerkleAirdrop.sol";
import { AirdropToken } from "./mocks/AirdropToken.sol";
import { Deploy } from "script/Deploy.s.sol";
import { Test } from "forge-std/Test.sol";
import { console } from "forge-std/Test.sol";
contract MerkleAirdropTest is Test, Deploy {
MerkleAirdrop public airdrop;
IERC20 public tokenIERC20;
AirdropToken public token;
uint256 amountToCollect = (25 * 1e6); // 25.000000
uint256 amountToSend = amountToCollect * 4;
address collectorOne = 0x20F41376c713072937eb02Be70ee1eD0D639966C;
// using proof from makeMerkle.js
bytes32 proofOne = 0x4fd31fee0e75780cd67704fbc43caee70fddcaa43631e2e1bc9fb233fada2394;
bytes32 proofTwo = 0xc88d18957ad6849229355580c1bde5de3ae3b78024db2e6c2a9ad674f7b59f84;
bytes32[] public proof = [proofOne, proofTwo];
function setUp() public {
// assume deployer have enough USDC balance, we use deal
address correctUSDCAddress = 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4;
deal(address(IERC20(correctUSDCAddress)), address(msg.sender), amountToSend);
console.log("USDC before:", IERC20(correctUSDCAddress).balanceOf(address(msg.sender)));
// running the Deploy.s.sol script
(airdrop, tokenIERC20) = Deploy.run();
console.log("USDC after:", IERC20(correctUSDCAddress).balanceOf(address(msg.sender)));
}
function testPOCUsingDeployScriptContractBrokenBecauseWrongMerkleRoot() public {
// assume the correct USDC address are used in deploying MerkleAirdrop.sol
uint256 startingBalance = tokenIERC20.balanceOf(collectorOne);
vm.deal(collectorOne, airdrop.getFee());
vm.startPrank(collectorOne);
airdrop.claim{ value: airdrop.getFee() }(collectorOne, amountToCollect, proof);
vm.stopPrank();
uint256 endingBalance = tokenIERC20.balanceOf(collectorOne);
assertEq(endingBalance - startingBalance, amountToCollect);
}
after that run the following forge test --zksync --fork-url $ZKSYNC_MAINNET_RPC_URL --mt testPOCUsingDeployScriptContractBrokenBecauseWrongMerkleRoot
the test will FAIL:
Failing tests:
Encountered 1 failing test in test/MerkleAirdropDeployScriptTest.t.sol:MerkleAirdropTest
[FAIL. Reason: MerkleAirdrop__InvalidProof()] testPOCUsingDeployScriptContractBrokenBecauseWrongMerkleRoot() (gas: 32340)
user cannot claim the airdrop
manual review and foundry
Changing the correct Merkle Root can solve this problem.
Using modified makeMerkle.js
we can generate the correct value:
.
.
.
/*//////////////////////////////////////////////////////////////
INPUTS
//////////////////////////////////////////////////////////////*/
- const amount = (25 * 1e18).toString();
+ const amount = (25 * 1e6).toString();
const userToGetProofOf = "0x20F41376c713072937eb02Be70ee1eD0D639966C";
// (1)
const values = [
[userToGetProofOf, amount],
["0x277D26a45Add5775F21256159F089769892CEa5B", amount],
["0x0c8Ca207e27a1a8224D1b602bf856479b03319e7", amount],
["0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D", amount],
];
/*//////////////////////////////////////////////////////////////
PROCESS
//////////////////////////////////////////////////////////////*/
// (2)
const tree = StandardMerkleTree.of(values, ["address", "uint256"]);
// (3)
console.log("Merkle Root:", tree.root);
// (4)
for (const [i, v] of tree.entries()) {
// if (v[0] === userToGetProofOf) {
// (3)
const proof = tree.getProof(i);
+ console.log(`Proof for address: ${v} with amount: ${amount}:\n`, proof);
- console.log(`Proof for address: ${userToGetProofOf} with amount: ${amount}:\n`, proof);
- }
}
.
.
.
after that run yarn run makeMerkle
and the following should returned:
Merkle Root: 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd
Proof for address: 0x20F41376c713072937eb02Be70ee1eD0D639966C,25000000 with amount: 25000000:
[
'0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838',
'0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c'
]
Proof for address: 0x277D26a45Add5775F21256159F089769892CEa5B,25000000 with amount: 25000000:
[
'0x2683f462a4457349d6d7ef62d4208ef42c89c2cff9543cd8292d9269d832c3e8',
'0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366'
]
Proof for address: 0x0c8Ca207e27a1a8224D1b602bf856479b03319e7,25000000 with amount: 25000000:
[
'0xee1cda884ead2c9f34338f48263e7edd6e5f35bf4f09c9c0930d995911004eed',
'0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c'
]
Proof for address: 0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D,25000000 with amount: 25000000:
[
'0x1e6784ff835523401f4db6e3ab48fa5bdf523a46a5bc0410a5639d837352b194',
'0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366'
]
using the correct value above, we should change the Deploy.s.sol
value of s_merkleRoot
:
Deploy.s.sol
:
- bytes32 public s_merkleRoot = 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05;
+ bytes32 public s_merkleRoot = 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd;
// 4 users, 25 USDC each
uint256 public s_amountToAirdrop = 4 * (25 * 1e6);
after that add the following code to MerkleAirdropDeployScriptTest.t.sol
that we already created beforehand.
MerkleAirdropDeployScriptTest.t.sol
:
function testUsingDeployScriptContractCorrectMerkleRoot() public {
// assume the correct USDC address are used in deploying MerkleAirdrop.sol
// using the correctly generated merkleProof
bytes32[] memory correctProofs = new bytes32[](8);
correctProofs[0] = 0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838;
correctProofs[1] = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c;
correctProofs[2] = 0x2683f462a4457349d6d7ef62d4208ef42c89c2cff9543cd8292d9269d832c3e8;
correctProofs[3] = 0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366;
correctProofs[4] = 0xee1cda884ead2c9f34338f48263e7edd6e5f35bf4f09c9c0930d995911004eed;
correctProofs[5] = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c;
correctProofs[6] = 0x1e6784ff835523401f4db6e3ab48fa5bdf523a46a5bc0410a5639d837352b194;
correctProofs[7] = 0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366;
// list of eligible addresses
address[4] memory eligibleAddress = [
0x20F41376c713072937eb02Be70ee1eD0D639966C,
0x277D26a45Add5775F21256159F089769892CEa5B,
0x0c8Ca207e27a1a8224D1b602bf856479b03319e7,
0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D
];
for (uint256 i = 0; i < eligibleAddress.length; ++i) {
uint256 startingBalance = tokenIERC20.balanceOf(eligibleAddress[i]);
vm.deal(eligibleAddress[i], airdrop.getFee());
// get corresponding proof for address i
bytes32[] memory proofs = new bytes32[](2);
proofs[0] = correctProofs[i * 2];
proofs[1] = correctProofs[i * 2 + 1];
vm.startPrank(eligibleAddress[i]);
airdrop.claim{ value: airdrop.getFee() }(eligibleAddress[i], amountToCollect, proofs);
vm.stopPrank();
uint256 endingBalance = tokenIERC20.balanceOf(eligibleAddress[i]);
assertEq(endingBalance - startingBalance, amountToCollect);
}
}
then run the following command forge test --zksync --fork-url $ZKSYNC_MAINNET_RPC_URL --mt testUsingDeployScriptContractCorrectMerkleRoot
the test result PASS:
Ran 1 test for test/MerkleAirdropDeployScriptTest.t.sol:MerkleAirdropTest
[PASS] testUsingDeployScriptContractCorrectMerkleRoot() (gas: 63544)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 76.19s (23.89s CPU time)
Submitted by mgnfyview, NightHawK, Vesper, Bube. Selected submission by: Bube.
The users that use account abstraction wallets have different addresses across chains for the same account.
In the docs is said:
"Our team is looking to airdrop 100 USDC tokens on the zkSync era chain to 4 lucky addresses based on their activity on the Ethereum L1. The Ethereum addresses are:
0x20F41376c713072937eb02Be70ee1eD0D639966C
0x277D26a45Add5775F21256159F089769892CEa5B
0x0c8Ca207e27a1a8224D1b602bf856479b03319e7
0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D"
The user can claim his/her USDC tokens through the MerkleAirdrop::claim
function. This function requires account, amount and proof array
. With the help of this three arguments the merkle proof will ensure that the caller is eligible to claim. But in the generated merkle root are used the Ethereum addresses of the lucky users. But the protocol will be deployed on the zkSync era chain. If any of them uses account abstraction wallet, this lucky user will not be able to claim his/her tokens. The account abstraction wallets have different addresses in the different chains for the same account.
The users that use account abstraction wallets have different addresses on the zkSync era chain. That means these users will not be able to claim their USDC tokens, because the merkle root will require another account address (this on Ethereum).
Manual Review
Ensure that the addresses in makeMerkle
file for the lucky users are their addresses for the zkSync era chain.
Submitted by vile, imth3AK, n0kto, blackSquirrel. Selected submission by: vile.
https://github.com/Cyfrin/2024-04-airdropper/blob/main/src/MerkleAirdrop.sol#L15
https://github.com/Cyfrin/2024-04-airdropper/blob/main/src/MerkleAirdrop.sol#L42-L47
The low MerkleAirdrop::FEE
(1 Gwei) makes it economically impractical (ETH-wise) for the owner to claim fees, even with the low gas cost of the zkSync chain. The fee should either be removed or increased to make it economically practical to claim by the owner.
The low MerkleAirdrop::FEE
(1 Gwei) makes it economically impractical (ETH-wise) for the owner to claim fees, even with the low gas cost of the zkSync chain.
The gas cost for the owner to call MerkleAirdrop::claimFees
is 30,479 gas units. Using the average zkSync gas price of 0.02 Gwei, the effective total gas cost would be ~609 Gwei or 0.000000609 Ether. For it to be economically sensible to claim fees (using the current fee price of 1 Gwei), there would need to be greater than or equal to 609 successful airdrop claims to meet or exceed the gas cost. Compared to the current number of addresses that are a part of the merkle tree, there is a significant discrepancy.
MerkleAirdropTest.t.sol
address owner = vm.addr(1);
...
// deploy contracts as an EOA instead of contract
function setUp() public {
vm.startPrank(owner);
token = new AirdropToken();
airdrop = new MerkleAirdrop(merkleRoot, token);
token.mint(owner, amountToSend);
token.transfer(address(airdrop), amountToSend);
vm.stopPrank();
}
...
function test_GasExeceedsFeeClaimAmount() public {
uint256 assumedZksyncGasPrice = 0.00000000002 ether; // 0.02 Gwei
uint256 airdropFee = airdrop.getFee();
vm.deal(collectorOne, airdropFee);
vm.startPrank(collectorOne);
airdrop.claim{ value: airdropFee }(collectorOne, amountToCollect, proof);
vm.stopPrank();
// assert the contract and owner have the proper balances
assertEq(address(airdrop).balance, airdropFee);
assertEq(owner.balance, 0);
vm.startPrank(owner);
uint256 gasBeforeClaim = gasleft();
airdrop.claimFees();
uint256 gasAfterClaim = gasleft();
vm.stopPrank();
// assert the contract has had its fees claimed by owner
assertEq(address(airdrop).balance, 0);
// assert that the amount of gas spent is greater than the fees obtained (in wei)
uint256 gasDelta = gasBeforeClaim - gasAfterClaim;
assertGt((gasDelta * assumedZksyncGasPrice), owner.balance);
}
forge test --match-test test_GasExeceedsFeeClaimAmount --gas-report -vvvv
Ran 1 test for test/MerkleAirdropTest.t.sol:MerkleAirdropTest
[PASS] test_GasExeceedsFeeClaimAmount() (gas: 129297)
Traces:
[129297] MerkleAirdropTest::test_GasExeceedsFeeClaimAmount()
│ ...
├─ [0] VM::assertGt(620640000000 [6.206e11], 1000000000 [1e9]) [staticcall]
│ └─ ← ()
└─ ← ()
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.38ms (690.20µs CPU time)
| src/MerkleAirdrop.sol:MerkleAirdrop contract | | | | | |
| -------------------------------------------- | --------------- | ----- | ------ | ----- | ------- |
| Deployment Cost | Deployment Size | | | | |
| 540806 | 2502 | | | | |
| Function Name | min | avg | median | max | # calls |
| claim | 59686 | 59686 | 59686 | 59686 | 1 |
| claimFees | 30479 | 30479 | 30479 | 30479 | 1 | <---
| getFee | 225 | 225 | 225 | 225 | 1 |
...
Ran 1 test suite in 5.26ms (2.38ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
There exists an economic disinsentive for the owner to claim fees from the contract.
Manual Analysis, Foundry Tests & Gas Report
Either remove the need for a fee to be paid during a claim or increase the claim fee to make it economically practical.