Address Contract Partially Verified
Address
0x5A96C8981CAbad76D54af9DCe4E2A7189c5a4c6C
Balance
0 ETH
Nonce
1
Code Size
3990 bytes
Creator
0x39bcb776...Aa7A at tx 0x008093df...1733f2
Indexed Transactions
0
Contract Bytecode
3990 bytes
0x6080806040526004361015610012575f80fd5b5f3560e01c9081630d0e96da146108b757508063158ef93e1461089557806318d052e3146105875780634cd620981461051a5780635ce97dbb146104e05780636588103b1461049c5780636d7779d9146104585780638129fc1c146103ea578063830953ab146103cd5780639e34070f146102a2578063cd52d7c9146102d1578063ce516507146102a2578063d1a26ed314610285578063d54ad2a114610268578063da25de3c146101bd578063ddd5e1b214610197578063e6798baa1461017b578063e83e2081146101355763fc0c546a146100ed575f80fd5b34610131575f366003190112610131576040517f0000000000000000000000006b419b281491473da2948b23fcb42f80e4ef25296001600160a01b03168152602090f35b5f80fd5b346101315760603660031901126101315761014e6108d1565b604435906001600160a01b03821682036101315760209161017191600435610a1b565b6040519015158152f35b34610131575f3660031901126101315760205f54604051908152f35b34610131576040366003190112610131576101bb6101b36108d1565b600435610c54565b005b34610131575f366003190112610131576040516370a0823160e01b81523060048201526020816024817f0000000000000000000000006b419b281491473da2948b23fcb42f80e4ef25296001600160a01b03165afa801561025d575f9061022a575b602090604051908152f35b506020813d602011610255575b81610244602093836108fd565b81010312610131576020905161021f565b3d9150610237565b6040513d5f823e3d90fd5b34610131575f366003190112610131576020600554604051908152f35b34610131575f366003190112610131576020600154604051908152f35b34610131576020366003190112610131576004355f526006602052602060ff60405f2054166040519015158152f35b34610131576102df366108e7565b9060ff600454166103bf577f00000000000000000000000097a294a099e8bcccf5adbdcf8050f186d67968e96001600160a01b031633036103b057808211156103a157805f55816001558082039180831161038d578260025514610379577f00000000000000000000000000000000000000000052b7d2dcc80cd2e400000004806003551561036a57005b63797fe85f60e11b5f5260045ffd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b63561ce9bb60e01b5f5260045ffd5b63ea8e4eb560e01b5f5260045ffd5b62dc149f60e41b5f5260045ffd5b34610131575f366003190112610131576020600354604051908152f35b34610131575f3660031901126101315760045460ff81166103bf577f00000000000000000000000097a294a099e8bcccf5adbdcf8050f186d67968e96001600160a01b031633036103b057600254156104495760ff1916600117600455005b63ff67cee560e01b5f5260045ffd5b34610131575f366003190112610131576040517f00000000000000000000000097a294a099e8bcccf5adbdcf8050f186d67968e96001600160a01b03168152602090f35b34610131575f366003190112610131576040517f000000000000000000000000282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d6001600160a01b03168152602090f35b34610131575f3660031901126101315760206040517f00000000000000000000000000000000000000000052b7d2dcc80cd2e40000008152f35b3461013157610528366108e7565b5f915f5481106103a15760015482116103a157818110156103a1575b81811061055657602083604051908152f35b805f52600660205260ff60405f205416610573575b600101610544565b9161057f60019161097d565b92905061056b565b346101315760403660031901126101315760043567ffffffffffffffff8111610131573660238201121561013157806004013567ffffffffffffffff8111610131573660248260051b84010111610131576105e06108d1565b905f60ff60045416156108865781156108775790915f906106008461094b565b6001600160a01b037f000000000000000000000000282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d811696908316801515955f959392905b888710156107bc5760248760051b830101355f54811080156107b0575b6106d957805f52600660205260ff60405f2054166106d9573389806107a6575b610727575b60249060208d604051938480926331a9108f60e11b82528760048301525afa91821561025d575f926106f7575b506001600160a01b039081169116036106d9576001916106cb886106d093610c54565b61097d565b965b0195610639565b96600191976106f16106ea8861097d565b978761098b565b526106d2565b61071991925060203d8111610720575b61071181836108fd565b8101906109fc565b908d6106a8565b503d610707565b5061074c6020828d6040519384928392632e7cda1d60e21b84528d33600486016109cb565b03816c447e69651d841bd8d104bed4935afa90811561025d575f91610778575b50156106d9578661067b565b610799915060203d811161079f575b61079181836108fd565b8101906109b3565b8c61076c565b503d610787565b5033851415610676565b50600154811015610656565b929390506107c98161094b565b935f5b8281106108525750506040519083825260208201527f825d262cdc35d64b4acb49b9d7488315b54f08fcf7e7c3fafaac63cd0f12aaf060403392a3604051918291604083019083526040602084015281518091526020606084019201905f5b818110610839575050500390f35b825184528594506020938401939092019160010161082b565b806108626001928498979861098b565b5161086d828861098b565b52019493946107cc565b63c2e5347d60e01b5f5260045ffd5b6321c4e35760e21b5f5260045ffd5b34610131575f36600319011261013157602060ff600454166040519015158152f35b34610131575f366003190112610131576020906002548152f35b602435906001600160a01b038216820361013157565b6040906003190112610131576004359060243590565b90601f8019910116810190811067ffffffffffffffff82111761091f57604052565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161091f5760051b60200190565b9061095582610933565b61096260405191826108fd565b8281528092610973601f1991610933565b0190602036910137565b5f19811461038d5760010190565b805182101561099f5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b90816020910312610131575180151581036101315790565b6001600160a01b0391821681529181166020830152909116604082015260608101919091525f608082015260a00190565b9081602091031261013157516001600160a01b03811681036101315790565b9160ff6004541615610c41575f5483108015610c48575b610c4157825f52600660205260ff60405f205416610c415782826001600160a01b0383168015159081610c2d575b50610b89575b50506040516331a9108f60e11b81526004810193909352506020826024817f000000000000000000000000282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d6001600160a01b03165afa91821561025d575f92610b68575b506001600160a01b03908116911603610b32576040516370a0823160e01b81523060048201526020816024817f0000000000000000000000006b419b281491473da2948b23fcb42f80e4ef25296001600160a01b03165afa90811561025d575f91610b36575b5060035411610b3257600190565b5f90565b90506020813d602011610b60575b81610b51602093836108fd565b8101031261013157515f610b24565b3d9150610b44565b610b8291925060203d6020116107205761071181836108fd565b905f610abe565b604051632e7cda1d60e21b815292935060209183918291610bda917f000000000000000000000000282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d6001600160a01b0316908890600486016109cb565b03816c447e69651d841bd8d104bed4935afa90811561025d575f91610c0e575b5015610c08575f8281610a66565b50505f90565b610c27915060203d60201161079f5761079181836108fd565b5f610bfa565b6001600160a01b038316141590505f610a60565b5050505f90565b50600154831015610a32565b60ff6004541615610886575f5481108015610f54575b610f4557805f52600660205260ff60405f205416610f36576001600160a01b03821691339083151580610f2c575b610e94575b506040516331a9108f60e11b815260048101839052906020826024817f000000000000000000000000282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d6001600160a01b03165afa91821561025d575f92610e73575b506001600160a01b03908116911603610e64576040516370a0823160e01b81523060048201527f0000000000000000000000006b419b281491473da2948b23fcb42f80e4ef25296001600160a01b031690602081602481855afa90811561025d575f91610e32575b5060035411610e235760205f918383526006825260408320600160ff19825416179055610d8960055461097d565b6005556044600354604051948593849263a9059cbb60e01b845233600485015260248401525af190811561025d575f91610e04575b5015610df5576003546040519081527fca8bf70624ec0ecfc925e5746a0e4625afe01129043c1c7201c7ce01075ea3ac60203392a4565b6312171d8360e31b5f5260045ffd5b610e1d915060203d60201161079f5761079181836108fd565b5f610dbe565b631e9acf1760e31b5f5260045ffd5b90506020813d602011610e5c575b81610e4d602093836108fd565b8101031261013157515f610d5b565b3d9150610e40565b631022318760e21b5f5260045ffd5b610e8d91925060203d6020116107205761071181836108fd565b905f610cf3565b604051632e7cda1d60e21b815290915060208180610ee1867f000000000000000000000000282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d6001600160a01b03168733600486016109cb565b03816c447e69651d841bd8d104bed4935afa90811561025d575f91610f0d575b50156103b0575f610c9d565b610f26915060203d60201161079f5761079181836108fd565b5f610f01565b5033841415610c98565b630c8d9eab60e31b5f5260045ffd5b635f6f1f0b60e01b5f5260045ffd5b50600154811015610c6a56fea26469706673582212209928deb161e4e81c1e9c82f92647672404a712fe1d2e81e23d90acab9ef724cf64736f6c634300081e0033
Verified Source Code Partial Match
Compiler: v0.8.30+commit.73712a01
EVM: prague
Optimization: Yes (200 runs)
StrategicDrop.sol 332 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/**
* @title StrategicDrop
* @notice Strategic Reserve airdrop contract for any collection
* @dev Allows NFT holder to claim an a share of deposited tokens once per NFT
*/
interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address);
}
interface IDelegateRegistry {
function checkDelegateForERC721(
address to, // The delegate wallet
address from, // The NFT owner's wallet
address contract_, // The NFT collection address
uint256 tokenId, // Specific NFT ID being checked
bytes32 rights // Subdelegation rights (use "" for all)
) external view returns (bool);
}
/* ═══════════════════════════════════════════════════ */
/* CUSTOM ERRORS */
/* ═══════════════════════════════════════════════════ */
/// @notice Invalid address (zero address)
error InvalidAddress();
/// @notice Zero amount provided
error ZeroAmount();
/// @notice Contract already initialized
error AlreadyInitialized();
/// @notice Contract not initialized yet
error NotInitialized();
/// @notice Caller not authorized
error NotAuthorized();
/// @notice Token ID ranges not set
error RangesNotSet();
/// @notice Invalid token ID range
error InvalidRange();
/// @notice Invalid NFT ID for claim
error InvalidNFTId();
/// @notice Claim amount rounds to zero
error ClaimAmountRoundsToZero();
/// @notice NFT already claimed airdrop
error AlreadyClaimed();
/// @notice Caller not NFT owner
error NotNFTOwner();
/// @notice Insufficient contract balance
error InsufficientBalance();
/// @notice Token transfer failed
error TransferFailed();
/// @notice Empty NFT ID array provided
error EmptyBatch();
contract StrategicDrop {
//
// _____ __ __ _
// / ___// /__________ _/ /____ ____ _(_)____
// \__ \/ __/ ___/ __ `/ __/ _ \/ __ `/ / ___/
// ___/ / /_/ / / /_/ / /_/ __/ /_/ / / /__
// /____/\__/_/ \__,_/\__/\___/\__, /_/\___/
// / __ \___ ________ _____/____/___
// / /_/ / _ \/ ___/ _ \/ ___/ | / / _ \
// / _, _/ __(__ ) __/ / | |/ / __/
// /_/ |_|\___/____/\___/_/ |___/\___/
address constant DELEGATE_REGISTRY = 0x00000000000000447e69651d841bD8D104Bed493;
IERC20 public immutable token; // The ERC20 token being airdropped
IERC721 public immutable nftCollection; // The NFT collection eligible for claims
uint256 public immutable totalAirdrop; // Total tokens allocated for airdrop
address public immutable authorizedInitializer; // Address authorized to initialize ranges
uint256 public startTokenId; // First valid token ID (set during initialization)
uint256 public endTokenId; // Last valid token ID + 1 (set during initialization)
uint256 public totalNFTs; // Total count of NFTs (calculated during initialization)
uint256 public claimAmount; // Amount each NFT can claim (calculated during initialization)
bool public initialized; // Whether the airdrop has been initialized
uint256 public totalClaimed; // Total number of claims made
// Track which NFT IDs have claimed
mapping(uint256 => bool) public hasClaimed;
// Events
event Claimed(address indexed claimer, uint256 indexed nftId, uint256 amount, address indexed nftOwner);
event BatchClaimed(address indexed claimer, uint256 successCount, uint256 failCount, address indexed nftOwner);
/// @notice Constructor - sets immutable values (ranges set later via initialize)
/// @param _token Address of the ERC20 token to distribute
/// @param _nftCollection Address of the NFT collection
/// @param _totalAirdrop Total amount of tokens to distribute across all NFTs
/// @param _authorizedInitializer Address authorized to call initialize()
constructor(
address _token,
address _nftCollection,
uint256 _totalAirdrop,
address _authorizedInitializer
) {
if (_token == address(0)) revert InvalidAddress();
if (_nftCollection == address(0)) revert InvalidAddress();
if (_totalAirdrop == 0) revert ZeroAmount();
if (_authorizedInitializer == address(0)) revert InvalidAddress();
token = IERC20(_token);
nftCollection = IERC721(_nftCollection);
totalAirdrop = _totalAirdrop;
authorizedInitializer = _authorizedInitializer;
}
/// @notice Finalize and enable claims
/// @dev Locks token ID ranges and enables claiming
function initialize() external {
if (initialized) revert AlreadyInitialized();
if (msg.sender != authorizedInitializer) revert NotAuthorized();
if (totalNFTs == 0) revert RangesNotSet();
initialized = true;
}
/// @notice Set or update the token ID ranges for the airdrop
/// @param _startTokenId First valid token ID in the collection
/// @param _endTokenId Last valid token ID + 1 (exclusive)
/// @dev Can be called multiple times before initialization
function setTokenIdRange(uint256 _startTokenId, uint256 _endTokenId) external {
if (initialized) revert AlreadyInitialized();
if (msg.sender != authorizedInitializer) revert NotAuthorized();
if (_endTokenId <= _startTokenId) revert InvalidRange();
startTokenId = _startTokenId;
endTokenId = _endTokenId;
totalNFTs = _endTokenId - _startTokenId;
claimAmount = totalAirdrop / totalNFTs;
if (claimAmount == 0) revert ClaimAmountRoundsToZero();
}
/// @notice Internal claim logic - validates and processes a single claim
/// @param _nftId The ID of the NFT to claim for
/// @param _nftOwner The address that owns the NFT (use address(0) if caller is owner)
function _claim(uint256 _nftId, address _nftOwner) internal {
// Check 0: Airdrop must be initialized
if (!initialized) revert NotInitialized();
// Check 1: NFT ID must be within valid range
if (_nftId < startTokenId || _nftId >= endTokenId) revert InvalidNFTId();
// Check 2: NFT must not have already claimed
if (hasClaimed[_nftId]) revert AlreadyClaimed();
// Resolve actual NFT owner (supports delegation)
address requester = msg.sender;
if (_nftOwner != address(0) && _nftOwner != msg.sender) {
bool isDelegateValid = IDelegateRegistry(DELEGATE_REGISTRY)
.checkDelegateForERC721(
msg.sender, // The delegate calling this function
_nftOwner, // The actual NFT owner
address(nftCollection), // The NFT collection being checked
_nftId, // Specific NFT ID being claimed
"" // Empty string = accept any delegation rights
);
if (!isDelegateValid) revert NotAuthorized();
requester = _nftOwner; // Use NFT owner as the actual owner for all checks
}
// Check 3: Requester must own the NFT
if (nftCollection.ownerOf(_nftId) != requester) revert NotNFTOwner();
// Check 4: Contract must have sufficient balance
if (token.balanceOf(address(this)) < claimAmount) revert InsufficientBalance();
// Effects: Mark as claimed BEFORE transfer (reentrancy protection)
hasClaimed[_nftId] = true;
totalClaimed++;
// Interaction: Transfer tokens to claimer (delegate)
if (!token.transfer(msg.sender, claimAmount)) revert TransferFailed();
// Emit event
emit Claimed(msg.sender, _nftId, claimAmount, _nftOwner);
}
/// @notice Allows the owner of an NFT (or their delegate) to claim airdrop allocation
/// @param _nftId The ID of the NFT to claim for
/// @param _nftOwner The address that owns the NFT (use address(0) if caller is owner)
function claim(uint256 _nftId, address _nftOwner) external {
_claim(_nftId, _nftOwner);
}
/// @notice Claim airdrop for multiple NFTs in a single transaction
/// @param _nftIds Array of NFT IDs to claim for
/// @param _nftOwner The address that owns the NFTs (use address(0) if caller is owner)
/// @return successCount Number of successful claims
/// @return failedIds Array of NFT IDs that failed to claim
/// @dev Validates each NFT before claiming. Skips invalid NFTs. Critical errors revert entire batch.
function claimBatch(uint256[] calldata _nftIds, address _nftOwner)
external
returns (uint256 successCount, uint256[] memory failedIds)
{
// Validate batch input
if (!initialized) revert NotInitialized();
if (_nftIds.length == 0) revert EmptyBatch();
// Track failures
uint256 failCount = 0;
uint256[] memory tempFailedIds = new uint256[](_nftIds.length);
// Process each NFT with explicit pre-validation
for (uint256 i = 0; i < _nftIds.length; i++) {
uint256 nftId = _nftIds[i];
// Skip if out of valid range
if (nftId < startTokenId || nftId >= endTokenId) {
tempFailedIds[failCount++] = nftId;
continue;
}
// Skip if already claimed
if (hasClaimed[nftId]) {
tempFailedIds[failCount++] = nftId;
continue;
}
// Resolve delegation and validate authorization
address requester = msg.sender;
if (_nftOwner != address(0) && _nftOwner != msg.sender) {
bool isDelegateValid = IDelegateRegistry(DELEGATE_REGISTRY)
.checkDelegateForERC721(
msg.sender, // The delegate calling this function
_nftOwner, // The actual NFT owner
address(nftCollection), // The NFT collection being checked
nftId, // Specific NFT ID being claimed
"" // Empty string = accept any delegation rights
);
if (!isDelegateValid) {
tempFailedIds[failCount++] = nftId;
continue;
}
requester = _nftOwner;
}
// Skip if requester doesn't own the NFT
if (nftCollection.ownerOf(nftId) != requester) {
tempFailedIds[failCount++] = nftId;
continue;
}
// All pre-checks passed - call internal claim logic
// Critical errors (InsufficientBalance, TransferFailed) will revert entire batch
_claim(nftId, _nftOwner);
successCount++;
}
// Build right-sized result array
failedIds = new uint256[](failCount);
for (uint256 i = 0; i < failCount; i++) {
failedIds[i] = tempFailedIds[i];
}
// Emit batch summary event
emit BatchClaimed(msg.sender, successCount, failCount, _nftOwner);
}
/// @notice Check if an NFT has claimed its allocation
/// @param _nftId The NFT ID to check
/// @return bool True if claimed, false otherwise
function isClaimed(uint256 _nftId) external view returns (bool) {
return hasClaimed[_nftId];
}
/// @notice Get the current balance of airdrop tokens in the contract
/// @return uint256 The token balance
function remainingBalance() external view returns (uint256) {
return token.balanceOf(address(this));
}
/// @notice Calculate how many claims have been made
/// @param _startId Starting NFT ID to check (inclusive)
/// @param _endId Ending NFT ID to check (exclusive)
/// @return count Number of claims in the range
/// @dev Use this to count claims in batches to avoid gas limits
function countClaims(uint256 _startId, uint256 _endId) external view returns (uint256 count) {
if (_startId < startTokenId) revert InvalidRange();
if (_endId > endTokenId) revert InvalidRange();
if (_startId >= _endId) revert InvalidRange();
for (uint256 i = _startId; i < _endId; i++) {
if (hasClaimed[i]) {
count++;
}
}
}
/// @notice Check if a wallet can claim for a specific NFT (direct ownership or delegation)
/// @param _nftId The NFT ID to check
/// @param _claimer The wallet attempting to claim
/// @param _nftOwner The address that owns the NFT (use address(0) if claimer is owner)
/// @return canClaimNow True if claim is possible, false otherwise
function canClaim(uint256 _nftId, address _claimer, address _nftOwner) external view returns (bool canClaimNow) {
// Must be initialized
if (!initialized) return false;
// Must be in valid range
if (_nftId < startTokenId || _nftId >= endTokenId) return false;
// Must not have claimed already
if (hasClaimed[_nftId]) return false;
// Resolve requester
address requester = _claimer;
if (_nftOwner != address(0) && _nftOwner != _claimer) {
// Check delegation
bool isDelegateValid = IDelegateRegistry(DELEGATE_REGISTRY)
.checkDelegateForERC721(_claimer, _nftOwner, address(nftCollection), _nftId, "");
if (!isDelegateValid) return false;
requester = _nftOwner;
}
// Check ownership
if (nftCollection.ownerOf(_nftId) != requester) return false;
// Check balance
if (token.balanceOf(address(this)) < claimAmount) return false;
return true;
}
}
Read Contract
authorizedInitializer 0x6d7779d9 → address
canClaim 0xe83e2081 → bool
claimAmount 0x830953ab → uint256
countClaims 0x4cd62098 → uint256
endTokenId 0xd1a26ed3 → uint256
hasClaimed 0xce516507 → bool
initialized 0x158ef93e → bool
isClaimed 0x9e34070f → bool
nftCollection 0x6588103b → address
remainingBalance 0xda25de3c → uint256
startTokenId 0xe6798baa → uint256
token 0xfc0c546a → address
totalAirdrop 0x5ce97dbb → uint256
totalClaimed 0xd54ad2a1 → uint256
totalNFTs 0x0d0e96da → uint256
Write Contract 4 functions
These functions modify contract state and require a wallet transaction to execute.
claim 0xddd5e1b2
uint256 _nftId
address _nftOwner
claimBatch 0x18d052e3
uint256[] _nftIds
address _nftOwner
returns: uint256, uint256[]
initialize 0x8129fc1c
No parameters
setTokenIdRange 0xcd52d7c9
uint256 _startTokenId
uint256 _endTokenId
Recent Transactions
No transactions found for this address