fix(deploy): verify airdrop reserve allowance so it can't ship unclaimable#49
fix(deploy): verify airdrop reserve allowance so it can't ship unclaimable#49rplusq wants to merge 1 commit into
Conversation
…mable Airdrop.claimTokens pulls tokens via transferFrom(reserveAddress, ...), but verifyDeployments never checked the reserve's allowance to the Airdrop. A deployment with an unfunded/unapproved treasury passed verification yet was unclaimable. Assert allowance(treasury, airdrop) > 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @rplusq's task in 1m 37s —— View job PR Review
Found 1 issue(s)Issue 1:
|
| // claimTokens pulls via transferFrom(reserveAddress, ...), so the reserve MUST have approved the | ||
| // Airdrop to spend L2WCT — without it the airdrop is deployed but unclaimable. Verify the allowance | ||
| // so a missing treasury approval fails verification instead of shipping a dead airdrop. | ||
| if (deps.l2wct.allowance(params.treasury, address(deps.airdrop)) == 0) { |
There was a problem hiding this comment.
🤖 Auto Review Issue: allowance == 0 guard is too weak — non-zero but insufficient approval still ships an unclaimable airdrop
Severity: MEDIUM
Category: code_quality
Tool: Claude Auto Review
Context:
- Pattern: The check
deps.l2wct.allowance(params.treasury, address(deps.airdrop)) == 0only catches a completely missing approval - Risk: An approval of 1 wei (or any amount less than the airdrop total) passes verification, yet
claimTokenswill revert onsafeTransferFromfor virtually every real claim - Impact: The stated goal is "can't ship unclaimable" — but this check permits shipping an airdrop where allowance = 1 and the merkle tree distributes millions of tokens; the airdrop is still unclaimable in practice
- Trigger: Treasury
approved a non-zero but insufficient amount (easy to do by mistake, e.g. approving in the wrong decimals)
Recommendation: If the standard deployment pattern uses a max approval for the treasury (common for reserve-based airdrops), assert that explicitly:
if (deps.l2wct.allowance(params.treasury, address(deps.airdrop)) != type(uint256).max) {
revert("Airdrop reserve allowance must be type(uint256).max");
}If exact amounts are used instead, cross-check against the treasury balance as a proxy:
uint256 allowance = deps.l2wct.allowance(params.treasury, address(deps.airdrop));
uint256 reserve = deps.l2wct.balanceOf(params.treasury);
if (allowance < reserve) {
revert("Airdrop reserve allowance is less than treasury balance");
}Either bound is tighter than > 0 and actually guarantees the airdrop is claimable up to the treasury's current holding.
|
Closing — verifying the airdrop reserve allowance is an operational (deploy-runbook / Safe-checklist) concern rather than something the deploy script should enforce. Not carrying this in the script layer. |
Airdrop.claimTokenspulls tokens viatransferFrom(reserveAddress, ...), butverifyDeploymentsnever checked the reserve's allowance to the Airdrop. A deployment with an unfunded/unapproved treasury passed verification yet was unclaimable. Assertallowance(treasury, airdrop) > 0.🤖 Generated with Claude Code