IDOGUCashcatAirdrop
0x030bd8ac3d24c8e4781e24f43c135dd8fbc37b10
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
3,356 bytes
ABI entries
31
10 read · 4 write
License
mit
Contract information
- Address
- 0x030bd8ac3d24c8e4781e24f43c135dd8fbc37b10
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0x650Ea8D190…8dCfD75609
- Creation tx
- 0xa4b7140da9…4a85ebebfc
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (10)
MAX_BATCH_SIZE() → uint256
batchCount() → uint256
campaignHash() → bytes32
distributedAmount() → uint256
distributedRecipients() → uint256
owner() → address
paused() → bool
processed(address) → bool
processedStatuses(address[]) → bool[]
token() → address
Events (4)
BatchAirdroppedOwnershipTransferredPauseChangedTokenRecovered
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"internalType": "bytes32",
"name": "manifestHash",
"type": "bytes32"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "AlreadyProcessed",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "supplied",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maximum",
"type": "uint256"
}
],
"name": "BatchTooLarge",
"type": "error"
},
{
"inputs": [],
"name": "EmptyBatch",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "available",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "required",
"type": "uint256"
}
],
"name": "InsufficientTokenBalance",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAddress",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAmount",
"type": "error"
},
{
"inputs": [],
"name": "NativeCurrencyNotAccepted",
"type": "error"
},
{
"inputs": [],
"name": "NotOwner",
"type": "error"
},
{
"inputs": [],
"name": "Paused",
"type": "error"
},
{
"inputs": [],
"name": "ReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "TokenTransferFailed",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "batchNumber",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "recipientCount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountEach",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "totalAmount",
"type": "uint256"
}
],
"name": "BatchAirdropped",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "paused",
"type": "bool"
}
],
"name": "PauseChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "TokenRecovered",
"type": "event"
},
{
"inputs": [],
"name": "MAX_BATCH_SIZE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "recipients",
"type": "address[]"
},
{
"internalType": "uint256",
"name": "amountEach",
"type": "uint256"
}
],
"name": "airdropEqual",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "batchCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "campaignHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "distributedAmount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "distributedRecipients",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "processed",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "recipients",
"type": "address[]"
}
],
"name": "processedStatuses",
"outputs": [
{
"internalType": "bool[]",
"name": "statuses",
"type": "bool[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "tokenAddress",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "recoverToken",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "shouldPause",
"type": "bool"
}
],
"name": "setPaused",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "token",
"outputs": [
{
"internalType": "contract IERC20AirdropToken",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
interface IERC20AirdropToken {
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}
/// @title IDOGU CASHCAT Holder Airdrop
/// @notice A transparent, non-upgradeable, one-purpose batch distributor.
/// @dev The campaign hash commits to the off-chain snapshot manifest used by the local dashboard.
contract IDOGUCashcatAirdrop {
error AlreadyProcessed(address recipient);
error BatchTooLarge(uint256 supplied, uint256 maximum);
error EmptyBatch();
error InsufficientTokenBalance(uint256 available, uint256 required);
error InvalidAddress();
error InvalidAmount();
error NativeCurrencyNotAccepted();
error NotOwner();
error Paused();
error ReentrantCall();
error TokenTransferFailed(address token, address recipient, uint256 amount);
event BatchAirdropped(uint256 indexed batchNumber, uint256 recipientCount, uint256 amountEach, uint256 totalAmount);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event PauseChanged(bool paused);
event TokenRecovered(address indexed token, address indexed recipient, uint256 amount);
uint256 public constant MAX_BATCH_SIZE = 150;
IERC20AirdropToken public immutable token;
bytes32 public immutable campaignHash;
address public owner;
bool public paused;
uint256 public batchCount;
uint256 public distributedRecipients;
uint256 public distributedAmount;
mapping(address => bool) public processed;
uint256 private _entered = 1;
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
modifier nonReentrant() {
if (_entered != 1) revert ReentrantCall();
_entered = 2;
_;
_entered = 1;
}
constructor(address tokenAddress, bytes32 manifestHash) {
if (tokenAddress == address(0) || tokenAddress.code.length == 0) revert InvalidAddress();
token = IERC20AirdropToken(tokenAddress);
campaignHash = manifestHash;
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Sends the same token amount to each unprocessed recipient.
function airdropEqual(address[] calldata recipients, uint256 amountEach) external onlyOwner nonReentrant {
if (paused) revert Paused();
uint256 length = recipients.length;
if (length == 0) revert EmptyBatch();
if (length > MAX_BATCH_SIZE) revert BatchTooLarge(length, MAX_BATCH_SIZE);
if (amountEach == 0) revert InvalidAmount();
uint256 required = length * amountEach;
uint256 available = token.balanceOf(address(this));
if (available < required) revert InsufficientTokenBalance(available, required);
for (uint256 i; i < length; ) {
address recipient = recipients[i];
if (recipient == address(0) || recipient == address(this)) revert InvalidAddress();
if (processed[recipient]) revert AlreadyProcessed(recipient);
processed[recipient] = true;
_safeTransfer(address(token), recipient, amountEach);
unchecked {
++i;
}
}
unchecked {
++batchCount;
distributedRecipients += length;
distributedAmount += required;
}
emit BatchAirdropped(batchCount, length, amountEach, required);
}
/// @notice Returns processing status for a dashboard-sized group of addresses.
function processedStatuses(address[] calldata recipients) external view returns (bool[] memory statuses) {
uint256 length = recipients.length;
statuses = new bool[](length);
for (uint256 i; i < length; ) {
statuses[i] = processed[recipients[i]];
unchecked {
++i;
}
}
}
function setPaused(bool shouldPause) external onlyOwner {
paused = shouldPause;
emit PauseChanged(shouldPause);
}
function transferOwnership(address newOwner) external onlyOwner {
if (newOwner == address(0)) revert InvalidAddress();
address previousOwner = owner;
owner = newOwner;
emit OwnershipTransferred(previousOwner, newOwner);
}
/// @notice Recovers leftover campaign tokens or unrelated tokens accidentally sent here.
function recoverToken(address tokenAddress, address recipient, uint256 amount) external onlyOwner nonReentrant {
if (tokenAddress == address(0) || recipient == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
_safeTransfer(tokenAddress, recipient, amount);
emit TokenRecovered(tokenAddress, recipient, amount);
}
function _safeTransfer(address tokenAddress, address recipient, uint256 amount) private {
(bool success, bytes memory returnData) = tokenAddress.call(
abi.encodeWithSelector(IERC20AirdropToken.transfer.selector, recipient, amount)
);
if (!success || (returnData.length != 0 && !abi.decode(returnData, (bool)))) {
revert TokenTransferFailed(tokenAddress, recipient, amount);
}
}
receive() external payable {
revert NativeCurrencyNotAccepted();
}
}
Chain explorer2952msChain node73ms