ETH Price: $2,928.59 (-1.02%)
 

More Info

Private Name Tags

Multichain Info

Transaction Hash
Block
From
To
Route Single1468600072026-01-25 5:26:313 hrs ago1769318791IN
Enso: Router V2
0 ETH0.000000059620.00010168
Route Single1468362582026-01-24 16:14:5316 hrs ago1769271293IN
Enso: Router V2
0 ETH0.0000001775930.00009441
Route Single1468269972026-01-24 11:06:1121 hrs ago1769252771IN
Enso: Router V2
0 ETH0.0000000765690.00010537
Route Single1468251702026-01-24 10:05:1722 hrs ago1769249117IN
Enso: Router V2
0 ETH0.0000000623390.00010626
Route Single1468217842026-01-24 8:12:2524 hrs ago1769242345IN
Enso: Router V2
0 ETH0.0000000385640.00002861
Route Single1467914512026-01-23 15:21:1941 hrs ago1769181679IN
Enso: Router V2
0 ETH0.0000001278250.0001936
Route Single1467679842026-01-23 2:19:052 days ago1769134745IN
Enso: Router V2
0 ETH0.0000000292130.00004122
Route Single1467546812026-01-22 18:55:392 days ago1769108139IN
Enso: Router V2
0 ETH0.0000000871460.00015476
Route Single1467523292026-01-22 17:37:152 days ago1769103435IN
Enso: Router V2
0 ETH0.0000000426220.00003954
Route Single1467477052026-01-22 15:03:072 days ago1769094187IN
Enso: Router V2
0 ETH0.0000000851810.00010724
Route Single1467473922026-01-22 14:52:412 days ago1769093561IN
Enso: Router V2
0 ETH0.0000000881230.00010604
Route Single1467279792026-01-22 4:05:353 days ago1769054735IN
Enso: Router V2
0.000817235757136 ETH0.0000000916940.0001029
Route Single1467268492026-01-22 3:27:553 days ago1769052475IN
Enso: Router V2
0 ETH0.0000000170090.00002282
Route Single1467090122026-01-21 17:33:213 days ago1769016801IN
Enso: Router V2
0 ETH0.0000007470130.00121348
Route Single1467072382026-01-21 16:34:133 days ago1769013253IN
Enso: Router V2
0 ETH0.0000009309050.00120668
Route Single1467033472026-01-21 14:24:313 days ago1769005471IN
Enso: Router V2
0 ETH0.0000013953610.00120391
Route Single1466859252026-01-21 4:43:474 days ago1768970627IN
Enso: Router V2
0 ETH0.000000059270.0000707
Route Single1466735882026-01-20 21:52:334 days ago1768945953IN
Enso: Router V2
0 ETH0.0000000077880.00000134
Route Single1466664412026-01-20 17:54:194 days ago1768931659IN
Enso: Router V2
0 ETH0.0000000721130.000103
Route Multi1466596582026-01-20 14:08:134 days ago1768918093IN
Enso: Router V2
0.000032464921746 ETH0.0000001042330.00010197
Route Single1466502402026-01-20 8:54:174 days ago1768899257IN
Enso: Router V2
0 ETH0.0000005291180.00032323
Route Single1466459972026-01-20 6:32:515 days ago1768890771IN
Enso: Router V2
0 ETH0.0000002976890.0020065
Route Single1466433372026-01-20 5:04:115 days ago1768885451IN
Enso: Router V2
0 ETH0.0000005331490.00120433
Route Single1466433242026-01-20 5:03:455 days ago1768885425IN
Enso: Router V2
0 ETH0.0000034344830.00120427
Route Single1466415942026-01-20 4:06:055 days ago1768881965IN
Enso: Router V2
0 ETH0.0000002079080.00200357
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1468213882026-01-24 7:59:1324 hrs ago1769241553
Enso: Router V2
0.2819 ETH
1468213882026-01-24 7:59:1324 hrs ago1769241553
Enso: Router V2
0.2819 ETH
1468021972026-01-23 21:19:3135 hrs ago1769203171
Enso: Router V2
0.000035912936106 ETH
1468021972026-01-23 21:19:3135 hrs ago1769203171
Enso: Router V2
0.000035912936106 ETH
1468019012026-01-23 21:09:3935 hrs ago1769202579
Enso: Router V2
0.000036029194186 ETH
1468019012026-01-23 21:09:3935 hrs ago1769202579
Enso: Router V2
0.000036029194186 ETH
1467974982026-01-23 18:42:5338 hrs ago1769193773
Enso: Router V2
0.000019431738098 ETH
1467974982026-01-23 18:42:5338 hrs ago1769193773
Enso: Router V2
0.000019431738098 ETH
1467879182026-01-23 13:23:3343 hrs ago1769174613
Enso: Router V2
0.029746112597607 ETH
1467879182026-01-23 13:23:3343 hrs ago1769174613
Enso: Router V2
0.029746112597607 ETH
1467707782026-01-23 3:52:132 days ago1769140333
Enso: Router V2
0.000200470413891 ETH
1467707782026-01-23 3:52:132 days ago1769140333
Enso: Router V2
0.000200470413891 ETH
1467438542026-01-22 12:54:452 days ago1769086485
Enso: Router V2
0.001881544522813 ETH
1467438542026-01-22 12:54:452 days ago1769086485
Enso: Router V2
0.001881544522813 ETH
1467279792026-01-22 4:05:353 days ago1769054735
Enso: Router V2
0.000817235757136 ETH
1466901112026-01-21 7:03:194 days ago1768978999
Enso: Router V2
0.00003637757732 ETH
1466901112026-01-21 7:03:194 days ago1768978999
Enso: Router V2
0.00003637757732 ETH
1466884512026-01-21 6:07:594 days ago1768975679
Enso: Router V2
0.000019784537249 ETH
1466884512026-01-21 6:07:594 days ago1768975679
Enso: Router V2
0.000019784537249 ETH
1466622432026-01-20 15:34:234 days ago1768923263
Enso: Router V2
0.022550961867846 ETH
1466622432026-01-20 15:34:234 days ago1768923263
Enso: Router V2
0.022550961867846 ETH
1466618472026-01-20 15:21:114 days ago1768922471
Enso: Router V2
0.020181912026655 ETH
1466618472026-01-20 15:21:114 days ago1768922471
Enso: Router V2
0.020181912026655 ETH
1466617452026-01-20 15:17:474 days ago1768922267
Enso: Router V2
0.032629935495227 ETH
1466617452026-01-20 15:17:474 days ago1768922267
Enso: Router V2
0.032629935495227 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EnsoRouter

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.28;

import { EnsoShortcuts } from "../EnsoShortcuts.sol";
import { SafeERC20, IERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC721 } from "openzeppelin-contracts/token/ERC721/IERC721.sol";
import { IERC1155 } from "openzeppelin-contracts/token/ERC1155/IERC1155.sol";

enum TokenType {
    Native,
    ERC20,
    ERC721,
    ERC1155
}

struct Token {
    TokenType tokenType;
    bytes data;
}

contract EnsoRouter {
    using SafeERC20 for IERC20;

    address public immutable shortcuts;

    error WrongMsgValue(uint256 value, uint256 expectedAmount);
    error AmountTooLow(Token token, uint256 amount, uint256 minAmount);
    error DuplicateNativeAsset();
    error UnsupportedTokenType(TokenType tokenType);

    constructor() {
        shortcuts = address(new EnsoShortcuts(address(this)));
    }

    /// @notice Route a single token via a call to the shortcuts contract
    /// @param tokenIn The encoded data for the token to send
    /// @param data The call data to be sent to the shortcuts contract
    function routeSingle(
        Token calldata tokenIn,
        bytes calldata data
    ) public payable returns (bytes memory response) {
        bool isNativeAsset = _transfer(tokenIn);
        if (!isNativeAsset && msg.value != 0) revert WrongMsgValue(msg.value, 0);
        response = _execute(data);
    }

    /// @notice Route multiple tokens via a call to the shortcuts contract
    /// @param tokensIn The encoded data for the tokens to send
    /// @param data The call data to be sent to the shortcuts contract
    function routeMulti(
        Token[] calldata tokensIn,
        bytes calldata data
    ) public payable returns (bytes memory response) {
        bool isNativeAsset;
        for (uint256 i; i < tokensIn.length; ++i) {
            if (_transfer(tokensIn[i])) {
                if (isNativeAsset) revert DuplicateNativeAsset(); // Native asset can only be included once
                isNativeAsset = true;
            }
        }
        if (!isNativeAsset && msg.value != 0) revert WrongMsgValue(msg.value, 0);
        
        response = _execute(data);
    }

    /// @notice Route a single token via a call to the shortcuts contract and revert if there is insufficient token received
    /// @param tokenIn The encoded data for the token to send
    /// @param tokenOut The encoded data for the token to receive
    /// @param receiver The address of the wallet that will receive the tokens
    /// @param data The call data to be sent to the shortcuts contract
    function safeRouteSingle(
        Token calldata tokenIn,
        Token calldata tokenOut,
        address receiver,
        bytes calldata data
    ) external payable returns (bytes memory response) {
        uint256 balance = _balance(tokenOut, receiver);
        response = routeSingle(tokenIn, data);
        _checkMinAmountOut(tokenOut, receiver, balance);
    }

    /// @notice Route multiple tokens via a call to the shortcuts contract and revert if there is insufficient tokens received
    /// @param tokensIn The encoded data for the tokens to send
    /// @param tokensOut The encoded data for the tokens to receive
    /// @param receiver The address of the wallet that will receive the tokens
    /// @param data The call data to be sent to the shortcuts contract
    function safeRouteMulti(
        Token[] calldata tokensIn,
        Token[] calldata tokensOut,
        address receiver,
        bytes calldata data
    ) external payable returns (bytes memory response) {
        uint256[] memory balances = new uint256[](tokensOut.length);
        for (uint256 i; i < tokensOut.length; ++i) {
            balances[i] = _balance(tokensOut[i], receiver);
        }

        response = routeMulti(tokensIn, data);

        for (uint256 i; i < tokensOut.length; ++i) {
            _checkMinAmountOut(tokensOut[i], receiver, balances[i]);
        }
    }

    /// @notice A function to execute a call on the shortcuts contract
    /// @param data The call data to be sent to the shortcuts contract
    function _execute(
        bytes calldata data
    ) internal returns (bytes memory response) {
        bool success;
        (success, response) = shortcuts.call{value: msg.value}(data);
        if (!success) {
            assembly{
                revert(add(response, 32), mload(response))
            }
        }
    }

    function _transfer(Token calldata token) internal returns (bool isNativeAsset) {
        TokenType tokenType = token.tokenType;

        if (tokenType == TokenType.ERC20) {
            (IERC20 erc20, uint256 amount) = abi.decode(token.data, (IERC20, uint256));
            erc20.safeTransferFrom(msg.sender, shortcuts, amount);
        } else if (tokenType == TokenType.Native) {
            (uint256 amount) = abi.decode(token.data, (uint256));
            if (msg.value != amount) revert WrongMsgValue(msg.value, amount);
            isNativeAsset = true;
        } else if (tokenType == TokenType.ERC721) {
            (IERC721 erc721, uint256 tokenId) = abi.decode(token.data, (IERC721, uint256));
            erc721.safeTransferFrom(msg.sender, shortcuts, tokenId);
        } else if (tokenType == TokenType.ERC1155) {
            (IERC1155 erc1155, uint256 tokenId, uint256 amount) = abi.decode(token.data, (IERC1155, uint256, uint256));
            erc1155.safeTransferFrom(msg.sender, shortcuts, tokenId, amount, "0x");
        } else {
            revert UnsupportedTokenType(tokenType);
        }
    }

    function _balance(Token calldata token, address receiver) internal view returns (uint256 balance) {
        TokenType tokenType = token.tokenType;

        if (tokenType == TokenType.ERC20) {
            (IERC20 erc20, ) = abi.decode(token.data, (IERC20, uint256));
            balance = erc20.balanceOf(receiver);
        } else if (tokenType == TokenType.Native) {
            balance = receiver.balance;
        } else if (tokenType == TokenType.ERC721) {
            (IERC721 erc721, ) = abi.decode(token.data, (IERC721, uint256));
            balance = erc721.balanceOf(receiver);
        } else if (tokenType == TokenType.ERC1155) {
            (IERC1155 erc1155, uint256 tokenId, ) = abi.decode(token.data, (IERC1155, uint256, uint256));
            balance = erc1155.balanceOf(receiver, tokenId);
        } else {
            revert UnsupportedTokenType(tokenType);
        }
    }

    function _checkMinAmountOut(Token calldata token, address receiver, uint256 prevBalance) internal view {
        TokenType tokenType = token.tokenType;

        uint256 balance;
        uint256 minAmountOut;
        if (tokenType == TokenType.ERC20) {
            IERC20 erc20;
            (erc20, minAmountOut) = abi.decode(token.data, (IERC20, uint256));
            balance = erc20.balanceOf(receiver);
        } else if (tokenType == TokenType.Native) {
            (minAmountOut) = abi.decode(token.data, (uint256));
            balance = receiver.balance;
        } else if (tokenType == TokenType.ERC721) {
            IERC721 erc721;
            (erc721, minAmountOut) = abi.decode(token.data, (IERC721, uint256));
            balance = erc721.balanceOf(receiver);
        } else if (tokenType == TokenType.ERC1155) {
            IERC1155 erc1155;
            uint256 tokenId;
            (erc1155, tokenId, minAmountOut) = abi.decode(token.data, (IERC1155, uint256, uint256));
            balance = erc1155.balanceOf(receiver, tokenId);
        } else {
            revert UnsupportedTokenType(tokenType);
        }

        uint256 amountOut = balance - prevBalance;
        if (amountOut < minAmountOut) revert AmountTooLow(token, amountOut, minAmountOut);
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.28;

import { AbstractEnsoShortcuts } from "./AbstractEnsoShortcuts.sol";

contract EnsoShortcuts is AbstractEnsoShortcuts {
    address immutable public executor;

    error NotPermitted();

    constructor(address executor_) {
        executor = executor_;
    }

    function _checkMsgSender() internal override view {
        if (msg.sender != executor) revert NotPermitted();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[ERC].
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the value of tokens of token type `id` owned by `account`.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] calldata accounts,
        uint256[] calldata ids
    ) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the zero address.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
     *
     * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
     * to an untrusted contract, when invoking {onERC1155Received} on the receiver.
     * Ensure to follow the checks-effects-interactions pattern and consider employing
     * reentrancy guards when interacting with untrusted contracts.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `value` amount.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
     * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
     * Ensure to follow the checks-effects-interactions pattern and consider employing
     * reentrancy guards when interacting with untrusted contracts.
     *
     * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
     *
     * Requirements:
     *
     * - `ids` and `values` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external;
}

File 6 of 18 : AbstractEnsoShortcuts.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.28;

import { VM } from "enso-weiroll/VM.sol";
import { ERC721Holder } from "openzeppelin-contracts/token/ERC721/utils/ERC721Holder.sol";
import { ERC1155Holder } from "openzeppelin-contracts/token/ERC1155/utils/ERC1155Holder.sol";

abstract contract AbstractEnsoShortcuts is VM, ERC721Holder, ERC1155Holder {

    event ShortcutExecuted(bytes32 accountId, bytes32 requestId);

    // @notice Execute a shortcut
    // @param accountId The bytes32 value representing an API user
    // @param requestId The bytes32 value representing an API request
    // @param commands An array of bytes32 values that encode calls
    // @param state An array of bytes that are used to generate call data for each command
    function executeShortcut(
        bytes32 accountId,
        bytes32 requestId,
        bytes32[] calldata commands,
        bytes[] calldata state
    ) public virtual payable returns (bytes[] memory response) {
        _checkMsgSender();
        response = _execute(commands, state);
        emit ShortcutExecuted(accountId, requestId);
    }

    //@notice Abstract function to validate msg.sender
    function _checkMsgSender() internal virtual view;

    receive() external virtual payable {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.16;

import "./CommandBuilder.sol";

abstract contract VM {
    using CommandBuilder for bytes[];

    uint256 constant FLAG_CT_DELEGATECALL = 0x00; // Delegate call not currently supported
    uint256 constant FLAG_CT_CALL = 0x01;
    uint256 constant FLAG_CT_STATICCALL = 0x02;
    uint256 constant FLAG_CT_VALUECALL = 0x03;
    uint256 constant FLAG_CT_MASK = 0x03;
    uint256 constant FLAG_DATA = 0x20;
    uint256 constant FLAG_EXTENDED_COMMAND = 0x40;
    uint256 constant FLAG_TUPLE_RETURN = 0x80;

    uint256 constant SHORT_COMMAND_FILL =
        0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    error ExecutionFailed(
        uint256 command_index,
        address target,
        string message
    );

    function _execute(bytes32[] calldata commands, bytes[] memory state)
        internal
        returns (bytes[] memory)
    {
        bytes32 command;
        uint256 flags;
        bytes32 indices;

        bool success;
        bytes memory outData;

        uint256 commandsLength = commands.length;
        uint256 indicesLength;
        for (uint256 i; i < commandsLength; i = _uncheckedIncrement(i)) {
            command = commands[i];
            flags = uint256(uint8(bytes1(command << 32)));

            if (flags & FLAG_EXTENDED_COMMAND != 0) {
                i = _uncheckedIncrement(i);
                indices = commands[i];
                indicesLength = 32;
            } else {
                indices = bytes32(uint256(command << 40) | SHORT_COMMAND_FILL);
                indicesLength = 6;
            }

            if (flags & FLAG_CT_MASK == FLAG_CT_CALL) {
                (success, outData) = address(uint160(uint256(command))).call( // target
                    // inputs
                    flags & FLAG_DATA == 0
                        ? state.buildInputs(
                            bytes4(command), // selector
                            indices,
                            indicesLength
                        )
                        : state[
                            uint8(bytes1(indices)) &
                            CommandBuilder.IDX_VALUE_MASK
                        ]
                );
            } else if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) {
                (success, outData) = address(uint160(uint256(command))) // target
                    .staticcall(
                        // inputs
                        flags & FLAG_DATA == 0
                            ? state.buildInputs(
                                bytes4(command), // selector
                                indices,
                                indicesLength
                            )
                            : state[
                                uint8(bytes1(indices)) &
                                CommandBuilder.IDX_VALUE_MASK
                            ]
                    );
            } else if (flags & FLAG_CT_MASK == FLAG_CT_VALUECALL) {
                bytes memory v = state[
                    uint8(bytes1(indices)) &
                    CommandBuilder.IDX_VALUE_MASK
                ];
                require(v.length == 32, "Value must be 32 bytes");
                uint256 callEth = uint256(bytes32(v));
                (success, outData) = address(uint160(uint256(command))).call{ // target
                    value: callEth
                }(
                    // inputs
                    flags & FLAG_DATA == 0
                        ? state.buildInputs(
                            bytes4(command), // selector
                            indices << 8, // skip value input
                            indicesLength - 1 // max indices length reduced by value input
                        )
                        : state[
                            uint8(bytes1(indices << 8)) & // first byte after value input
                            CommandBuilder.IDX_VALUE_MASK
                        ]
                );
            } else {
                revert("Invalid calltype");
            }

            if (!success) {
                string memory message = "Unknown";
                if (outData.length > 68) {
                    // This might be an error message, parse the outData
                    // Estimate the bytes length of the possible error message
                    uint256 estimatedLength = _estimateBytesLength(outData, 68);
                    // Remove selector. First 32 bytes should be a pointer that indicates the start of data in memory
                    assembly {
                        outData := add(outData, 4)
                    }
                    uint256 pointer = uint256(bytes32(outData));
                    if (pointer == 32) {
                        // Remove pointer. If it is a string, the next 32 bytes will hold the size
                        assembly {
                            outData := add(outData, 32)
                        }
                        uint256 size = uint256(bytes32(outData));
                        // If the size variable is the same as the estimated bytes length, we can be fairly certain
                        // this is a dynamic string, so convert the bytes to a string and emit the message. While an
                        // error function with 3 static parameters is capable of producing a similar output, there is
                        // low risk of a contract unintentionally emitting a message.
                        if (size == estimatedLength) {
                            // Remove size. The remaining data should be the string content
                            assembly {
                                outData := add(outData, 32)
                            }
                            message = string(outData);
                        }
                    }
                }
                revert ExecutionFailed({
                    command_index: flags & FLAG_EXTENDED_COMMAND == 0
                        ? i
                        : i - 1,
                    target: address(uint160(uint256(command))),
                    message: message
                });
            }

            if (flags & FLAG_TUPLE_RETURN != 0) {
                state.writeTuple(bytes1(command << 88), outData);
            } else {
                state = state.writeOutputs(bytes1(command << 88), outData);
            }
        }
        return state;
    }

    function _estimateBytesLength(bytes memory data, uint256 pos) internal pure returns (uint256 estimate) {
        uint256 length = data.length;
        estimate = length - pos; // Assume length equals alloted space
        for (uint256 i = pos; i < length; ) {
            if (data[i] == 0) {
                // Zero bytes found, adjust estimated length
                estimate = i - pos;
                break;
            }
            unchecked {
                ++i;
            }
        }
    }

    function _uncheckedIncrement(uint256 i) private pure returns (uint256) {
        unchecked {
            ++i;
        }
        return i;
    }
}

File 11 of 18 : ERC721Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.20;

import {IERC721Receiver} from "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
 * {IERC721-setApprovalForAll}.
 */
abstract contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/utils/ERC1155Holder.sol)

pragma solidity ^0.8.20;

import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
import {IERC1155Receiver} from "../IERC1155Receiver.sol";

/**
 * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC-1155 tokens.
 *
 * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
 * stuck.
 */
abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
    }

    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}

File 13 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

File 14 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.16;

library CommandBuilder {
    uint256 constant IDX_VARIABLE_LENGTH = 0x80;
    uint256 constant IDX_VALUE_MASK = 0x7f;
    uint256 constant IDX_END_OF_ARGS = 0xff;
    uint256 constant IDX_USE_STATE = 0xfe;
    uint256 constant IDX_ARRAY_START = 0xfd;
    uint256 constant IDX_TUPLE_START = 0xfc;
    uint256 constant IDX_DYNAMIC_END = 0xfb;

    function buildInputs(
        bytes[] memory state,
        bytes4 selector,
        bytes32 indices,
        uint256 indicesLength
    ) internal view returns (bytes memory ret) {
        uint256 idx; // The current command index
        uint256 offsetIdx; // The index of the current free offset

        uint256 count; // Number of bytes in whole ABI encoded message
        uint256 free; // Pointer to first free byte in tail part of message
        uint256[] memory dynamicLengths = new uint256[](10); // Optionally store the length of all dynamic types (a command cannot fit more than 10 dynamic types)

        bytes memory stateData; // Optionally encode the current state if the call requires it

        // Determine the length of the encoded data
        for (uint256 i; i < indicesLength; ) {
            idx = uint8(indices[i]);
            if (idx == IDX_END_OF_ARGS) {
                indicesLength = i;
                break;
            }
            if (idx & IDX_VARIABLE_LENGTH != 0) {
                if (idx == IDX_USE_STATE) {
                    if (stateData.length == 0) {
                        stateData = abi.encode(state);
                    }
                    unchecked {
                        count += stateData.length;
                    }
                } else {
                    (dynamicLengths, offsetIdx, count, i) = setupDynamicType(
                        state,
                        indices,
                        dynamicLengths,
                        idx,
                        offsetIdx,
                        count,
                        i
                    );
                }
            } else {
                count = setupStaticVariable(state, count, idx);
            }
            unchecked {
                free += 32;
                ++i;
            }
        }

        // Encode it
        ret = new bytes(count + 4);
        assembly {
            mstore(add(ret, 32), selector)
        }
        offsetIdx = 0;
        // Use count to track current memory slot
        assembly {
            count := add(ret, 36)
        }
        for (uint256 i; i < indicesLength; ) {
            idx = uint8(indices[i]);
            if (idx & IDX_VARIABLE_LENGTH != 0) {
                if (idx == IDX_USE_STATE) {
                    assembly {
                        mstore(count, free)
                    }
                    memcpy(stateData, 32, ret, free + 4, stateData.length - 32);
                    unchecked {
                        free += stateData.length - 32;
                    }
                } else if (idx == IDX_ARRAY_START) {
                    // Start of dynamic type, put pointer in current slot
                    assembly {
                        mstore(count, free)
                    }
                    (offsetIdx, free, i, ) = encodeDynamicArray(
                        ret,
                        state,
                        indices,
                        dynamicLengths,
                        offsetIdx,
                        free,
                        i
                    );
                } else if (idx == IDX_TUPLE_START) {
                    // Start of dynamic type, put pointer in current slot
                    assembly {
                        mstore(count, free)
                    }
                    (offsetIdx, free, i, ) = encodeDynamicTuple(
                        ret,
                        state,
                        indices,
                        dynamicLengths,
                        offsetIdx,
                        free,
                        i
                    );
                } else {
                    // Variable length data
                    uint256 argLen = state[idx & IDX_VALUE_MASK].length;
                    // Put a pointer in the current slot and write the data to first free slot
                    assembly {
                        mstore(count, free)
                    }
                    memcpy(
                        state[idx & IDX_VALUE_MASK],
                        0,
                        ret,
                        free + 4,
                        argLen
                    );
                    unchecked {
                        free += argLen;
                    }
                }
            } else {
                // Fixed length data (length previously checked to be 32 bytes)
                bytes memory stateVar = state[idx & IDX_VALUE_MASK];
                // Write the data to current slot
                assembly {
                    mstore(count, mload(add(stateVar, 32)))
                }
            }
            unchecked {
                count += 32;
                ++i;
            }
        }
    }

    function setupStaticVariable(
        bytes[] memory state,
        uint256 count,
        uint256 idx
    ) internal pure returns (uint256 newCount) {
        require(
            state[idx & IDX_VALUE_MASK].length == 32,
            "Static state variables must be 32 bytes"
        );
        unchecked {
            newCount = count + 32;
        }
    }

    function setupDynamicVariable(
        bytes[] memory state,
        uint256 count,
        uint256 idx
    ) internal pure returns (uint256 newCount) {
        bytes memory arg = state[idx & IDX_VALUE_MASK];
        // Validate the length of the data in state is a multiple of 32
        uint256 argLen = arg.length;
        require(
            argLen != 0 && argLen % 32 == 0,
            "Dynamic state variables must be a multiple of 32 bytes"
        );
        // Add the length of the value, rounded up to the next word boundary, plus space for pointer
        unchecked {
            newCount = count + argLen + 32;
        }
    }

    function setupDynamicType(
        bytes[] memory state,
        bytes32 indices,
        uint256[] memory dynamicLengths,
        uint256 idx,
        uint256 offsetIdx,
        uint256 count,
        uint256 index
    ) internal view returns (
        uint256[] memory newDynamicLengths,
        uint256 newOffsetIdx,
        uint256 newCount,
        uint256 newIndex
    ) {
        if (idx == IDX_ARRAY_START) {
            (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicArray(
                state,
                indices,
                dynamicLengths,
                offsetIdx,
                count,
                index
            );
        } else if (idx == IDX_TUPLE_START) {
            (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicTuple(
                state,
                indices,
                dynamicLengths,
                offsetIdx,
                count,
                index
            );
        } else {
            newDynamicLengths = dynamicLengths;
            newOffsetIdx = offsetIdx;
            newIndex = index;
            newCount = setupDynamicVariable(state, count, idx);
        }
    }

    function setupDynamicArray(
        bytes[] memory state,
        bytes32 indices,
        uint256[] memory dynamicLengths,
        uint256 offsetIdx,
        uint256 count,
        uint256 index
    ) internal view returns (
        uint256[] memory newDynamicLengths,
        uint256 newOffsetIdx,
        uint256 newCount,
        uint256 newIndex
    ) {
        // Current idx is IDX_ARRAY_START, next idx will contain the array length
        unchecked {
            newIndex = index + 1;
            newCount = count + 32;
        }
        uint256 idx = uint8(indices[newIndex]);
        require(
            state[idx & IDX_VALUE_MASK].length == 32,
            "Array length must be 32 bytes"
        );
        (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicTuple(
            state,
            indices,
            dynamicLengths,
            offsetIdx,
            newCount,
            newIndex
        );
    }

    function setupDynamicTuple(
        bytes[] memory state,
        bytes32 indices,
        uint256[] memory dynamicLengths,
        uint256 offsetIdx,
        uint256 count,
        uint256 index
    ) internal view returns (
        uint256[] memory newDynamicLengths,
        uint256 newOffsetIdx,
        uint256 newCount,
        uint256 newIndex
    ) {
        uint256 idx;
        uint256 offset;
        newDynamicLengths = dynamicLengths;
        // Progress to first index of the data and progress the next offset idx
        unchecked {
            newIndex = index + 1;
            newOffsetIdx = offsetIdx + 1;
            newCount = count + 32;
        }
        while (newIndex < 32) {
            idx = uint8(indices[newIndex]);
            if (idx & IDX_VARIABLE_LENGTH != 0) {
                if (idx == IDX_DYNAMIC_END) {
                    newDynamicLengths[offsetIdx] = offset;
                    // explicit return saves gas ¯\_(ツ)_/¯
                    return (newDynamicLengths, newOffsetIdx, newCount, newIndex);
                } else {
                    require(idx != IDX_USE_STATE, "Cannot use state from inside dynamic type");
                    (newDynamicLengths, newOffsetIdx, newCount, newIndex) = setupDynamicType(
                        state,
                        indices,
                        newDynamicLengths,
                        idx,
                        newOffsetIdx,
                        newCount,
                        newIndex
                    );
                }
            } else {
                newCount = setupStaticVariable(state, newCount, idx);
            }
            unchecked {
                offset += 32;
                ++newIndex;
            }
        }
        revert("Dynamic type was not properly closed");
    }

    function encodeDynamicArray(
        bytes memory ret,
        bytes[] memory state,
        bytes32 indices,
        uint256[] memory dynamicLengths,
        uint256 offsetIdx,
        uint256 currentSlot,
        uint256 index
    ) internal view returns (
        uint256 newOffsetIdx,
        uint256 newSlot,
        uint256 newIndex,
        uint256 length
    ) {
        // Progress to array length metadata
        unchecked {
            newIndex = index + 1;
            newSlot = currentSlot + 32;
        }
        // Encode array length
        uint256 idx = uint8(indices[newIndex]);
        // Array length value previously checked to be 32 bytes
        bytes memory stateVar = state[idx & IDX_VALUE_MASK];
        assembly {
            mstore(add(add(ret, 36), currentSlot), mload(add(stateVar, 32)))
        }
        (newOffsetIdx, newSlot, newIndex, length) = encodeDynamicTuple(
            ret,
            state,
            indices,
            dynamicLengths,
            offsetIdx,
            newSlot,
            newIndex
        );
        unchecked {
            length += 32; // Increase length to account for array length metadata
        }
    }

    function encodeDynamicTuple(
        bytes memory ret,
        bytes[] memory state,
        bytes32 indices,
        uint256[] memory dynamicLengths,
        uint256 offsetIdx,
        uint256 currentSlot,
        uint256 index
    ) internal view returns (
        uint256 newOffsetIdx,
        uint256 newSlot,
        uint256 newIndex,
        uint256 length
    ) {
        uint256 idx;
        uint256 argLen;
        uint256 freePointer = dynamicLengths[offsetIdx]; // The pointer to the next free slot
        unchecked {
            newSlot = currentSlot + freePointer; // Update the next slot
            newOffsetIdx = offsetIdx + 1; // Progress to next offsetIdx
            newIndex = index + 1; // Progress to first index of the data
        }
        // Shift currentSlot to correct location in memory
        assembly {
            currentSlot := add(add(ret, 36), currentSlot)
        }
        while (newIndex < 32) {
            idx = uint8(indices[newIndex]);
            if (idx & IDX_VARIABLE_LENGTH != 0) {
                if (idx == IDX_DYNAMIC_END) {
                    break;
                } else if (idx == IDX_ARRAY_START) {
                    // Start of dynamic type, put pointer in current slot
                    assembly {
                        mstore(currentSlot, freePointer)
                    }
                    (newOffsetIdx, newSlot, newIndex, argLen) = encodeDynamicArray(
                        ret,
                        state,
                        indices,
                        dynamicLengths,
                        newOffsetIdx,
                        newSlot,
                        newIndex
                    );
                    unchecked {
                        freePointer += argLen;
                        length += (argLen + 32); // data + pointer
                    }
                } else if (idx == IDX_TUPLE_START) {
                    // Start of dynamic type, put pointer in current slot
                    assembly {
                        mstore(currentSlot, freePointer)
                    }
                    (newOffsetIdx, newSlot, newIndex, argLen) = encodeDynamicTuple(
                        ret,
                        state,
                        indices,
                        dynamicLengths,
                        newOffsetIdx,
                        newSlot,
                        newIndex
                    );
                    unchecked {
                        freePointer += argLen;
                        length += (argLen + 32); // data + pointer
                    }
                } else  {
                    // Variable length data
                    argLen = state[idx & IDX_VALUE_MASK].length;
                    // Start of dynamic type, put pointer in current slot
                    assembly {
                        mstore(currentSlot, freePointer)
                    }
                    memcpy(
                        state[idx & IDX_VALUE_MASK],
                        0,
                        ret,
                        newSlot + 4,
                        argLen
                    );
                    unchecked {
                        newSlot += argLen;
                        freePointer += argLen;
                        length += (argLen + 32); // data + pointer
                    }
                }
            } else {
                // Fixed length data (length previously checked to be 32 bytes)
                bytes memory stateVar = state[idx & IDX_VALUE_MASK];
                // Write to first free slot
                assembly {
                    mstore(currentSlot, mload(add(stateVar, 32)))
                }
                unchecked {
                    length += 32;
                }
            }
            unchecked {
                currentSlot += 32;
                ++newIndex;
            }
        }
    }

    function writeOutputs(
        bytes[] memory state,
        bytes1 index,
        bytes memory output
    ) internal pure returns (bytes[] memory) {
        uint256 idx = uint8(index);
        if (idx == IDX_END_OF_ARGS) return state;

        if (idx & IDX_VARIABLE_LENGTH != 0) {
            if (idx == IDX_USE_STATE) {
                state = abi.decode(output, (bytes[]));
            } else {
                require(idx & IDX_VALUE_MASK < state.length, "Index out-of-bounds");
                // Check the first field is 0x20 (because we have only a single return value)
                uint256 argPtr;
                assembly {
                    argPtr := mload(add(output, 32))
                }
                require(
                    argPtr == 32,
                    "Only one return value permitted (variable)"
                );

                assembly {
                    // Overwrite the first word of the return data with the length - 32
                    mstore(add(output, 32), sub(mload(output), 32))
                    // Insert a pointer to the return data, starting at the second word, into state
                    mstore(
                        add(add(state, 32), mul(and(idx, IDX_VALUE_MASK), 32)),
                        add(output, 32)
                    )
                }
            }
        } else {
            require(idx & IDX_VALUE_MASK < state.length, "Index out-of-bounds");
            // Single word
            require(
                output.length == 32,
                "Only one return value permitted (static)"
            );

            state[idx & IDX_VALUE_MASK] = output;
        }

        return state;
    }

    function writeTuple(
        bytes[] memory state,
        bytes1 index,
        bytes memory output
    ) internal view {
        uint256 idx = uint8(index);
        if (idx == IDX_END_OF_ARGS) return;

        bytes memory entry = state[idx & IDX_VALUE_MASK] = new bytes(output.length + 32);
        memcpy(output, 0, entry, 32, output.length);
        assembly {
            let l := mload(output)
            mstore(add(entry, 32), l)
        }
    }

    function memcpy(
        bytes memory src,
        uint256 srcIdx,
        bytes memory dest,
        uint256 destIdx,
        uint256 len
    ) internal view {
        assembly {
            pop(
                staticcall(
                    gas(),
                    4,
                    add(add(src, 32), srcIdx),
                    len,
                    add(add(dest, 32), destIdx),
                    len
                )
            )
        }
    }
}

File 16 of 18 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC-721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC-721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Interface that must be implemented by smart contracts in order to receive
 * ERC-1155 token transfers.
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC-1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC-1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

Settings
{
  "remappings": [
    "@ensdomains/=lib/shortcuts-contracts/node_modules/@ensdomains/",
    "@ensofinance/=lib/shortcuts-contracts/node_modules/@ensofinance/",
    "@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/",
    "@layerzerolabs/lz-evm-protocol-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/protocol/",
    "@layerzerolabs/lz-evm-oapp-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/oapp/",
    "@openzeppelin/=lib/shortcuts-contracts/node_modules/@openzeppelin/",
    "@rari-capital/=lib/shortcuts-contracts/node_modules/@rari-capital/",
    "@uniswap/v4-core/=lib/v4-core/",
    "@uniswap/v4-periphery/=lib/v4-periphery/",
    "clones-with-immutable-args/=lib/shortcuts-contracts/node_modules/clones-with-immutable-args/",
    "devtools/=lib/devtools/packages/toolbox-foundry/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "enso-weiroll/=lib/enso-weiroll/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "eth-gas-reporter/=lib/shortcuts-contracts/node_modules/eth-gas-reporter/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "hardhat-deploy/=lib/shortcuts-contracts/node_modules/hardhat-deploy/",
    "hardhat/=lib/shortcuts-contracts/node_modules/hardhat/",
    "layerzero-v2/=lib/layerzero-v2/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "safe-contracts/=lib/safe-tools/lib/safe-contracts/contracts/",
    "safe-tools/=lib/safe-tools/src/",
    "shortcuts-contracts/=lib/shortcuts-contracts/contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solady/lib/solmate/src/",
    "forge-gas-snapshot/=lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "permit2/=lib/v4-periphery/lib/permit2/",
    "v4-core/=lib/v4-core/src/",
    "v4-periphery/=lib/v4-periphery/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token","name":"token","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"AmountTooLow","type":"error"},{"inputs":[],"name":"DuplicateNativeAsset","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"}],"name":"UnsupportedTokenType","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"expectedAmount","type":"uint256"}],"name":"WrongMsgValue","type":"error"},{"inputs":[{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token[]","name":"tokensIn","type":"tuple[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"routeMulti","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token","name":"tokenIn","type":"tuple"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"routeSingle","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token[]","name":"tokensIn","type":"tuple[]"},{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token[]","name":"tokensOut","type":"tuple[]"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeRouteMulti","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token","name":"tokenIn","type":"tuple"},{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Token","name":"tokenOut","type":"tuple"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeRouteSingle","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"shortcuts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60a0806040523461009a57611548818101906001600160401b03821183831017610086576020918391610d9083393081520301905ff0801561007b576001600160a01b0316608052604051610cf1908161009f82396080518181816101fc015281816109d001528181610a8e01528181610b3f0152610bb20152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806321025a0614610064578063b94c36091461005f578063ea270f421461005a578063f35cae90146100555763f52e33f514610050575f80fd5b610380565b61025b565b6101e7565b610186565b608036600319011261010c576004356001600160401b03811161010c5761008f903690600401610110565b6024356001600160401b03811161010c576100ae903690600401610110565b604435916100bb8361011e565b606435906001600160401b03821161010c57610108936100f66100e56100fc94369060040161012f565b906100f084886105fb565b946103da565b93610817565b6040519182918261015c565b0390f35b5f80fd5b9081604091031261010c5790565b6001600160a01b0381160361010c57565b9181601f8401121561010c578235916001600160401b03831161010c576020838186019501011161010c57565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b604036600319011261010c576004356001600160401b03811161010c576101b1903690600401610110565b6024356001600160401b03811161010c57610108916101d76100fc92369060040161012f565b916103da565b5f91031261010c57565b3461010c575f36600319011261010c576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9181601f8401121561010c578235916001600160401b03831161010c576020808501948460051b01011161010c57565b608036600319011261010c576004356001600160401b03811161010c5761028690369060040161022b565b6024356001600160401b03811161010c576102a590369060040161022b565b9190604435916102b48361011e565b6064356001600160401b03811161010c576102d390369060040161012f565b906102dd86610457565b926102eb6040519485610431565b868452601f196102fa88610457565b013660208601375f5b8781106103585750906103179291886104bd565b925f5b85811061032f5760405180610108878261015c565b806103526103406001938988610482565b8461034b84886104a9565b5191610817565b0161031a565b8061036f8861036a6001948c8b610482565b6105fb565b61037982886104a9565b5201610303565b604036600319011261010c576004356001600160401b03811161010c576103ab90369060040161022b565b602435906001600160401b03821161010c57610108926103d26100fc93369060040161012f565b9290916104bd565b6103e69092919261099b565b1580610414575b6103fd576103fa91610b9b565b90565b63dfd87a7360e01b5f52346004525f60245260445ffd5b503415156103ed565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761045257604052565b61041d565b6001600160401b0381116104525760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b91908110156104a45760051b81013590603e198136030182121561010c570190565b61046e565b80518210156104a45760209160051b010190565b9291925f915f5b8181106104e1575050501580610414576103fd576103fa91610b9b565b6104f46104ef828486610482565b61099b565b610501575b6001016104c4565b9261050e576001926104f9565b63eacf98b760e01b5f5260045ffd5b6004111561010c57565b6004111561053157565b634e487b7160e01b5f52602160045260245ffd5b356103fa8161051d565b9060048210156105315752565b9061056a600460249361054f565b565b903590601e198136030182121561010c57018035906001600160401b03821161010c5760200191813603831361010c57565b9081606091031261010c5780356105b48161011e565b916040602083013592013590565b9081602091031261010c575190565b6040513d5f823e3d90fd5b919082604091031261010c57602082356105f58161011e565b92013590565b61060481610545565b61060d81610527565b600181036106ae57506106629161063461062c8360208095019061056c565b8101906105dc565b506040516370a0823160e01b81526001600160a01b0390921660048301529092839190829081906024820190565b03916001600160a01b03165afa9081156106a9575f91610680575090565b6103fa915060203d6020116106a2575b61069a8183610431565b8101906105c2565b503d610690565b6105d1565b6106b781610527565b806106c25750503190565b6106cb81610527565b600281036106ea57506106629161063461062c8360208095019061056c565b6106f381610527565b6003810361074c57506106629161071a6107128360208095019061056c565b81019061059e565b50604051627eeac760e11b81526001600160a01b03909316600484015260248301529092839190829081906044820190565b63292c317f60e11b5f5261075f9061055c565b5ffd5b9081602091031261010c573590565b9190820391821161077e57565b634e487b7160e01b5f52601160045260245ffd5b9093929193606082526107b381356107a98161051d565b606084019061054f565b6020810135601e198236030181121561010c5701906020823592016001600160401b03831161010c57823603811361010c578260409360c0928560808601528160a0860152838501375f828285010152601f80199101168201019460208201520152565b909161082282610545565b61082b81610527565b600181036108e7575061087690602061084961062c8286018661056c565b6040516370a0823160e01b81526001600160a01b0390971660048801529593849190829081906024820190565b03916001600160a01b03165afa80156106a95761089a925f916108c8575b50610771565b918083106108a757505050565b6108c490604051938493631cab723f60e21b855260048501610792565b0390fd5b6108e1915060203d6020116106a25761069a8183610431565b5f610894565b6108f081610527565b80610919575061089a9061091261090a602085018561056c565b810190610762565b9331610771565b61092281610527565b60028103610940575061087690602061084961062c8286018661056c565b61094981610527565b6003810361074c57506108769060206109676107128286018661056c565b604051627eeac760e11b81526001600160a01b03909816600489015260248801919091529593849190829081906044820190565b905f916109a781610545565b6109b081610527565b600181036109f757506109cd61062c82602061056a94019061056c565b907f0000000000000000000000000000000000000000000000000000000000000000903390610c24565b610a0081610527565b80610a3d5750610a1a91925080602061090a92019061056c565b803403610a275750600190565b63dfd87a7360e01b5f523460045260245260445ffd5b610a4681610527565b60028103610aec575061062c816020610a6093019061056c565b906001600160a01b0316803b1561010c57604051632142170760e11b81523360048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016602482015260448101929092525f908290818381606481015b03925af180156106a957610ad85750565b80610ae65f61056a93610431565b806101dd565b610af581610527565b6003810361074c5750610712816020610b0f93019061056c565b6001600160a01b0390921691823b1561010c57604051637921219560e11b81523360048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201526044810192909252606482015260a06084820152600260a482015261060f60f31b60c4820152905f90829081838160e48101610ac7565b5f91829181604051928392833781018381520390347f00000000000000000000000000000000000000000000000000000000000000005af1903d15610c1d573d6001600160401b0381116104525760405190610c01601f8201601f191660200183610431565b81523d5f602083013e5b809215610c155750565b602081519101fd5b6060610c0b565b6040516323b872dd60e01b60208083019182526001600160a01b039485166024840152949093166044820152606480820195909552938452925f9190610c6b608482610431565b519082855af1156105d1575f513d610cb257506001600160a01b0381163b155b610c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415610c8b56fea2646970667358221220b31f038bfacab66c91c151fafd3ad97c27ed33ae04b9aea97887965ea369f60564736f6c634300081c003360a034606d57601f61154838819003918201601f19168301916001600160401b03831184841017607157808492602094604052833981010312606d57516001600160a01b0381168103606d576080526040516114c29081610086823960805181818161047201526105630152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c806301ffc9a714610079578063150b7a021461007457806395352c9f1461006f578063bc197c811461006a578063c34c08e5146100655763f23a6e610361000e576104a1565b61045d565b6103c8565b6102a0565b610198565b346100cd5760203660031901126100cd5760043563ffffffff60e01b81168091036100cd57630271189760e51b81149081156100bc575b50151560805260206080f35b6301ffc9a760e01b149050816100b0565b5f80fd5b600435906001600160a01b03821682036100cd57565b602435906001600160a01b03821682036100cd57565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761013257604052565b6100fd565b6001600160401b03811161013257601f01601f191660200190565b81601f820112156100cd5780359061016982610137565b926101776040519485610111565b828452602083830101116100cd57815f926020809301838601378301015290565b346100cd5760803660031901126100cd576101b16100d1565b506101ba6100e7565b506064356001600160401b0381116100cd576101da903690600401610152565b50604051630a85bd0160e11b8152602090f35b9181601f840112156100cd578235916001600160401b0383116100cd576020808501948460051b0101116100cd57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b83831061027357505050505090565b9091929394602080610291600193603f19868203018752895161021d565b97019301930191939290610264565b60803660031901126100cd576024356004356044356001600160401b0381116100cd576102d19036906004016101ed565b90926064356001600160401b0381116100cd57610350947f049d8dd84b6a6cc45d5f68a74b23450bd3e54d84fd405d91b8b286c78d51d2499361032d61031e6103339436906004016101ed565b610326610561565b36916104f6565b916106fb565b60408051948552602085019290925292a160405191829182610241565b0390f35b6001600160401b0381116101325760051b60200190565b9080601f830112156100cd57813561038281610354565b926103906040519485610111565b81845260208085019260051b8201019283116100cd57602001905b8282106103b85750505090565b81358152602091820191016103ab565b346100cd5760a03660031901126100cd576103e16100d1565b506103ea6100e7565b506044356001600160401b0381116100cd5761040a90369060040161036b565b506064356001600160401b0381116100cd5761042a90369060040161036b565b506084356001600160401b0381116100cd5761044a903690600401610152565b5060405163bc197c8160e01b8152602090f35b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd5760a03660031901126100cd576104ba6100d1565b506104c36100e7565b506084356001600160401b0381116100cd576104e3903690600401610152565b5060405163f23a6e6160e01b8152602090f35b92919061050281610354565b936105106040519586610111565b602085838152019160051b8101918383116100cd5781905b838210610536575050505050565b81356001600160401b0381116100cd576020916105568784938701610152565b815201910190610528565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361059357565b6339218f3b60e01b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b91908110156105c65760051b0190565b6105a2565b80518210156105c65760209160051b010190565b156105e657565b60405162461bcd60e51b815260206004820152601660248201527556616c7565206d75737420626520333220627974657360501b6044820152606490fd5b602081519101519060208110610638575090565b5f199060200360031b1b1690565b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161066857565b610646565b601f1981019190821161066857565b3d156106a6573d9061068d82610137565b9161069b6040519384610111565b82523d5f602084013e565b606090565b604051906106ba604083610111565b60078252662ab735b737bbb760c91b6020830152565b9081526001600160a01b0390911660208201526060604082018190526106f89291019061021d565b90565b905f5b81811061070b5750505090565b6107168183856105b6565b359061074461073e61073861072b8560201b90565b6001600160f81b03191690565b60f81c90565b60ff1690565b9160408316159182610a02576001019261075f8486886105b6565b356020905b60038316600181036108bd57505f91829190602085166108a657610793916001600160e01b031987168c610a7b565b805190602001826001600160a01b0387165af1926107af61067c565b935b84901561080a575050608016156107e8576107e392916107d761072b6107dd9360581b90565b87610f7f565b60010190565b6106fe565b906107e392956107fe61072b6108049460581b90565b90610ed4565b936107dd565b8592506108156106ab565b946044815111610865575b505090610852915f1461085657925b60405163ef3dcb2f60e01b81529384936001600160a01b031690600485016106d0565b0390fd5b61085f9061065a565b9261082f565b602061087c600461087584610ca8565b9301610624565b036108205761088d60248301610624565b14610899575b80610820565b6044019350610852610893565b50607f6108b79160f81c168a6105cb565b51610793565b6002810361092057505f9182919060208516610909576108e8916001600160e01b031987168c610a7b565b8051906020016001600160a01b0386165afa9261090361067c565b936107b1565b50607f61091a9160f81c168a6105cb565b516108e8565b6003036109ca575f918161095161093e607f869560f81c168d6105cb565b5161094c60208251146105df565b610624565b91602086161584146109a35761097361096d6109859360081b90565b9161065a565b906001600160e01b031988168d610a7b565b905b815191602001906001600160a01b0387165af19261090361067c565b50607f6109bc61073e61073861072b6109c39560081b90565b168b6105cb565b5190610987565b60405162461bcd60e51b815260206004820152601060248201526f496e76616c69642063616c6c7479706560801b6044820152606490fd5b926006602883901b6001600160d01b0317610764565b6040516101609190610a2a8382610111565b600a815291601f1901366020840137565b906004820180921161066857565b90610a5382610137565b610a606040519182610111565b8281528092610a71601f1991610137565b0190602036910137565b91939290935f5f915f92610a8d610a18565b906060935f905b878210610be9575b5050610aaa610aaf91610a3b565b610a49565b9760208901525f9060248901925f955b878710610ad157505050505050505050565b60208710156105c657888b83891a6080811615610bc75760fe8103610b2c575050506020816001928752610b198d610b0883610a3b565b610b128b5161066d565b918b61112d565b875101601f1901955b0196019593610abf565b60fd819794959697145f14610b61575092610b5592868660019b948a9998978560209c52611283565b50979195909594610b22565b60fc8103610b83575092610b5592868660019b948a9998978560209c5261113f565b93610bc0888293610baf602096607f6001999c9b9a1690610ba482826105cb565b5151978895526105cb565b5190610bba85610a3b565b9161111b565b0195610b22565b60209250600193979150610bdf607f8492168d6105cb565b5101518152610b22565b9093959160208510156105c65786851a60ff8114610c94576080811615610c7f5760fe8103610c5f5750855115610c2f575b6020600191875101935b0196940190610a94565b945060016020604051610c5681610c488d858301610241565b03601f198101835282610111565b96915050610c1b565b90610c739260019692602095968a8d61104c565b95919390939294610c25565b610c8e6020916001938c610fd9565b93610c25565b509396509094929050610aaa610aaf610a9c565b9081516043198101818111610668579260445b828110610cc757505050565b81518110156105c657818101602001516001600160f81b03191615610cee57600101610cbb565b929350505060431981019081116106685790565b15610d0957565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f75742d6f662d626f756e647360681b6044820152606490fd5b15610d4b57565b60405162461bcd60e51b815260206004820152602860248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d697474656420604482015267287374617469632960c01b6064820152608490fd5b15610da857565b60405162461bcd60e51b815260206004820152602a60248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d697474656420604482015269287661726961626c652960b01b6064820152608490fd5b6020818303126100cd578051906001600160401b0382116100cd57019080601f830112156100cd57815191610e3483610354565b92610e426040519485610111565b80845260208085019160051b830101918383116100cd5760208101915b838310610e6e57505050505090565b82516001600160401b0381116100cd57820185603f820112156100cd57602081015191610e9a83610137565b610ea76040519182610111565b83815260408385010188106100cd575f602085819660408397018386015e83010152815201920191610e5f565b91908060f81c60ff8114610f79576080811615610f415760fe8103610f095750506106f8915060208082518301019101610e00565b602091610f1f610fe092607f8751911610610d02565b82840193610f2f84865114610da1565b51601f1901845260f31c168301015290565b610f75929150607f1690610f5784518310610d02565b610f646020825114610d44565b610f6e82856105cb565b52826105cb565b5090565b50505090565b9060f81c60ff8114610fd4578251906020820180921161066857602092607f610faa610fbd94610a49565b921691610fb783836105cb565b526105cb565b51918051604084018184840160045afa5051910152565b505050565b610fea90607f6020939416906105cb565b515103610ff75760200190565b60405162461bcd60e51b815260206004820152602760248201527f537461746963207374617465207661726961626c6573206d75737420626520336044820152663220627974657360c81b6064820152608490fd5b90969594939260fd810361106c5750956110669596611410565b90919293565b60fc81036110805750956110669596611329565b919650919493929161109591607f16906105cb565b515180151580611110575b156110ac570160200191565b60405162461bcd60e51b815260206004820152603660248201527f44796e616d6963207374617465207661726961626c6573206d7573742062652060448201527561206d756c7469706c65206f6620333220627974657360501b6064820152608490fd5b50601f8116156110a0565b916020809185930101920160045afa50565b910160200190829060400160045afa50565b9193959692905f9461115188846105cb565b51936024600180878b019b019b0198820101915b60208910611176575b505050505050565b80891a608081161561125a5760fb8103611190575061116e565b60fd819c92959a9499969b93979c145f146111e15750906111b892918b89528a858b89611283565b9260209a93926001928c969480919d939d97929d9e01970101985b019301979291939490611165565b60fc810361121b5750906111fc92918b89528a858b8961113f565b9260209a93926001928c969480919d939d97929d9e01970101986111d3565b6020898b8e6001959f9e97989661124e607f869716948d610baf61123f88886105cb565b515197889788978895526105cb565b019d01970101986111d3565b602060019293979694998161127788607f999e99839616906105cb565b5101518b5201986111d3565b9392919094956001019460208610156105c65760206112bd97816112ac607f868b1a16856105cb565b51015160248289010152019461113f565b929391929091602090910190565b156112d257565b60405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207573652073746174652066726f6d20696e736964652064796e604482015268616d6963207479706560b81b6064820152608490fd5b6001808501976020909601960194935f929091905b602087106113975760405162461bcd60e51b8152602060048201526024808201527f44796e616d6963207479706520776173206e6f742070726f7065726c7920636c6044820152631bdcd95960e21b6064820152608490fd5b80871a60808116156113f85760fb81036113bf575050506113b890836105cb565b5293929190565b6113df94956113d860fe839c949a969b959c14156112cb565b888b61104c565b9197909692939192916001906020905b0193019561133e565b61140a60209160019399969a85610fd9565b986113ef565b92919093946001019360208510156105c6576020611433607f83881a16866105cb565b515103611447576020611066960193611329565b60405162461bcd60e51b815260206004820152601d60248201527f4172726179206c656e677468206d7573742062652033322062797465730000006044820152606490fdfea2646970667358221220e2fb320215783417f0d03eadac743b8ea521353ce3d3b7d6166a03b67b81aff564736f6c634300081c0033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806321025a0614610064578063b94c36091461005f578063ea270f421461005a578063f35cae90146100555763f52e33f514610050575f80fd5b610380565b61025b565b6101e7565b610186565b608036600319011261010c576004356001600160401b03811161010c5761008f903690600401610110565b6024356001600160401b03811161010c576100ae903690600401610110565b604435916100bb8361011e565b606435906001600160401b03821161010c57610108936100f66100e56100fc94369060040161012f565b906100f084886105fb565b946103da565b93610817565b6040519182918261015c565b0390f35b5f80fd5b9081604091031261010c5790565b6001600160a01b0381160361010c57565b9181601f8401121561010c578235916001600160401b03831161010c576020838186019501011161010c57565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b604036600319011261010c576004356001600160401b03811161010c576101b1903690600401610110565b6024356001600160401b03811161010c57610108916101d76100fc92369060040161012f565b916103da565b5f91031261010c57565b3461010c575f36600319011261010c576040517f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e0036001600160a01b03168152602090f35b9181601f8401121561010c578235916001600160401b03831161010c576020808501948460051b01011161010c57565b608036600319011261010c576004356001600160401b03811161010c5761028690369060040161022b565b6024356001600160401b03811161010c576102a590369060040161022b565b9190604435916102b48361011e565b6064356001600160401b03811161010c576102d390369060040161012f565b906102dd86610457565b926102eb6040519485610431565b868452601f196102fa88610457565b013660208601375f5b8781106103585750906103179291886104bd565b925f5b85811061032f5760405180610108878261015c565b806103526103406001938988610482565b8461034b84886104a9565b5191610817565b0161031a565b8061036f8861036a6001948c8b610482565b6105fb565b61037982886104a9565b5201610303565b604036600319011261010c576004356001600160401b03811161010c576103ab90369060040161022b565b602435906001600160401b03821161010c57610108926103d26100fc93369060040161012f565b9290916104bd565b6103e69092919261099b565b1580610414575b6103fd576103fa91610b9b565b90565b63dfd87a7360e01b5f52346004525f60245260445ffd5b503415156103ed565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761045257604052565b61041d565b6001600160401b0381116104525760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b91908110156104a45760051b81013590603e198136030182121561010c570190565b61046e565b80518210156104a45760209160051b010190565b9291925f915f5b8181106104e1575050501580610414576103fd576103fa91610b9b565b6104f46104ef828486610482565b61099b565b610501575b6001016104c4565b9261050e576001926104f9565b63eacf98b760e01b5f5260045ffd5b6004111561010c57565b6004111561053157565b634e487b7160e01b5f52602160045260245ffd5b356103fa8161051d565b9060048210156105315752565b9061056a600460249361054f565b565b903590601e198136030182121561010c57018035906001600160401b03821161010c5760200191813603831361010c57565b9081606091031261010c5780356105b48161011e565b916040602083013592013590565b9081602091031261010c575190565b6040513d5f823e3d90fd5b919082604091031261010c57602082356105f58161011e565b92013590565b61060481610545565b61060d81610527565b600181036106ae57506106629161063461062c8360208095019061056c565b8101906105dc565b506040516370a0823160e01b81526001600160a01b0390921660048301529092839190829081906024820190565b03916001600160a01b03165afa9081156106a9575f91610680575090565b6103fa915060203d6020116106a2575b61069a8183610431565b8101906105c2565b503d610690565b6105d1565b6106b781610527565b806106c25750503190565b6106cb81610527565b600281036106ea57506106629161063461062c8360208095019061056c565b6106f381610527565b6003810361074c57506106629161071a6107128360208095019061056c565b81019061059e565b50604051627eeac760e11b81526001600160a01b03909316600484015260248301529092839190829081906044820190565b63292c317f60e11b5f5261075f9061055c565b5ffd5b9081602091031261010c573590565b9190820391821161077e57565b634e487b7160e01b5f52601160045260245ffd5b9093929193606082526107b381356107a98161051d565b606084019061054f565b6020810135601e198236030181121561010c5701906020823592016001600160401b03831161010c57823603811361010c578260409360c0928560808601528160a0860152838501375f828285010152601f80199101168201019460208201520152565b909161082282610545565b61082b81610527565b600181036108e7575061087690602061084961062c8286018661056c565b6040516370a0823160e01b81526001600160a01b0390971660048801529593849190829081906024820190565b03916001600160a01b03165afa80156106a95761089a925f916108c8575b50610771565b918083106108a757505050565b6108c490604051938493631cab723f60e21b855260048501610792565b0390fd5b6108e1915060203d6020116106a25761069a8183610431565b5f610894565b6108f081610527565b80610919575061089a9061091261090a602085018561056c565b810190610762565b9331610771565b61092281610527565b60028103610940575061087690602061084961062c8286018661056c565b61094981610527565b6003810361074c57506108769060206109676107128286018661056c565b604051627eeac760e11b81526001600160a01b03909816600489015260248801919091529593849190829081906044820190565b905f916109a781610545565b6109b081610527565b600181036109f757506109cd61062c82602061056a94019061056c565b907f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e003903390610c24565b610a0081610527565b80610a3d5750610a1a91925080602061090a92019061056c565b803403610a275750600190565b63dfd87a7360e01b5f523460045260245260445ffd5b610a4681610527565b60028103610aec575061062c816020610a6093019061056c565b906001600160a01b0316803b1561010c57604051632142170760e11b81523360048201526001600160a01b037f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e00316602482015260448101929092525f908290818381606481015b03925af180156106a957610ad85750565b80610ae65f61056a93610431565b806101dd565b610af581610527565b6003810361074c5750610712816020610b0f93019061056c565b6001600160a01b0390921691823b1561010c57604051637921219560e11b81523360048201526001600160a01b037f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e0031660248201526044810192909252606482015260a06084820152600260a482015261060f60f31b60c4820152905f90829081838160e48101610ac7565b5f91829181604051928392833781018381520390347f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e0035af1903d15610c1d573d6001600160401b0381116104525760405190610c01601f8201601f191660200183610431565b81523d5f602083013e5b809215610c155750565b602081519101fd5b6060610c0b565b6040516323b872dd60e01b60208083019182526001600160a01b039485166024840152949093166044820152606480820195909552938452925f9190610c6b608482610431565b519082855af1156105d1575f513d610cb257506001600160a01b0381163b155b610c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415610c8b56fea2646970667358221220b31f038bfacab66c91c151fafd3ad97c27ed33ae04b9aea97887965ea369f60564736f6c634300081c0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.