Address Contract Verified
Address
0x4091428afFC997ff9e81B99a95827CEca3Fe6C39
Balance
0 ETH
Nonce
1
Code Size
5516 bytes
Creator
0x22385b5b...85e9 at tx 0x266b9802...9969b5
Indexed Transactions
0
Contract Bytecode
5516 bytes
0x60806040526004361061011f5760003560e01c8063736c0d5b116100a0578063d9caed1211610064578063d9caed121461039b578063dc19fba3146103bb578063dfc5d02f146103e8578063f0e8c98a146103fe578063f2fde38b1461041157600080fd5b8063736c0d5b146102a45780637ecebe00146102e45780638da5cb5b14610311578063c98df3921461032f578063d6687efa1461036357600080fd5b80633f250be9116100e75780633f250be9146101e35780634451d89f146102035780635eddd1571461023b5780636b0509b11461025b578063715018a61461028f57600080fd5b80630edca014146101245780631b9a91a41461014d57806331cb61051461016f5780633644e5151461018f57806337c7d97c146101c3575b600080fd5b34801561013057600080fd5b5061013a60055481565b6040519081526020015b60405180910390f35b34801561015957600080fd5b5061016d6101683660046110ab565b610431565b005b34801561017b57600080fd5b5061016d61018a3660046110e5565b61048e565b34801561019b57600080fd5b5061013a7f78e757e3d9baf308c623f8d098bc43f873efeed88dae274b9b1ee39ec0374c9c81565b3480156101cf57600080fd5b5061016d6101de36600461111e565b610520565b3480156101ef57600080fd5b5061016d6101fe36600461111e565b610534565b34801561020f57600080fd5b50600154610223906001600160a01b031681565b6040516001600160a01b039091168152602001610144565b34801561024757600080fd5b5061016d610256366004611180565b610578565b34801561026757600080fd5b5061013a7f35b2ec7f6b8de98ecac4ebad77eeb027055db9f351e3cb22ed6b51e57858295e81565b34801561029b57600080fd5b5061016d6107e7565b3480156102b057600080fd5b506102d46102bf3660046111d3565b60026020526000908152604090205460ff1681565b6040519015158152602001610144565b3480156102f057600080fd5b5061013a6102ff3660046111d3565b60046020526000908152604090205481565b34801561031d57600080fd5b506000546001600160a01b0316610223565b34801561033b57600080fd5b5061013a7fefd394092f3438d3d70b69849488ded459b111ec21a7e85f79a7d6f43be5ee2f81565b34801561036f57600080fd5b5061013a61037e3660046110ab565b600360209081526000928352604080842090915290825290205481565b3480156103a757600080fd5b5061016d6103b63660046111f7565b6107fb565b3480156103c757600080fd5b506103db6103d636600461124e565b610897565b6040516101449190611330565b3480156103f457600080fd5b5061013a60065481565b61016d61040c3660046113b8565b610962565b34801561041d57600080fd5b5061016d61042c3660046111d3565b610da6565b610439610de1565b6040516001600160a01b038316908290600081818185875af1925050503d8060008114610482576040519150601f19603f3d011682016040523d82523d6000602084013e610487565b606091505b5050505050565b610496610de1565b6001600160a01b0382166104bd57604051632057875960e21b815260040160405180910390fd5b6001600160a01b038216600081815260026020908152604091829020805460ff19168515159081179091558251938452908301527f863d338cad74814b108a06288ad5e0e80d56495e0332238b1d2cdcfa0ca8e5ce910160405180910390a15050565b610528610de1565b61053181610e0e565b50565b61053c610de1565b60058190556040518181527f2e9adf472a7bfcd9c05257af22428d595e2425953b906b268d6a90f3a5db8bd2906020015b60405180910390a150565b336000818152600460209081526040808320546003835281842089855283528184205482517f35b2ec7f6b8de98ecac4ebad77eeb027055db9f351e3cb22ed6b51e57858295e9481019490945291830194909452606082018890526080820187905260a0820184905260c082015260e00160408051601f1981840301815290829052805160209182012061190160f01b918301919091527f78e757e3d9baf308c623f8d098bc43f873efeed88dae274b9b1ee39ec0374c9c602283015260428201819052915060009060620160405160208183030381529060405280519060200120905060006106a086868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508693925050610e439050565b6001600160a01b03811660009081526002602052604090205490915060ff166106dc57604051638baa579f60e01b815260040160405180910390fd5b3360008181526003602090815260408083208c84528252808320429055838352600491829052918290208054600190810190915554915163a9059cbb60e01b815290810192909252602482018990526001600160a01b03169063a9059cbb906044016020604051808303816000875af115801561075d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610781919061145e565b6107a65760405162461bcd60e51b815260040161079d9061147b565b60405180910390fd5b604051878152889033907f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a9060200160405180910390a35050505050505050565b6107ef610de1565b6107f96000610e6d565b565b610803610de1565b60405163a9059cbb60e01b81526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303816000875af1158015610852573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610876919061145e565b6108925760405162461bcd60e51b815260040161079d9061147b565b505050565b60606000825167ffffffffffffffff8111156108b5576108b5611238565b6040519080825280602002602001820160405280156108de578160200160208202803683370190505b50905060005b8351811015610958576001600160a01b0385166000908152600360205260408120855190919086908490811061091c5761091c6114aa565b6020026020010151815260200190815260200160002054828281518110610945576109456114aa565b60209081029190910101526001016108e4565b5090505b92915050565b848314801561097057508215155b61098d5760405163512509d360e11b815260040160405180910390fd5b33600090815260046020526040812054908667ffffffffffffffff8111156109b7576109b7611238565b6040519080825280602002602001820160405280156109e0578160200160208202803683370190505b5090506000805b88811015610b2657336000908152600360205260408120908b8b84818110610a1157610a116114aa565b90506020020135815260200190815260200160002054838281518110610a3957610a396114aa565b60209081029190910181019190915233600090815260039091526040812042918c8c85818110610a6b57610a6b6114aa565b90506020020135815260200190815260200160002081905550878782818110610a9657610a966114aa565b9050602002013582610aa891906114c0565b9150898982818110610abc57610abc6114aa565b90506020020135336001600160a01b03167f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a8a8a85818110610b0057610b006114aa565b90506020020135604051610b1691815260200190565b60405180910390a36001016109e7565b5060007fefd394092f3438d3d70b69849488ded459b111ec21a7e85f79a7d6f43be5ee2f338b8b604051602001610b5e9291906114e1565b604051602081830303815290604052805190602001208a8a604051602001610b879291906114e1565b604051602081830303815290604052805190602001208787604051602001610baf919061150a565b60408051601f198184030181528282528051602091820120908301979097526001600160a01b03909516948101949094526060840192909252608083015260a082015260c081019190915260e00160408051601f1981840301815290829052805160209182012061190160f01b918301919091527f78e757e3d9baf308c623f8d098bc43f873efeed88dae274b9b1ee39ec0374c9c60228301526042820181905291506000906062016040516020818303038152906040528051906020012090506000610cb488888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508693925050610e439050565b6001600160a01b03811660009081526002602052604090205490915060ff16610cf057604051638baa579f60e01b815260040160405180910390fd5b3360008181526004602081905260409182902080548f019055600154915163a9059cbb60e01b815290810192909252602482018690526001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610d58573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7c919061145e565b610d985760405162461bcd60e51b815260040161079d9061147b565b505050505050505050505050565b610dae610de1565b6001600160a01b038116610dd857604051631e4fbdf760e01b81526000600482015260240161079d565b61053181610e6d565b6000546001600160a01b031633146107f95760405163118cdaa760e01b815233600482015260240161079d565b60068190556040518181527f242053990196329abfe592ccd51c34486fc2a0e8f5a9f7e551b5dd470d9a7d369060200161056d565b600080600080610e538686610ebd565b925092509250610e638282610f0a565b5090949350505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008351604103610ef75760208401516040850151606086015160001a610ee988828585610fc7565b955095509550505050610f03565b50508151600091506002905b9250925092565b6000826003811115610f1e57610f1e611540565b03610f27575050565b6001826003811115610f3b57610f3b611540565b03610f595760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115610f6d57610f6d611540565b03610f8e5760405163fce698f760e01b81526004810182905260240161079d565b6003826003811115610fa257610fa2611540565b03610fc3576040516335e2f38360e21b81526004810182905260240161079d565b5050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611002575060009150600390508261108c565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611056573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166110825750600092506001915082905061108c565b9250600091508190505b9450945094915050565b6001600160a01b038116811461053157600080fd5b600080604083850312156110be57600080fd5b82356110c981611096565b946020939093013593505050565b801515811461053157600080fd5b600080604083850312156110f857600080fd5b823561110381611096565b91506020830135611113816110d7565b809150509250929050565b60006020828403121561113057600080fd5b5035919050565b60008083601f84011261114957600080fd5b50813567ffffffffffffffff81111561116157600080fd5b60208301915083602082850101111561117957600080fd5b9250929050565b6000806000806060858703121561119657600080fd5b8435935060208501359250604085013567ffffffffffffffff8111156111bb57600080fd5b6111c787828801611137565b95989497509550505050565b6000602082840312156111e557600080fd5b81356111f081611096565b9392505050565b60008060006060848603121561120c57600080fd5b833561121781611096565b9250602084013561122781611096565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561126157600080fd5b823561126c81611096565b9150602083013567ffffffffffffffff81111561128857600080fd5b8301601f8101851361129957600080fd5b803567ffffffffffffffff8111156112b3576112b3611238565b8060051b604051601f19603f830116810181811067ffffffffffffffff821117156112e0576112e0611238565b6040529182526020818401810192908101888411156112fe57600080fd5b6020850194505b8385101561132157843580825260209586019590935001611305565b50809450505050509250929050565b602080825282518282018190526000918401906040840190835b8181101561136857835183526020938401939092019160010161134a565b509095945050505050565b60008083601f84011261138557600080fd5b50813567ffffffffffffffff81111561139d57600080fd5b6020830191508360208260051b850101111561117957600080fd5b600080600080600080606087890312156113d157600080fd5b863567ffffffffffffffff8111156113e857600080fd5b6113f489828a01611373565b909750955050602087013567ffffffffffffffff81111561141457600080fd5b61142089828a01611373565b909550935050604087013567ffffffffffffffff81111561144057600080fd5b61144c89828a01611137565b979a9699509497509295939492505050565b60006020828403121561147057600080fd5b81516111f0816110d7565b602080825260159082015274151bdad95b881d1c985b9cd9995c8819985a5b1959605a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b8082018082111561095c57634e487b7160e01b600052601160045260246000fd5b60006001600160fb1b038311156114f757600080fd5b8260051b80858437919091019392505050565b8151600090829060208501835b82811015611535578151845260209384019390910190600101611517565b509195945050505050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122061efd3097bf0067e0651ff6c224ff8c71b8c4a655532e92fb9cae20f7264954264736f6c634300081c0033
Verified Source Code Full Match
Compiler: v0.8.28+commit.7893614a
EVM: paris
Optimization: Yes (200 runs)
NodemarketClaim.sol 216 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/*
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░ ░▒▓██████▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
░▒▓████████▓▒░░▒▓██████▓▒░░▒▓███████▓▒░░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░
/// @title NodemarketClaim - A contract for claiming ERC20 tokens with signature verification
/// @notice This contract allows users to claim ERC20 tokens based on off-chain signature verification
/// @dev Uses EIP-712 for secure signature verification and supports both single and batch claims
*/ contract NodemarketClaim is Ownable {
using ECDSA for bytes32;
// EIP-712 typehash for the claim
bytes32 public constant CLAIM_TYPEHASH =
keccak256("Claim(address holder,uint256 tokenId,uint256 amount,uint256 nonce,uint256 lastClaimTimestamp)");
// EIP-712 typehash for the multi-claim
bytes32 public constant MULTI_CLAIM_TYPEHASH = keccak256(
"MultiClaim(address holder,uint256[] tokenIds,uint256[] amounts,uint256 nonce,uint256[] lastClaimTimestamps)"
);
// EIP-712 domain separator
bytes32 public immutable DOMAIN_SEPARATOR;
/// @notice The ERC20 token that can be claimed
IERC20 public claimToken;
/// @notice Mapping of authorized signers
mapping(address => bool) public signers;
/// @notice Mapping of last claim timestamp per holder and token ID
mapping(address => mapping(uint256 => uint256)) public lastClaimTimestamp;
/// @notice Mapping of nonces per holder to prevent replay attacks
mapping(address => uint256) public nonces;
/// @notice The daily rate at which tokens can be claimed
uint256 public dailyClaimRate;
/// @notice The timestamp when claiming becomes available
uint256 public claimStartTimestamp;
/// @notice Emitted when tokens are claimed
/// @param holder The address claiming tokens
/// @param tokenId The ID of the token being claimed for
/// @param amount The amount of tokens claimed
event Claimed(address indexed holder, uint256 indexed tokenId, uint256 amount);
/// @notice Emitted when the daily claim rate is updated
event UpdatedDailyClaimRate(uint256 dailyClaimRate);
/// @notice Emitted when the claim start timestamp is updated
event UpdatedClaimStartTimestamp(uint256 claimStartTimestamp);
/// @notice Emitted when a signer's status is updated
event UpdateSigner(address signer, bool value);
error InvalidNonce();
error InvalidSignature();
error ArrayLengthMismatch();
error InvalidSigner();
/// @notice Initializes the contract with the claim token and initial signer
/// @param _claimToken Address of the ERC20 token that can be claimed
/// @param signer Address of the initial authorized signer
constructor(address _claimToken, address signer) Ownable(msg.sender) {
require(signer != address(0), InvalidSigner());
claimToken = IERC20(_claimToken);
signers[signer] = true;
emit UpdateSigner(signer, true);
_setClaimStartTimestamp(block.timestamp);
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("NodemarketClaim")),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
}
/// @notice Claims tokens for a single token ID
/// @param tokenId The ID of the token to claim for
/// @param amount The amount of tokens to claim
/// @param signature The EIP-712 signature authorizing the claim
function claim(uint256 tokenId, uint256 amount, bytes calldata signature) external {
uint256 nonce = nonces[msg.sender];
bytes32 structHash = keccak256(
abi.encode(CLAIM_TYPEHASH, msg.sender, tokenId, amount, nonce, lastClaimTimestamp[msg.sender][tokenId])
);
bytes32 hash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash));
address signer = hash.recover(signature);
require(signers[signer], InvalidSignature());
lastClaimTimestamp[msg.sender][tokenId] = block.timestamp;
unchecked {
nonces[msg.sender]++;
}
require(claimToken.transfer(msg.sender, amount), "Token transfer failed");
emit Claimed(msg.sender, tokenId, amount);
}
/// @notice Claims tokens for multiple token IDs in a single transaction
/// @param tokenIds Array of token IDs to claim for
/// @param amounts Array of amounts to claim for each token ID
/// @param signature The EIP-712 signature authorizing the multi-claim
function multiClaim(uint256[] calldata tokenIds, uint256[] calldata amounts, bytes calldata signature) payable external {
require(tokenIds.length == amounts.length && amounts.length > 0, ArrayLengthMismatch());
uint256 nonce = nonces[msg.sender];
uint256[] memory lastClaimTimestamps = new uint256[](tokenIds.length);
uint256 totalAmount = 0;
for (uint256 i = 0; i < tokenIds.length; i++) {
lastClaimTimestamps[i] = lastClaimTimestamp[msg.sender][tokenIds[i]];
lastClaimTimestamp[msg.sender][tokenIds[i]] = block.timestamp;
totalAmount += amounts[i];
emit Claimed(msg.sender, tokenIds[i], amounts[i]);
}
bytes32 structHash = keccak256(
abi.encode(
MULTI_CLAIM_TYPEHASH,
msg.sender,
keccak256(abi.encodePacked(tokenIds)),
keccak256(abi.encodePacked(amounts)),
nonce,
keccak256(abi.encodePacked(lastClaimTimestamps))
)
);
bytes32 hash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash));
address signer = hash.recover(signature);
require(signers[signer], InvalidSignature());
unchecked {
nonces[msg.sender] += tokenIds.length;
}
require(claimToken.transfer(msg.sender, totalAmount), "Token transfer failed");
}
/// @notice Sets the daily rate at which tokens can be claimed
/// @param _dailyClaimRate The new daily claim rate
function setDailyClaimRate(uint256 _dailyClaimRate) external onlyOwner {
dailyClaimRate = _dailyClaimRate;
emit UpdatedDailyClaimRate(_dailyClaimRate);
}
/// @notice Adds or removes an authorized signer
/// @param signer Address of the signer to update
/// @param value True to authorize, false to revoke
function setSigner(address signer, bool value) external onlyOwner {
require(signer != address(0), InvalidSigner());
signers[signer] = value;
emit UpdateSigner(signer, value);
}
/// @notice Allows the owner to withdraw any ERC20 tokens from the contract
/// @param token The ERC20 token to withdraw
/// @param to The address to send the tokens to
/// @param amount The amount of tokens to withdraw
function withdraw(IERC20 token, address to, uint256 amount) external onlyOwner {
require(token.transfer(to, amount), "Token transfer failed");
}
function withdrawEth(address to, uint256 amount) external onlyOwner {
payable(to).call{value: amount}("");
}
/// @notice Sets the timestamp when claiming becomes available
/// @param _claimStartTimestamp The new claim start timestamp
function setClaimStartTimestamp(uint256 _claimStartTimestamp) external onlyOwner {
_setClaimStartTimestamp(_claimStartTimestamp);
}
/// @dev Internal function to set the claim start timestamp
function _setClaimStartTimestamp(uint256 _claimStartTimestamp) internal {
claimStartTimestamp = _claimStartTimestamp;
emit UpdatedClaimStartTimestamp(_claimStartTimestamp);
}
/// @notice Gets the last claim timestamps for multiple token IDs
/// @param owner The address to check timestamps for
/// @param tokenIds Array of token IDs to check
/// @return Array of last claim timestamps corresponding to the input token IDs
function getMultiLastClaimTimestamp(address owner, uint256[] memory tokenIds)
public
view
returns (uint256[] memory)
{
uint256[] memory values = new uint256[](tokenIds.length);
for (uint256 i = 0; i < tokenIds.length; i++) {
values[i] = lastClaimTimestamp[owner][tokenIds[i]];
}
return values;
}
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
ECDSA.sol 174 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
Read Contract
CLAIM_TYPEHASH 0x6b0509b1 → bytes32
DOMAIN_SEPARATOR 0x3644e515 → bytes32
MULTI_CLAIM_TYPEHASH 0xc98df392 → bytes32
claimStartTimestamp 0xdfc5d02f → uint256
claimToken 0x4451d89f → address
dailyClaimRate 0x0edca014 → uint256
getMultiLastClaimTimestamp 0xdc19fba3 → uint256[]
lastClaimTimestamp 0xd6687efa → uint256
nonces 0x7ecebe00 → uint256
owner 0x8da5cb5b → address
signers 0x736c0d5b → bool
Write Contract 9 functions
These functions modify contract state and require a wallet transaction to execute.
claim 0x5eddd157
uint256 tokenId
uint256 amount
bytes signature
multiClaim 0xf0e8c98a
uint256[] tokenIds
uint256[] amounts
bytes signature
renounceOwnership 0x715018a6
No parameters
setClaimStartTimestamp 0x37c7d97c
uint256 _claimStartTimestamp
setDailyClaimRate 0x3f250be9
uint256 _dailyClaimRate
setSigner 0x31cb6105
address signer
bool value
transferOwnership 0xf2fde38b
address newOwner
withdraw 0xd9caed12
address token
address to
uint256 amount
withdrawEth 0x1b9a91a4
address to
uint256 amount
Recent Transactions
No transactions found for this address