Source Code
Latest 25 from a total of 6,130 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Get Grinding | 36449305 | 13 hrs ago | IN | 0 ETH | 0.00001944 | ||||
| Get Grinding | 36129313 | 2 days ago | IN | 0 ETH | 0.00002248 | ||||
| Get Grinding | 36123097 | 2 days ago | IN | 0 ETH | 0.0000107 | ||||
| Get Grinding | 36122209 | 2 days ago | IN | 0 ETH | 0.00001032 | ||||
| Get Grinding | 36117916 | 2 days ago | IN | 0 ETH | 0.00001358 | ||||
| Get Grinding | 36025301 | 3 days ago | IN | 0 ETH | 0.00001182 | ||||
| Get Grinding | 35976186 | 3 days ago | IN | 0 ETH | 0.0000127 | ||||
| Get Grinding | 35965381 | 3 days ago | IN | 0 ETH | 0.00001031 | ||||
| Get Grinding | 35964746 | 3 days ago | IN | 0 ETH | 0.00001023 | ||||
| Get Grinding | 35848901 | 4 days ago | IN | 0 ETH | 0.00001752 | ||||
| Get Grinding | 35836883 | 4 days ago | IN | 0 ETH | 0.00001815 | ||||
| Get Grinding | 35821839 | 4 days ago | IN | 0 ETH | 0.00001269 | ||||
| Get Grinding | 35817092 | 4 days ago | IN | 0 ETH | 0.00001526 | ||||
| Get Grinding | 35813235 | 4 days ago | IN | 0 ETH | 0.00002462 | ||||
| Get Grinding | 35646606 | 5 days ago | IN | 0 ETH | 0.00001224 | ||||
| Get Grinding | 35566655 | 6 days ago | IN | 0 ETH | 0.00000934 | ||||
| Get Grinding | 35516779 | 6 days ago | IN | 0 ETH | 0.000011 | ||||
| Get Grinding | 35475197 | 6 days ago | IN | 0 ETH | 0.00001811 | ||||
| Get Grinding | 35439518 | 7 days ago | IN | 0 ETH | 0.00001348 | ||||
| Get Grinding | 35344014 | 7 days ago | IN | 0 ETH | 0.00001728 | ||||
| Get Grinding | 35260410 | 8 days ago | IN | 0 ETH | 0.00001811 | ||||
| Get Grinding | 35249722 | 8 days ago | IN | 0 ETH | 0.000011 | ||||
| Get Grinding | 35230090 | 8 days ago | IN | 0 ETH | 0.00001312 | ||||
| Get Grinding | 35208600 | 8 days ago | IN | 0 ETH | 0.00002301 | ||||
| Get Grinding | 35208030 | 8 days ago | IN | 0 ETH | 0.00000825 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 13129263 | 208 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
GrindClaim
Compiler Version
v0.8.28+commit.7893614a
ZkSolc Version
v1.5.12
Optimization Enabled:
Yes with Mode 3
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IERC20} from "forge-std/interfaces/IERC20.sol";
import {IERC721} from "forge-std/interfaces/IERC721.sol";
import {Ownable} from "solady-0.1.19/src/auth/Ownable.sol";
import {EIP712} from "solady-0.1.19/src/utils/EIP712.sol";
import {SignatureCheckerLib} from "solady-0.1.19/src/utils/SignatureCheckerLib.sol";
import {SafeTransferLib} from "solady-0.1.19/src/utils/ext/zksync/SafeTransferLib.sol";
import {Claim, ClaimDetail, ClaimHistory} from "./Structs.sol";
contract GrindClaim is Ownable, EIP712 {
using SafeTransferLib for address;
using SafeTransferLib for address payable;
bytes32 public constant CLAIM_TYPEHASH = keccak256(
"Claim(address to,uint256 deadline,ClaimDetail[] details)ClaimDetail(bytes32 data,uint128 amount,uint128 bonusAmount)"
);
bytes32 public constant CLAIM_DETAIL_TYPEHASH =
keccak256("ClaimDetail(bytes32 data,uint128 amount,uint128 bonusAmount)");
address public immutable GRIND;
address public signer;
bool public claimEnabled;
mapping(bytes32 => ClaimHistory) private _amountsClaimed;
error ClaimNotActive();
error InvalidSignature();
error SignatureExpired();
event Claimed(bytes32 indexed key, address indexed to, uint256 claimedAmount, uint256 totalClaimed);
event BonusClaimed(bytes32 indexed key, address indexed bonusWallet, uint256 bonusAmount);
constructor(address owner, address grind, address _signer) {
_initializeOwner(owner);
GRIND = grind;
signer = _signer;
}
function setSigner(address _signer) external onlyOwner {
signer = _signer;
}
function setClaimEnabled(bool _claimEnabled) external onlyOwner {
claimEnabled = _claimEnabled;
}
function withdrawERC20(address token, uint256 amount) external onlyOwner {
token.safeTransfer(msg.sender, amount);
}
function rescueETH(uint256 amount) external onlyOwner {
payable(msg.sender).safeTransferETH(amount);
}
function rescueERC721(address token, uint256 tokenId) external onlyOwner {
IERC721(token).transferFrom(address(this), msg.sender, tokenId);
}
function getGrinding(Claim calldata claim, bytes calldata signature) external {
require(claimEnabled, ClaimNotActive());
require(block.timestamp <= claim.deadline, SignatureExpired());
bytes32 digest = _hashTypedData(_hashClaim(claim));
require(SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, signature), InvalidSignature());
uint256 totalAmount;
uint256 claimDetailCount = claim.details.length;
if (claimDetailCount > 0) {
for (uint256 i; i < claimDetailCount; ++i) {
ClaimDetail calldata claimDetail = claim.details[i];
ClaimHistory memory alreadyClaimed = _amountsClaimed[claimDetail.data];
if (claimDetail.amount > alreadyClaimed.amount) {
_amountsClaimed[claimDetail.data].amount = claimDetail.amount;
uint256 amountToClaim = claimDetail.amount - alreadyClaimed.amount;
totalAmount += amountToClaim;
emit Claimed(claimDetail.data, claim.to, amountToClaim, claimDetail.amount);
}
// bonus can only be claimed once per claim
if (claimDetail.bonusAmount > 0 && alreadyClaimed.bonusAmount == 0) {
_amountsClaimed[claimDetail.data].bonusAmount = claimDetail.bonusAmount;
totalAmount += claimDetail.bonusAmount;
emit BonusClaimed(claimDetail.data, claim.to, claimDetail.bonusAmount);
}
}
}
if (totalAmount > 0) {
GRIND.safeTransfer(claim.to, totalAmount);
}
}
function nftClaimStatus(address nftContract, uint96 tokenId) external view returns (uint256 amount) {
return _amountsClaimed[_packNft(nftContract, tokenId)].amount;
}
function getClaimStatus(bytes32 data) external view returns (uint256 amountClaimed) {
return _amountsClaimed[data].amount;
}
function getClaimBonus(bytes32 data) external view returns (uint256 amount) {
return _amountsClaimed[data].bonusAmount;
}
function _domainNameAndVersion() internal pure override returns (string memory, string memory) {
return ("GrindClaim", "1");
}
function _packNft(address nftContract, uint96 tokenId) internal pure returns (bytes32) {
return bytes32((uint256(uint160(nftContract)) << 96) | tokenId);
}
function _hashClaimDetail(ClaimDetail calldata claimDetail) internal pure returns (bytes32) {
return
keccak256(abi.encode(CLAIM_DETAIL_TYPEHASH, claimDetail.data, claimDetail.amount, claimDetail.bonusAmount));
}
function _hashClaim(Claim calldata claim) internal pure returns (bytes32) {
uint256 claimDetailCount = claim.details.length;
bytes32[] memory claimDetailHashes = new bytes32[](claimDetailCount);
for (uint256 i; i < claimDetailCount; ++i) {
claimDetailHashes[i] = _hashClaimDetail(claim.details[i]);
}
return keccak256(
abi.encode(CLAIM_TYPEHASH, claim.to, claim.deadline, keccak256(abi.encodePacked(claimDetailHashes)))
);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
/// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
event Transfer(address indexed from, address indexed to, uint256 value);
/// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
/// is the new allowance.
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice Returns the amount of tokens in existence.
function totalSupply() external view returns (uint256);
/// @notice Returns the amount of tokens owned by `account`.
function balanceOf(address account) external view returns (uint256);
/// @notice Moves `amount` tokens from the caller's account to `to`.
function transfer(address to, uint256 amount) external returns (bool);
/// @notice Returns the remaining number of tokens that `spender` is allowed
/// to spend on behalf of `owner`
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
/// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
/// `amount` is then deducted from the caller's allowance.
function transferFrom(address from, address to, uint256 amount) external returns (bool);
/// @notice Returns the name of the token.
function name() external view returns (string memory);
/// @notice Returns the symbol of the token.
function symbol() external view returns (string memory);
/// @notice Returns the decimals places of the token.
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
import {IERC165} from "./IERC165.sol";
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface IERC721 is IERC165 {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner) external view returns (uint256);
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId) external view returns (address);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Change or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId) external payable;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets
/// @dev Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT.
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface IERC721TokenReceiver {
/// @notice Handle the receipt of an NFT
/// @dev The ERC721 smart contract calls this function on the recipient
/// after a `transfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data)
external
returns (bytes4);
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface IERC721Metadata is IERC721 {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string memory _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external view returns (string memory _symbol);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(uint256 _tokenId) external view returns (string memory);
}
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x780e9d63.
interface IERC721Enumerable is IERC721 {
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply() external view returns (uint256);
/// @notice Enumerate valid NFTs
/// @dev Throws if `_index` >= `totalSupply()`.
/// @param _index A counter less than `totalSupply()`
/// @return The token identifier for the `_index`th NFT,
/// (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);
/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
/// `_owner` is the zero address, representing invalid NFTs.
/// @param _owner An address where we are interested in NFTs owned by them
/// @param _index A counter less than `balanceOf(_owner)`
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
/// (sort order not specified)
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
/// This is only used in `_hashTypedDataSansChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;
/// @dev `keccak256("EIP712Domain(string name,string version)")`.
/// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
/// This is only used in `_hashTypedDataSansVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID.
/// Included for the niche use case of cross-chain workflows.
function _hashTypedDataSansChainId(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, address())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of cross-chain and multi-verifier workflows.
function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of multi-verifier workflows.
function _hashTypedDataSansVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, chainid())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
/// to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNATURE CHECKING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
isValid := and(eq(mload(d), f), and(isValid, copied))
break
}
}
}
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
}
default { break }
mstore(0x00, hash)
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
mstore(0x00, hash)
mstore(0x20, and(v, 0xff)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, s) // `s`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1271 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: These ERC1271 operations do NOT have an ECDSA fallback.
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
isValid := and(eq(mload(d), f), and(isValid, copied))
}
}
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNowCalldata(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC6492 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: These ERC6492 operations now include an ECDSA fallback at the very end.
// The calldata variants are excluded for brevity.
/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt to
/// deploy / prepare the `signer` smart account before doing a regular ERC1271 check.
/// Note: This function is NOT reentrancy safe.
/// The verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/011d6becff6e0a73e42fe100f8d7ef04
/// With a dedicated verifier, this function is safe to use in contracts
/// that have been granted special permissions.
function isValidERC6492SignatureNowAllowSideEffects(
address signer,
bytes32 hash,
bytes memory signature
) internal returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
_isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
_isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
}
let noCode := iszero(extcodesize(signer))
let n := mload(signature)
for {} 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
pop(
call(
gas(), // Remaining gas.
0x0000bc370E4DC924F427d84e2f4B9Ec81626ba7E, // Non-reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
0x00 // Length of returndata to write.
)
)
isValid := returndatasize()
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
for {} gt(noCode, isValid) {} {
switch n
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt
/// to use a reverting verifier to deploy / prepare the `signer` smart account
/// and do a `isValidSignature` check via the reverting verifier.
/// Note: This function is reentrancy safe.
/// The reverting verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
_isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
_isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
}
let noCode := iszero(extcodesize(signer))
let n := mload(signature)
for {} 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
let willBeZeroIfRevertingVerifierExists :=
call(
gas(), // Remaining gas.
0x00007bd799e4A591FeA53f8A8a3E9f931626Ba7e, // Reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
0x00 // Length of returndata to write.
)
isValid := gt(returndatasize(), willBeZeroIfRevertingVerifierExists)
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
for {} gt(noCode, isValid) {} {
switch n
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes.
function emptySignature() internal pure returns (bytes calldata signature) {
/// @solidity memory-safe-assembly
assembly {
signature.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {SingleUseETHVault} from "./SingleUseETHVault.sol";
/// @notice Library for force safe transferring ETH and ERC20s in ZKsync.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SafeTransferLib.sol)
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A single use ETH vault has been created for `to`, with `amount`.
event SingleUseETHVaultCreated(address indexed to, uint256 amount, address vault);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/// @dev The ERC20 `totalSupply` query has failed.
error TotalSupplyQueryFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 1000000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, 0x00, 0x00, 0x00, 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (address vault)
{
if (amount == uint256(0)) return address(0); // Early return if `amount` is zero.
uint256 selfBalanceBefore = address(this).balance;
/// @solidity memory-safe-assembly
assembly {
if lt(selfBalanceBefore, amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
pop(call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00))
}
if (address(this).balance == selfBalanceBefore) {
vault = address(new SingleUseETHVault());
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, shr(96, shl(96, to)))
if iszero(call(gas(), vault, amount, 0x00, 0x20, 0x00, 0x00)) { revert(0x00, 0x00) }
}
emit SingleUseETHVaultCreated(to, amount, vault);
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (address vault)
{
vault = forceSafeTransferETH(to, address(this).balance, gasStipend);
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferETH(address to, uint256 amount) internal returns (address vault) {
vault = forceSafeTransferETH(to, amount, GAS_STIPEND_NO_GRIEF);
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferAllETH(address to) internal returns (address vault) {
vault = forceSafeTransferETH(to, address(this).balance, GAS_STIPEND_NO_GRIEF);
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), 0x00, 0x00, 0x00, 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
// Check the `extcodesize` again just in case the token selfdestructs lol.
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Returns the total supply of the `token`.
/// Reverts if the token does not exist or does not implement `totalSupply()`.
function totalSupply(address token) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
) {
mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
struct Claim {
address to;
uint256 deadline;
ClaimDetail[] details;
}
struct ClaimDetail {
bytes32 data;
uint128 amount;
uint128 bonusAmount;
}
struct ClaimHistory {
uint128 amount;
uint128 bonusAmount;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice A single-use vault that allows a designated caller to withdraw all ETH in it.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SingleUseETHVault.sol)
contract SingleUseETHVault {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to withdraw all.
error WithdrawAllFailed();
/// @dev Not authorized.
error Unauthorized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WITHDRAW ALL */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
fallback() external payable virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, 0) // Optimization trick to remove free memory pointer initialization.
let owner := sload(0)
// Initialization.
if iszero(owner) {
sstore(0, calldataload(0x00)) // Store the owner.
return(0x00, 0x00) // Early return.
}
// Authorization check.
if iszero(eq(caller(), owner)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
let to := calldataload(0x00)
// If the calldata is less than 32 bytes, zero-left-pad it to 32 bytes.
// Then use the rightmost 20 bytes of the word as the `to` address.
// This allows for the calldata to be `abi.encode(to)` or `abi.encodePacked(to)`.
to := shr(mul(lt(calldatasize(), 0x20), shl(3, sub(0x20, calldatasize()))), to)
// If `to` is `address(0)`, set it to `msg.sender`.
to := xor(mul(xor(to, caller()), iszero(to)), to)
// Transfers the whole balance to `to`.
if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
mstore(0x00, 0x651aee10) // `WithdrawAllFailed()`.
revert(0x1c, 0x04)
}
}
}
}{
"viaIR": false,
"codegen": "yul",
"remappings": [
"forge-std-1.9.7/=dependencies/forge-std-1.9.7/",
"solady-0.1.19/=dependencies/solady-0.1.19/",
"forge-std/=lib/forge-std/src/",
"forge-zksync-std/=lib/forge-zksync-std/src/"
],
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"abi"
]
}
},
"optimizer": {
"enabled": true,
"mode": "3",
"size_fallback": false,
"disable_system_request_memoization": true
},
"metadata": {},
"libraries": {},
"enableEraVMExtensions": false,
"forceEVMLA": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"grind","type":"address"},{"internalType":"address","name":"_signer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ClaimNotActive","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":true,"internalType":"address","name":"bonusWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"bonusAmount","type":"uint256"}],"name":"BonusClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"CLAIM_DETAIL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLAIM_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GRIND","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claimEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"data","type":"bytes32"}],"name":"getClaimBonus","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"data","type":"bytes32"}],"name":"getClaimStatus","outputs":[{"internalType":"uint256","name":"amountClaimed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"components":[{"internalType":"bytes32","name":"data","type":"bytes32"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"bonusAmount","type":"uint128"}],"internalType":"struct ClaimDetail[]","name":"details","type":"tuple[]"}],"internalType":"struct Claim","name":"claim","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"getGrinding","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint96","name":"tokenId","type":"uint96"}],"name":"nftClaimStatus","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"rescueERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_claimEnabled","type":"bool"}],"name":"setClaimEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
9c4d535b000000000000000000000000000000000000000000000000000000000000000001000273620de76580922d11f3b829e090ce11a7f02ce9dd6e3ac11d7caa82a900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000340f19e6a630b58f892abdd742f3ce04ef4ad77a0000000000000000000000001c26da604221466976beeb509698152ba8a3a13f0000000000000000000000003d1efc86eefc8310c0ab0c9248711fd2c22e0d06
Deployed Bytecode
0x0002000000000002000f000000000002000100000001035500000060031002700000020d0030019d0000020d0330019700000001002001900000002f0000c13d0000008002000039000000400020043f000000040030008c000003a90000413d000000000201043b000000e0022002700000021d0020009c0000007d0000213d0000022c0020009c000000b40000a13d0000022d0020009c000000ee0000213d000002310020009c000002160000613d000002320020009c000001ac0000613d000002330020009c000003a90000c13d0000021901000041000000000501041a0000000001000411000000000051004b0000029e0000c13d00000000010004140000020d0010009c0000020d01008041000000c00110021000000216011001c70000800d0200003900000003030000390000021a040000410000000006000019082f08250000040f0000000100200190000003a90000613d0000021901000041000000000001041b0000000001000019000008300001042e0000014004000039000000400040043f0000000002000416000000000002004b000003a90000c13d0000001f023000390000020e022001970000014002200039000000400020043f0000001f0530018f0000020f063001980000014002600039000000410000613d000000000701034f000000007807043c0000000004840436000000000024004b0000003d0000c13d000000000005004b0000004e0000613d000000000161034f0000000304500210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000600030008c000003a90000413d000001400200043d000002100020009c000003a90000213d000001600100043d000d00000001001d000002100010009c000003a90000213d000001800100043d000c00000001001d000002100010009c000003a90000213d000b00000002001d0000000001000410000000800010043f0000021101000041000000000010044300000000010004140000020d0010009c0000020d01008041000000c00110021000000212011001c70000800b02000039082f082a0000040f0000000100200190000007650000613d000000000401043b000000a00040043f000000400100043d000002130010009c000000770000213d0000004002100039000000400020043f0000000a02000039000000000221043600000214030000410000000000320435000000400500043d000002130050009c000002a20000a13d0000025201000041000000000010043f0000004101000039000000040010043f000002530100004100000831000104300000021e0020009c000000d40000a13d0000021f0020009c000001060000213d000002230020009c0000021d0000613d000002240020009c000001bd0000613d000002250020009c000003a90000c13d000000440030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000402100370000000000202043b000c00000002001d000002410020009c000003a90000213d0000000c0230006a000002420020009c000003a90000213d000000640020008c000003a90000413d0000002402100370000000000202043b000600000002001d000002410020009c000003a90000213d00000006020000290000002302200039000000000032004b000003a90000813d00000006020000290000000402200039000000000221034f000000000202043b000500000002001d000002410020009c000003a90000213d00000006020000290000002404200039000400000004001d0000000502400029000000000032004b000003a90000213d000000000200041a000300000002001d0000024300200198000003640000c13d0000025f01000041000000000010043f0000025d010000410000083100010430000002340020009c0000011f0000a13d000002350020009c000001830000613d000002360020009c000001690000613d000002370020009c000003a90000c13d0000023a010000410000000c0010043f0000000001000411000000000010043f00000000010004140000020d0010009c0000020d01008041000000c0011002100000023e011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b000000000001041b00000000010004140000020d0010009c0000020d01008041000000c00110021000000216011001c70000800d0200003900000002030000390000026b04000041000001490000013d000002260020009c0000014e0000a13d000002270020009c0000018d0000613d000002280020009c000001700000613d000002290020009c000003a90000c13d000000240030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000101043b000000000010043f0000000101000039000000200010043f00000040020000390000000001000019082f07f20000040f000000000101041a0000008001100270000000800010043f0000023b01000041000008300001042e0000022e0020009c0000025f0000613d0000022f0020009c000001ce0000613d000002300020009c000003a90000c13d000000240030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000101043b000000000010043f0000000101000039000000200010043f00000040020000390000000001000019082f07f20000040f000000000101041a0000024601100197000000800010043f0000023b01000041000008300001042e000002200020009c0000026f0000613d000002210020009c000002050000613d000002220020009c000003a90000c13d000000240030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000101043b000002100010009c000003a90000213d0000023a020000410000000c0020043f000000000010043f0000000c010000390000002002000039082f07f20000040f000000000101041a000000800010043f0000023b01000041000008300001042e000002380020009c000001a10000613d000002390020009c000003a90000c13d0000023a010000410000000c0010043f0000000001000411000000000010043f0000023f01000041000000000010044300000000010004140000020d0010009c0000020d01008041000000c00110021000000212011001c70000800b02000039082f082a0000040f0000000100200190000007650000613d000000000101043b000d00000001001d00000000010004140000020d0010009c0000020d01008041000000c0011002100000023e011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b0000000d020000290000026c0220009a000000000021041b00000000010004140000020d0010009c0000020d01008041000000c00110021000000216011001c70000800d0200003900000002030000390000026d040000410000000005000411082f08250000040f0000000100200190000003290000c13d000003a90000013d0000022a0020009c000001a60000613d0000022b0020009c000003a90000c13d000000240030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000201043b000000000002004b0000000001000039000000010100c039000d00000002001d000000000012004b000003a90000c13d082f07820000040f0000000d0000006b0000000001000019000002630100c041000000000200041a0000026402200197000000000112019f000000000010041b0000000001000019000008300001042e0000000001000416000000000001004b000003a90000c13d0000024701000041000000800010043f0000023b01000041000008300001042e000000440030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000101043b000d00000001001d000002100010009c000003a90000213d082f07820000040f00000024010000390000000101100367000000000301043b00000000020004110000000d01000029082f07a20000040f0000000001000019000008300001042e0000000001000416000000000001004b000003a90000c13d000000000100041a00000243001001980000000001000039000000010100c039000000800010043f0000023b01000041000008300001042e000000240030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000021902000041000000000402041a0000000002000411000000000042004b0000029e0000c13d0000000401100370000000000301043b00000000010004140000020d0010009c0000020d01008041000000c001100210000000000003004b000003230000c13d0000000002040019000003260000013d0000000001000416000000000001004b000003a90000c13d000000000100041a000001ca0000013d0000000001000416000000000001004b000003a90000c13d0000021901000041000000000101041a000001ca0000013d000000240030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000101043b000d00000001001d000002100010009c000003a90000213d082f07820000040f000000000100041a0000021b011001970000000d011001af000000000010041b0000000001000019000008300001042e0000000001000416000000000001004b000003a90000c13d0000000001000412000f00000001001d000e00a00000003d0000800501000039000000440300003900000000040004150000000f0440008a00000005044002100000024a02000041082f08070000040f0000021001100197000000800010043f0000023b01000041000008300001042e0000000001000416000000000001004b000003a90000c13d0000000a01000039000000800010043f0000021402000041000000a00020043f0000010002000039000000400020043f0000000102000039000000c00020043f0000021503000041000000e00030043f0000026503000041000001000030043f000000e003000039000001200030043f000001e00010043f000002000100043d000002660110019700000214011001c7000002000010043f0000020a0000043f0000012001000039000001400010043f000002200020043f000002400100043d000002670110019700000215011001c7000002400010043f000002410000043f0000021101000041000000000010044300000000010004140000020d0010009c0000020d01008041000000c00110021000000212011001c70000800b02000039082f082a0000040f0000000100200190000007650000613d000000000101043b000001600010043f0000000001000410000001800010043f000001a00000043f0000016001000039000001c00010043f000000600100043d000002600010043f000000000001004b0000032f0000c13d0000018001000039000003390000013d000000240030008c000003a90000413d0000000401100370000000000101043b000002100010009c000003a90000213d0000021902000041000000000202041a0000000003000411000000000023004b0000029e0000c13d000000000001004b000003410000c13d0000023c01000041000000000010043f0000023d0100004100000831000104300000000001000416000000000001004b000003a90000c13d0000024901000041000000800010043f0000023b01000041000008300001042e000000440030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000401100370000000000101043b000d00000001001d000002100010009c000003a90000213d0000021901000041000000000201041a0000000001000411000000000021004b0000029e0000c13d000c00000002001d0000024e0100004100000000001004430000000d01000029000000040010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024f011001c70000800202000039082f082a0000040f0000000100200190000007650000613d000000000101043b000000000001004b000003a90000613d000000400300043d00000024013000390000000c0200002900000000002104350000026001000041000000000013043500000004013000390000000002000410000000000021043500000024010000390000000101100367000000000101043b000000440230003900000000001204350000020d0030009c000c00000003001d0000020d010000410000000001034019000000400110021000000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000261011001c70000000d02000029082f08250000040f0000000100200190000003440000613d0000000c010000290000000002000019082f07700000040f0000000001000019000008300001042e000000440030008c000003a90000413d0000000002000416000000000002004b000003a90000c13d0000000402100370000000000202043b000002100020009c000003a90000213d0000002401100370000000000101043b000002690010009c000003a90000213d0000006002200210000000000121019f000000fb0000013d000000240030008c000003a90000413d0000000401100370000000000101043b000d00000001001d000002100010009c000003a90000213d0000021901000041000000000101041a0000000002000411000000000012004b0000029e0000c13d0000023a010000410000000c0010043f0000000d01000029000000000010043f00000000010004140000020d0010009c0000020d01008041000000c0011002100000023e011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b000b00000001001d000000000101041a000c00000001001d0000023f01000041000000000010044300000000010004140000020d0010009c0000020d01008041000000c00110021000000212011001c70000800b02000039082f082a0000040f0000000100200190000007650000613d000000000101043b0000000c0010006c0000033e0000a13d0000024001000041000000000010043f0000023d0100004100000831000104300000026a01000041000000000010043f0000023d010000410000083100010430000800000004001d0000004003500039000000400030043f0000000103000039000a00000005001d00000000043504360000021503000041000900000004001d00000000003404350000020d0020009c0000020d02008041000000400220021000000000010104330000020d0010009c0000020d010080410000006001100210000000000121019f00000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000216011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d00000009020000290000020d0020009c0000020d0200804100000040022002100000000a0300002900000000030304330000020d0030009c0000020d030080410000006003300210000000000223019f000000000101043b000a00000001001d00000000010004140000020d0010009c0000020d01008041000000c001100210000000000121019f00000216011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b0000000a05000029000000c00050043f000000e00010043f000000400200043d0000008003200039000000000400041000000000004304350000006003200039000000080400002900000000004304350000004003200039000000000013043500000020012000390000000000510435000002170100004100000000001204350000020d0020009c0000020d02008041000000400120021000000000020004140000020d0020009c0000020d02008041000000c002200210000000000121019f00000218011001c70000801002000039082f082a0000040f0000000b060000290000000100200190000003a90000613d000000000101043b000001000010043f0000021901000041000000000061041b00000000010004140000020d0010009c0000020d01008041000000c00110021000000216011001c70000800d0200003900000003030000390000021a040000410000000005000019082f08250000040f0000000100200190000003a90000613d0000000d04000029000001200040043f000000000100041a0000021b011001970000000c011001af000000000010041b000000800100043d000001400000044300000160001004430000002001000039000000a00200043d0000018000100443000001a0002004430000004002000039000000c00300043d000001c000200443000001e0003004430000006002000039000000e00300043d000002000020044300000220003004430000008002000039000001000300043d00000240002004430000026000300443000000a0020000390000028000200443000002a0004004430000010000100443000000060100003900000120001004430000021c01000041000008300001042e00000216011001c700008009020000390000000005000019082f08250000040f00000001002001900000032b0000613d0000000001000019000008300001042e0000026201000041000000000010043f0000023d0100004100000831000104300000028003000039000000000200001900000080050000390000000004030019000000005305043400000000033404360000000102200039000000000012004b000003320000413d000000e00140008a0000020d0010009c0000020d01008041000000600110021000000268011001c7000008300001042e0000000b01000029000000000001041b0000000d01000029082f078c0000040f0000000001000019000008300001042e00000060061002700000001f0460018f0000020f05600198000000400200043d0000000003520019000003500000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b0000034c0000c13d0000020d06600197000000000004004b0000035e0000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000013043500000060016002100000020d0020009c0000020d020080410000004002200210000000000112019f00000831000104300000000c02000029000d00240020003d0000000d01100360000000000101043b000200000001001d0000023f01000041000000000010044300000000010004140000020d0010009c0000020d01008041000000c00110021000000212011001c70000800b02000039082f082a0000040f0000000100200190000007650000613d000000000101043b000000020010006c0000037b0000a13d0000025e01000041000000000010043f0000025d0100004100000831000104300000000d01000029000100200010003d00000001010003670000000102100360000000000302043b00000000020000310000000c0420006a000000230440008a00000244054001970000024406300197000000000756013f000000000056004b00000000050000190000024405004041000000000043004b00000000040000190000024404008041000002440070009c000000000504c019000000000005004b000003a90000c13d0000000c04000029000a00040040003d0000000a03300029000000000431034f000000000404043b000b00000004001d000002410040009c000003a90000213d000000600400008a0000000b044000b900000000042400190000002003300039000000000043004b0000000005000019000002440500204100000244044001970000024403300197000000000643013f000000000043004b00000000030000190000024403004041000002440060009c000000000305c019000000000003004b000003ab0000613d000000000100001900000831000104300000000b0300002900000005033002100000003f043000390000024504400197000000400500043d0000000004450019000900000005001d000000000054004b00000000050000390000000105004039000002410040009c000000770000213d0000000100500190000000770000c13d000000400040043f0000000b0400002900000009050000290000000004450436000800000004001d0000001f0430018f000000000003004b000003c80000613d000000000221034f00000008050000290000000003350019000000002602043c0000000005650436000000000035004b000003c40000c13d000000000004004b0000000c02000029000700440020003d0000000b0000006b000004890000c13d0000000102000029000000400220008a000000000121034f000000000101043b000d00000001001d000002100010009c000003a90000213d000000400100043d000000200210003900000009030000290000000003030433000000000003004b00000000040200190000000807000029000003e30000613d00000000050000190000000004020019000000007607043400000000046404360000000105500039000000000035004b000003de0000413d0000000003140049000000200430008a00000000004104350000001f033000390000026e043001970000000003140019000000000043004b00000000040000390000000104004039000002410030009c000000770000213d0000000100400190000000770000c13d000000400030043f0000020d0020009c0000020d02008041000000400220021000000000010104330000020d0010009c0000020d010080410000006001100210000000000121019f00000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000216011001c70000801002000039082f082a0000040f00000001002001900000008004000039000003a90000613d000000000201043b000000400100043d000000800310003900000000002304350000006002100039000000020300002900000000003204350000000d020000290000021002200197000000400310003900000000002304350000002002100039000002490300004100000000003204350000000000410435000002480010009c000000770000213d000000a003100039000000400030043f0000020d0020009c0000020d02008041000000400220021000000000010104330000020d0010009c0000020d010080410000006001100210000000000121019f00000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000216011001c70000801002000039082f082a0000040f00000001002001900000008002000039000003a90000613d000000000101043b000d00000001001d0000024a01000041000000000010044300000000010004120000000400100443000000240020044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024b011001c70000800502000039082f082a0000040f0000000100200190000007650000613d000000000101043b000b00000001001d0000024a01000041000000000010044300000000010004120000000400100443000000240000044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024b011001c70000800502000039082f082a0000040f0000000100200190000007650000613d000000000101043b000900000001001d0000024a010000410000000000100443000000000100041200000004001004430000002001000039000000240010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024b011001c70000800502000039082f082a0000040f0000000100200190000007650000613d000000000101043b000800000001001d0000021101000041000000000010044300000000010004140000020d0010009c0000020d01008041000000c00110021000000212011001c70000800b02000039082f082a0000040f0000000100200190000007650000613d0000000002000410000000000101043b000200000001001d000000090020006c000004f40000c13d0000000802000029000000020020006b000004f40000c13d0000024c01000041000000000010043f0000000b010000290000001a0010043f0000000d010000290000003a0010043f00000000010004140000020d0010009c0000020d01008041000000c0011002100000024d011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b000b00000001001d0000003a0000043f0000000301000029000d02100010019c000005340000c13d0000025c01000041000000000010043f0000025d0100004100000831000104300000000008000019000000800700003900000000010000310000000c0210006a000000230320008a00000007020000290000000102200367000000000202043b00000244043001970000024405200197000000000645013f000000000045004b00000000040000190000024404004041000000000032004b00000000030000190000024403008041000002440060009c000000000403c019000000000004004b000003a90000c13d0000000a032000290000000102300367000000000202043b000002410020009c000003a90000213d0000026f042000d10000000004140019000000200130003900000244034001970000024405100197000000000635013f000000000035004b00000000030000190000024403004041000000000041004b00000000040000190000024404002041000002440060009c000000000304c019000000000003004b000003a90000c13d000000000028004b000006fe0000813d00000060028000c9000000000421001900000020014000390000000102100367000000000202043b000002460020009c000003a90000213d00000020011000390000000101100367000000000501043b000002460050009c000003a90000213d000d00000008001d000000400100043d0000002003100039000002470600004100000000006304350000000104400367000000000404043b0000008006100039000000000056043500000060051000390000000000250435000000400210003900000000004204350000000000710435000002480010009c000000770000213d000000a002100039000000400020043f0000020d0030009c0000020d03008041000000400230021000000000010104330000020d0010009c0000020d010080410000006001100210000000000121019f00000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000216011001c70000801002000039082f082a0000040f000000010020019000000080070000390000000d08000029000003a90000613d00000009020000290000000002020433000000000028004b000006fe0000813d00000005028002100000000802200029000000000101043b000000000012043500000001088000390000000b0080006c0000048b0000413d0000000101000367000003cd0000013d000000400200043d0000021701000041000900000002001d0000000001120436000b00000001001d0000024a010000410000000000100443000000000100041200000004001004430000004001000039000000240010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024b011001c70000800502000039082f082a0000040f0000000100200190000007650000613d000000000101043b0000000b0200002900000000001204350000024a010000410000000000100443000000000100041200000004001004430000006001000039000000240010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024b011001c70000800502000039082f082a0000040f0000000100200190000007650000613d000000000101043b0000000904000029000000800240003900000000030004100000000000320435000000600240003900000002030000290000000000320435000000400240003900000000001204350000020d0040009c0000020d04008041000000400140021000000000020004140000020d0020009c0000020d02008041000000c002200210000000000121019f00000218011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b000b00000001001d000004700000013d000000400100043d000900000001001d0000024e0100004100000000001004430000000d01000029000000040010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024f011001c70000800202000039082f082a0000040f0000000100200190000007650000613d000000000101043b000000000001004b0000055a0000c13d0000000501000029000000400010008c000005b50000613d0000000501000029000000410010008c000004850000c13d000000060100002900000064011000390000000101100367000000000101043b000000f801100270000000200010043f000000040100002900000001011003670000004002000039000000001301043c0000000002320436000000800020008c000005550000c13d000005c20000013d00000009050000290000006402500039000000440150003900000024065000390000000403500039000002500400004100000000004504350000000b0400002900000000004304350000004003000039000b00000006001d0000000000360435000000050400002900000000004104350000026e034001980000001f0440018f000000000132001900000004050000290000000105500367000005730000613d000000000605034f000000006706043c0000000002720436000000000012004b0000056f0000c13d000000000004004b000005800000613d000000000235034f0000000303400210000000000401043300000000043401cf000000000434022f000000000202043b0000010003300089000000000232022f00000000023201cf000000000242019f000000000021043500000009010000290000020d0010009c0000020d010080410000004001100210000000050200002900000064022000390000020d0020009c0000020d020080410000006002200210000000000121019f00000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f0000000d02000029082f082a0000040f00000060031002700000020d03300197000000200030008c00000020030080390000001f0430018f00000020053001900000000b035000290000059f0000613d000000000601034f0000000b07000029000000006806043c0000000007870436000000000037004b0000059b0000c13d000000000004004b000005ac0000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000000b010000290000000001010433000002500010009c00000000010000390000000101006039000000000112016f0000000100100190000004850000613d000005f20000013d000000060100002900000044011000390000000101100367000000000101043b000000ff031002700000001b03300039000000200030043f00000004030000290000000102300367000000000202043b000000400020043f0000024201100197000000600010043f0000000b01000029000000000010043f00000000010004140000020d0010009c0000020d01008041000000c00110021000000251011001c70000000102000039000d00000002001d082f082a0000040f00000060031002700000020d03300197000000200030008c000000200400003900000000040340190000001f0540018f000000200640019000000001046001bf000005dc0000613d000000000701034f000000007807043c0000000d090000290000000009890436000d00000009001d000000000049004b000005d60000c13d000000010220018f000000000005004b000005ea0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000000001020433000000600000043f0000000902000029000000400020043f000000030110014f0000006001100210000000000013004b000004850000a13d00000001010003670000000702100360000000000302043b00000000020000310000000c0420006a000000230440008a00000244054001970000024406300197000000000756013f000000000056004b00000000050000190000024405004041000000000043004b00000000040000190000024404008041000002440070009c000000000504c019000000000005004b000003a90000c13d0000000a03300029000000000431034f000000000404043b000300000004001d000002410040009c000003a90000213d000000600400008a00000003044000b900000000022400190000002003300039000000000023004b0000000004000019000002440400204100000244022001970000024403300197000000000523013f000000000023004b00000000020000190000024402004041000002440050009c000000000204c019000000000002004b000003a90000c13d000000030000006b000003290000613d00000007020000290002004000200092000d00000000001d000500000000001d000006280000013d0000000d030000290000000103300039000d00000003001d000000030030006c000007040000813d0000000702100360000000000302043b00000000020000310000000c0420006a000000230440008a00000244054001970000024406300197000000000756013f000000000056004b00000000050000190000024405004041000000000043004b00000000040000190000024404008041000002440070009c000000000504c019000000000005004b000003a90000c13d0000000a04300029000000000341034f000000000303043b000002410030009c000003a90000213d0000026f053000d10000000005250019000000200240003900000244045001970000024406200197000000000746013f000000000046004b00000000040000190000024404004041000000000052004b00000000050000190000024405002041000002440070009c000000000405c019000000000004004b000003a90000c13d0000000d0030006b000006fe0000813d0000000d0300002900000060033000c9000900000032001d0000000901100360000000000101043b000600000001001d000000000010043f0000000101000039000000200010043f00000000010004140000020d0010009c0000020d01008041000000c00110021000000254011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000400200043d000b00000002001d000002130020009c000000770000213d000000000101043b0000000b040000290000004002400039000000400020043f000000000101041a00000020034000390000008002100270000400000003001d0000000000230435000002460210019700000000002404350000000901000029000800200010003d00000001010003670000000803100360000000000303043b000900000003001d000002460030009c000003a90000213d000000090020006b000006b90000a13d0000000601000029000000000010043f0000000101000039000000200010043f00000000010004140000020d0010009c0000020d01008041000000c00110021000000254011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d000000000101043b000000000201041a000002550220019700000009022001af000000000021041b00000001020003670000000801200360000000000101043b000002460010009c000003a90000213d0000000b03000029000000000303043300000246033001970000000003310049000b00000003001d000002460030009c000007660000213d0000000b04000029000000050040002a000007660000413d0000000202200360000000000602043b000002100060009c000003a90000213d000000400200043d000000200320003900000000001304350000000b0100002900000000001204350000020d0020009c0000020d02008041000000400120021000000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000254011001c70000800d02000039000000030300003900000256040000410000000605000029082f08250000040f0000000100200190000003a90000613d0000000b02000029000500050020002d00000001010003670000000802000029000900200020003d0000000902100360000000000202043b000b00000002001d000002460020009c000003a90000213d0000000b0000006b000006230000613d000000040200002900000000020204330000024600200198000006230000c13d0000000601000029000000000010043f0000000101000039000000200010043f00000000010004140000020d0010009c0000020d01008041000000c00110021000000254011001c70000801002000039082f082a0000040f0000000100200190000003a90000613d0000000b020000290000008002200210000000000101043b000000000301041a0000024603300197000000000223019f000000000021041b00000001010003670000000902100360000000000202043b000b00000002001d000002460020009c000003a90000213d0000000b03000029000000050030002a000007660000413d0000000a01100360000000000601043b000002100060009c000003a90000213d000000400100043d0000000b0200002900000000002104350000020d0010009c0000020d01008041000000400110021000000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000257011001c70000800d02000039000000030300003900000258040000410000000605000029082f08250000040f0000000100200190000003a90000613d0000000b02000029000500050020002d0000000101000367000006230000013d0000025201000041000000000010043f0000003201000039000000040010043f00000253010000410000083100010430000000050000006b000003290000613d0000000a01100360000000000101043b000d00000001001d000002100010009c000003a90000213d0000024a01000041000000000010044300000000010004120000000400100443000000a001000039000000240010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024b011001c70000800502000039082f082a0000040f0000000100200190000007650000613d000000000201043b0000000d01000029000000140010043f0000000501000029000000340010043f0000025901000041000000000010043f00000000010004140000020d0010009c0000020d01008041000000c0011002100000025a011001c7000b00000002001d082f08250000040f000d00000002001d00000060021002700000020d02200197000000200020008c000c00000002001d00000020020080390000001f0320018f0000002002200190000007370000613d000000000401034f0000000005000019000000004604043c0000000005650436000000000025004b000007330000c13d000000000003004b000007440000613d000000000121034f0000000303300210000000000402043300000000043401cf000000000434022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000141019f0000000000120435000000000100043d000000010010008c000000000100003900000001010060390000000d0110017f0000000100100190000007620000c13d0000024e0100004100000000001004430000000b01000029000000040010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024f011001c70000800202000039082f082a0000040f0000000100200190000007650000613d000000000101043b000000000001004b000000000100003900000001010060390000000c001001b0000000000100003900000001010060390000000d0110017f00000001001001900000076c0000613d000000340000043f0000000001000019000008300001042e000000000001042f0000025201000041000000000010043f0000001101000039000000040010043f000002530100004100000831000104300000025b01000041000000000010043f0000023d0100004100000831000104300000001f022000390000026e022001970000000001120019000000000021004b00000000020000390000000102004039000002410010009c0000077c0000213d00000001002001900000077c0000c13d000000400010043f000000000001042d0000025201000041000000000010043f0000004101000039000000040010043f000002530100004100000831000104300000021901000041000000000101041a0000000002000411000000000012004b000007880000c13d000000000001042d0000026a01000041000000000010043f0000023d01000041000008310001043000010000000000020000021902000041000000000502041a000000000200041400000210061001970000020d0020009c0000020d02008041000000c00120021000000216011001c70000800d0200003900000003030000390000021a04000041000100000006001d082f08250000040f0000000100200190000007a00000613d00000219010000410000000102000029000000000021041b000000000001042d000000000100001900000831000104300003000000000002000300000001001d000000140020043f000000340030043f0000025901000041000000000010043f00000000010004140000020d0010009c0000020d01008041000000c0011002100000025a011001c70000000302000029082f08250000040f00000060031002700000020d07300197000000200070008c000000200400003900000000040740190000001f0340018f0000002008400190000007bd0000613d000000000401034f0000000005000019000000004604043c0000000005650436000000000085004b000007b90000c13d000000000003004b000007ca0000613d000000000181034f0000000303300210000000000408043300000000043401cf000000000434022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000141019f0000000000180435000000000100043d000000010010008c00000000010000390000000101006039000000000112016f0000000100100190000007ea0000c13d000100000002001d000200000007001d0000024e0100004100000000001004430000000301000029000000040010044300000000010004140000020d0010009c0000020d01008041000000c0011002100000024f011001c70000800202000039082f082a0000040f0000000100200190000007ec0000613d000000010200008a000000010220014f000000000101043b000000000001004b0000000001000039000000010100603900000002001001b0000000010220c1bf0000000100200190000007ed0000c13d000000340000043f000000000001042d000000000001042f0000025b01000041000000000010043f0000023d010000410000083100010430000000000001042f0000020d0010009c0000020d0100804100000040011002100000020d0020009c0000020d020080410000006002200210000000000112019f00000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000216011001c70000801002000039082f082a0000040f0000000100200190000008050000613d000000000101043b000000000001042d0000000001000019000008310001043000000000050100190000000000200443000000050030008c000008150000413d000000040100003900000000020000190000000506200210000000000664001900000005066002700000000006060031000000000161043a0000000102200039000000000031004b0000080d0000413d0000020d0030009c0000020d03008041000000600130021000000000020004140000020d0020009c0000020d02008041000000c002200210000000000112019f00000270011001c70000000002050019082f082a0000040f0000000100200190000008240000613d000000000101043b000000000001042d000000000001042f00000828002104210000000102000039000000000001042d0000000002000019000000000001042d0000082d002104230000000102000039000000000001042d0000000002000019000000000001042d0000082f00000432000008300001042e00000831000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffff9a8a0592ac89c5ad3bc6df8224c17b485976f597df104ee20d0df415241f670b0200000200000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffbf4772696e64436c61696d00000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f02000000000000000000000000000000000000a0000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0ffffffffffffffffffffffff000000000000000000000000000000000000000000000002000000000000000000000000000001c0000001000000000000000000000000000000000000000000000000000000000000000000000000008da5cb5a00000000000000000000000000000000000000000000000000000000d798183300000000000000000000000000000000000000000000000000000000f04e283d00000000000000000000000000000000000000000000000000000000f04e283e00000000000000000000000000000000000000000000000000000000f2fde38b00000000000000000000000000000000000000000000000000000000fee81cf400000000000000000000000000000000000000000000000000000000d798183400000000000000000000000000000000000000000000000000000000e03b142e00000000000000000000000000000000000000000000000000000000e85c7d66000000000000000000000000000000000000000000000000000000009e252eff000000000000000000000000000000000000000000000000000000009e252f0000000000000000000000000000000000000000000000000000000000a1db978200000000000000000000000000000000000000000000000000000000c56cd75a000000000000000000000000000000000000000000000000000000008da5cb5b0000000000000000000000000000000000000000000000000000000092929a09000000000000000000000000000000000000000000000000000000006b0509b00000000000000000000000000000000000000000000000000000000083782df90000000000000000000000000000000000000000000000000000000083782dfa0000000000000000000000000000000000000000000000000000000084b0196e00000000000000000000000000000000000000000000000000000000896a6715000000000000000000000000000000000000000000000000000000006b0509b1000000000000000000000000000000000000000000000000000000006c19e78300000000000000000000000000000000000000000000000000000000715018a6000000000000000000000000000000000000000000000000000000002866ed20000000000000000000000000000000000000000000000000000000002866ed2100000000000000000000000000000000000000000000000000000000286c11310000000000000000000000000000000000000000000000000000000054d1f13d00000000000000000000000000000000000000000000000000000000238ac933000000000000000000000000000000000000000000000000000000002569296200000000000000000000000000000000000000000000000000000000389a75e10000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000007448fbae00000000000000000000000000000000000000040000001c000000000000000002000000000000000000000000000000000000200000000c0000000000000000796b89b91644bc98cd93958e4c9038275d622183e25ac5af08cc6b5d95539132000000000000000000000000000000000000000000000000000000006f5e8818000000000000000000000000000000000000000000000000ffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000ff000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000fffffffffffffffffffffffffffffffff55fe0ef9f358856b02b37b358f1a53c005c7cf185ed8ee7a0c12b7009b67f6e000000000000000000000000000000000000000000000000ffffffffffffff5f6fd89efe05b5144175efc9d1dd8dc36b20ec07b7084630f79b26a4425c7f17d0310ab089e4439a4c15d089f94afb7896ff553aecb10793d0ab882de59d99a32e0200000200000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000000190100000000000002000000000000000000000000000000000000420000001800000000000000001806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b8302000002000000000000000000000000000000240000000000000000000000001626ba7e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000004e487b710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000200000000000000000000000000000000000040000000000000000000000000ffffffffffffffffffffffffffffffff000000000000000000000000000000002500ada8ba581126c805692756f230258621bbe372c90d928a7284fab4a9c1ea020000000000000000000000000000000000002000000000000000000000000096f716c4697b77fc4841e9d28b7fdc9950d2c3f86a436449e0b6fe28977eb8cf00000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000000000440000001000000000000000000000000000000000000000000000000000000000000000000000000090b8ec188baa579f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000819bdcd0000000000000000000000000000000000000000000000000000000024fbaa900000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000000000000000000000b12d13eb0000000000000000000000010000000000000000000000000000000000000000ffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000082b42900fa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd5d00dbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576aa792c370f9c78cfc1c5a13325e141cc1e2c4385ef60ebfd701dcaa215df7
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000340f19e6a630b58f892abdd742f3ce04ef4ad77a0000000000000000000000001c26da604221466976beeb509698152ba8a3a13f0000000000000000000000003d1efc86eefc8310c0ab0c9248711fd2c22e0d06
-----Decoded View---------------
Arg [0] : owner (address): 0x340f19E6A630B58F892aBdd742f3cE04Ef4aD77a
Arg [1] : grind (address): 0x1C26DA604221466976bEeB509698152bA8A3A13F
Arg [2] : _signer (address): 0x3d1eFc86Eefc8310c0ab0C9248711FD2C22E0d06
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000340f19e6a630b58f892abdd742f3ce04ef4ad77a
Arg [1] : 0000000000000000000000001c26da604221466976beeb509698152ba8a3a13f
Arg [2] : 0000000000000000000000003d1efc86eefc8310c0ab0c9248711fd2c22e0d06
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.20
Net Worth in ETH
0.000067
Token Allocations
BEAVER
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ABSTRACT | 100.00% | $0.000002 | 100,000 | $0.1979 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.