ETH Price: $3,361.45 (+6.94%)

Contract

0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Transaction Hash
Method
Block
From
To
Route Single1385660562025-07-17 5:41:2911 secs ago1752730889IN
0xF75584eF...0B8eA22Cf
0.00001 ETH0.0000001007680.00066841
Route Single1385660512025-07-17 5:41:1921 secs ago1752730879IN
0xF75584eF...0B8eA22Cf
0.0000006 ETH0.0000002634250.00066056
Route Single1385660502025-07-17 5:41:1723 secs ago1752730877IN
0xF75584eF...0B8eA22Cf
110 wei0.0000007006320.0012455
Route Single1385660472025-07-17 5:41:1129 secs ago1752730871IN
0xF75584eF...0B8eA22Cf
0.0000005 ETH0.0000002565760.0006622
Route Single1385660472025-07-17 5:41:1129 secs ago1752730871IN
0xF75584eF...0B8eA22Cf
0.0000005 ETH0.0000002504190.0006622
Route Single1385660472025-07-17 5:41:1129 secs ago1752730871IN
0xF75584eF...0B8eA22Cf
0.0000005 ETH0.0000002653250.0006622
Route Single1385660462025-07-17 5:41:0931 secs ago1752730869IN
0xF75584eF...0B8eA22Cf
0.0000005 ETH0.0000002504940.00066244
Route Single1385660462025-07-17 5:41:0931 secs ago1752730869IN
0xF75584eF...0B8eA22Cf
0.0000006 ETH0.0000002566380.00066244
Route Single1385660462025-07-17 5:41:0931 secs ago1752730869IN
0xF75584eF...0B8eA22Cf
0.0000006 ETH0.0000002566950.00066244
Route Single1385660462025-07-17 5:41:0931 secs ago1752730869IN
0xF75584eF...0B8eA22Cf
0.0000005 ETH0.0000002504860.00066244
Route Single1385660462025-07-17 5:41:0931 secs ago1752730869IN
0xF75584eF...0B8eA22Cf
0.0000006 ETH0.0000002566380.00066244
Route Single1385660462025-07-17 5:41:0931 secs ago1752730869IN
0xF75584eF...0B8eA22Cf
0.0000006 ETH0.000000265440.00066244
Route Single1385660452025-07-17 5:41:0733 secs ago1752730867IN
0xF75584eF...0B8eA22Cf
0.0000011 ETH0.000000344390.00066379
Route Single1385660452025-07-17 5:41:0733 secs ago1752730867IN
0xF75584eF...0B8eA22Cf
0.0000013 ETH0.000000344520.00066379
Route Single1385660452025-07-17 5:41:0733 secs ago1752730867IN
0xF75584eF...0B8eA22Cf
0.0000011 ETH0.0000003662220.00071917
Route Single1385660432025-07-17 5:41:0337 secs ago1752730863IN
0xF75584eF...0B8eA22Cf
0.0000015 ETH0.0000003442190.00066359
Route Single1385660432025-07-17 5:41:0337 secs ago1752730863IN
0xF75584eF...0B8eA22Cf
0.0000014 ETH0.0000003802920.00071897
Route Single1385660422025-07-17 5:41:0139 secs ago1752730861IN
0xF75584eF...0B8eA22Cf
0 ETH0.0000000871310.00066324
Route Single1385660412025-07-17 5:40:5941 secs ago1752730859IN
0xF75584eF...0B8eA22Cf
0.00000126 ETH0.0000002644090.00066338
Route Single1385660402025-07-17 5:40:5743 secs ago1752730857IN
0xF75584eF...0B8eA22Cf
0.0000014 ETH0.0000002557780.0006632
Route Single1385660402025-07-17 5:40:5743 secs ago1752730857IN
0xF75584eF...0B8eA22Cf
0.0000013 ETH0.0000002556520.0006632
Route Single1385660392025-07-17 5:40:5545 secs ago1752730855IN
0xF75584eF...0B8eA22Cf
0.0000014 ETH0.0000002496790.0006636
Route Single1385660392025-07-17 5:40:5545 secs ago1752730855IN
0xF75584eF...0B8eA22Cf
0.0000012 ETH0.0000002497980.0006636
Route Single1385660392025-07-17 5:40:5545 secs ago1752730855IN
0xF75584eF...0B8eA22Cf
0.0000011 ETH0.0000002763020.00071898
Route Single1385660382025-07-17 5:40:5347 secs ago1752730853IN
0xF75584eF...0B8eA22Cf
0.00000166 ETH0.000000283690.00074058
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1385660562025-07-17 5:41:2911 secs ago1752730889
0xF75584eF...0B8eA22Cf
0.00001 ETH
1385660512025-07-17 5:41:1921 secs ago1752730879
0xF75584eF...0B8eA22Cf
0.0000006 ETH
1385660502025-07-17 5:41:1723 secs ago1752730877
0xF75584eF...0B8eA22Cf
110 wei
1385660472025-07-17 5:41:1129 secs ago1752730871
0xF75584eF...0B8eA22Cf
0.0000005 ETH
1385660472025-07-17 5:41:1129 secs ago1752730871
0xF75584eF...0B8eA22Cf
0.0000005 ETH
1385660472025-07-17 5:41:1129 secs ago1752730871
0xF75584eF...0B8eA22Cf
0.0000005 ETH
1385660462025-07-17 5:41:0931 secs ago1752730869
0xF75584eF...0B8eA22Cf
0.0000005 ETH
1385660462025-07-17 5:41:0931 secs ago1752730869
0xF75584eF...0B8eA22Cf
0.0000006 ETH
1385660462025-07-17 5:41:0931 secs ago1752730869
0xF75584eF...0B8eA22Cf
0.0000006 ETH
1385660462025-07-17 5:41:0931 secs ago1752730869
0xF75584eF...0B8eA22Cf
0.0000005 ETH
1385660462025-07-17 5:41:0931 secs ago1752730869
0xF75584eF...0B8eA22Cf
0.0000006 ETH
1385660462025-07-17 5:41:0931 secs ago1752730869
0xF75584eF...0B8eA22Cf
0.0000006 ETH
1385660452025-07-17 5:41:0733 secs ago1752730867
0xF75584eF...0B8eA22Cf
0.0000011 ETH
1385660452025-07-17 5:41:0733 secs ago1752730867
0xF75584eF...0B8eA22Cf
0.0000013 ETH
1385660452025-07-17 5:41:0733 secs ago1752730867
0xF75584eF...0B8eA22Cf
0.0000011 ETH
1385660432025-07-17 5:41:0337 secs ago1752730863
0xF75584eF...0B8eA22Cf
0.0000015 ETH
1385660432025-07-17 5:41:0337 secs ago1752730863
0xF75584eF...0B8eA22Cf
0.0000014 ETH
1385660412025-07-17 5:40:5941 secs ago1752730859
0xF75584eF...0B8eA22Cf
0.00000126 ETH
1385660402025-07-17 5:40:5743 secs ago1752730857
0xF75584eF...0B8eA22Cf
0.0000014 ETH
1385660402025-07-17 5:40:5743 secs ago1752730857
0xF75584eF...0B8eA22Cf
0.0000013 ETH
1385660392025-07-17 5:40:5545 secs ago1752730855
0xF75584eF...0B8eA22Cf
0.0000014 ETH
1385660392025-07-17 5:40:5545 secs ago1752730855
0xF75584eF...0B8eA22Cf
0.0000012 ETH
1385660392025-07-17 5:40:5545 secs ago1752730855
0xF75584eF...0B8eA22Cf
0.0000011 ETH
1385660382025-07-17 5:40:5347 secs ago1752730853
0xF75584eF...0B8eA22Cf
0.00000166 ETH
1385660372025-07-17 5:40:5149 secs ago1752730851
0xF75584eF...0B8eA22Cf
0.00000182 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EnsoRouter

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

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

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

enum TokenType {
    Native,
    ERC20,
    ERC721,
    ERC1155
}

struct Token {
    TokenType tokenType;
    bytes data;
}

contract EnsoRouter {
    using SafeERC20 for IERC20;

    address public immutable shortcuts;

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

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

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

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

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

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

        response = routeMulti(tokensIn, data);

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

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

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

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

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

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

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

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

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

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

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

contract EnsoShortcuts is AbstractEnsoShortcuts {
    address immutable public executor;

    error NotPermitted();

    constructor(address executor_) {
        executor = executor_;
    }

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

File 2 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 2 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 2 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 2 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 2 of 18 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

Contract Security Audit

Contract ABI

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

60a0806040523461009a57611548818101906001600160401b03821183831017610086576020918391610d9083393081520301905ff0801561007b576001600160a01b0316608052604051610cf1908161009f82396080518181816101fc015281816109d001528181610a8e01528181610b3f0152610bb20152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806321025a0614610064578063b94c36091461005f578063ea270f421461005a578063f35cae90146100555763f52e33f514610050575f80fd5b610380565b61025b565b6101e7565b610186565b608036600319011261010c576004356001600160401b03811161010c5761008f903690600401610110565b6024356001600160401b03811161010c576100ae903690600401610110565b604435916100bb8361011e565b606435906001600160401b03821161010c57610108936100f66100e56100fc94369060040161012f565b906100f084886105fb565b946103da565b93610817565b6040519182918261015c565b0390f35b5f80fd5b9081604091031261010c5790565b6001600160a01b0381160361010c57565b9181601f8401121561010c578235916001600160401b03831161010c576020838186019501011161010c57565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b604036600319011261010c576004356001600160401b03811161010c576101b1903690600401610110565b6024356001600160401b03811161010c57610108916101d76100fc92369060040161012f565b916103da565b5f91031261010c57565b3461010c575f36600319011261010c576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9181601f8401121561010c578235916001600160401b03831161010c576020808501948460051b01011161010c57565b608036600319011261010c576004356001600160401b03811161010c5761028690369060040161022b565b6024356001600160401b03811161010c576102a590369060040161022b565b9190604435916102b48361011e565b6064356001600160401b03811161010c576102d390369060040161012f565b906102dd86610457565b926102eb6040519485610431565b868452601f196102fa88610457565b013660208601375f5b8781106103585750906103179291886104bd565b925f5b85811061032f5760405180610108878261015c565b806103526103406001938988610482565b8461034b84886104a9565b5191610817565b0161031a565b8061036f8861036a6001948c8b610482565b6105fb565b61037982886104a9565b5201610303565b604036600319011261010c576004356001600160401b03811161010c576103ab90369060040161022b565b602435906001600160401b03821161010c57610108926103d26100fc93369060040161012f565b9290916104bd565b6103e69092919261099b565b1580610414575b6103fd576103fa91610b9b565b90565b63dfd87a7360e01b5f52346004525f60245260445ffd5b503415156103ed565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761045257604052565b61041d565b6001600160401b0381116104525760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b91908110156104a45760051b81013590603e198136030182121561010c570190565b61046e565b80518210156104a45760209160051b010190565b9291925f915f5b8181106104e1575050501580610414576103fd576103fa91610b9b565b6104f46104ef828486610482565b61099b565b610501575b6001016104c4565b9261050e576001926104f9565b63eacf98b760e01b5f5260045ffd5b6004111561010c57565b6004111561053157565b634e487b7160e01b5f52602160045260245ffd5b356103fa8161051d565b9060048210156105315752565b9061056a600460249361054f565b565b903590601e198136030182121561010c57018035906001600160401b03821161010c5760200191813603831361010c57565b9081606091031261010c5780356105b48161011e565b916040602083013592013590565b9081602091031261010c575190565b6040513d5f823e3d90fd5b919082604091031261010c57602082356105f58161011e565b92013590565b61060481610545565b61060d81610527565b600181036106ae57506106629161063461062c8360208095019061056c565b8101906105dc565b506040516370a0823160e01b81526001600160a01b0390921660048301529092839190829081906024820190565b03916001600160a01b03165afa9081156106a9575f91610680575090565b6103fa915060203d6020116106a2575b61069a8183610431565b8101906105c2565b503d610690565b6105d1565b6106b781610527565b806106c25750503190565b6106cb81610527565b600281036106ea57506106629161063461062c8360208095019061056c565b6106f381610527565b6003810361074c57506106629161071a6107128360208095019061056c565b81019061059e565b50604051627eeac760e11b81526001600160a01b03909316600484015260248301529092839190829081906044820190565b63292c317f60e11b5f5261075f9061055c565b5ffd5b9081602091031261010c573590565b9190820391821161077e57565b634e487b7160e01b5f52601160045260245ffd5b9093929193606082526107b381356107a98161051d565b606084019061054f565b6020810135601e198236030181121561010c5701906020823592016001600160401b03831161010c57823603811361010c578260409360c0928560808601528160a0860152838501375f828285010152601f80199101168201019460208201520152565b909161082282610545565b61082b81610527565b600181036108e7575061087690602061084961062c8286018661056c565b6040516370a0823160e01b81526001600160a01b0390971660048801529593849190829081906024820190565b03916001600160a01b03165afa80156106a95761089a925f916108c8575b50610771565b918083106108a757505050565b6108c490604051938493631cab723f60e21b855260048501610792565b0390fd5b6108e1915060203d6020116106a25761069a8183610431565b5f610894565b6108f081610527565b80610919575061089a9061091261090a602085018561056c565b810190610762565b9331610771565b61092281610527565b60028103610940575061087690602061084961062c8286018661056c565b61094981610527565b6003810361074c57506108769060206109676107128286018661056c565b604051627eeac760e11b81526001600160a01b03909816600489015260248801919091529593849190829081906044820190565b905f916109a781610545565b6109b081610527565b600181036109f757506109cd61062c82602061056a94019061056c565b907f0000000000000000000000000000000000000000000000000000000000000000903390610c24565b610a0081610527565b80610a3d5750610a1a91925080602061090a92019061056c565b803403610a275750600190565b63dfd87a7360e01b5f523460045260245260445ffd5b610a4681610527565b60028103610aec575061062c816020610a6093019061056c565b906001600160a01b0316803b1561010c57604051632142170760e11b81523360048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016602482015260448101929092525f908290818381606481015b03925af180156106a957610ad85750565b80610ae65f61056a93610431565b806101dd565b610af581610527565b6003810361074c5750610712816020610b0f93019061056c565b6001600160a01b0390921691823b1561010c57604051637921219560e11b81523360048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201526044810192909252606482015260a06084820152600260a482015261060f60f31b60c4820152905f90829081838160e48101610ac7565b5f91829181604051928392833781018381520390347f00000000000000000000000000000000000000000000000000000000000000005af1903d15610c1d573d6001600160401b0381116104525760405190610c01601f8201601f191660200183610431565b81523d5f602083013e5b809215610c155750565b602081519101fd5b6060610c0b565b6040516323b872dd60e01b60208083019182526001600160a01b039485166024840152949093166044820152606480820195909552938452925f9190610c6b608482610431565b519082855af1156105d1575f513d610cb257506001600160a01b0381163b155b610c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415610c8b56fea2646970667358221220b31f038bfacab66c91c151fafd3ad97c27ed33ae04b9aea97887965ea369f60564736f6c634300081c003360a034606d57601f61154838819003918201601f19168301916001600160401b03831184841017607157808492602094604052833981010312606d57516001600160a01b0381168103606d576080526040516114c29081610086823960805181818161047201526105630152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c806301ffc9a714610079578063150b7a021461007457806395352c9f1461006f578063bc197c811461006a578063c34c08e5146100655763f23a6e610361000e576104a1565b61045d565b6103c8565b6102a0565b610198565b346100cd5760203660031901126100cd5760043563ffffffff60e01b81168091036100cd57630271189760e51b81149081156100bc575b50151560805260206080f35b6301ffc9a760e01b149050816100b0565b5f80fd5b600435906001600160a01b03821682036100cd57565b602435906001600160a01b03821682036100cd57565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761013257604052565b6100fd565b6001600160401b03811161013257601f01601f191660200190565b81601f820112156100cd5780359061016982610137565b926101776040519485610111565b828452602083830101116100cd57815f926020809301838601378301015290565b346100cd5760803660031901126100cd576101b16100d1565b506101ba6100e7565b506064356001600160401b0381116100cd576101da903690600401610152565b50604051630a85bd0160e11b8152602090f35b9181601f840112156100cd578235916001600160401b0383116100cd576020808501948460051b0101116100cd57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b83831061027357505050505090565b9091929394602080610291600193603f19868203018752895161021d565b97019301930191939290610264565b60803660031901126100cd576024356004356044356001600160401b0381116100cd576102d19036906004016101ed565b90926064356001600160401b0381116100cd57610350947f049d8dd84b6a6cc45d5f68a74b23450bd3e54d84fd405d91b8b286c78d51d2499361032d61031e6103339436906004016101ed565b610326610561565b36916104f6565b916106fb565b60408051948552602085019290925292a160405191829182610241565b0390f35b6001600160401b0381116101325760051b60200190565b9080601f830112156100cd57813561038281610354565b926103906040519485610111565b81845260208085019260051b8201019283116100cd57602001905b8282106103b85750505090565b81358152602091820191016103ab565b346100cd5760a03660031901126100cd576103e16100d1565b506103ea6100e7565b506044356001600160401b0381116100cd5761040a90369060040161036b565b506064356001600160401b0381116100cd5761042a90369060040161036b565b506084356001600160401b0381116100cd5761044a903690600401610152565b5060405163bc197c8160e01b8152602090f35b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd5760a03660031901126100cd576104ba6100d1565b506104c36100e7565b506084356001600160401b0381116100cd576104e3903690600401610152565b5060405163f23a6e6160e01b8152602090f35b92919061050281610354565b936105106040519586610111565b602085838152019160051b8101918383116100cd5781905b838210610536575050505050565b81356001600160401b0381116100cd576020916105568784938701610152565b815201910190610528565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361059357565b6339218f3b60e01b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b91908110156105c65760051b0190565b6105a2565b80518210156105c65760209160051b010190565b156105e657565b60405162461bcd60e51b815260206004820152601660248201527556616c7565206d75737420626520333220627974657360501b6044820152606490fd5b602081519101519060208110610638575090565b5f199060200360031b1b1690565b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161066857565b610646565b601f1981019190821161066857565b3d156106a6573d9061068d82610137565b9161069b6040519384610111565b82523d5f602084013e565b606090565b604051906106ba604083610111565b60078252662ab735b737bbb760c91b6020830152565b9081526001600160a01b0390911660208201526060604082018190526106f89291019061021d565b90565b905f5b81811061070b5750505090565b6107168183856105b6565b359061074461073e61073861072b8560201b90565b6001600160f81b03191690565b60f81c90565b60ff1690565b9160408316159182610a02576001019261075f8486886105b6565b356020905b60038316600181036108bd57505f91829190602085166108a657610793916001600160e01b031987168c610a7b565b805190602001826001600160a01b0387165af1926107af61067c565b935b84901561080a575050608016156107e8576107e392916107d761072b6107dd9360581b90565b87610f7f565b60010190565b6106fe565b906107e392956107fe61072b6108049460581b90565b90610ed4565b936107dd565b8592506108156106ab565b946044815111610865575b505090610852915f1461085657925b60405163ef3dcb2f60e01b81529384936001600160a01b031690600485016106d0565b0390fd5b61085f9061065a565b9261082f565b602061087c600461087584610ca8565b9301610624565b036108205761088d60248301610624565b14610899575b80610820565b6044019350610852610893565b50607f6108b79160f81c168a6105cb565b51610793565b6002810361092057505f9182919060208516610909576108e8916001600160e01b031987168c610a7b565b8051906020016001600160a01b0386165afa9261090361067c565b936107b1565b50607f61091a9160f81c168a6105cb565b516108e8565b6003036109ca575f918161095161093e607f869560f81c168d6105cb565b5161094c60208251146105df565b610624565b91602086161584146109a35761097361096d6109859360081b90565b9161065a565b906001600160e01b031988168d610a7b565b905b815191602001906001600160a01b0387165af19261090361067c565b50607f6109bc61073e61073861072b6109c39560081b90565b168b6105cb565b5190610987565b60405162461bcd60e51b815260206004820152601060248201526f496e76616c69642063616c6c7479706560801b6044820152606490fd5b926006602883901b6001600160d01b0317610764565b6040516101609190610a2a8382610111565b600a815291601f1901366020840137565b906004820180921161066857565b90610a5382610137565b610a606040519182610111565b8281528092610a71601f1991610137565b0190602036910137565b91939290935f5f915f92610a8d610a18565b906060935f905b878210610be9575b5050610aaa610aaf91610a3b565b610a49565b9760208901525f9060248901925f955b878710610ad157505050505050505050565b60208710156105c657888b83891a6080811615610bc75760fe8103610b2c575050506020816001928752610b198d610b0883610a3b565b610b128b5161066d565b918b61112d565b875101601f1901955b0196019593610abf565b60fd819794959697145f14610b61575092610b5592868660019b948a9998978560209c52611283565b50979195909594610b22565b60fc8103610b83575092610b5592868660019b948a9998978560209c5261113f565b93610bc0888293610baf602096607f6001999c9b9a1690610ba482826105cb565b5151978895526105cb565b5190610bba85610a3b565b9161111b565b0195610b22565b60209250600193979150610bdf607f8492168d6105cb565b5101518152610b22565b9093959160208510156105c65786851a60ff8114610c94576080811615610c7f5760fe8103610c5f5750855115610c2f575b6020600191875101935b0196940190610a94565b945060016020604051610c5681610c488d858301610241565b03601f198101835282610111565b96915050610c1b565b90610c739260019692602095968a8d61104c565b95919390939294610c25565b610c8e6020916001938c610fd9565b93610c25565b509396509094929050610aaa610aaf610a9c565b9081516043198101818111610668579260445b828110610cc757505050565b81518110156105c657818101602001516001600160f81b03191615610cee57600101610cbb565b929350505060431981019081116106685790565b15610d0957565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f75742d6f662d626f756e647360681b6044820152606490fd5b15610d4b57565b60405162461bcd60e51b815260206004820152602860248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d697474656420604482015267287374617469632960c01b6064820152608490fd5b15610da857565b60405162461bcd60e51b815260206004820152602a60248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d697474656420604482015269287661726961626c652960b01b6064820152608490fd5b6020818303126100cd578051906001600160401b0382116100cd57019080601f830112156100cd57815191610e3483610354565b92610e426040519485610111565b80845260208085019160051b830101918383116100cd5760208101915b838310610e6e57505050505090565b82516001600160401b0381116100cd57820185603f820112156100cd57602081015191610e9a83610137565b610ea76040519182610111565b83815260408385010188106100cd575f602085819660408397018386015e83010152815201920191610e5f565b91908060f81c60ff8114610f79576080811615610f415760fe8103610f095750506106f8915060208082518301019101610e00565b602091610f1f610fe092607f8751911610610d02565b82840193610f2f84865114610da1565b51601f1901845260f31c168301015290565b610f75929150607f1690610f5784518310610d02565b610f646020825114610d44565b610f6e82856105cb565b52826105cb565b5090565b50505090565b9060f81c60ff8114610fd4578251906020820180921161066857602092607f610faa610fbd94610a49565b921691610fb783836105cb565b526105cb565b51918051604084018184840160045afa5051910152565b505050565b610fea90607f6020939416906105cb565b515103610ff75760200190565b60405162461bcd60e51b815260206004820152602760248201527f537461746963207374617465207661726961626c6573206d75737420626520336044820152663220627974657360c81b6064820152608490fd5b90969594939260fd810361106c5750956110669596611410565b90919293565b60fc81036110805750956110669596611329565b919650919493929161109591607f16906105cb565b515180151580611110575b156110ac570160200191565b60405162461bcd60e51b815260206004820152603660248201527f44796e616d6963207374617465207661726961626c6573206d7573742062652060448201527561206d756c7469706c65206f6620333220627974657360501b6064820152608490fd5b50601f8116156110a0565b916020809185930101920160045afa50565b910160200190829060400160045afa50565b9193959692905f9461115188846105cb565b51936024600180878b019b019b0198820101915b60208910611176575b505050505050565b80891a608081161561125a5760fb8103611190575061116e565b60fd819c92959a9499969b93979c145f146111e15750906111b892918b89528a858b89611283565b9260209a93926001928c969480919d939d97929d9e01970101985b019301979291939490611165565b60fc810361121b5750906111fc92918b89528a858b8961113f565b9260209a93926001928c969480919d939d97929d9e01970101986111d3565b6020898b8e6001959f9e97989661124e607f869716948d610baf61123f88886105cb565b515197889788978895526105cb565b019d01970101986111d3565b602060019293979694998161127788607f999e99839616906105cb565b5101518b5201986111d3565b9392919094956001019460208610156105c65760206112bd97816112ac607f868b1a16856105cb565b51015160248289010152019461113f565b929391929091602090910190565b156112d257565b60405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207573652073746174652066726f6d20696e736964652064796e604482015268616d6963207479706560b81b6064820152608490fd5b6001808501976020909601960194935f929091905b602087106113975760405162461bcd60e51b8152602060048201526024808201527f44796e616d6963207479706520776173206e6f742070726f7065726c7920636c6044820152631bdcd95960e21b6064820152608490fd5b80871a60808116156113f85760fb81036113bf575050506113b890836105cb565b5293929190565b6113df94956113d860fe839c949a969b959c14156112cb565b888b61104c565b9197909692939192916001906020905b0193019561133e565b61140a60209160019399969a85610fd9565b986113ef565b92919093946001019360208510156105c6576020611433607f83881a16866105cb565b515103611447576020611066960193611329565b60405162461bcd60e51b815260206004820152601d60248201527f4172726179206c656e677468206d7573742062652033322062797465730000006044820152606490fdfea2646970667358221220e2fb320215783417f0d03eadac743b8ea521353ce3d3b7d6166a03b67b81aff564736f6c634300081c0033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806321025a0614610064578063b94c36091461005f578063ea270f421461005a578063f35cae90146100555763f52e33f514610050575f80fd5b610380565b61025b565b6101e7565b610186565b608036600319011261010c576004356001600160401b03811161010c5761008f903690600401610110565b6024356001600160401b03811161010c576100ae903690600401610110565b604435916100bb8361011e565b606435906001600160401b03821161010c57610108936100f66100e56100fc94369060040161012f565b906100f084886105fb565b946103da565b93610817565b6040519182918261015c565b0390f35b5f80fd5b9081604091031261010c5790565b6001600160a01b0381160361010c57565b9181601f8401121561010c578235916001600160401b03831161010c576020838186019501011161010c57565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b604036600319011261010c576004356001600160401b03811161010c576101b1903690600401610110565b6024356001600160401b03811161010c57610108916101d76100fc92369060040161012f565b916103da565b5f91031261010c57565b3461010c575f36600319011261010c576040517f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e0036001600160a01b03168152602090f35b9181601f8401121561010c578235916001600160401b03831161010c576020808501948460051b01011161010c57565b608036600319011261010c576004356001600160401b03811161010c5761028690369060040161022b565b6024356001600160401b03811161010c576102a590369060040161022b565b9190604435916102b48361011e565b6064356001600160401b03811161010c576102d390369060040161012f565b906102dd86610457565b926102eb6040519485610431565b868452601f196102fa88610457565b013660208601375f5b8781106103585750906103179291886104bd565b925f5b85811061032f5760405180610108878261015c565b806103526103406001938988610482565b8461034b84886104a9565b5191610817565b0161031a565b8061036f8861036a6001948c8b610482565b6105fb565b61037982886104a9565b5201610303565b604036600319011261010c576004356001600160401b03811161010c576103ab90369060040161022b565b602435906001600160401b03821161010c57610108926103d26100fc93369060040161012f565b9290916104bd565b6103e69092919261099b565b1580610414575b6103fd576103fa91610b9b565b90565b63dfd87a7360e01b5f52346004525f60245260445ffd5b503415156103ed565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761045257604052565b61041d565b6001600160401b0381116104525760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b91908110156104a45760051b81013590603e198136030182121561010c570190565b61046e565b80518210156104a45760209160051b010190565b9291925f915f5b8181106104e1575050501580610414576103fd576103fa91610b9b565b6104f46104ef828486610482565b61099b565b610501575b6001016104c4565b9261050e576001926104f9565b63eacf98b760e01b5f5260045ffd5b6004111561010c57565b6004111561053157565b634e487b7160e01b5f52602160045260245ffd5b356103fa8161051d565b9060048210156105315752565b9061056a600460249361054f565b565b903590601e198136030182121561010c57018035906001600160401b03821161010c5760200191813603831361010c57565b9081606091031261010c5780356105b48161011e565b916040602083013592013590565b9081602091031261010c575190565b6040513d5f823e3d90fd5b919082604091031261010c57602082356105f58161011e565b92013590565b61060481610545565b61060d81610527565b600181036106ae57506106629161063461062c8360208095019061056c565b8101906105dc565b506040516370a0823160e01b81526001600160a01b0390921660048301529092839190829081906024820190565b03916001600160a01b03165afa9081156106a9575f91610680575090565b6103fa915060203d6020116106a2575b61069a8183610431565b8101906105c2565b503d610690565b6105d1565b6106b781610527565b806106c25750503190565b6106cb81610527565b600281036106ea57506106629161063461062c8360208095019061056c565b6106f381610527565b6003810361074c57506106629161071a6107128360208095019061056c565b81019061059e565b50604051627eeac760e11b81526001600160a01b03909316600484015260248301529092839190829081906044820190565b63292c317f60e11b5f5261075f9061055c565b5ffd5b9081602091031261010c573590565b9190820391821161077e57565b634e487b7160e01b5f52601160045260245ffd5b9093929193606082526107b381356107a98161051d565b606084019061054f565b6020810135601e198236030181121561010c5701906020823592016001600160401b03831161010c57823603811361010c578260409360c0928560808601528160a0860152838501375f828285010152601f80199101168201019460208201520152565b909161082282610545565b61082b81610527565b600181036108e7575061087690602061084961062c8286018661056c565b6040516370a0823160e01b81526001600160a01b0390971660048801529593849190829081906024820190565b03916001600160a01b03165afa80156106a95761089a925f916108c8575b50610771565b918083106108a757505050565b6108c490604051938493631cab723f60e21b855260048501610792565b0390fd5b6108e1915060203d6020116106a25761069a8183610431565b5f610894565b6108f081610527565b80610919575061089a9061091261090a602085018561056c565b810190610762565b9331610771565b61092281610527565b60028103610940575061087690602061084961062c8286018661056c565b61094981610527565b6003810361074c57506108769060206109676107128286018661056c565b604051627eeac760e11b81526001600160a01b03909816600489015260248801919091529593849190829081906044820190565b905f916109a781610545565b6109b081610527565b600181036109f757506109cd61062c82602061056a94019061056c565b907f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e003903390610c24565b610a0081610527565b80610a3d5750610a1a91925080602061090a92019061056c565b803403610a275750600190565b63dfd87a7360e01b5f523460045260245260445ffd5b610a4681610527565b60028103610aec575061062c816020610a6093019061056c565b906001600160a01b0316803b1561010c57604051632142170760e11b81523360048201526001600160a01b037f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e00316602482015260448101929092525f908290818381606481015b03925af180156106a957610ad85750565b80610ae65f61056a93610431565b806101dd565b610af581610527565b6003810361074c5750610712816020610b0f93019061056c565b6001600160a01b0390921691823b1561010c57604051637921219560e11b81523360048201526001600160a01b037f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e0031660248201526044810192909252606482015260a06084820152600260a482015261060f60f31b60c4820152905f90829081838160e48101610ac7565b5f91829181604051928392833781018381520390347f0000000000000000000000004fe93ebc4ce6ae4f81601cc7ce7139023919e0035af1903d15610c1d573d6001600160401b0381116104525760405190610c01601f8201601f191660200183610431565b81523d5f602083013e5b809215610c155750565b602081519101fd5b6060610c0b565b6040516323b872dd60e01b60208083019182526001600160a01b039485166024840152949093166044820152606480820195909552938452925f9190610c6b608482610431565b519082855af1156105d1575f513d610cb257506001600160a01b0381163b155b610c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415610c8b56fea2646970667358221220b31f038bfacab66c91c151fafd3ad97c27ed33ae04b9aea97887965ea369f60564736f6c634300081c0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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