Source Code
Overview
S Balance
S Value
$1.29 (@ $0.07/S)Latest 25 from a total of 146 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Claim | 61207311 | 9 hrs ago | IN | 0 S | 0.00361644 | ||||
| Claim | 60888516 | 5 days ago | IN | 0 S | 0.0037136 | ||||
| Claim | 60801930 | 6 days ago | IN | 0 S | 0.0028573 | ||||
| Claim | 60378162 | 12 days ago | IN | 0 S | 0.00662187 | ||||
| Claim | 60344817 | 12 days ago | IN | 0 S | 0.00429935 | ||||
| Claim | 60126096 | 15 days ago | IN | 0 S | 0.0037141 | ||||
| Claim | 60095529 | 16 days ago | IN | 0 S | 0.00430144 | ||||
| Claim | 60074498 | 16 days ago | IN | 0 S | 0.0037129 | ||||
| Claim | 60054231 | 16 days ago | IN | 0 S | 0.00430072 | ||||
| Claim | 60049773 | 17 days ago | IN | 0 S | 0.00414481 | ||||
| Claim | 60042842 | 17 days ago | IN | 0 S | 0.00301315 | ||||
| Claim | 60042728 | 17 days ago | IN | 0 S | 0.00331435 | ||||
| Claim | 60038226 | 17 days ago | IN | 0 S | 0.00430006 | ||||
| Claim | 60037051 | 17 days ago | IN | 0 S | 0.00494807 | ||||
| Claim | 60021713 | 17 days ago | IN | 0 S | 0.0037134 | ||||
| Claim | 60021139 | 17 days ago | IN | 0 S | 0.00430017 | ||||
| Claim | 60017016 | 17 days ago | IN | 0 S | 0.0037129 | ||||
| Claim | 60016896 | 17 days ago | IN | 0 S | 0.0037122 | ||||
| Claim | 60016732 | 17 days ago | IN | 0 S | 0.0037146 | ||||
| Claim | 60016591 | 17 days ago | IN | 0 S | 0.00307407 | ||||
| Claim | 60005417 | 17 days ago | IN | 0 S | 0.00331369 | ||||
| Claim | 60005382 | 17 days ago | IN | 0 S | 0.0039108 | ||||
| Claim | 60005052 | 17 days ago | IN | 0 S | 0.0037133 | ||||
| Claim | 60004127 | 17 days ago | IN | 0 S | 0.0037134 | ||||
| Claim | 59999890 | 17 days ago | IN | 0 S | 0.0037134 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 61207311 | 9 hrs ago | 0.07466986 S | ||||
| 60888516 | 5 days ago | 0.02931199 S | ||||
| 60801930 | 6 days ago | 0.03438706 S | ||||
| 60378162 | 12 days ago | 0.02294991 S | ||||
| 60344817 | 12 days ago | 0.00025846 S | ||||
| 60126096 | 15 days ago | 0.00318858 S | ||||
| 60095529 | 16 days ago | 0.00381935 S | ||||
| 60074498 | 16 days ago | 0.05829114 S | ||||
| 60054231 | 16 days ago | 0.06824302 S | ||||
| 60049773 | 17 days ago | 0.0009921 S | ||||
| 60042842 | 17 days ago | 0.0219731 S | ||||
| 60042728 | 17 days ago | 0.310378 S | ||||
| 60038226 | 17 days ago | 0.0167645 S | ||||
| 60037051 | 17 days ago | 0.04638015 S | ||||
| 60021713 | 17 days ago | 0.37299747 S | ||||
| 60021139 | 17 days ago | 0.04244305 S | ||||
| 60017016 | 17 days ago | 0.04049163 S | ||||
| 60016896 | 17 days ago | 0.04119647 S | ||||
| 60016732 | 17 days ago | 0.03820778 S | ||||
| 60016591 | 17 days ago | 0.02228103 S | ||||
| 60005417 | 17 days ago | 0.40509701 S | ||||
| 60005382 | 17 days ago | 0.10766305 S | ||||
| 60005052 | 17 days ago | 0.00629969 S | ||||
| 60004127 | 17 days ago | 0.00707741 S | ||||
| 59999890 | 17 days ago | 2.29239342 S |
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x9B8E0fb6...b2EA5bD81 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
MerkleDistributor
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title MerkleDistributor
* @author Fee Distributor Team
* @notice Distributes native tokens (ETH/PLS) to users via Merkle proofs
* @dev Features:
* - Cumulative claims: Users claim the difference between proof amount and already-claimed
* - Epoch system: Each sweep starts a new epoch, resetting claim tracking
* - Emergency pause: Owner can pause claims
* - Whitelist: Multiple addresses can set merkle roots
* - Owner sweep: Owner can reclaim unclaimed funds anytime
*
* Security features:
* - Merkle root invalidated on sweep to prevent stale proof attacks
* - ReentrancyGuard on claim function
* - CEI pattern (Checks-Effects-Interactions)
* - Zero address validation
*/
contract MerkleDistributor is ReentrancyGuard {
using SafeERC20 for IERC20;
// ============================================
// STATE VARIABLES
// ============================================
/// @notice Contract owner
address public owner;
/// @notice Pending owner for 2-step transfer
address public pendingOwner;
/// @notice Current merkle root for proof verification
bytes32 public merkleRoot;
/// @notice Current distribution epoch (increments on sweep)
uint256 public epoch;
/// @notice Timestamp when merkle root was last set
uint256 public lastRootTimestamp;
/// @notice Whether claims are paused
bool public paused;
/// @notice Whitelist of addresses that can set merkle root
mapping(address => bool) public whitelist;
/// @notice Tracks claimed amounts: epoch => user => claimed amount
mapping(uint256 => mapping(address => uint256)) public claimed;
/// @notice List of V3 pools to collect fees from
address[] public pools;
/// @notice Quick lookup for pool existence
mapping(address => bool) public isPool;
// ============================================
// EVENTS
// ============================================
event Claimed(address indexed account, uint256 indexed epoch, uint256 amount);
event MerkleRootSet(bytes32 indexed root, uint256 indexed epoch, uint256 timestamp);
event MerkleRootInvalidated(uint256 indexed epoch);
event EpochAdvanced(uint256 indexed oldEpoch, uint256 indexed newEpoch);
event Swept(address indexed to, uint256 indexed epoch, uint256 amount);
event WhitelistUpdated(address indexed account, bool indexed status);
event PauseUpdated(bool indexed paused);
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event ERC20Rescued(address indexed token, address indexed to, uint256 amount);
event Deposited(address indexed from, uint256 amount);
event EmergencyWithdraw(address indexed to, uint256 amount);
event PoolAdded(address indexed pool);
event PoolRemoved(address indexed pool);
event PoolsCleared();
// ============================================
// ERRORS
// ============================================
error NotOwner();
error NotWhitelisted();
error ContractPaused();
error InvalidProof();
error NothingToClaim();
error TransferFailed();
error ZeroAddress();
error InvalidMerkleRoot();
error InsufficientBalance();
error NoRootSet();
error NotPendingOwner();
error NoPendingOwner();
error NotPausedForEmergency();
error PoolAlreadyExists();
error PoolNotFound();
// ============================================
// MODIFIERS
// ============================================
modifier onlyOwner() {
_checkOwner();
_;
}
modifier onlyWhitelisted() {
_checkWhitelisted();
_;
}
modifier whenNotPaused() {
_checkNotPaused();
_;
}
// ============================================
// CONSTRUCTOR
// ============================================
constructor() {
owner = msg.sender;
whitelist[msg.sender] = true;
epoch = 1;
emit OwnershipTransferred(address(0), msg.sender);
emit WhitelistUpdated(msg.sender, true);
}
// ============================================
// EXTERNAL FUNCTIONS
// ============================================
/**
* @notice Claim native tokens using a Merkle proof
* @param cumulativeAmount Total amount user is entitled to (cumulative across rounds)
* @param merkleProof Proof that (msg.sender, cumulativeAmount) is in the tree
* @dev Follows CEI pattern. Updates state before transfer.
*/
function claim(
uint256 cumulativeAmount,
bytes32[] calldata merkleProof
) external nonReentrant whenNotPaused {
// Check merkle root is set
if (merkleRoot == bytes32(0)) {
revert NoRootSet();
}
// Verify the proof
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(msg.sender, cumulativeAmount))));
if (!MerkleProof.verify(merkleProof, merkleRoot, leaf)) {
revert InvalidProof();
}
// Calculate claimable amount (cumulative - already claimed this epoch)
uint256 alreadyClaimed = claimed[epoch][msg.sender];
if (cumulativeAmount <= alreadyClaimed) {
revert NothingToClaim();
}
uint256 claimable = cumulativeAmount - alreadyClaimed;
// Check sufficient balance
if (address(this).balance < claimable) {
revert InsufficientBalance();
}
// Update claimed amount BEFORE transfer (CEI pattern)
claimed[epoch][msg.sender] = cumulativeAmount;
// Transfer native token
(bool success, ) = msg.sender.call{value: claimable}("");
if (!success) revert TransferFailed();
emit Claimed(msg.sender, epoch, claimable);
}
/**
* @notice Set a new merkle root (whitelisted addresses only)
* @param _merkleRoot New merkle root (must be non-zero)
*/
function setMerkleRoot(bytes32 _merkleRoot) external onlyWhitelisted {
if (_merkleRoot == bytes32(0)) {
revert InvalidMerkleRoot();
}
merkleRoot = _merkleRoot;
lastRootTimestamp = block.timestamp;
emit MerkleRootSet(_merkleRoot, epoch, block.timestamp);
}
/**
* @notice Advance to a new epoch (owner only)
* @dev Resets claim tracking AND invalidates root to prevent double-claims.
* A new merkle root must be set before users can claim again.
*/
function newEpoch() external onlyOwner {
uint256 oldEpoch = epoch;
// SECURITY: Invalidate root to prevent double-claim attack
// Without this, users could claim again after epoch change with same proof
merkleRoot = bytes32(0);
// Using unchecked is safe: would take 10^70 years at 1 epoch/second
unchecked {
epoch = oldEpoch + 1;
}
emit MerkleRootInvalidated(oldEpoch);
emit EpochAdvanced(oldEpoch, epoch);
}
/**
* @notice Sweep unclaimed funds to specified address (owner only)
* @param to Address to send funds to
* @dev Owner can sweep anytime. Invalidates merkle root to prevent stale proof attacks.
*/
function sweep(address to) external onlyOwner {
if (to == address(0)) revert ZeroAddress();
uint256 balance = address(this).balance;
uint256 currentEpoch = epoch;
// CRITICAL: Invalidate merkle root to prevent stale proof attacks
// After sweep, old proofs must not be usable against new deposits
merkleRoot = bytes32(0);
// Advance epoch (resets claim tracking)
unchecked {
epoch = currentEpoch + 1;
}
// Reset timestamp
lastRootTimestamp = 0;
emit MerkleRootInvalidated(currentEpoch);
emit EpochAdvanced(currentEpoch, epoch);
// Transfer all funds (do last - CEI pattern)
if (balance > 0) {
(bool success, ) = to.call{value: balance}("");
if (!success) revert TransferFailed();
}
emit Swept(to, currentEpoch, balance);
}
/**
* @notice Pause or unpause claims (owner only)
* @param _paused New pause state
*/
function setPaused(bool _paused) external onlyOwner {
paused = _paused;
emit PauseUpdated(_paused);
}
/**
* @notice Update whitelist status for an address (owner only)
* @param account Address to update
* @param status New whitelist status
*/
function setWhitelist(address account, bool status) external onlyOwner {
if (account == address(0)) revert ZeroAddress();
whitelist[account] = status;
emit WhitelistUpdated(account, status);
}
/**
* @notice Start 2-step ownership transfer (owner only)
* @param newOwner New owner address
* @dev New owner must call acceptOwnership() to complete transfer
*/
function transferOwnership(address newOwner) external onlyOwner {
if (newOwner == address(0)) revert ZeroAddress();
pendingOwner = newOwner;
emit OwnershipTransferStarted(owner, newOwner);
}
/**
* @notice Accept ownership (pending owner only)
* @dev Completes the 2-step ownership transfer
*/
function acceptOwnership() external {
if (pendingOwner == address(0)) revert NoPendingOwner();
if (msg.sender != pendingOwner) revert NotPendingOwner();
address oldOwner = owner;
owner = pendingOwner;
pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/**
* @notice Cancel pending ownership transfer (owner only)
*/
function cancelOwnershipTransfer() external onlyOwner {
pendingOwner = address(0);
}
/**
* @notice Emergency withdraw when contract is paused (owner only)
* @param to Recipient address
* @dev Can only be called when paused. Use for critical bugs.
* SECURITY: Invalidates root to prevent stale proof attacks after withdrawal.
*/
function emergencyWithdraw(address to) external onlyOwner {
if (!paused) revert NotPausedForEmergency();
if (to == address(0)) revert ZeroAddress();
uint256 balance = address(this).balance;
uint256 currentEpoch = epoch;
// SECURITY: Invalidate root to prevent stale proof attacks
// After emergency withdrawal, old proofs must not work against new deposits
merkleRoot = bytes32(0);
if (balance > 0) {
(bool success, ) = to.call{value: balance}("");
if (!success) revert TransferFailed();
}
emit MerkleRootInvalidated(currentEpoch);
emit EmergencyWithdraw(to, balance);
}
/**
* @notice Rescue ERC20 tokens accidentally sent to this contract
* @param token ERC20 token address
* @param to Recipient address
* @param amount Amount to rescue
* @dev Only owner can call. Cannot rescue native tokens (use sweep instead).
*/
function rescueERC20(
address token,
address to,
uint256 amount
) external onlyOwner {
if (token == address(0)) revert ZeroAddress();
if (to == address(0)) revert ZeroAddress();
IERC20(token).safeTransfer(to, amount);
emit ERC20Rescued(token, to, amount);
}
// ============================================
// POOL MANAGEMENT
// ============================================
/**
* @notice Add a pool to the collection list (owner only)
* @param pool Pool address to add
*/
function addPool(address pool) external onlyOwner {
if (pool == address(0)) revert ZeroAddress();
if (isPool[pool]) revert PoolAlreadyExists();
pools.push(pool);
isPool[pool] = true;
emit PoolAdded(pool);
}
/**
* @notice Add multiple pools at once (owner only)
* @param _pools Array of pool addresses
*/
function addPools(address[] calldata _pools) external onlyOwner {
for (uint256 i = 0; i < _pools.length; i++) {
address pool = _pools[i];
if (pool == address(0)) revert ZeroAddress();
if (isPool[pool]) revert PoolAlreadyExists();
pools.push(pool);
isPool[pool] = true;
emit PoolAdded(pool);
}
}
/**
* @notice Remove a pool from the collection list (owner only)
* @param pool Pool address to remove
*/
function removePool(address pool) external onlyOwner {
if (!isPool[pool]) revert PoolNotFound();
// Find and remove from array
for (uint256 i = 0; i < pools.length; i++) {
if (pools[i] == pool) {
// Swap with last element and pop
pools[i] = pools[pools.length - 1];
pools.pop();
break;
}
}
isPool[pool] = false;
emit PoolRemoved(pool);
}
/**
* @notice Clear all pools (owner only)
* @dev Use before setting a new list of pools
*/
function clearPools() external onlyOwner {
for (uint256 i = 0; i < pools.length; i++) {
isPool[pools[i]] = false;
}
delete pools;
emit PoolsCleared();
}
/**
* @notice Replace all pools with a new list (owner only)
* @param _pools New array of pool addresses
*/
function setPools(address[] calldata _pools) external onlyOwner {
// Clear existing
for (uint256 i = 0; i < pools.length; i++) {
isPool[pools[i]] = false;
}
delete pools;
// Add new
for (uint256 i = 0; i < _pools.length; i++) {
address pool = _pools[i];
if (pool == address(0)) revert ZeroAddress();
if (isPool[pool]) revert PoolAlreadyExists();
pools.push(pool);
isPool[pool] = true;
}
emit PoolsCleared();
}
/**
* @notice Get all pools
* @return Array of pool addresses
*/
function getPools() external view returns (address[] memory) {
return pools;
}
/**
* @notice Get number of pools
* @return Pool count
*/
function getPoolCount() external view returns (uint256) {
return pools.length;
}
// ============================================
// VIEW FUNCTIONS
// ============================================
/**
* @notice Get amount claimed by user in current epoch
* @param account User address
* @return Amount claimed in current epoch
*/
function getClaimedAmount(address account) external view returns (uint256) {
return claimed[epoch][account];
}
/**
* @notice Get amount claimed by user in a specific epoch
* @param _epoch Epoch number
* @param account User address
* @return Amount claimed in specified epoch
*/
function getClaimedAmountForEpoch(
uint256 _epoch,
address account
) external view returns (uint256) {
return claimed[_epoch][account];
}
/**
* @notice Calculate claimable amount for a user
* @param account User address
* @param cumulativeAmount User's cumulative amount from proof
* @return claimable Amount user can claim
*/
function getClaimableAmount(
address account,
uint256 cumulativeAmount
) external view returns (uint256 claimable) {
uint256 alreadyClaimed = claimed[epoch][account];
if (cumulativeAmount > alreadyClaimed) {
claimable = cumulativeAmount - alreadyClaimed;
}
}
/**
* @notice Check if sweep is available (owner can always sweep)
* @return True - owner can sweep anytime
*/
function canSweep() external pure returns (bool) {
return true;
}
/**
* @notice Check if a merkle root is set and valid
* @return True if root is non-zero
*/
function hasValidRoot() external view returns (bool) {
return merkleRoot != bytes32(0);
}
/**
* @notice Get contract's native token balance
* @return Contract balance
*/
function getBalance() external view returns (uint256) {
return address(this).balance;
}
// ============================================
// INTERNAL FUNCTIONS
// ============================================
function _checkOwner() internal view {
if (msg.sender != owner) revert NotOwner();
}
function _checkWhitelisted() internal view {
if (!whitelist[msg.sender] && msg.sender != owner) revert NotWhitelisted();
}
function _checkNotPaused() internal view {
if (paused) revert ContractPaused();
}
// ============================================
// RECEIVE
// ============================================
/// @notice Accept native token deposits
receive() external payable {
emit Deposited(msg.sender, msg.value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/MerkleProof.sol)
// This file was procedurally generated from scripts/generate/templates/MerkleProof.js.
pragma solidity ^0.8.20;
import {Hashes} from "./Hashes.sol";
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*
* IMPORTANT: Consider memory side-effects when using custom hashing functions
* that access memory in an unsafe way.
*
* NOTE: This library supports proof verification for merkle trees built using
* custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
* leaf inclusion in trees built using non-commutative hashing functions requires
* additional logic that is not supported by this library.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in memory with the default hashing function.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in memory with the default hashing function.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in memory with a custom hashing function.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processProof(proof, leaf, hasher) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in memory with a custom hashing function.
*/
function processProof(
bytes32[] memory proof,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = hasher(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with the default hashing function.
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with the default hashing function.
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with a custom hashing function.
*/
function verifyCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processProofCalldata(proof, leaf, hasher) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leaves & pre-images are assumed to be sorted.
*
* This version handles proofs in calldata with a custom hashing function.
*/
function processProofCalldata(
bytes32[] calldata proof,
bytes32 leaf,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = hasher(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in memory with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProof}.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in memory with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = Hashes.commutativeKeccak256(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in memory with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProof}.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processMultiProof(proof, proofFlags, leaves, hasher) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in memory with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = hasher(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in calldata with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProofCalldata}.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in calldata with the default hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = Hashes.commutativeKeccak256(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* This version handles multiproofs in calldata with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*
* NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
* The `leaves` must be validated independently. See {processMultiProofCalldata}.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* This version handles multiproofs in calldata with a custom hashing function.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
* and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
* validating the leaves elsewhere.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves,
function(bytes32, bytes32) view returns (bytes32) hasher
) internal view returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofFlagsLen = proofFlags.length;
// Check proof validity.
if (leavesLen + proof.length != proofFlagsLen + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < proofFlagsLen; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = hasher(a, b);
}
if (proofFlagsLen > 0) {
if (proofPos != proof.length) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[proofFlagsLen - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced
* by the {ReentrancyGuardTransient} variant in v6.0.
*
* @custom:stateless
*/
abstract contract ReentrancyGuard {
using StorageSlot for bytes32;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/**
* @dev A `view` only version of {nonReentrant}. Use to block view functions
* from being called, preventing reading from inconsistent contract state.
*
* CAUTION: This is a "view" modifier and does not change the reentrancy
* status. Use it only on view functions. For payable or non-payable functions,
* use the standard {nonReentrant} modifier instead.
*/
modifier nonReentrantView() {
_nonReentrantBeforeView();
_;
}
function _nonReentrantBeforeView() private view {
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
_nonReentrantBeforeView();
// Any calls to nonReentrant after this point will fail
_reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
}
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
return REENTRANCY_GUARD_STORAGE;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_safeTransfer(token, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
if (!_safeTransferFrom(token, from, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _safeTransfer(token, to, value, false);
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _safeTransferFrom(token, from, to, value, false);
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
if (!_safeApprove(token, spender, value, false)) {
if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Oppositely, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity `token.transfer(to, value)` call, relaxing the requirement on the return value: the
* return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.transfer.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(to, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
/**
* @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, relaxing the requirement on the return
* value: the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param from The sender of the tokens
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value,
bool bubble
) private returns (bool success) {
bytes4 selector = IERC20.transferFrom.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(from, shr(96, not(0))))
mstore(0x24, and(to, shr(96, not(0))))
mstore(0x44, value)
success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
mstore(0x60, 0)
}
}
/**
* @dev Imitates a Solidity `token.approve(spender, value)` call, relaxing the requirement on the return value:
* the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param spender The spender of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.approve.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(spender, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/Hashes.sol)
pragma solidity ^0.8.20;
/**
* @dev Library of standard hash functions.
*
* _Available since v5.1._
*/
library Hashes {
/**
* @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
*
* NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
*/
function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
return a < b ? efficientKeccak256(a, b) : efficientKeccak256(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function efficientKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
assembly ("memory-safe") {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"forge-std/=lib/forge-std/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidMerkleRoot","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"NoPendingOwner","type":"error"},{"inputs":[],"name":"NoRootSet","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotPausedForEmergency","type":"error"},{"inputs":[],"name":"NotPendingOwner","type":"error"},{"inputs":[],"name":"NotWhitelisted","type":"error"},{"inputs":[],"name":"NothingToClaim","type":"error"},{"inputs":[],"name":"PoolAlreadyExists","type":"error"},{"inputs":[],"name":"PoolNotFound","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20Rescued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"oldEpoch","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newEpoch","type":"uint256"}],"name":"EpochAdvanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"MerkleRootInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"MerkleRootSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"paused","type":"bool"}],"name":"PauseUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"PoolRemoved","type":"event"},{"anonymous":false,"inputs":[],"name":"PoolsCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Swept","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"}],"name":"WhitelistUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"}],"name":"addPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canSweep","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cumulativeAmount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clearPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"cumulativeAmount","type":"uint256"}],"name":"getClaimableAmount","outputs":[{"internalType":"uint256","name":"claimable","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getClaimedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"getClaimedAmountForEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPools","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasValidRoot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRootTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"removePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"}],"name":"setPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
0x608060405234801561000f575f80fd5b5060017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00555f80546001600160a01b03191633908117825580825260066020526040808320805460ff19166001908117909155600355519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a360405160019033907ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d905f90a3611b16806100c85f395ff3fe6080604052600436106101f4575f3560e01c80636ff1c9bc116101085780639b19251a1161009d578063b3743f601161006d578063b3743f60146105d4578063c83eec91146105f3578063d914cd4b14610606578063e30c397814610625578063f2fde38b14610644575f80fd5b80639b19251a14610549578063ac4afa3814610577578063b2118a8d14610596578063b36a4ab1146105b5575f80fd5b80638da5cb5b116100d85780638da5cb5b146104a85780638df40be8146104de5780638eec5d7014610520578063900cf0cf14610534575f80fd5b80636ff1c9bc1461043757806379ba5097146104565780637cb647591461046a5780638544c53b14610489575f80fd5b80632dd9135f116101895780633b7d0946116101595780633b7d09461461039157806353d6fd59146103b05780635b16ebb7146103cf5780635c975abb146103fd578063673a2a1f14610416575f80fd5b80632dd9135f146103295780632eb4a7ab146103485780632ee3e20b1461035d5780632f52ebb714610372575f80fd5b806316c38b3c116101c457806316c38b3c146102c05780631e8b1135146102df57806323452b9c146102f35780632c45728014610307575f80fd5b806301681a621461023457806310f95fbe1461025557806312065fe014610269578063120aa8771461028a575f80fd5b366102305760405134815233907f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c49060200160405180910390a2005b5f80fd5b34801561023f575f80fd5b5061025361024e366004611853565b610663565b005b348015610260575f80fd5b506102536107c1565b348015610274575f80fd5b50475b6040519081526020015b60405180910390f35b348015610295575f80fd5b506102776102a436600461186c565b600760209081525f928352604080842090915290825290205481565b3480156102cb575f80fd5b506102536102da3660046118a5565b610835565b3480156102ea575f80fd5b50610253610879565b3480156102fe575f80fd5b5061025361091f565b348015610312575f80fd5b5060025415155b6040519015158152602001610281565b348015610334575f80fd5b5061027761034336600461186c565b610939565b348015610353575f80fd5b5061027760025481565b348015610368575f80fd5b5061027760045481565b34801561037d575f80fd5b5061025361038c366004611906565b610962565b34801561039c575f80fd5b506102536103ab366004611853565b610b8c565b3480156103bb575f80fd5b506102536103ca36600461194e565b610d15565b3480156103da575f80fd5b506103196103e9366004611853565b60096020525f908152604090205460ff1681565b348015610408575f80fd5b506005546103199060ff1681565b348015610421575f80fd5b5061042a610d97565b6040516102819190611976565b348015610442575f80fd5b50610253610451366004611853565b610df7565b348015610461575f80fd5b50610253610f3c565b348015610475575f80fd5b506102536104843660046119c2565b610fe9565b348015610494575f80fd5b506102536104a33660046119d9565b611056565b3480156104b3575f80fd5b505f546104c6906001600160a01b031681565b6040516001600160a01b039091168152602001610281565b3480156104e9575f80fd5b506102776104f8366004611853565b6003545f9081526007602090815260408083206001600160a01b039094168352929052205490565b34801561052b575f80fd5b50600854610277565b34801561053f575f80fd5b5061027760035481565b348015610554575f80fd5b50610319610563366004611853565b60066020525f908152604090205460ff1681565b348015610582575f80fd5b506104c66105913660046119c2565b611209565b3480156105a1575f80fd5b506102536105b0366004611a18565b611231565b3480156105c0575f80fd5b506102536105cf3660046119d9565b6112e0565b3480156105df575f80fd5b506102776105ee366004611a51565b61141a565b3480156105fe575f80fd5b506001610319565b348015610611575f80fd5b50610253610620366004611853565b61145b565b348015610630575f80fd5b506001546104c6906001600160a01b031681565b34801561064f575f80fd5b5061025361065e366004611853565b611551565b61066b6115d0565b6001600160a01b0381166106925760405163d92e233d60e01b815260040160405180910390fd5b600380545f6002819055600182019092556004829055604051479282917f6f0e8124492339de1992bb885ea32b474e8622264f8d5ad915f82c358d0ee1879190a260035460405182907facfa06cebbab8226f9a388bff9f5fcf4569298028d939f4765958dc57dbb330b905f90a38115610778575f836001600160a01b0316836040515f6040518083038185875af1925050503d805f811461074f576040519150601f19603f3d011682016040523d82523d5f602084013e610754565b606091505b5050905080610776576040516312171d8360e31b815260040160405180910390fd5b505b80836001600160a01b03167f8a89918ff5a1192aacedc279940d66eec9c0a1c2c6490aea91350ff42981c1a9846040516107b491815260200190565b60405180910390a3505050565b6107c96115d0565b600380545f600281905560018201909255604051909182917f6f0e8124492339de1992bb885ea32b474e8622264f8d5ad915f82c358d0ee1879190a260035460405182907facfa06cebbab8226f9a388bff9f5fcf4569298028d939f4765958dc57dbb330b905f90a350565b61083d6115d0565b6005805460ff19168215159081179091556040517f77860e247ab9186dbe64e5bd0e0b93273cc4273e01818420e788f500078886f5905f90a250565b6108816115d0565b5f5b6008548110156108e9575f60095f600884815481106108a4576108a4611a79565b5f918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055806108e181611aa1565b915050610883565b506108f560085f611802565b6040517fba5af8d05396412592ad39670c8400c2d53710beccd60480a5df682505d84b49905f90a1565b6109276115d0565b600180546001600160a01b0319169055565b5f8281526007602090815260408083206001600160a01b03851684529091529020545b92915050565b61096a6115fc565b61097261162a565b60025461099257604051636bda6a5f60e11b815260040160405180910390fd5b604080513360208201529081018490525f9060600160408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052805190602001209050610a1c8383808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050600254915084905061164e565b610a39576040516309bde33960e01b815260040160405180910390fd5b6003545f908152600760209081526040808320338452909152902054808511610a75576040516312d37ee560e31b815260040160405180910390fd5b5f610a808287611ab9565b905080471015610aa357604051631e9acf1760e31b815260040160405180910390fd5b6003545f9081526007602090815260408083203380855292528083208990555183908381818185875af1925050503d805f8114610afb576040519150601f19603f3d011682016040523d82523d5f602084013e610b00565b606091505b5050905080610b22576040516312171d8360e31b815260040160405180910390fd5b60035460405183815233907f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a9060200160405180910390a350505050610b8760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b505050565b610b946115d0565b6001600160a01b0381165f9081526009602052604090205460ff16610bcc576040516301dbb3ff60e61b815260040160405180910390fd5b5f5b600854811015610ccc57816001600160a01b031660088281548110610bf557610bf5611a79565b5f918252602090912001546001600160a01b031603610cba5760088054610c1e90600190611ab9565b81548110610c2e57610c2e611a79565b5f91825260209091200154600880546001600160a01b039092169183908110610c5957610c59611a79565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506008805480610c9557610c95611acc565b5f8281526020902081015f1990810180546001600160a01b0319169055019055610ccc565b80610cc481611aa1565b915050610bce565b506001600160a01b0381165f81815260096020526040808220805460ff19169055517f4106dfdaa577573db51c0ca93f766dbedfa0758faa2e7f5bcdb7c142be803c3f9190a250565b610d1d6115d0565b6001600160a01b038216610d445760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382165f81815260066020526040808220805460ff191685151590811790915590519092917ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d91a35050565b60606008805480602002602001604051908101604052809291908181526020018280548015610ded57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610dcf575b5050505050905090565b610dff6115d0565b60055460ff16610e2257604051637f5c1fcb60e11b815260040160405180910390fd5b6001600160a01b038116610e495760405163d92e233d60e01b815260040160405180910390fd5b6003545f60025547908115610eca575f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114610ea1576040519150601f19603f3d011682016040523d82523d5f602084013e610ea6565b606091505b5050905080610ec8576040516312171d8360e31b815260040160405180910390fd5b505b60405181907f6f0e8124492339de1992bb885ea32b474e8622264f8d5ad915f82c358d0ee187905f90a2826001600160a01b03167f5fafa99d0643513820be26656b45130b01e1c03062e1266bf36f88cbd3bd969583604051610f2f91815260200190565b60405180910390a2505050565b6001546001600160a01b0316610f6557604051633e31d61b60e11b815260040160405180910390fd5b6001546001600160a01b03163314610f9057604051630614e5c760e21b815260040160405180910390fd5b5f8054600180546001600160a01b03198084166001600160a01b038381169190911786559116909155604051911691339183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610ff1611663565b8061100f57604051639dd854d360e01b815260040160405180910390fd5b60028190554260048190556003546040519182529082907fc7ce8037cbac8a577b7d8edd9340b4dbb1e691f2eb699b97f5c7576b0e9ede049060200160405180910390a350565b61105e6115d0565b5f5b6008548110156110c6575f60095f6008848154811061108157611081611a79565b5f918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055806110be81611aa1565b915050611060565b506110d260085f611802565b5f5b818110156111dc575f8383838181106110ef576110ef611a79565b90506020020160208101906111049190611853565b90506001600160a01b03811661112d5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381165f9081526009602052604090205460ff161561116657604051630188c99160e11b815260040160405180910390fd5b6008805460018181019092557ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b039093166001600160a01b0319909316831790555f91825260096020526040909120805460ff19169091179055806111d481611aa1565b9150506110d4565b506040517fba5af8d05396412592ad39670c8400c2d53710beccd60480a5df682505d84b49905f90a15050565b60088181548110611218575f80fd5b5f918252602090912001546001600160a01b0316905081565b6112396115d0565b6001600160a01b0383166112605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382166112875760405163d92e233d60e01b815260040160405180910390fd5b61129b6001600160a01b03841683836116aa565b816001600160a01b0316836001600160a01b03167f8bbfbb5d7fcacf6fc74005cdede0635561638507f576c95f7f294c22141be2e5836040516107b491815260200190565b6112e86115d0565b5f5b81811015610b87575f83838381811061130557611305611a79565b905060200201602081019061131a9190611853565b90506001600160a01b0381166113435760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381165f9081526009602052604090205460ff161561137c57604051630188c99160e11b815260040160405180910390fd5b6008805460018082019092557ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b0319166001600160a01b0384169081179091555f81815260096020526040808220805460ff1916909417909355915190917f73cca62ab1b520c9715bf4e6c71e3e518c754e7148f65102f43289a7df0efea691a2508061141281611aa1565b9150506112ea565b6003545f9081526007602090815260408083206001600160a01b038616845290915281205480831115611454576114518184611ab9565b91505b5092915050565b6114636115d0565b6001600160a01b03811661148a5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381165f9081526009602052604090205460ff16156114c357604051630188c99160e11b815260040160405180910390fd5b6008805460018082019092557ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b0319166001600160a01b0384169081179091555f81815260096020526040808220805460ff1916909417909355915190917f73cca62ab1b520c9715bf4e6c71e3e518c754e7148f65102f43289a7df0efea691a250565b6115596115d0565b6001600160a01b0381166115805760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b038381169182179092555f8054604051929316917f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227009190a350565b5f546001600160a01b031633146115fa576040516330cd747160e01b815260040160405180910390fd5b565b6116046116e3565b60027f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b60055460ff16156115fa5760405163ab35696f60e01b815260040160405180910390fd5b5f8261165a8584611725565b14949350505050565b335f9081526006602052604090205460ff1615801561168c57505f546001600160a01b03163314155b156115fa57604051630b094f2760e31b815260040160405180910390fd5b6116b78383836001611771565b610b8757604051635274afe760e01b81526001600160a01b038416600482015260240160405180910390fd5b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00546002036115fa57604051633ee5aeb560e01b815260040160405180910390fd5b5f81815b8451811015611769576117558286838151811061174857611748611a79565b60200260200101516117d3565b91508061176181611aa1565b915050611729565b509392505050565b60405163a9059cbb60e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f511483166117c75783831516156117bb573d5f823e3d81fd5b5f873b113d1516831692505b60405250949350505050565b5f8183106117ed575f8281526020849052604090206117fb565b5f8381526020839052604090205b9392505050565b5080545f8255905f5260205f209081019061181d9190611820565b50565b5b80821115611834575f8155600101611821565b5090565b80356001600160a01b038116811461184e575f80fd5b919050565b5f60208284031215611863575f80fd5b6117fb82611838565b5f806040838503121561187d575f80fd5b8235915061188d60208401611838565b90509250929050565b8035801515811461184e575f80fd5b5f602082840312156118b5575f80fd5b6117fb82611896565b5f8083601f8401126118ce575f80fd5b50813567ffffffffffffffff8111156118e5575f80fd5b6020830191508360208260051b85010111156118ff575f80fd5b9250929050565b5f805f60408486031215611918575f80fd5b83359250602084013567ffffffffffffffff811115611935575f80fd5b611941868287016118be565b9497909650939450505050565b5f806040838503121561195f575f80fd5b61196883611838565b915061188d60208401611896565b602080825282518282018190525f9190848201906040850190845b818110156119b65783516001600160a01b031683529284019291840191600101611991565b50909695505050505050565b5f602082840312156119d2575f80fd5b5035919050565b5f80602083850312156119ea575f80fd5b823567ffffffffffffffff811115611a00575f80fd5b611a0c858286016118be565b90969095509350505050565b5f805f60608486031215611a2a575f80fd5b611a3384611838565b9250611a4160208501611838565b9150604084013590509250925092565b5f8060408385031215611a62575f80fd5b611a6b83611838565b946020939093013593505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201611ab257611ab2611a8d565b5060010190565b8181038181111561095c5761095c611a8d565b634e487b7160e01b5f52603160045260245ffdfea264697066735822122021bfc34f8e389bc8064c0436092981f540562d2a66de471e21af63931b8ba20364736f6c63430008140033
Deployed Bytecode
0x6080604052600436106101f4575f3560e01c80636ff1c9bc116101085780639b19251a1161009d578063b3743f601161006d578063b3743f60146105d4578063c83eec91146105f3578063d914cd4b14610606578063e30c397814610625578063f2fde38b14610644575f80fd5b80639b19251a14610549578063ac4afa3814610577578063b2118a8d14610596578063b36a4ab1146105b5575f80fd5b80638da5cb5b116100d85780638da5cb5b146104a85780638df40be8146104de5780638eec5d7014610520578063900cf0cf14610534575f80fd5b80636ff1c9bc1461043757806379ba5097146104565780637cb647591461046a5780638544c53b14610489575f80fd5b80632dd9135f116101895780633b7d0946116101595780633b7d09461461039157806353d6fd59146103b05780635b16ebb7146103cf5780635c975abb146103fd578063673a2a1f14610416575f80fd5b80632dd9135f146103295780632eb4a7ab146103485780632ee3e20b1461035d5780632f52ebb714610372575f80fd5b806316c38b3c116101c457806316c38b3c146102c05780631e8b1135146102df57806323452b9c146102f35780632c45728014610307575f80fd5b806301681a621461023457806310f95fbe1461025557806312065fe014610269578063120aa8771461028a575f80fd5b366102305760405134815233907f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c49060200160405180910390a2005b5f80fd5b34801561023f575f80fd5b5061025361024e366004611853565b610663565b005b348015610260575f80fd5b506102536107c1565b348015610274575f80fd5b50475b6040519081526020015b60405180910390f35b348015610295575f80fd5b506102776102a436600461186c565b600760209081525f928352604080842090915290825290205481565b3480156102cb575f80fd5b506102536102da3660046118a5565b610835565b3480156102ea575f80fd5b50610253610879565b3480156102fe575f80fd5b5061025361091f565b348015610312575f80fd5b5060025415155b6040519015158152602001610281565b348015610334575f80fd5b5061027761034336600461186c565b610939565b348015610353575f80fd5b5061027760025481565b348015610368575f80fd5b5061027760045481565b34801561037d575f80fd5b5061025361038c366004611906565b610962565b34801561039c575f80fd5b506102536103ab366004611853565b610b8c565b3480156103bb575f80fd5b506102536103ca36600461194e565b610d15565b3480156103da575f80fd5b506103196103e9366004611853565b60096020525f908152604090205460ff1681565b348015610408575f80fd5b506005546103199060ff1681565b348015610421575f80fd5b5061042a610d97565b6040516102819190611976565b348015610442575f80fd5b50610253610451366004611853565b610df7565b348015610461575f80fd5b50610253610f3c565b348015610475575f80fd5b506102536104843660046119c2565b610fe9565b348015610494575f80fd5b506102536104a33660046119d9565b611056565b3480156104b3575f80fd5b505f546104c6906001600160a01b031681565b6040516001600160a01b039091168152602001610281565b3480156104e9575f80fd5b506102776104f8366004611853565b6003545f9081526007602090815260408083206001600160a01b039094168352929052205490565b34801561052b575f80fd5b50600854610277565b34801561053f575f80fd5b5061027760035481565b348015610554575f80fd5b50610319610563366004611853565b60066020525f908152604090205460ff1681565b348015610582575f80fd5b506104c66105913660046119c2565b611209565b3480156105a1575f80fd5b506102536105b0366004611a18565b611231565b3480156105c0575f80fd5b506102536105cf3660046119d9565b6112e0565b3480156105df575f80fd5b506102776105ee366004611a51565b61141a565b3480156105fe575f80fd5b506001610319565b348015610611575f80fd5b50610253610620366004611853565b61145b565b348015610630575f80fd5b506001546104c6906001600160a01b031681565b34801561064f575f80fd5b5061025361065e366004611853565b611551565b61066b6115d0565b6001600160a01b0381166106925760405163d92e233d60e01b815260040160405180910390fd5b600380545f6002819055600182019092556004829055604051479282917f6f0e8124492339de1992bb885ea32b474e8622264f8d5ad915f82c358d0ee1879190a260035460405182907facfa06cebbab8226f9a388bff9f5fcf4569298028d939f4765958dc57dbb330b905f90a38115610778575f836001600160a01b0316836040515f6040518083038185875af1925050503d805f811461074f576040519150601f19603f3d011682016040523d82523d5f602084013e610754565b606091505b5050905080610776576040516312171d8360e31b815260040160405180910390fd5b505b80836001600160a01b03167f8a89918ff5a1192aacedc279940d66eec9c0a1c2c6490aea91350ff42981c1a9846040516107b491815260200190565b60405180910390a3505050565b6107c96115d0565b600380545f600281905560018201909255604051909182917f6f0e8124492339de1992bb885ea32b474e8622264f8d5ad915f82c358d0ee1879190a260035460405182907facfa06cebbab8226f9a388bff9f5fcf4569298028d939f4765958dc57dbb330b905f90a350565b61083d6115d0565b6005805460ff19168215159081179091556040517f77860e247ab9186dbe64e5bd0e0b93273cc4273e01818420e788f500078886f5905f90a250565b6108816115d0565b5f5b6008548110156108e9575f60095f600884815481106108a4576108a4611a79565b5f918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055806108e181611aa1565b915050610883565b506108f560085f611802565b6040517fba5af8d05396412592ad39670c8400c2d53710beccd60480a5df682505d84b49905f90a1565b6109276115d0565b600180546001600160a01b0319169055565b5f8281526007602090815260408083206001600160a01b03851684529091529020545b92915050565b61096a6115fc565b61097261162a565b60025461099257604051636bda6a5f60e11b815260040160405180910390fd5b604080513360208201529081018490525f9060600160408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052805190602001209050610a1c8383808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050600254915084905061164e565b610a39576040516309bde33960e01b815260040160405180910390fd5b6003545f908152600760209081526040808320338452909152902054808511610a75576040516312d37ee560e31b815260040160405180910390fd5b5f610a808287611ab9565b905080471015610aa357604051631e9acf1760e31b815260040160405180910390fd5b6003545f9081526007602090815260408083203380855292528083208990555183908381818185875af1925050503d805f8114610afb576040519150601f19603f3d011682016040523d82523d5f602084013e610b00565b606091505b5050905080610b22576040516312171d8360e31b815260040160405180910390fd5b60035460405183815233907f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a9060200160405180910390a350505050610b8760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b505050565b610b946115d0565b6001600160a01b0381165f9081526009602052604090205460ff16610bcc576040516301dbb3ff60e61b815260040160405180910390fd5b5f5b600854811015610ccc57816001600160a01b031660088281548110610bf557610bf5611a79565b5f918252602090912001546001600160a01b031603610cba5760088054610c1e90600190611ab9565b81548110610c2e57610c2e611a79565b5f91825260209091200154600880546001600160a01b039092169183908110610c5957610c59611a79565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506008805480610c9557610c95611acc565b5f8281526020902081015f1990810180546001600160a01b0319169055019055610ccc565b80610cc481611aa1565b915050610bce565b506001600160a01b0381165f81815260096020526040808220805460ff19169055517f4106dfdaa577573db51c0ca93f766dbedfa0758faa2e7f5bcdb7c142be803c3f9190a250565b610d1d6115d0565b6001600160a01b038216610d445760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382165f81815260066020526040808220805460ff191685151590811790915590519092917ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d91a35050565b60606008805480602002602001604051908101604052809291908181526020018280548015610ded57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610dcf575b5050505050905090565b610dff6115d0565b60055460ff16610e2257604051637f5c1fcb60e11b815260040160405180910390fd5b6001600160a01b038116610e495760405163d92e233d60e01b815260040160405180910390fd5b6003545f60025547908115610eca575f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114610ea1576040519150601f19603f3d011682016040523d82523d5f602084013e610ea6565b606091505b5050905080610ec8576040516312171d8360e31b815260040160405180910390fd5b505b60405181907f6f0e8124492339de1992bb885ea32b474e8622264f8d5ad915f82c358d0ee187905f90a2826001600160a01b03167f5fafa99d0643513820be26656b45130b01e1c03062e1266bf36f88cbd3bd969583604051610f2f91815260200190565b60405180910390a2505050565b6001546001600160a01b0316610f6557604051633e31d61b60e11b815260040160405180910390fd5b6001546001600160a01b03163314610f9057604051630614e5c760e21b815260040160405180910390fd5b5f8054600180546001600160a01b03198084166001600160a01b038381169190911786559116909155604051911691339183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610ff1611663565b8061100f57604051639dd854d360e01b815260040160405180910390fd5b60028190554260048190556003546040519182529082907fc7ce8037cbac8a577b7d8edd9340b4dbb1e691f2eb699b97f5c7576b0e9ede049060200160405180910390a350565b61105e6115d0565b5f5b6008548110156110c6575f60095f6008848154811061108157611081611a79565b5f918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055806110be81611aa1565b915050611060565b506110d260085f611802565b5f5b818110156111dc575f8383838181106110ef576110ef611a79565b90506020020160208101906111049190611853565b90506001600160a01b03811661112d5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381165f9081526009602052604090205460ff161561116657604051630188c99160e11b815260040160405180910390fd5b6008805460018181019092557ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b039093166001600160a01b0319909316831790555f91825260096020526040909120805460ff19169091179055806111d481611aa1565b9150506110d4565b506040517fba5af8d05396412592ad39670c8400c2d53710beccd60480a5df682505d84b49905f90a15050565b60088181548110611218575f80fd5b5f918252602090912001546001600160a01b0316905081565b6112396115d0565b6001600160a01b0383166112605760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382166112875760405163d92e233d60e01b815260040160405180910390fd5b61129b6001600160a01b03841683836116aa565b816001600160a01b0316836001600160a01b03167f8bbfbb5d7fcacf6fc74005cdede0635561638507f576c95f7f294c22141be2e5836040516107b491815260200190565b6112e86115d0565b5f5b81811015610b87575f83838381811061130557611305611a79565b905060200201602081019061131a9190611853565b90506001600160a01b0381166113435760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381165f9081526009602052604090205460ff161561137c57604051630188c99160e11b815260040160405180910390fd5b6008805460018082019092557ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b0319166001600160a01b0384169081179091555f81815260096020526040808220805460ff1916909417909355915190917f73cca62ab1b520c9715bf4e6c71e3e518c754e7148f65102f43289a7df0efea691a2508061141281611aa1565b9150506112ea565b6003545f9081526007602090815260408083206001600160a01b038616845290915281205480831115611454576114518184611ab9565b91505b5092915050565b6114636115d0565b6001600160a01b03811661148a5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381165f9081526009602052604090205460ff16156114c357604051630188c99160e11b815260040160405180910390fd5b6008805460018082019092557ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b0319166001600160a01b0384169081179091555f81815260096020526040808220805460ff1916909417909355915190917f73cca62ab1b520c9715bf4e6c71e3e518c754e7148f65102f43289a7df0efea691a250565b6115596115d0565b6001600160a01b0381166115805760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b038381169182179092555f8054604051929316917f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227009190a350565b5f546001600160a01b031633146115fa576040516330cd747160e01b815260040160405180910390fd5b565b6116046116e3565b60027f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b60055460ff16156115fa5760405163ab35696f60e01b815260040160405180910390fd5b5f8261165a8584611725565b14949350505050565b335f9081526006602052604090205460ff1615801561168c57505f546001600160a01b03163314155b156115fa57604051630b094f2760e31b815260040160405180910390fd5b6116b78383836001611771565b610b8757604051635274afe760e01b81526001600160a01b038416600482015260240160405180910390fd5b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00546002036115fa57604051633ee5aeb560e01b815260040160405180910390fd5b5f81815b8451811015611769576117558286838151811061174857611748611a79565b60200260200101516117d3565b91508061176181611aa1565b915050611729565b509392505050565b60405163a9059cbb60e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f511483166117c75783831516156117bb573d5f823e3d81fd5b5f873b113d1516831692505b60405250949350505050565b5f8183106117ed575f8281526020849052604090206117fb565b5f8381526020839052604090205b9392505050565b5080545f8255905f5260205f209081019061181d9190611820565b50565b5b80821115611834575f8155600101611821565b5090565b80356001600160a01b038116811461184e575f80fd5b919050565b5f60208284031215611863575f80fd5b6117fb82611838565b5f806040838503121561187d575f80fd5b8235915061188d60208401611838565b90509250929050565b8035801515811461184e575f80fd5b5f602082840312156118b5575f80fd5b6117fb82611896565b5f8083601f8401126118ce575f80fd5b50813567ffffffffffffffff8111156118e5575f80fd5b6020830191508360208260051b85010111156118ff575f80fd5b9250929050565b5f805f60408486031215611918575f80fd5b83359250602084013567ffffffffffffffff811115611935575f80fd5b611941868287016118be565b9497909650939450505050565b5f806040838503121561195f575f80fd5b61196883611838565b915061188d60208401611896565b602080825282518282018190525f9190848201906040850190845b818110156119b65783516001600160a01b031683529284019291840191600101611991565b50909695505050505050565b5f602082840312156119d2575f80fd5b5035919050565b5f80602083850312156119ea575f80fd5b823567ffffffffffffffff811115611a00575f80fd5b611a0c858286016118be565b90969095509350505050565b5f805f60608486031215611a2a575f80fd5b611a3384611838565b9250611a4160208501611838565b9150604084013590509250925092565b5f8060408385031215611a62575f80fd5b611a6b83611838565b946020939093013593505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201611ab257611ab2611a8d565b5060010190565b8181038181111561095c5761095c611a8d565b634e487b7160e01b5f52603160045260245ffdfea264697066735822122021bfc34f8e389bc8064c0436092981f540562d2a66de471e21af63931b8ba20364736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$1.29
Net Worth in S
Token Allocations
S
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| SONIC | 100.00% | $0.068019 | 18.8998 | $1.29 |
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.