S Price: $0.475852 (+2.22%)

Contract

0x485EaBED09819428E7aC87139102CD9D83a784bD

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim210131312025-04-19 9:21:242 days ago1745054484IN
0x485EaBED...D83a784bD
0 S0.0058728655.01
Claim210092692025-04-19 8:45:132 days ago1745052313IN
0x485EaBED...D83a784bD
0 S0.0048837855.01
Claim209785002025-04-19 3:10:192 days ago1745032219IN
0x485EaBED...D83a784bD
0 S0.0050832650.0001
Claim209715222025-04-19 1:49:192 days ago1745027359IN
0x485EaBED...D83a784bD
0 S0.0048837855.01
Claim209453602025-04-18 21:08:382 days ago1745010518IN
0x485EaBED...D83a784bD
0 S0.004884555.01
Claim209453402025-04-18 21:08:242 days ago1745010504IN
0x485EaBED...D83a784bD
0 S0.0048465455.01
Claim209296832025-04-18 18:33:542 days ago1745001234IN
0x485EaBED...D83a784bD
0 S0.0048842755
Claim209076362025-04-18 15:18:082 days ago1744989488IN
0x485EaBED...D83a784bD
0 S0.0058728655.01
Claim209055412025-04-18 15:01:462 days ago1744988506IN
0x485EaBED...D83a784bD
0 S0.0048824155.01
Claim209039992025-04-18 14:47:113 days ago1744987631IN
0x485EaBED...D83a784bD
0 S0.0042270550.0001
Claim209025932025-04-18 14:33:383 days ago1744986818IN
0x485EaBED...D83a784bD
0 S0.0042288550.0001
Claim209002962025-04-18 14:11:323 days ago1744985492IN
0x485EaBED...D83a784bD
0 S0.0050820650.0001
Claim208982222025-04-18 13:51:373 days ago1744984297IN
0x485EaBED...D83a784bD
0 S0.0049805556.1
Claim208951142025-04-18 13:20:573 days ago1744982457IN
0x485EaBED...D83a784bD
0 S0.0048851655.01
Claim208950972025-04-18 13:20:473 days ago1744982447IN
0x485EaBED...D83a784bD
0 S0.0048479255.01
Claim208950702025-04-18 13:20:313 days ago1744982431IN
0x485EaBED...D83a784bD
0 S0.0058728655.01
Claim208934042025-04-18 13:03:343 days ago1744981414IN
0x485EaBED...D83a784bD
0 S0.0042282550.0001
Claim208923372025-04-18 12:52:323 days ago1744980752IN
0x485EaBED...D83a784bD
0 S0.0058711455
Claim208910072025-04-18 12:39:593 days ago1744979999IN
0x485EaBED...D83a784bD
0 S0.004884555.01
Claim208909742025-04-18 12:39:433 days ago1744979983IN
0x485EaBED...D83a784bD
0 S0.0058356255.01
Claim208849132025-04-18 11:47:123 days ago1744976832IN
0x485EaBED...D83a784bD
0 S0.004884555.01
Claim208820422025-04-18 11:21:123 days ago1744975272IN
0x485EaBED...D83a784bD
0 S0.0048858755.01
Claim208811592025-04-18 11:12:563 days ago1744974776IN
0x485EaBED...D83a784bD
0 S0.005872255.01
Claim208667252025-04-18 8:55:583 days ago1744966558IN
0x485EaBED...D83a784bD
0 S0.0018964358.44
Claim208663082025-04-18 8:53:043 days ago1744966384IN
0x485EaBED...D83a784bD
0 S0.0048851655.01
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RewardsDistributor

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 1000 runs

Other Settings:
cancun EvmVersion
File 1 of 7 : RewardsDistributor.sol
//SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import { Ownable } from "solady/auth/Ownable.sol";
import { MerkleProofLib } from "solady/utils/MerkleProofLib.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
import { IRewardsDistributor } from "./interfaces/IRewardsDistributor.sol";
import { AOperator } from "./abstracts/AOperator.sol";
import { Errors } from "./libraries/Errors.sol";

/**
 * @title AMerkleDistributor
 * @author 0xMemoryGrinder
 * @dev Abstract contract which implements the merkle root update
 */
contract RewardsDistributor is IRewardsDistributor, Ownable, AOperator {
    /*//////////////////////////////////////////////////////////////
                            STATE VARIABLES
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 epoch => bytes32 merkleRoot) public epochsMerkleRoots;

    mapping(uint256 epoch => mapping(address account => bool claimed)) public claimedStatus;

    uint256 public constant CLAIM_AVAILABILITY_DELAY = 365 days;

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event MerkleRootUpdated(uint256 epoch, bytes32 merkleRoot);
    event Claimed(uint256 epoch, address token, address account, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address initialOperator, address initialOwner) AOperator(initialOperator, initialOwner) { }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Get the merkle root for a given epoch
     * @param epoch Epoch id
     * @return bytes32 : merkle root
     */
    function getEpochMerkleRoot(uint256 epoch) external view returns (bytes32) {
        return epochsMerkleRoots[epoch];
    }

    /**
     * @notice Checks if the rewards were claimed for an user on a given period
     * @dev Checks if the rewards were claimed for an user (based on the index) on a given period
     * @param epoch Epoch id
     * @param account Account performing the claim
     * @return bool : true if already claimed
     */
    function isClaimed(uint256 epoch, address account) public view returns (bool) {
        return claimedStatus[epoch][account];
    }

    /*//////////////////////////////////////////////////////////////
                        EXTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function claim(
        uint256 epoch,
        address token,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external override {
        _claim(epoch, token, account, amount, merkleProof, true);
    }

    function multiClaim(address account, IRewardsDistributor.ClaimParams[] calldata params) external override {
        (address[] memory tokens, uint256[] memory amounts) = _verifyMultiClaim(account, params);
        
        for (uint256 i; i < tokens.length; ++i) {
            SafeTransferLib.safeTransfer(tokens[i], account, amounts[i]);
        }
    }

    /*//////////////////////////////////////////////////////////////
                        OPERATOR FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Update the merkle root
     * @param epoch Epoch id
     * @param newMerkleRoot New merkle root after a harvest, set by the operator
     */
    function addEpochRewards(uint256 epoch, bytes32 newMerkleRoot) external onlyOperatorOrOwner {
        if (epoch == 0) revert Errors.ZeroValue();
        if (newMerkleRoot == bytes32(0)) revert Errors.ZeroValue();
        if (epochsMerkleRoots[epoch] != bytes32(0)) revert Errors.EpochAlreadyDistributed();

        epochsMerkleRoots[epoch] = newMerkleRoot;
        emit MerkleRootUpdated(epoch, newMerkleRoot);
    }

    /*//////////////////////////////////////////////////////////////
                            OWNER FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function retrieveUnclaimedReward(
        uint256 epoch,
        address token,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external onlyOwner {
        if (epoch + CLAIM_AVAILABILITY_DELAY > block.timestamp) revert Errors.ClaimStillAvailable();

        _claim(epoch, token, account, amount, merkleProof, false);

        // Transfer the reward to the owner
        SafeTransferLib.safeTransfer(token, owner(), amount);
    }

    function retrieveUnclaimedRewards(
        address account, IRewardsDistributor.ClaimParams[] calldata params
    ) external onlyOwner {
        for (uint256 i; i < params.length; ++i) {
            if (params[i].epoch + CLAIM_AVAILABILITY_DELAY > block.timestamp) revert Errors.ClaimStillAvailable();
        }

       (address[] memory tokens, uint256[] memory amounts) = _verifyMultiClaim(account, params);
        
        for (uint256 i; i < tokens.length; ++i) {
            SafeTransferLib.safeTransfer(tokens[i], owner(), amounts[i]);
        } 
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Sets the rewards as claimed for the index on the given period
     * @param epoch Epoch id
     * @param account Account performing the claim
     */
    function _setClaimed(uint256 epoch, address account) private {
        claimedStatus[epoch][account] = true;
    }

    /**
     * @dev Claim a token reward for a given account
     * @param epoch Epoch id
     * @param token Token address to claim
     * @param account Account to claim rewards for
     * @param amount Rewards amount to claim
     * @param merkleProof Merkle proof to validate the claim
     */
    function _claim(
        uint256 epoch,
        address token,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof,
        bool transfer
    ) internal {
        if (token == address(0) || account == address(0)) revert Errors.ZeroAddress();
        if (amount == 0) revert Errors.ZeroValue();
        if (epochsMerkleRoots[epoch] == bytes32(0)) revert Errors.EpochNotInitialized();
        if (isClaimed(epoch, account)) revert Errors.AlreadyClaimed();

        // Verify the claim info with the merkle proof
        if (!_verifyProof(epoch, token, account, amount, merkleProof)) {
            revert Errors.InvalidMerkleProof();
        }
        _setClaimed(epoch, account);
        if (transfer) {
            // Transfer the reward to the account
            SafeTransferLib.safeTransfer(token, account, amount);
        }
        emit Claimed(epoch, token, account, amount);
    }

    /**
     * @dev Verify the claim info with the merkle proof
     */
    function _verifyProof(
        uint256 epoch,
        address token,
        address account,
        uint256 amount,
        bytes32[] calldata proof
    ) private view returns (bool) {
        // Create merkle leaf
        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(token, account, amount))));

        // Check if the computed hash (root) is equal to the provided root
        return MerkleProofLib.verifyCalldata(proof, epochsMerkleRoots[epoch], leaf);
    }

    /**
     * @dev Claim multiple token rewards for given accounts
     * @param account Account to claim rewards for
     * @param params ClaimParams array containing the indexesn tokens, amounts and merkle proofs
     */
    function _verifyMultiClaim(address account, IRewardsDistributor.ClaimParams[] calldata params) internal returns (address[] memory, uint256[] memory) {
        uint256 length = params.length;
        address[] memory tokens = new address[](length);
        uint256[] memory claimedAmounts = new uint256[](length);
        uint256 uniqueTokens = 0;

        if (length == 0) revert Errors.EmptyArray();

        // Fill the tokens array with unique tokens
        for (uint256 i; i < length; ++i) {
            bool found = false;
            for (uint256 j; j < i; ++j) {
                if (params[i].token == tokens[j]) {
                    found = true;
                    break;
                }
            }
            if (found) continue;

            tokens[i] = params[i].token;
            ++uniqueTokens;
        }

        for (uint256 i; i < length; ++i) {
            _claim(
                params[i].epoch,
                params[i].token,
                account,
                params[i].amount,
                params[i].merkleProof,
                false
            );

            // Find the index of the token in the tokens array and add the claimed amount
            for (uint256 j; j < uniqueTokens; ++j) {
                if (params[i].token == tokens[j]) {
                    claimedAmounts[j] += params[i].amount;
                    break;
                }
            }
        }

        // Resizing the arrays to the unique tokens
        assembly {
            mstore(tokens, uniqueTokens)
            mstore(claimedAmounts, uniqueTokens)
        }

        return (tokens, claimedAmounts);
    }
}

File 2 of 7 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 3 of 7 : MerkleProofLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            MERKLE PROOF VERIFICATION OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(proof) {
                // Initialize `offset` to the offset of `proof` elements in memory.
                let offset := add(proof, 0x20)
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(offset, shl(5, mload(proof)))
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, mload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), mload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if proof.length {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(proof.offset, shl(5, proof.length))
                // Initialize `offset` to the offset of `proof` in the calldata.
                let offset := proof.offset
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, calldataload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), calldataload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - The sum of the lengths of `proof` and `leaves` must never overflow.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The memory offset of `proof` must be non-zero
    ///   (i.e. `proof` is not pointing to the scratch space).
    function verifyMultiProof(
        bytes32[] memory proof,
        bytes32 root,
        bytes32[] memory leaves,
        bool[] memory flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // Cache the lengths of the arrays.
            let leavesLength := mload(leaves)
            let proofLength := mload(proof)
            let flagsLength := mload(flags)

            // Advance the pointers of the arrays to point to the data.
            leaves := add(0x20, leaves)
            proof := add(0x20, proof)
            flags := add(0x20, flags)

            // If the number of flags is correct.
            for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flagsLength) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof, shl(5, proofLength))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                leavesLength := shl(5, leavesLength)
                for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                    mstore(add(hashesFront, i), mload(add(leaves, i)))
                }
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, leavesLength)
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flagsLength := add(hashesBack, shl(5, flagsLength))

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(mload(flags)) {
                        // Loads the next proof.
                        b := mload(proof)
                        proof := add(proof, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag.
                    flags := add(flags, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flagsLength)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof)
                    )
                break
            }
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The calldata offset of `proof` must be non-zero
    ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
    function verifyMultiProofCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32[] calldata leaves,
        bool[] calldata flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // If the number of flags is correct.
            for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flags.length) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    // forgefmt: disable-next-item
                    isValid := eq(
                        calldataload(
                            xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                        ),
                        root
                    )
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof.offset, shl(5, proof.length))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, shl(5, leaves.length))
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flags.length := add(hashesBack, shl(5, flags.length))

                // We don't need to make a copy of `proof.offset` or `flags.offset`,
                // as they are pass-by-value (this trick may not always save gas).

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(calldataload(flags.offset)) {
                        // Loads the next proof.
                        b := calldataload(proof.offset)
                        proof.offset := add(proof.offset, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag offset.
                    flags.offset := add(flags.offset, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flags.length)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof.offset)
                    )
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes32 array.
    function emptyProof() internal pure returns (bytes32[] calldata proof) {
        /// @solidity memory-safe-assembly
        assembly {
            proof.length := 0
        }
    }

    /// @dev Returns an empty calldata bytes32 array.
    function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
        /// @solidity memory-safe-assembly
        assembly {
            leaves.length := 0
        }
    }

    /// @dev Returns an empty calldata bool array.
    function emptyFlags() internal pure returns (bool[] calldata flags) {
        /// @solidity memory-safe-assembly
        assembly {
            flags.length := 0
        }
    }
}

File 4 of 7 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success :=
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `1` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

File 5 of 7 : IRewardsDistributor.sol
//SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/**
 * @title IRewardsDistributor
 * @author 0xMemoryGrinder
 * @dev MerkleDistributor interface which updates the merkle root when the claims are made
 */
interface IRewardsDistributor {
    struct ClaimParams {
        address token;
        uint256 epoch;
        uint256 amount;
        bytes32[] merkleProof;
    }

    struct ReclaimParams {
        address account;
        ClaimParams[] params;
    }

    /**
     * @dev Claim a token reward for a given account
     * @param epoch Epoch to claim rewards for
     * @param token Token address to claim
     * @param account Account to claim rewards for
     * @param amount Rewards amount to claim
     * @param merkleProof Merkle proof to validate the claim
     */
    function claim(
        uint256 epoch,
        address token,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external;

    /**
     * @dev Claim multiple token rewards for given accounts
     * @param account Account to claim rewards for
     * @param params ClaimParams array containing the tokens, amounts and merkle proofs
     */
    function multiClaim(address account, ClaimParams[] calldata params) external;

    /**
     * @dev Update the merkle root
     * @param epoch Epoch id
     * @param newMerkleRoot New merkle root after a harvest, set by the operator
     */
    function addEpochRewards(uint256 epoch, bytes32 newMerkleRoot) external;
}

File 6 of 7 : AOperator.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import { Ownable } from "solady/auth/Ownable.sol";
import { Errors } from "../libraries/Errors.sol";

/// @author 0xtekgrinder
/// @title AOperator
/// @notice Abstract contract to allow access only to operator or owner
abstract contract AOperator is Ownable {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Event emitted when the operator is updated
     */
    event OperatorUpdated(address oldOperator, address newOperator);

    /*//////////////////////////////////////////////////////////////
                            MUTABLE VARIABLES
    //////////////////////////////////////////////////////////////*/

    /**
     *  @notice operator caller address to allow access only to puppeteer/adapter
     */
    address public operator;

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    modifier onlyOperatorOrOwner() {
        if (msg.sender != operator && msg.sender != owner()) revert Errors.NotOperatorOrOwner();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address initialOperator, address initialOwner) {
        if (initialOperator == address(0)) revert Errors.ZeroAddress();
        if (initialOwner == address(0)) revert Errors.ZeroAddress();

        _initializeOwner(initialOwner);
        operator = initialOperator;
    }

    /*//////////////////////////////////////////////////////////////
                               CONTRACT LOGIC
    //////////////////////////////////////////////////////////////*/

    function setOperator(address newOperator) external onlyOwner {
        if (newOperator == address(0)) revert Errors.ZeroAddress();

        address oldOperator = operator;
        operator = newOperator;

        emit OperatorUpdated(oldOperator, newOperator);
    }
}

File 7 of 7 : Errors.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library Errors {
    // General errors
    error ZeroValue();
    error ZeroAddress();
    error EmptyArray();
    error DifferentSizeArrays(uint256 length1, uint256 length2);

    // Ownable errors
    error NotOperatorOrOwner();

    // MerkleDistributor errors
    error EpochAlreadyDistributed();
    error EpochNotInitialized();
    error AlreadyClaimed();
    error InvalidMerkleProof();
    error ClaimStillAvailable();

    // Swapper errors
    error NotPuppeteer();
    error WithdrawMoreThanMax();
    error SwapError();

    // Fees errors
    error InvalidFee();
}

Settings
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "solmate/=lib/solmate/src/",
    "solady/=lib/solady/src/",
    "src/=src/",
    "test/=test/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"initialOperator","type":"address"},{"internalType":"address","name":"initialOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ClaimStillAvailable","type":"error"},{"inputs":[],"name":"EmptyArray","type":"error"},{"inputs":[],"name":"EpochAlreadyDistributed","type":"error"},{"inputs":[],"name":"EpochNotInitialized","type":"error"},{"inputs":[],"name":"InvalidMerkleProof","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotOperatorOrOwner","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOperator","type":"address"},{"indexed":false,"internalType":"address","name":"newOperator","type":"address"}],"name":"OperatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"CLAIM_AVAILABILITY_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"addEpochRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"claimedStatus","outputs":[{"internalType":"bool","name":"claimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"epochsMerkleRoots","outputs":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"isClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct IRewardsDistributor.ClaimParams[]","name":"params","type":"tuple[]"}],"name":"multiClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"retrieveUnclaimedReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct IRewardsDistributor.ClaimParams[]","name":"params","type":"tuple[]"}],"name":"retrieveUnclaimedRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"}]

608060405234801562000010575f80fd5b50604051620012ba380380620012ba833981016040819052620000339162000110565b81816001600160a01b0382166200005d5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116620000855760405163d92e233d60e01b815260040160405180910390fd5b6200009081620000b9565b505f80546001600160a01b0319166001600160a01b039290921691909117905550620001469050565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b80516001600160a01b03811681146200010b575f80fd5b919050565b5f806040838503121562000122575f80fd5b6200012d83620000f4565b91506200013d60208401620000f4565b90509250929050565b61116680620001545f395ff3fe60806040526004361061013d575f3560e01c80638da5cb5b116100bb578063eaa755d211610071578063f0f7df9c11610057578063f0f7df9c1461033f578063f2fde38b1461035e578063fee81cf414610371575f80fd5b8063eaa755d214610315578063f04e283e1461032c575f80fd5b8063cfd3a61a116100a1578063cfd3a61a146102b8578063d2ef0795146102d7578063e8da302c146102f6575f80fd5b80638da5cb5b14610281578063b3ab15fb14610299575f80fd5b806354d1f13d1161011057806363f9b02a116100f657806363f9b02a1461022f57806368dca5601461024e578063715018a614610279575f80fd5b806354d1f13d146101f1578063570ca735146101f9575f80fd5b8063222d95121461014157806325692962146101625780632dfc7abe1461016a5780633c8b76ae146101b8575b5f80fd5b34801561014c575f80fd5b5061016061015b366004610ee8565b6103a2565b005b6101606104dd565b348015610175575f80fd5b506101a3610184366004610f23565b600260209081525f928352604080842090915290825290205460ff1681565b60405190151581526020015b60405180910390f35b3480156101c3575f80fd5b506101e36101d2366004610f4d565b5f9081526001602052604090205490565b6040519081526020016101af565b61016061052a565b348015610204575f80fd5b505f54610217906001600160a01b031681565b6040516001600160a01b0390911681526020016101af565b34801561023a575f80fd5b50610160610249366004610fac565b610563565b348015610259575f80fd5b506101e3610268366004610f4d565b60016020525f908152604090205481565b61016061064a565b34801561028c575f80fd5b50638b78c6d81954610217565b3480156102a4575f80fd5b506101606102b3366004610ffb565b61065d565b3480156102c3575f80fd5b506101606102d236600461101b565b6106fd565b3480156102e2575f80fd5b506101a36102f1366004610f23565b610759565b348015610301575f80fd5b50610160610310366004610fac565b610785565b348015610320575f80fd5b506101e36301e1338081565b61016061033a366004610ffb565b6107d9565b34801561034a575f80fd5b5061016061035936600461101b565b610816565b61016061036c366004610ffb565b610826565b34801561037c575f80fd5b506101e361038b366004610ffb565b63389a75e1600c9081525f91909152602090205490565b5f546001600160a01b031633148015906103d45750638b78c6d819546001600160a01b0316336001600160a01b031614155b1561040b576040517ff20fc9eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f0361042b57604051637c946ed760e01b815260040160405180910390fd5b8061044957604051637c946ed760e01b815260040160405180910390fd5b5f828152600160205260409020541561048e576040517fcb92d06700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8281526001602090815260409182902083905581518481529081018390527f9ff79792a92b66fee090acb66e07837a2edb31ee6b9780fa551fd04b79dffcd291015b60405180910390a15050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b61056b61084c565b5f5b818110156105d457426301e1338084848481811061058d5761058d61108e565b905060200281019061059f91906110a2565b602001356105ad91906110d4565b11156105cc576040516378d58a6560e01b815260040160405180910390fd5b60010161056d565b505f806105e2858585610866565b915091505f5b82518110156106425761063a8382815181106106065761060661108e565b602002602001015161061b638b78c6d8195490565b84848151811061062d5761062d61108e565b6020026020010151610be7565b6001016105e8565b505050505050565b61065261084c565b61065b5f610c30565b565b61066561084c565b6001600160a01b03811661068c5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527ffbe5b6cbafb274f445d7fed869dc77a838d8243a22c460de156560e8857cad0391016104d1565b61070561084c565b426107146301e13380886110d4565b1115610733576040516378d58a6560e01b815260040160405180910390fd5b6107428686868686865f610c6d565b61064285610753638b78c6d8195490565b85610be7565b5f8281526002602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b5f80610792858585610866565b915091505f5b8251811015610642576107d18382815181106107b6576107b661108e565b60200260200101518784848151811061062d5761062d61108e565b600101610798565b6107e161084c565b63389a75e1600c52805f526020600c20805442111561080757636f5e88185f526004601cfd5b5f905561081381610c30565b50565b6106428686868686866001610c6d565b61082e61084c565b8060601b61084357637448fbae5f526004601cfd5b61081381610c30565b638b78c6d81954331461065b576382b429005f526004601cfd5b606080825f8167ffffffffffffffff811115610884576108846110e7565b6040519080825280602002602001820160405280156108ad578160200160208202803683370190505b5090505f8267ffffffffffffffff8111156108ca576108ca6110e7565b6040519080825280602002602001820160405280156108f3578160200160208202803683370190505b5090505f835f03610930576040517f521299a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b84811015610a37575f805b828110156109b9578581815181106109575761095761108e565b60200260200101516001600160a01b03168b8b8581811061097a5761097a61108e565b905060200281019061098c91906110a2565b61099a906020810190610ffb565b6001600160a01b0316036109b157600191506109b9565b60010161093d565b5080156109c65750610a2f565b8989838181106109d8576109d861108e565b90506020028101906109ea91906110a2565b6109f8906020810190610ffb565b858381518110610a0a57610a0a61108e565b6001600160a01b0390921660209283029190910190910152610a2b836110fb565b9250505b600101610932565b505f5b84811015610bd457610b00898983818110610a5757610a5761108e565b9050602002810190610a6991906110a2565b602001358a8a84818110610a7f57610a7f61108e565b9050602002810190610a9191906110a2565b610a9f906020810190610ffb565b8c8c8c86818110610ab257610ab261108e565b9050602002810190610ac491906110a2565b604001358d8d87818110610ada57610ada61108e565b9050602002810190610aec91906110a2565b610afa906060810190611113565b5f610c6d565b5f5b82811015610bcb57848181518110610b1c57610b1c61108e565b60200260200101516001600160a01b03168a8a84818110610b3f57610b3f61108e565b9050602002810190610b5191906110a2565b610b5f906020810190610ffb565b6001600160a01b031603610bc357898983818110610b7f57610b7f61108e565b9050602002810190610b9191906110a2565b60400135848281518110610ba757610ba761108e565b60200260200101818151610bbb91906110d4565b905250610bcb565b600101610b02565b50600101610a3a565b5080835281529097909650945050505050565b81601452806034526fa9059cbb0000000000000000000000005f5260205f604460105f875af13d1560015f51141716610c27576390b8ec185f526004601cfd5b5f603452505050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b6001600160a01b0386161580610c8a57506001600160a01b038516155b15610ca85760405163d92e233d60e01b815260040160405180910390fd5b835f03610cc857604051637c946ed760e01b815260040160405180910390fd5b5f87815260016020526040902054610d0c576040517f9ce8b6a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d168786610759565b15610d4d576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d5b878787878787610e27565b610d91576040517fb05e92fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8781526002602090815260408083206001600160a01b03891684529091529020805460ff191660011790558015610dce57610dce868686610be7565b604080518881526001600160a01b0388811660208301528716818301526060810186905290517fce3bcb6e219596cf26007ffdfaae8953bc3f76e3f36c0a79b23e28020da3222e9181900360800190a150505050505050565b604080516001600160a01b038088166020830152861691810191909152606081018490525f90819060800160408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052805190602001209050610ea4848460015f8c81526020019081526020015f205484610eb0565b98975050505050505050565b5f8315610ee0578360051b8501855b803580851160051b94855260209485185260405f209301818110610ebf5750505b501492915050565b5f8060408385031215610ef9575f80fd5b50508035926020909101359150565b80356001600160a01b0381168114610f1e575f80fd5b919050565b5f8060408385031215610f34575f80fd5b82359150610f4460208401610f08565b90509250929050565b5f60208284031215610f5d575f80fd5b5035919050565b5f8083601f840112610f74575f80fd5b50813567ffffffffffffffff811115610f8b575f80fd5b6020830191508360208260051b8501011115610fa5575f80fd5b9250929050565b5f805f60408486031215610fbe575f80fd5b610fc784610f08565b9250602084013567ffffffffffffffff811115610fe2575f80fd5b610fee86828701610f64565b9497909650939450505050565b5f6020828403121561100b575f80fd5b61101482610f08565b9392505050565b5f805f805f8060a08789031215611030575f80fd5b8635955061104060208801610f08565b945061104e60408801610f08565b935060608701359250608087013567ffffffffffffffff811115611070575f80fd5b61107c89828a01610f64565b979a9699509497509295939492505050565b634e487b7160e01b5f52603260045260245ffd5b5f8235607e198336030181126110b6575f80fd5b9190910192915050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561077f5761077f6110c0565b634e487b7160e01b5f52604160045260245ffd5b5f6001820161110c5761110c6110c0565b5060010190565b5f808335601e19843603018112611128575f80fd5b83018035915067ffffffffffffffff821115611142575f80fd5b6020019150600581901b3603821315610fa5575f80fdfea164736f6c6343000818000a000000000000000000000000c04fb43668c8c4cfb6e18dccd0085ed98b1d4008000000000000000000000000b1cf5c852b908a85624878452a3f3fdb6ce94f05

Deployed Bytecode

0x60806040526004361061013d575f3560e01c80638da5cb5b116100bb578063eaa755d211610071578063f0f7df9c11610057578063f0f7df9c1461033f578063f2fde38b1461035e578063fee81cf414610371575f80fd5b8063eaa755d214610315578063f04e283e1461032c575f80fd5b8063cfd3a61a116100a1578063cfd3a61a146102b8578063d2ef0795146102d7578063e8da302c146102f6575f80fd5b80638da5cb5b14610281578063b3ab15fb14610299575f80fd5b806354d1f13d1161011057806363f9b02a116100f657806363f9b02a1461022f57806368dca5601461024e578063715018a614610279575f80fd5b806354d1f13d146101f1578063570ca735146101f9575f80fd5b8063222d95121461014157806325692962146101625780632dfc7abe1461016a5780633c8b76ae146101b8575b5f80fd5b34801561014c575f80fd5b5061016061015b366004610ee8565b6103a2565b005b6101606104dd565b348015610175575f80fd5b506101a3610184366004610f23565b600260209081525f928352604080842090915290825290205460ff1681565b60405190151581526020015b60405180910390f35b3480156101c3575f80fd5b506101e36101d2366004610f4d565b5f9081526001602052604090205490565b6040519081526020016101af565b61016061052a565b348015610204575f80fd5b505f54610217906001600160a01b031681565b6040516001600160a01b0390911681526020016101af565b34801561023a575f80fd5b50610160610249366004610fac565b610563565b348015610259575f80fd5b506101e3610268366004610f4d565b60016020525f908152604090205481565b61016061064a565b34801561028c575f80fd5b50638b78c6d81954610217565b3480156102a4575f80fd5b506101606102b3366004610ffb565b61065d565b3480156102c3575f80fd5b506101606102d236600461101b565b6106fd565b3480156102e2575f80fd5b506101a36102f1366004610f23565b610759565b348015610301575f80fd5b50610160610310366004610fac565b610785565b348015610320575f80fd5b506101e36301e1338081565b61016061033a366004610ffb565b6107d9565b34801561034a575f80fd5b5061016061035936600461101b565b610816565b61016061036c366004610ffb565b610826565b34801561037c575f80fd5b506101e361038b366004610ffb565b63389a75e1600c9081525f91909152602090205490565b5f546001600160a01b031633148015906103d45750638b78c6d819546001600160a01b0316336001600160a01b031614155b1561040b576040517ff20fc9eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f0361042b57604051637c946ed760e01b815260040160405180910390fd5b8061044957604051637c946ed760e01b815260040160405180910390fd5b5f828152600160205260409020541561048e576040517fcb92d06700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8281526001602090815260409182902083905581518481529081018390527f9ff79792a92b66fee090acb66e07837a2edb31ee6b9780fa551fd04b79dffcd291015b60405180910390a15050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b61056b61084c565b5f5b818110156105d457426301e1338084848481811061058d5761058d61108e565b905060200281019061059f91906110a2565b602001356105ad91906110d4565b11156105cc576040516378d58a6560e01b815260040160405180910390fd5b60010161056d565b505f806105e2858585610866565b915091505f5b82518110156106425761063a8382815181106106065761060661108e565b602002602001015161061b638b78c6d8195490565b84848151811061062d5761062d61108e565b6020026020010151610be7565b6001016105e8565b505050505050565b61065261084c565b61065b5f610c30565b565b61066561084c565b6001600160a01b03811661068c5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527ffbe5b6cbafb274f445d7fed869dc77a838d8243a22c460de156560e8857cad0391016104d1565b61070561084c565b426107146301e13380886110d4565b1115610733576040516378d58a6560e01b815260040160405180910390fd5b6107428686868686865f610c6d565b61064285610753638b78c6d8195490565b85610be7565b5f8281526002602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b5f80610792858585610866565b915091505f5b8251811015610642576107d18382815181106107b6576107b661108e565b60200260200101518784848151811061062d5761062d61108e565b600101610798565b6107e161084c565b63389a75e1600c52805f526020600c20805442111561080757636f5e88185f526004601cfd5b5f905561081381610c30565b50565b6106428686868686866001610c6d565b61082e61084c565b8060601b61084357637448fbae5f526004601cfd5b61081381610c30565b638b78c6d81954331461065b576382b429005f526004601cfd5b606080825f8167ffffffffffffffff811115610884576108846110e7565b6040519080825280602002602001820160405280156108ad578160200160208202803683370190505b5090505f8267ffffffffffffffff8111156108ca576108ca6110e7565b6040519080825280602002602001820160405280156108f3578160200160208202803683370190505b5090505f835f03610930576040517f521299a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b84811015610a37575f805b828110156109b9578581815181106109575761095761108e565b60200260200101516001600160a01b03168b8b8581811061097a5761097a61108e565b905060200281019061098c91906110a2565b61099a906020810190610ffb565b6001600160a01b0316036109b157600191506109b9565b60010161093d565b5080156109c65750610a2f565b8989838181106109d8576109d861108e565b90506020028101906109ea91906110a2565b6109f8906020810190610ffb565b858381518110610a0a57610a0a61108e565b6001600160a01b0390921660209283029190910190910152610a2b836110fb565b9250505b600101610932565b505f5b84811015610bd457610b00898983818110610a5757610a5761108e565b9050602002810190610a6991906110a2565b602001358a8a84818110610a7f57610a7f61108e565b9050602002810190610a9191906110a2565b610a9f906020810190610ffb565b8c8c8c86818110610ab257610ab261108e565b9050602002810190610ac491906110a2565b604001358d8d87818110610ada57610ada61108e565b9050602002810190610aec91906110a2565b610afa906060810190611113565b5f610c6d565b5f5b82811015610bcb57848181518110610b1c57610b1c61108e565b60200260200101516001600160a01b03168a8a84818110610b3f57610b3f61108e565b9050602002810190610b5191906110a2565b610b5f906020810190610ffb565b6001600160a01b031603610bc357898983818110610b7f57610b7f61108e565b9050602002810190610b9191906110a2565b60400135848281518110610ba757610ba761108e565b60200260200101818151610bbb91906110d4565b905250610bcb565b600101610b02565b50600101610a3a565b5080835281529097909650945050505050565b81601452806034526fa9059cbb0000000000000000000000005f5260205f604460105f875af13d1560015f51141716610c27576390b8ec185f526004601cfd5b5f603452505050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b6001600160a01b0386161580610c8a57506001600160a01b038516155b15610ca85760405163d92e233d60e01b815260040160405180910390fd5b835f03610cc857604051637c946ed760e01b815260040160405180910390fd5b5f87815260016020526040902054610d0c576040517f9ce8b6a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d168786610759565b15610d4d576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d5b878787878787610e27565b610d91576040517fb05e92fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8781526002602090815260408083206001600160a01b03891684529091529020805460ff191660011790558015610dce57610dce868686610be7565b604080518881526001600160a01b0388811660208301528716818301526060810186905290517fce3bcb6e219596cf26007ffdfaae8953bc3f76e3f36c0a79b23e28020da3222e9181900360800190a150505050505050565b604080516001600160a01b038088166020830152861691810191909152606081018490525f90819060800160408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052805190602001209050610ea4848460015f8c81526020019081526020015f205484610eb0565b98975050505050505050565b5f8315610ee0578360051b8501855b803580851160051b94855260209485185260405f209301818110610ebf5750505b501492915050565b5f8060408385031215610ef9575f80fd5b50508035926020909101359150565b80356001600160a01b0381168114610f1e575f80fd5b919050565b5f8060408385031215610f34575f80fd5b82359150610f4460208401610f08565b90509250929050565b5f60208284031215610f5d575f80fd5b5035919050565b5f8083601f840112610f74575f80fd5b50813567ffffffffffffffff811115610f8b575f80fd5b6020830191508360208260051b8501011115610fa5575f80fd5b9250929050565b5f805f60408486031215610fbe575f80fd5b610fc784610f08565b9250602084013567ffffffffffffffff811115610fe2575f80fd5b610fee86828701610f64565b9497909650939450505050565b5f6020828403121561100b575f80fd5b61101482610f08565b9392505050565b5f805f805f8060a08789031215611030575f80fd5b8635955061104060208801610f08565b945061104e60408801610f08565b935060608701359250608087013567ffffffffffffffff811115611070575f80fd5b61107c89828a01610f64565b979a9699509497509295939492505050565b634e487b7160e01b5f52603260045260245ffd5b5f8235607e198336030181126110b6575f80fd5b9190910192915050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561077f5761077f6110c0565b634e487b7160e01b5f52604160045260245ffd5b5f6001820161110c5761110c6110c0565b5060010190565b5f808335601e19843603018112611128575f80fd5b83018035915067ffffffffffffffff821115611142575f80fd5b6020019150600581901b3603821315610fa5575f80fdfea164736f6c6343000818000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000c04fb43668c8c4cfb6e18dccd0085ed98b1d4008000000000000000000000000b1cf5c852b908a85624878452a3f3fdb6ce94f05

-----Decoded View---------------
Arg [0] : initialOperator (address): 0xC04FB43668C8C4cFb6e18dCCd0085ED98B1d4008
Arg [1] : initialOwner (address): 0xb1Cf5c852b908A85624878452A3F3fDb6cE94f05

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c04fb43668c8c4cfb6e18dccd0085ed98b1d4008
Arg [1] : 000000000000000000000000b1cf5c852b908a85624878452a3f3fdb6ce94f05


Block Transaction Gas Used Reward
view all blocks ##produced##

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.