HYPE Price: $23.40 (+3.64%)
 

Overview

HYPE Balance

HyperEVM LogoHyperEVM LogoHyperEVM Logo0 HYPE

HYPE Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

Transaction Hash
Block
From
To
Route Single254276332026-01-24 14:25:2432 mins ago1769264724IN
0xF75584eF...0B8eA22Cf
0 HYPE0.000097740.1
Route Single254276172026-01-24 14:25:0832 mins ago1769264708IN
0xF75584eF...0B8eA22Cf
0 HYPE0.000823070.54444444
Route Single254269442026-01-24 14:14:0643 mins ago1769264046IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000171140.14961024
Route Single254265792026-01-24 14:08:0749 mins ago1769263687IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000099120.10030109
Route Single254265442026-01-24 14:07:3350 mins ago1769263653IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000116820.1
Route Single254261122026-01-24 14:00:2857 mins ago1769263228IN
0xF75584eF...0B8eA22Cf
0 HYPE0.000287830.2798776
Route Single254260912026-01-24 14:00:0757 mins ago1769263207IN
0xF75584eF...0B8eA22Cf
0 HYPE0.003179773.09225023
Route Single254260882026-01-24 14:00:0457 mins ago1769263204IN
0xF75584eF...0B8eA22Cf
0 HYPE0.012084588.35226855
Route Single254260852026-01-24 14:00:0157 mins ago1769263201IN
0xF75584eF...0B8eA22Cf
0 HYPE0.0123248510.19605161
Route Single254255472026-01-24 13:51:121 hr ago1769262672IN
0xF75584eF...0B8eA22Cf
0 HYPE0.000246330.1303086
Route Single254255442026-01-24 13:51:091 hr ago1769262669IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000131610.1
Route Single254253682026-01-24 13:48:161 hr ago1769262496IN
0xF75584eF...0B8eA22Cf
0 HYPE0.000662210.33910646
Route Single254253132026-01-24 13:47:221 hr ago1769262442IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000254440.22704132
Route Single254252452026-01-24 13:46:151 hr ago1769262375IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000745490.6454642
Route Single254252042026-01-24 13:45:351 hr ago1769262335IN
0xF75584eF...0B8eA22Cf
0 HYPE0.011636186.87379909
Route Single254249692026-01-24 13:41:441 hr ago1769262104IN
0xF75584eF...0B8eA22Cf
15 HYPE0.00009410.10083894
Route Single254249352026-01-24 13:41:101 hr ago1769262070IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000100080.1
Route Single254248012026-01-24 13:38:591 hr ago1769261939IN
0xF75584eF...0B8eA22Cf
0 HYPE0.000056510.3385
Route Single254238932026-01-24 13:24:051 hr ago1769261045IN
0xF75584eF...0B8eA22Cf
10 HYPE0.000568671.1
Route Single254238152026-01-24 13:22:491 hr ago1769260969IN
0xF75584eF...0B8eA22Cf
1,300 HYPE0.000407861.43043182
Route Single254230452026-01-24 13:10:111 hr ago1769260211IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000355050.32039586
Route Single254229142026-01-24 13:08:021 hr ago1769260082IN
0xF75584eF...0B8eA22Cf
0 HYPE0.001326831.24296588
Route Single254228532026-01-24 13:07:021 hr ago1769260022IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000094270.11991911
Route Single254226392026-01-24 13:03:321 hr ago1769259812IN
0xF75584eF...0B8eA22Cf
85.50804084 HYPE0.000054180.1199
Route Single254216242026-01-24 12:46:542 hrs ago1769258814IN
0xF75584eF...0B8eA22Cf
15 HYPE0.000130640.1
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
254269442026-01-24 14:14:0643 mins ago1769264046
0xF75584eF...0B8eA22Cf
15 HYPE
254265792026-01-24 14:08:0749 mins ago1769263687
0xF75584eF...0B8eA22Cf
15 HYPE
254265442026-01-24 14:07:3350 mins ago1769263653
0xF75584eF...0B8eA22Cf
15 HYPE
254255442026-01-24 13:51:091 hr ago1769262669
0xF75584eF...0B8eA22Cf
15 HYPE
254253132026-01-24 13:47:221 hr ago1769262442
0xF75584eF...0B8eA22Cf
15 HYPE
254252452026-01-24 13:46:151 hr ago1769262375
0xF75584eF...0B8eA22Cf
15 HYPE
254249692026-01-24 13:41:441 hr ago1769262104
0xF75584eF...0B8eA22Cf
15 HYPE
254249352026-01-24 13:41:101 hr ago1769262070
0xF75584eF...0B8eA22Cf
15 HYPE
254238932026-01-24 13:24:051 hr ago1769261045
0xF75584eF...0B8eA22Cf
10 HYPE
254238152026-01-24 13:22:491 hr ago1769260969
0xF75584eF...0B8eA22Cf
1,300 HYPE
254230452026-01-24 13:10:111 hr ago1769260211
0xF75584eF...0B8eA22Cf
15 HYPE
254228532026-01-24 13:07:021 hr ago1769260022
0xF75584eF...0B8eA22Cf
15 HYPE
254226392026-01-24 13:03:321 hr ago1769259812
0xF75584eF...0B8eA22Cf
85.50804084 HYPE
254216242026-01-24 12:46:542 hrs ago1769258814
0xF75584eF...0B8eA22Cf
15 HYPE
254215902026-01-24 12:46:202 hrs ago1769258780
0xF75584eF...0B8eA22Cf
15 HYPE
254200662026-01-24 12:21:212 hrs ago1769257281
0xF75584eF...0B8eA22Cf
15 HYPE
254200002026-01-24 12:20:162 hrs ago1769257216
0xF75584eF...0B8eA22Cf
15 HYPE
254191882026-01-24 12:06:582 hrs ago1769256418
0xF75584eF...0B8eA22Cf
15 HYPE
254186962026-01-24 11:58:542 hrs ago1769255934
0xF75584eF...0B8eA22Cf
1,500 HYPE
254173102026-01-24 11:36:103 hrs ago1769254570
0xF75584eF...0B8eA22Cf
15 HYPE
254168312026-01-24 11:28:193 hrs ago1769254099
0xF75584eF...0B8eA22Cf
9.5 HYPE
254159972026-01-24 11:14:393 hrs ago1769253279
0xF75584eF...0B8eA22Cf
20 HYPE
254154972026-01-24 11:06:273 hrs ago1769252787
0xF75584eF...0B8eA22Cf
15 HYPE
254154312026-01-24 11:05:223 hrs ago1769252722
0xF75584eF...0B8eA22Cf
15 HYPE
254147462026-01-24 10:54:084 hrs ago1769252048
0xF75584eF...0B8eA22Cf
15 HYPE
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:
prague 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": "prague",
  "viaIR": true
}

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 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.