ETH Price: $4,534.35 (+1.90%)

Contract

0x3067BDBa0e6628497d527bEF511c22DA8b32cA3F

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Route Single132234352025-10-08 23:30:467 secs ago1759966246IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005460.03093195
Route Single132233912025-10-08 23:30:0251 secs ago1759966202IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005770.03293371
Route Single132233692025-10-08 23:29:401 min ago1759966180IN
0x3067BDBa...A8b32cA3F
0 ETH0.00000460.02055026
Route Single132233492025-10-08 23:29:201 min ago1759966160IN
0x3067BDBa...A8b32cA3F
0 ETH0.000006280.03294105
Route Single132233252025-10-08 23:28:561 min ago1759966136IN
0x3067BDBa...A8b32cA3F
0 ETH0.000006280.03294552
Route Single132232642025-10-08 23:27:552 mins ago1759966075IN
0x3067BDBa...A8b32cA3F
0 ETH0.000001350.00000026
Route Single132232422025-10-08 23:27:333 mins ago1759966053IN
0x3067BDBa...A8b32cA3F
0 ETH0.00000460.02056026
Route Single132232172025-10-08 23:27:083 mins ago1759966028IN
0x3067BDBa...A8b32cA3F
0 ETH0.000004110.02057
Route Single132231592025-10-08 23:26:104 mins ago1759965970IN
0x3067BDBa...A8b32cA3F
0 ETH0.00000460.02057275
Route Single132231422025-10-08 23:25:535 mins ago1759965953IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005420.03036274
Route Single132231422025-10-08 23:25:535 mins ago1759965953IN
0x3067BDBa...A8b32cA3F
0 ETH0.000013630.03036274
Route Single132231222025-10-08 23:25:335 mins ago1759965933IN
0x3067BDBa...A8b32cA3F
0 ETH0.000006690.04015024
Route Single132231202025-10-08 23:25:315 mins ago1759965931IN
0x3067BDBa...A8b32cA3F
0 ETH0.00000150.00100026
Route Single132231152025-10-08 23:25:265 mins ago1759965926IN
0x3067BDBa...A8b32cA3F
0 ETH0.000004080.02057026
Route Single132230932025-10-08 23:25:045 mins ago1759965904IN
0x3067BDBa...A8b32cA3F
0 ETH0.00000460.02058
Route Single132230662025-10-08 23:24:376 mins ago1759965877IN
0x3067BDBa...A8b32cA3F
0 ETH0.000004180.02058476
Route Single132230542025-10-08 23:24:256 mins ago1759965865IN
0x3067BDBa...A8b32cA3F
0 ETH0.000004090.02058476
Route Single132230422025-10-08 23:24:136 mins ago1759965853IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005490.0262
Route Single132230322025-10-08 23:24:036 mins ago1759965843IN
0x3067BDBa...A8b32cA3F
0 ETH0.000007360.04016
Route Single132230252025-10-08 23:23:566 mins ago1759965836IN
0x3067BDBa...A8b32cA3F
0 ETH0.000004110.02058476
Route Single132230222025-10-08 23:23:537 mins ago1759965833IN
0x3067BDBa...A8b32cA3F
0 ETH0.000001510.00100026
Route Single132229862025-10-08 23:23:177 mins ago1759965797IN
0x3067BDBa...A8b32cA3F
0 ETH0.00000150.00100026
Route Single132229772025-10-08 23:23:087 mins ago1759965788IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005490.02621
Route Single132229722025-10-08 23:23:037 mins ago1759965783IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005270.02620026
Route Single132229702025-10-08 23:23:017 mins ago1759965781IN
0x3067BDBa...A8b32cA3F
0 ETH0.000005270.02620026
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
132202852025-10-08 22:38:1652 mins ago1759963096
0x3067BDBa...A8b32cA3F
0.099 ETH
132196042025-10-08 22:26:551 hr ago1759962415
0x3067BDBa...A8b32cA3F
0.0098 ETH
132195292025-10-08 22:25:401 hr ago1759962340
0x3067BDBa...A8b32cA3F
0.01 ETH
132194572025-10-08 22:24:281 hr ago1759962268
0x3067BDBa...A8b32cA3F
0.014 ETH
132191612025-10-08 22:19:321 hr ago1759961972
0x3067BDBa...A8b32cA3F
0.0128 ETH
132190372025-10-08 22:17:281 hr ago1759961848
0x3067BDBa...A8b32cA3F
0.01202749 ETH
132189272025-10-08 22:15:381 hr ago1759961738
0x3067BDBa...A8b32cA3F
0.023 ETH
132176952025-10-08 21:55:061 hr ago1759960506
0x3067BDBa...A8b32cA3F
0.00001 ETH
132174782025-10-08 21:51:291 hr ago1759960289
0x3067BDBa...A8b32cA3F
1.295 ETH
132168352025-10-08 21:40:461 hr ago1759959646
0x3067BDBa...A8b32cA3F
0.00085 ETH
132166732025-10-08 21:38:041 hr ago1759959484
0x3067BDBa...A8b32cA3F
0.4 ETH
132165822025-10-08 21:36:331 hr ago1759959393
0x3067BDBa...A8b32cA3F
0.0007 ETH
132160982025-10-08 21:28:292 hrs ago1759958909
0x3067BDBa...A8b32cA3F
0.0001 ETH
132160862025-10-08 21:28:172 hrs ago1759958897
0x3067BDBa...A8b32cA3F
0.0001 ETH
132157962025-10-08 21:23:272 hrs ago1759958607
0x3067BDBa...A8b32cA3F
0.06450831 ETH
132153222025-10-08 21:15:332 hrs ago1759958133
0x3067BDBa...A8b32cA3F
0.2 ETH
132147302025-10-08 21:05:412 hrs ago1759957541
0x3067BDBa...A8b32cA3F
1.13 ETH
132145262025-10-08 21:02:172 hrs ago1759957337
0x3067BDBa...A8b32cA3F
0.27 ETH
132143272025-10-08 20:58:582 hrs ago1759957138
0x3067BDBa...A8b32cA3F
0.0159 ETH
132142782025-10-08 20:58:092 hrs ago1759957089
0x3067BDBa...A8b32cA3F
0.00017 ETH
132139972025-10-08 20:53:282 hrs ago1759956808
0x3067BDBa...A8b32cA3F
0.0131 ETH
132139192025-10-08 20:52:102 hrs ago1759956730
0x3067BDBa...A8b32cA3F
0.0001 ETH
132131832025-10-08 20:39:542 hrs ago1759955994
0x3067BDBa...A8b32cA3F
0.0001 ETH
132098862025-10-08 19:44:573 hrs ago1759952697
0x3067BDBa...A8b32cA3F
0.0003 ETH
132072802025-10-08 19:01:314 hrs ago1759950091
0x3067BDBa...A8b32cA3F
0.02165 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 { IERC1155 } from "openzeppelin-contracts/token/ERC1155/IERC1155.sol";
import { IERC20, SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC721 } from "openzeppelin-contracts/token/ERC721/IERC721.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.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
                )
            )
        }
    }
}

// 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;
    }
}

// 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);
}

File 5 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";

File 6 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";

// 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;
}

// 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);
}

// 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;
    }
}

// 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.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);
}

File 13 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);
}

File 14 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) (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) (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);
}

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

import { VM } from "enso-weiroll/VM.sol";

import { ERC1155Holder } from "openzeppelin-contracts/token/ERC1155/utils/ERC1155Holder.sol";
import { ERC721Holder } from "openzeppelin-contracts/token/ERC721/utils/ERC721Holder.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
        payable
        virtual
        returns (bytes[] memory response)
    {
        _checkMsgSender();
        response = _execute(commands, state);
        emit ShortcutExecuted(accountId, requestId);
    }

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

    receive() external payable virtual { }
}

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

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

contract EnsoShortcuts is AbstractEnsoShortcuts {
    address public immutable executor;

    error NotPermitted();

    constructor(address executor_) {
        executor = executor_;
    }

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

Settings
{
  "evmVersion": "cancun",
  "libraries": {},
  "metadata": {
    "appendCBOR": false,
    "bytecodeHash": "none",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "@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/",
    "@uniswap/v4-core/=lib/v4-core/",
    "@uniswap/v4-periphery/=lib/v4-periphery/",
    "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/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "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/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solady/lib/solmate/src/",
    "@ensdomains/=lib/v4-core/node_modules/@ensdomains/",
    "@openzeppelin/=lib/v4-core/lib/openzeppelin-contracts/",
    "forge-gas-snapshot/=lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "hardhat/=lib/v4-core/node_modules/hardhat/",
    "permit2/=lib/v4-periphery/lib/permit2/",
    "v4-core/=lib/v4-core/src/",
    "v4-periphery/=lib/v4-periphery/"
  ],
  "viaIR": true
}

Contract Security Audit

Contract ABI

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

60a0806040523461009a57611512818101906001600160401b03821183831017610086576020918391610d5a83393081520301905ff0801561007b576001600160a01b0316608052604051610cbb908161009f82396080518181816101fc015281816109d001528181610a8e01528181610b3f0152610bb20152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806321025a0614610064578063b94c36091461005f578063ea270f421461005a578063f35cae90146100555763f52e33f514610050575f80fd5b610380565b61025b565b6101e7565b610186565b608036600319011261010c576004356001600160401b03811161010c5761008f903690600401610110565b6024356001600160401b03811161010c576100ae903690600401610110565b604435916100bb8361011e565b606435906001600160401b03821161010c57610108936100f66100e56100fc94369060040161012f565b906100f084886105fb565b946103da565b93610817565b6040519182918261015c565b0390f35b5f80fd5b9081604091031261010c5790565b6001600160a01b0381160361010c57565b9181601f8401121561010c578235916001600160401b03831161010c576020838186019501011161010c57565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b604036600319011261010c576004356001600160401b03811161010c576101b1903690600401610110565b6024356001600160401b03811161010c57610108916101d76100fc92369060040161012f565b916103da565b5f91031261010c57565b3461010c575f36600319011261010c576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9181601f8401121561010c578235916001600160401b03831161010c576020808501948460051b01011161010c57565b608036600319011261010c576004356001600160401b03811161010c5761028690369060040161022b565b6024356001600160401b03811161010c576102a590369060040161022b565b9190604435916102b48361011e565b6064356001600160401b03811161010c576102d390369060040161012f565b906102dd86610457565b926102eb6040519485610431565b868452601f196102fa88610457565b013660208601375f5b8781106103585750906103179291886104bd565b925f5b85811061032f5760405180610108878261015c565b806103526103406001938988610482565b8461034b84886104a9565b5191610817565b0161031a565b8061036f8861036a6001948c8b610482565b6105fb565b61037982886104a9565b5201610303565b604036600319011261010c576004356001600160401b03811161010c576103ab90369060040161022b565b602435906001600160401b03821161010c57610108926103d26100fc93369060040161012f565b9290916104bd565b6103e69092919261099b565b1580610414575b6103fd576103fa91610b9b565b90565b63dfd87a7360e01b5f52346004525f60245260445ffd5b503415156103ed565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761045257604052565b61041d565b6001600160401b0381116104525760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b91908110156104a45760051b81013590603e198136030182121561010c570190565b61046e565b80518210156104a45760209160051b010190565b9291925f915f5b8181106104e1575050501580610414576103fd576103fa91610b9b565b6104f46104ef828486610482565b61099b565b610501575b6001016104c4565b9261050e576001926104f9565b63eacf98b760e01b5f5260045ffd5b6004111561010c57565b6004111561053157565b634e487b7160e01b5f52602160045260245ffd5b356103fa8161051d565b9060048210156105315752565b9061056a600460249361054f565b565b903590601e198136030182121561010c57018035906001600160401b03821161010c5760200191813603831361010c57565b9081606091031261010c5780356105b48161011e565b916040602083013592013590565b9081602091031261010c575190565b6040513d5f823e3d90fd5b919082604091031261010c57602082356105f58161011e565b92013590565b61060481610545565b61060d81610527565b600181036106ae57506106629161063461062c8360208095019061056c565b8101906105dc565b506040516370a0823160e01b81526001600160a01b0390921660048301529092839190829081906024820190565b03916001600160a01b03165afa9081156106a9575f91610680575090565b6103fa915060203d6020116106a2575b61069a8183610431565b8101906105c2565b503d610690565b6105d1565b6106b781610527565b806106c25750503190565b6106cb81610527565b600281036106ea57506106629161063461062c8360208095019061056c565b6106f381610527565b6003810361074c57506106629161071a6107128360208095019061056c565b81019061059e565b50604051627eeac760e11b81526001600160a01b03909316600484015260248301529092839190829081906044820190565b63292c317f60e11b5f5261075f9061055c565b5ffd5b9081602091031261010c573590565b9190820391821161077e57565b634e487b7160e01b5f52601160045260245ffd5b9093929193606082526107b381356107a98161051d565b606084019061054f565b6020810135601e198236030181121561010c5701906020823592016001600160401b03831161010c57823603811361010c578260409360c0928560808601528160a0860152838501375f828285010152601f80199101168201019460208201520152565b909161082282610545565b61082b81610527565b600181036108e7575061087690602061084961062c8286018661056c565b6040516370a0823160e01b81526001600160a01b0390971660048801529593849190829081906024820190565b03916001600160a01b03165afa80156106a95761089a925f916108c8575b50610771565b918083106108a757505050565b6108c490604051938493631cab723f60e21b855260048501610792565b0390fd5b6108e1915060203d6020116106a25761069a8183610431565b5f610894565b6108f081610527565b80610919575061089a9061091261090a602085018561056c565b810190610762565b9331610771565b61092281610527565b60028103610940575061087690602061084961062c8286018661056c565b61094981610527565b6003810361074c57506108769060206109676107128286018661056c565b604051627eeac760e11b81526001600160a01b03909816600489015260248801919091529593849190829081906044820190565b905f916109a781610545565b6109b081610527565b600181036109f757506109cd61062c82602061056a94019061056c565b907f0000000000000000000000000000000000000000000000000000000000000000903390610c24565b610a0081610527565b80610a3d5750610a1a91925080602061090a92019061056c565b803403610a275750600190565b63dfd87a7360e01b5f523460045260245260445ffd5b610a4681610527565b60028103610aec575061062c816020610a6093019061056c565b906001600160a01b0316803b1561010c57604051632142170760e11b81523360048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016602482015260448101929092525f908290818381606481015b03925af180156106a957610ad85750565b80610ae65f61056a93610431565b806101dd565b610af581610527565b6003810361074c5750610712816020610b0f93019061056c565b6001600160a01b0390921691823b1561010c57604051637921219560e11b81523360048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201526044810192909252606482015260a06084820152600260a482015261060f60f31b60c4820152905f90829081838160e48101610ac7565b5f91829181604051928392833781018381520390347f00000000000000000000000000000000000000000000000000000000000000005af1903d15610c1d573d6001600160401b0381116104525760405190610c01601f8201601f191660200183610431565b81523d5f602083013e5b809215610c155750565b602081519101fd5b6060610c0b565b6040516323b872dd60e01b60208083019182526001600160a01b039485166024840152949093166044820152606480820195909552938452925f9190610c6b608482610431565b519082855af1156105d1575f513d610cb257506001600160a01b0381163b155b610c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415610c8b5660a034606d57601f61151238819003918201601f19168301916001600160401b03831184841017607157808492602094604052833981010312606d57516001600160a01b0381168103606d5760805260405161148c9081610086823960805181818161047201526105630152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c806301ffc9a714610079578063150b7a021461007457806395352c9f1461006f578063bc197c811461006a578063c34c08e5146100655763f23a6e610361000e576104a1565b61045d565b6103c8565b6102a0565b610198565b346100cd5760203660031901126100cd5760043563ffffffff60e01b81168091036100cd57630271189760e51b81149081156100bc575b50151560805260206080f35b6301ffc9a760e01b149050816100b0565b5f80fd5b600435906001600160a01b03821682036100cd57565b602435906001600160a01b03821682036100cd57565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761013257604052565b6100fd565b6001600160401b03811161013257601f01601f191660200190565b81601f820112156100cd5780359061016982610137565b926101776040519485610111565b828452602083830101116100cd57815f926020809301838601378301015290565b346100cd5760803660031901126100cd576101b16100d1565b506101ba6100e7565b506064356001600160401b0381116100cd576101da903690600401610152565b50604051630a85bd0160e11b8152602090f35b9181601f840112156100cd578235916001600160401b0383116100cd576020808501948460051b0101116100cd57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b83831061027357505050505090565b9091929394602080610291600193603f19868203018752895161021d565b97019301930191939290610264565b60803660031901126100cd576024356004356044356001600160401b0381116100cd576102d19036906004016101ed565b90926064356001600160401b0381116100cd57610350947f049d8dd84b6a6cc45d5f68a74b23450bd3e54d84fd405d91b8b286c78d51d2499361032d61031e6103339436906004016101ed565b610326610561565b36916104f6565b916106fb565b60408051948552602085019290925292a160405191829182610241565b0390f35b6001600160401b0381116101325760051b60200190565b9080601f830112156100cd57813561038281610354565b926103906040519485610111565b81845260208085019260051b8201019283116100cd57602001905b8282106103b85750505090565b81358152602091820191016103ab565b346100cd5760a03660031901126100cd576103e16100d1565b506103ea6100e7565b506044356001600160401b0381116100cd5761040a90369060040161036b565b506064356001600160401b0381116100cd5761042a90369060040161036b565b506084356001600160401b0381116100cd5761044a903690600401610152565b5060405163bc197c8160e01b8152602090f35b346100cd575f3660031901126100cd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100cd5760a03660031901126100cd576104ba6100d1565b506104c36100e7565b506084356001600160401b0381116100cd576104e3903690600401610152565b5060405163f23a6e6160e01b8152602090f35b92919061050281610354565b936105106040519586610111565b602085838152019160051b8101918383116100cd5781905b838210610536575050505050565b81356001600160401b0381116100cd576020916105568784938701610152565b815201910190610528565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361059357565b6339218f3b60e01b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b91908110156105c65760051b0190565b6105a2565b80518210156105c65760209160051b010190565b156105e657565b60405162461bcd60e51b815260206004820152601660248201527556616c7565206d75737420626520333220627974657360501b6044820152606490fd5b602081519101519060208110610638575090565b5f199060200360031b1b1690565b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161066857565b610646565b601f1981019190821161066857565b3d156106a6573d9061068d82610137565b9161069b6040519384610111565b82523d5f602084013e565b606090565b604051906106ba604083610111565b60078252662ab735b737bbb760c91b6020830152565b9081526001600160a01b0390911660208201526060604082018190526106f89291019061021d565b90565b905f5b81811061070b5750505090565b6107168183856105b6565b359061074461073e61073861072b8560201b90565b6001600160f81b03191690565b60f81c90565b60ff1690565b9160408316159182610a02576001019261075f8486886105b6565b356020905b60038316600181036108bd57505f91829190602085166108a657610793916001600160e01b031987168c610a7b565b805190602001826001600160a01b0387165af1926107af61067c565b935b84901561080a575050608016156107e8576107e392916107d761072b6107dd9360581b90565b87610f7f565b60010190565b6106fe565b906107e392956107fe61072b6108049460581b90565b90610ed4565b936107dd565b8592506108156106ab565b946044815111610865575b505090610852915f1461085657925b60405163ef3dcb2f60e01b81529384936001600160a01b031690600485016106d0565b0390fd5b61085f9061065a565b9261082f565b602061087c600461087584610ca8565b9301610624565b036108205761088d60248301610624565b14610899575b80610820565b6044019350610852610893565b50607f6108b79160f81c168a6105cb565b51610793565b6002810361092057505f9182919060208516610909576108e8916001600160e01b031987168c610a7b565b8051906020016001600160a01b0386165afa9261090361067c565b936107b1565b50607f61091a9160f81c168a6105cb565b516108e8565b6003036109ca575f918161095161093e607f869560f81c168d6105cb565b5161094c60208251146105df565b610624565b91602086161584146109a35761097361096d6109859360081b90565b9161065a565b906001600160e01b031988168d610a7b565b905b815191602001906001600160a01b0387165af19261090361067c565b50607f6109bc61073e61073861072b6109c39560081b90565b168b6105cb565b5190610987565b60405162461bcd60e51b815260206004820152601060248201526f496e76616c69642063616c6c7479706560801b6044820152606490fd5b926006602883901b6001600160d01b0317610764565b6040516101609190610a2a8382610111565b600a815291601f1901366020840137565b906004820180921161066857565b90610a5382610137565b610a606040519182610111565b8281528092610a71601f1991610137565b0190602036910137565b91939290935f5f915f92610a8d610a18565b906060935f905b878210610be9575b5050610aaa610aaf91610a3b565b610a49565b9760208901525f9060248901925f955b878710610ad157505050505050505050565b60208710156105c657888b83891a6080811615610bc75760fe8103610b2c575050506020816001928752610b198d610b0883610a3b565b610b128b5161066d565b918b61112d565b875101601f1901955b0196019593610abf565b60fd819794959697145f14610b61575092610b5592868660019b948a9998978560209c52611283565b50979195909594610b22565b60fc8103610b83575092610b5592868660019b948a9998978560209c5261113f565b93610bc0888293610baf602096607f6001999c9b9a1690610ba482826105cb565b5151978895526105cb565b5190610bba85610a3b565b9161111b565b0195610b22565b60209250600193979150610bdf607f8492168d6105cb565b5101518152610b22565b9093959160208510156105c65786851a60ff8114610c94576080811615610c7f5760fe8103610c5f5750855115610c2f575b6020600191875101935b0196940190610a94565b945060016020604051610c5681610c488d858301610241565b03601f198101835282610111565b96915050610c1b565b90610c739260019692602095968a8d61104c565b95919390939294610c25565b610c8e6020916001938c610fd9565b93610c25565b509396509094929050610aaa610aaf610a9c565b9081516043198101818111610668579260445b828110610cc757505050565b81518110156105c657818101602001516001600160f81b03191615610cee57600101610cbb565b929350505060431981019081116106685790565b15610d0957565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f75742d6f662d626f756e647360681b6044820152606490fd5b15610d4b57565b60405162461bcd60e51b815260206004820152602860248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d697474656420604482015267287374617469632960c01b6064820152608490fd5b15610da857565b60405162461bcd60e51b815260206004820152602a60248201527f4f6e6c79206f6e652072657475726e2076616c7565207065726d697474656420604482015269287661726961626c652960b01b6064820152608490fd5b6020818303126100cd578051906001600160401b0382116100cd57019080601f830112156100cd57815191610e3483610354565b92610e426040519485610111565b80845260208085019160051b830101918383116100cd5760208101915b838310610e6e57505050505090565b82516001600160401b0381116100cd57820185603f820112156100cd57602081015191610e9a83610137565b610ea76040519182610111565b83815260408385010188106100cd575f602085819660408397018386015e83010152815201920191610e5f565b91908060f81c60ff8114610f79576080811615610f415760fe8103610f095750506106f8915060208082518301019101610e00565b602091610f1f610fe092607f8751911610610d02565b82840193610f2f84865114610da1565b51601f1901845260f31c168301015290565b610f75929150607f1690610f5784518310610d02565b610f646020825114610d44565b610f6e82856105cb565b52826105cb565b5090565b50505090565b9060f81c60ff8114610fd4578251906020820180921161066857602092607f610faa610fbd94610a49565b921691610fb783836105cb565b526105cb565b51918051604084018184840160045afa5051910152565b505050565b610fea90607f6020939416906105cb565b515103610ff75760200190565b60405162461bcd60e51b815260206004820152602760248201527f537461746963207374617465207661726961626c6573206d75737420626520336044820152663220627974657360c81b6064820152608490fd5b90969594939260fd810361106c5750956110669596611410565b90919293565b60fc81036110805750956110669596611329565b919650919493929161109591607f16906105cb565b515180151580611110575b156110ac570160200191565b60405162461bcd60e51b815260206004820152603660248201527f44796e616d6963207374617465207661726961626c6573206d7573742062652060448201527561206d756c7469706c65206f6620333220627974657360501b6064820152608490fd5b50601f8116156110a0565b916020809185930101920160045afa50565b910160200190829060400160045afa50565b9193959692905f9461115188846105cb565b51936024600180878b019b019b0198820101915b60208910611176575b505050505050565b80891a608081161561125a5760fb8103611190575061116e565b60fd819c92959a9499969b93979c145f146111e15750906111b892918b89528a858b89611283565b9260209a93926001928c969480919d939d97929d9e01970101985b019301979291939490611165565b60fc810361121b5750906111fc92918b89528a858b8961113f565b9260209a93926001928c969480919d939d97929d9e01970101986111d3565b6020898b8e6001959f9e97989661124e607f869716948d610baf61123f88886105cb565b515197889788978895526105cb565b019d01970101986111d3565b602060019293979694998161127788607f999e99839616906105cb565b5101518b5201986111d3565b9392919094956001019460208610156105c65760206112bd97816112ac607f868b1a16856105cb565b51015160248289010152019461113f565b929391929091602090910190565b156112d257565b60405162461bcd60e51b815260206004820152602960248201527f43616e6e6f74207573652073746174652066726f6d20696e736964652064796e604482015268616d6963207479706560b81b6064820152608490fd5b6001808501976020909601960194935f929091905b602087106113975760405162461bcd60e51b8152602060048201526024808201527f44796e616d6963207479706520776173206e6f742070726f7065726c7920636c6044820152631bdcd95960e21b6064820152608490fd5b80871a60808116156113f85760fb81036113bf575050506113b890836105cb565b5293929190565b6113df94956113d860fe839c949a969b959c14156112cb565b888b61104c565b9197909692939192916001906020905b0193019561133e565b61140a60209160019399969a85610fd9565b986113ef565b92919093946001019360208510156105c6576020611433607f83881a16866105cb565b515103611447576020611066960193611329565b60405162461bcd60e51b815260206004820152601d60248201527f4172726179206c656e677468206d7573742062652033322062797465730000006044820152606490fd

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806321025a0614610064578063b94c36091461005f578063ea270f421461005a578063f35cae90146100555763f52e33f514610050575f80fd5b610380565b61025b565b6101e7565b610186565b608036600319011261010c576004356001600160401b03811161010c5761008f903690600401610110565b6024356001600160401b03811161010c576100ae903690600401610110565b604435916100bb8361011e565b606435906001600160401b03821161010c57610108936100f66100e56100fc94369060040161012f565b906100f084886105fb565b946103da565b93610817565b6040519182918261015c565b0390f35b5f80fd5b9081604091031261010c5790565b6001600160a01b0381160361010c57565b9181601f8401121561010c578235916001600160401b03831161010c576020838186019501011161010c57565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b604036600319011261010c576004356001600160401b03811161010c576101b1903690600401610110565b6024356001600160401b03811161010c57610108916101d76100fc92369060040161012f565b916103da565b5f91031261010c57565b3461010c575f36600319011261010c576040517f000000000000000000000000dc0f215c6e6cbb900aa00afdec299d88c15ff7436001600160a01b03168152602090f35b9181601f8401121561010c578235916001600160401b03831161010c576020808501948460051b01011161010c57565b608036600319011261010c576004356001600160401b03811161010c5761028690369060040161022b565b6024356001600160401b03811161010c576102a590369060040161022b565b9190604435916102b48361011e565b6064356001600160401b03811161010c576102d390369060040161012f565b906102dd86610457565b926102eb6040519485610431565b868452601f196102fa88610457565b013660208601375f5b8781106103585750906103179291886104bd565b925f5b85811061032f5760405180610108878261015c565b806103526103406001938988610482565b8461034b84886104a9565b5191610817565b0161031a565b8061036f8861036a6001948c8b610482565b6105fb565b61037982886104a9565b5201610303565b604036600319011261010c576004356001600160401b03811161010c576103ab90369060040161022b565b602435906001600160401b03821161010c57610108926103d26100fc93369060040161012f565b9290916104bd565b6103e69092919261099b565b1580610414575b6103fd576103fa91610b9b565b90565b63dfd87a7360e01b5f52346004525f60245260445ffd5b503415156103ed565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761045257604052565b61041d565b6001600160401b0381116104525760051b60200190565b634e487b7160e01b5f52603260045260245ffd5b91908110156104a45760051b81013590603e198136030182121561010c570190565b61046e565b80518210156104a45760209160051b010190565b9291925f915f5b8181106104e1575050501580610414576103fd576103fa91610b9b565b6104f46104ef828486610482565b61099b565b610501575b6001016104c4565b9261050e576001926104f9565b63eacf98b760e01b5f5260045ffd5b6004111561010c57565b6004111561053157565b634e487b7160e01b5f52602160045260245ffd5b356103fa8161051d565b9060048210156105315752565b9061056a600460249361054f565b565b903590601e198136030182121561010c57018035906001600160401b03821161010c5760200191813603831361010c57565b9081606091031261010c5780356105b48161011e565b916040602083013592013590565b9081602091031261010c575190565b6040513d5f823e3d90fd5b919082604091031261010c57602082356105f58161011e565b92013590565b61060481610545565b61060d81610527565b600181036106ae57506106629161063461062c8360208095019061056c565b8101906105dc565b506040516370a0823160e01b81526001600160a01b0390921660048301529092839190829081906024820190565b03916001600160a01b03165afa9081156106a9575f91610680575090565b6103fa915060203d6020116106a2575b61069a8183610431565b8101906105c2565b503d610690565b6105d1565b6106b781610527565b806106c25750503190565b6106cb81610527565b600281036106ea57506106629161063461062c8360208095019061056c565b6106f381610527565b6003810361074c57506106629161071a6107128360208095019061056c565b81019061059e565b50604051627eeac760e11b81526001600160a01b03909316600484015260248301529092839190829081906044820190565b63292c317f60e11b5f5261075f9061055c565b5ffd5b9081602091031261010c573590565b9190820391821161077e57565b634e487b7160e01b5f52601160045260245ffd5b9093929193606082526107b381356107a98161051d565b606084019061054f565b6020810135601e198236030181121561010c5701906020823592016001600160401b03831161010c57823603811361010c578260409360c0928560808601528160a0860152838501375f828285010152601f80199101168201019460208201520152565b909161082282610545565b61082b81610527565b600181036108e7575061087690602061084961062c8286018661056c565b6040516370a0823160e01b81526001600160a01b0390971660048801529593849190829081906024820190565b03916001600160a01b03165afa80156106a95761089a925f916108c8575b50610771565b918083106108a757505050565b6108c490604051938493631cab723f60e21b855260048501610792565b0390fd5b6108e1915060203d6020116106a25761069a8183610431565b5f610894565b6108f081610527565b80610919575061089a9061091261090a602085018561056c565b810190610762565b9331610771565b61092281610527565b60028103610940575061087690602061084961062c8286018661056c565b61094981610527565b6003810361074c57506108769060206109676107128286018661056c565b604051627eeac760e11b81526001600160a01b03909816600489015260248801919091529593849190829081906044820190565b905f916109a781610545565b6109b081610527565b600181036109f757506109cd61062c82602061056a94019061056c565b907f000000000000000000000000dc0f215c6e6cbb900aa00afdec299d88c15ff743903390610c24565b610a0081610527565b80610a3d5750610a1a91925080602061090a92019061056c565b803403610a275750600190565b63dfd87a7360e01b5f523460045260245260445ffd5b610a4681610527565b60028103610aec575061062c816020610a6093019061056c565b906001600160a01b0316803b1561010c57604051632142170760e11b81523360048201526001600160a01b037f000000000000000000000000dc0f215c6e6cbb900aa00afdec299d88c15ff74316602482015260448101929092525f908290818381606481015b03925af180156106a957610ad85750565b80610ae65f61056a93610431565b806101dd565b610af581610527565b6003810361074c5750610712816020610b0f93019061056c565b6001600160a01b0390921691823b1561010c57604051637921219560e11b81523360048201526001600160a01b037f000000000000000000000000dc0f215c6e6cbb900aa00afdec299d88c15ff7431660248201526044810192909252606482015260a06084820152600260a482015261060f60f31b60c4820152905f90829081838160e48101610ac7565b5f91829181604051928392833781018381520390347f000000000000000000000000dc0f215c6e6cbb900aa00afdec299d88c15ff7435af1903d15610c1d573d6001600160401b0381116104525760405190610c01601f8201601f191660200183610431565b81523d5f602083013e5b809215610c155750565b602081519101fd5b6060610c0b565b6040516323b872dd60e01b60208083019182526001600160a01b039485166024840152949093166044820152606480820195909552938452925f9190610c6b608482610431565b519082855af1156105d1575f513d610cb257506001600160a01b0381163b155b610c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415610c8b56

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
0x3067BDBa0e6628497d527bEF511c22DA8b32cA3F
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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