Sepolia Testnet

Contract

0xcb1B178cBC345ee8728bac2eb0D960CcFbf2638c
Transaction Hash
Method
Block
From
To
Call64894992024-08-13 2:12:3651 days ago1723515156IN
0xcb1B178c...cFbf2638c
0 ETH0.0005409313.65829834
Call64894972024-08-13 2:12:1251 days ago1723515132IN
0xcb1B178c...cFbf2638c
0 ETH0.0057960813.28265276
Call64894862024-08-13 2:09:3651 days ago1723514976IN
0xcb1B178c...cFbf2638c
0 ETH0.0085158512.62749961
Call64894682024-08-13 2:04:3651 days ago1723514676IN
0xcb1B178c...cFbf2638c
0 ETH0.003878095.49037768
Payable Call64894172024-08-13 1:53:4851 days ago1723514028IN
0xcb1B178c...cFbf2638c
0.00999165 ETH0.00360316.71844079
Payable Call64893872024-08-13 1:47:1251 days ago1723513632IN
0xcb1B178c...cFbf2638c
0.01001855 ETH0.001426432.8464546

Latest 3 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
64894172024-08-13 1:53:4851 days ago1723514028
0xcb1B178c...cFbf2638c
0.00999165 ETH
64893872024-08-13 1:47:1251 days ago1723513632
0xcb1B178c...cFbf2638c
0.01001855 ETH
64893652024-08-13 1:42:1251 days ago1723513332  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x066DFbFc...dB215983d
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
VirtualAccount

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion
File 1 of 15 : VirtualAccount.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {LibZip} from "lib/solady/src/utils/LibZip.sol";
import {SafeTransferLib} from "lib/solady/src/utils/SafeTransferLib.sol";

import {ERC721} from "lib/solmate/src/tokens/ERC721.sol";

import {ERC1155Receiver} from "lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Receiver.sol";
import {IERC1155Receiver} from "lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol";
import {IERC721Receiver} from "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol";

import {IVirtualAccount, Call, PayableCall} from "./interfaces/IVirtualAccount.sol";
import {IRootPort} from "./interfaces/IRootPort.sol";

import {AddressCodeSize} from "./lib/AddressCodeSize.sol";

/// @title VirtualAccount - Contract for managing a virtual user account on the Root Chain
contract VirtualAccount is IVirtualAccount, ERC1155Receiver {
    using SafeTransferLib for address;
    using AddressCodeSize for address;

    /// @inheritdoc IVirtualAccount
    address public immutable override userAddress;

    /// @inheritdoc IVirtualAccount
    address public immutable override localPortAddress;

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

    /**
     * @notice Constructor for Virtual Account.
     * @param _userAddress Address of the user/owner.
     */
    constructor(address _userAddress) {
        localPortAddress = msg.sender;
        userAddress = _userAddress;
    }

    /*//////////////////////////////////////////////////////////////
                            FALLBACK FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    fallback() external payable {
        LibZip.cdFallback();
    }

    receive() external payable {}

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

    /// @inheritdoc IVirtualAccount
    function withdrawNative(uint256 _amount) external override requiresApprovedCaller {
        msg.sender.safeTransferETH(_amount);
    }

    /// @inheritdoc IVirtualAccount
    function withdrawERC20(address _token, uint256 _amount) external override requiresApprovedCaller {
        _token.safeTransfer(msg.sender, _amount);
    }

    /// @inheritdoc IVirtualAccount
    function withdrawERC721(address _token, uint256 _tokenId) external override requiresApprovedCaller {
        ERC721(_token).transferFrom(address(this), msg.sender, _tokenId);
    }

    /// @inheritdoc IVirtualAccount
    function call(Call[] calldata calls) external override requiresApprovedCaller returns (bytes[] memory returnData) {
        uint256 length = calls.length;
        returnData = new bytes[](length);

        for (uint256 i = 0; i < length;) {
            bool success;
            Call calldata _call = calls[i];

            if (_call.target.isContract()) (success, returnData[i]) = _call.target.call(_call.callData);

            if (!success) revert CallFailed(returnData[i]);

            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IVirtualAccount
    function payableCall(PayableCall[] calldata calls)
        public
        payable
        override
        requiresApprovedCaller
        returns (bytes[] memory returnData)
    {
        uint256 valAccumulator;
        uint256 length = calls.length;
        returnData = new bytes[](length);
        PayableCall calldata _call;
        for (uint256 i = 0; i < length;) {
            _call = calls[i];
            uint256 val = _call.value;
            // Humanity will be a Type V Kardashev Civilization before this overflows - andreas
            // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256
            unchecked {
                valAccumulator += val;
            }

            bool success;

            if (_call.target.isContract()) (success, returnData[i]) = _call.target.call{value: val}(_call.callData);

            if (!success) revert CallFailed(returnData[i]);

            unchecked {
                ++i;
            }
        }

        // Finally, make sure the msg.value = SUM(call[0...i].value) if not throw error with 'IncorrectEtherPaid()'
        if (msg.value != valAccumulator) revert CallFailed(hex"5c63102a");
    }

    /*//////////////////////////////////////////////////////////////
                            EXTERNAL HOOKS
    ///////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC721Receiver
    function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
        return this.onERC721Received.selector;
    }

    /// @inheritdoc IERC1155Receiver
    function onERC1155Received(address, address, uint256, uint256, bytes calldata)
        external
        pure
        override
        returns (bytes4)
    {
        return this.onERC1155Received.selector;
    }

    /// @inheritdoc IERC1155Receiver
    function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
        external
        pure
        override
        returns (bytes4)
    {
        return this.onERC1155BatchReceived.selector;
    }

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

    /// @notice Modifier that verifies msg sender is the approved to use the virtual account. Either the owner or an approved router.
    modifier requiresApprovedCaller() {
        if (!IRootPort(localPortAddress).isRouterApproved(this, msg.sender)) {
            if (msg.sender != userAddress) {
                revert UnauthorizedCaller();
            }
        }
        _;
    }
}

File 2 of 15 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 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 ERC1155 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);
}

File 3 of 15 : ERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155Receiver 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);
    }
}

File 4 of 15 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 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 5 of 15 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 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);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 6 of 15 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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 7 of 15 : LibZip.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for compressing and decompressing bytes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol)
/// @author Calldata compression by clabby (https://github.com/clabby/op-kompressor)
/// @author FastLZ by ariya (https://github.com/ariya/FastLZ)
///
/// @dev Note:
/// The accompanying solady.js library includes implementations of
/// FastLZ and calldata operations for convenience.
library LibZip {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     FAST LZ OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // LZ77 implementation based on FastLZ.
    // Equivalent to level 1 compression and decompression at the following commit:
    // https://github.com/ariya/FastLZ/commit/344eb4025f9ae866ebf7a2ec48850f7113a97a42
    // Decompression is backwards compatible.

    /// @dev Returns the compressed `data`.
    function flzCompress(bytes memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            function ms8(d_, v_) -> _d {
                mstore8(d_, v_)
                _d := add(d_, 1)
            }
            function u24(p_) -> _u {
                let w := mload(p_)
                _u := or(shl(16, byte(2, w)), or(shl(8, byte(1, w)), byte(0, w)))
            }
            function cmp(p_, q_, e_) -> _l {
                for { e_ := sub(e_, q_) } lt(_l, e_) { _l := add(_l, 1) } {
                    e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_)
                }
            }
            function literals(runs_, src_, dest_) -> _o {
                for { _o := dest_ } iszero(lt(runs_, 0x20)) { runs_ := sub(runs_, 0x20) } {
                    mstore(ms8(_o, 31), mload(src_))
                    _o := add(_o, 0x21)
                    src_ := add(src_, 0x20)
                }
                if iszero(runs_) { leave }
                mstore(ms8(_o, sub(runs_, 1)), mload(src_))
                _o := add(1, add(_o, runs_))
            }
            function match(l_, d_, o_) -> _o {
                for { d_ := sub(d_, 1) } iszero(lt(l_, 263)) { l_ := sub(l_, 262) } {
                    o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_))
                }
                if iszero(lt(l_, 7)) {
                    _o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_))
                    leave
                }
                _o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_))
            }
            function setHash(i_, v_) {
                let p := add(mload(0x40), shl(2, i_))
                mstore(p, xor(mload(p), shl(224, xor(shr(224, mload(p)), v_))))
            }
            function getHash(i_) -> _h {
                _h := shr(224, mload(add(mload(0x40), shl(2, i_))))
            }
            function hash(v_) -> _r {
                _r := and(shr(19, mul(2654435769, v_)), 0x1fff)
            }
            function setNextHash(ip_, ipStart_) -> _ip {
                setHash(hash(u24(ip_)), sub(ip_, ipStart_))
                _ip := add(ip_, 1)
            }
            codecopy(mload(0x40), codesize(), 0x8000) // Zeroize the hashmap.
            let op := add(mload(0x40), 0x8000)
            let a := add(data, 0x20)
            let ipStart := a
            let ipLimit := sub(add(ipStart, mload(data)), 13)
            for { let ip := add(2, a) } lt(ip, ipLimit) {} {
                let r := 0
                let d := 0
                for {} 1 {} {
                    let s := u24(ip)
                    let h := hash(s)
                    r := add(ipStart, getHash(h))
                    setHash(h, sub(ip, ipStart))
                    d := sub(ip, r)
                    if iszero(lt(ip, ipLimit)) { break }
                    ip := add(ip, 1)
                    if iszero(gt(d, 0x1fff)) { if eq(s, u24(r)) { break } }
                }
                if iszero(lt(ip, ipLimit)) { break }
                ip := sub(ip, 1)
                if gt(ip, a) { op := literals(sub(ip, a), a, op) }
                let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9))
                op := match(l, d, op)
                ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart)
                a := ip
            }
            op := literals(sub(add(ipStart, mload(data)), a), a, op)
            result := mload(0x40)
            let t := add(result, 0x8000)
            let n := sub(op, t)
            mstore(result, n) // Store the length.
            // Copy the result to compact the memory, overwriting the hashmap.
            let o := add(result, 0x20)
            for { let i } lt(i, n) { i := add(i, 0x20) } { mstore(add(o, i), mload(add(t, i))) }
            mstore(add(o, n), 0) // Zeroize the slot after the string.
            mstore(0x40, add(add(o, n), 0x20)) // Allocate the memory.
        }
    }

    /// @dev Returns the decompressed `data`.
    function flzDecompress(bytes memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(add(data, 0x20), mload(data))
            result := mload(0x40)
            let op := add(result, 0x20)
            for { data := add(data, 0x20) } lt(data, end) {} {
                let w := mload(data)
                let c := byte(0, w)
                let t := shr(5, c)
                if iszero(t) {
                    mstore(op, mload(add(data, 1)))
                    data := add(data, add(2, c))
                    op := add(op, add(1, c))
                    continue
                }
                let g := eq(t, 7)
                let l := add(2, xor(t, mul(g, xor(t, add(7, byte(1, w)))))) // M
                for {
                    let s := add(add(shl(8, and(0x1f, c)), byte(add(1, g), w)), 1) // R
                    let r := sub(op, s)
                    let f := xor(s, mul(gt(s, 0x20), xor(s, 0x20)))
                    let j := 0
                } 1 {} {
                    mstore(add(op, j), mload(add(r, j)))
                    j := add(j, f)
                    if iszero(lt(j, l)) { break }
                }
                data := add(data, add(2, g))
                op := add(op, l)
            }
            mstore(result, sub(op, add(result, 0x20))) // Store the length.
            mstore(op, 0) // Zeroize the slot after the string.
            mstore(0x40, add(op, 0x20)) // Allocate the memory.
        }
    }

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

    // Calldata compression and decompression using selective run length encoding:
    // - Sequences of 0x00 (up to 128 consecutive).
    // - Sequences of 0xff (up to 32 consecutive).
    //
    // A run length encoded block consists of two bytes:
    // (0) 0x00
    // (1) A control byte with the following bit layout:
    //     - [7]     `0: 0x00, 1: 0xff`.
    //     - [0..6]  `runLength - 1`.
    //
    // The first 4 bytes are bitwise negated so that the compressed calldata
    // can be dispatched into the `fallback` and `receive` functions.

    /// @dev Returns the compressed `data`.
    function cdCompress(bytes memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            function rle(v_, o_, d_) -> _o, _d {
                mstore(o_, shl(240, or(and(0xff, add(d_, 0xff)), and(0x80, v_))))
                _o := add(o_, 2)
            }
            result := mload(0x40)
            let o := add(result, 0x20)
            let z := 0 // Number of consecutive 0x00.
            let y := 0 // Number of consecutive 0xff.
            for { let end := add(data, mload(data)) } iszero(eq(data, end)) {} {
                data := add(data, 1)
                let c := byte(31, mload(data))
                if iszero(c) {
                    if y { o, y := rle(0xff, o, y) }
                    z := add(z, 1)
                    if eq(z, 0x80) { o, z := rle(0x00, o, 0x80) }
                    continue
                }
                if eq(c, 0xff) {
                    if z { o, z := rle(0x00, o, z) }
                    y := add(y, 1)
                    if eq(y, 0x20) { o, y := rle(0xff, o, 0x20) }
                    continue
                }
                if y { o, y := rle(0xff, o, y) }
                if z { o, z := rle(0x00, o, z) }
                mstore8(o, c)
                o := add(o, 1)
            }
            if y { o, y := rle(0xff, o, y) }
            if z { o, z := rle(0x00, o, z) }
            // Bitwise negate the first 4 bytes.
            mstore(add(result, 4), not(mload(add(result, 4))))
            mstore(result, sub(o, add(result, 0x20))) // Store the length.
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Returns the decompressed `data`.
    function cdDecompress(bytes memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(data) {
                result := mload(0x40)
                let o := add(result, 0x20)
                let s := add(data, 4)
                let v := mload(s)
                let end := add(data, mload(data))
                mstore(s, not(v)) // Bitwise negate the first 4 bytes.
                for {} lt(data, end) {} {
                    data := add(data, 1)
                    let c := byte(31, mload(data))
                    if iszero(c) {
                        data := add(data, 1)
                        let d := byte(31, mload(data))
                        // Fill with either 0xff or 0x00.
                        mstore(o, not(0))
                        if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
                        o := add(o, add(and(d, 0x7f), 1))
                        continue
                    }
                    mstore8(o, c)
                    o := add(o, 1)
                }
                mstore(s, v) // Restore the first 4 bytes.
                mstore(result, sub(o, add(result, 0x20))) // Store the length.
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate the memory.
            }
        }
    }

    /// @dev To be called in the `fallback` function.
    /// ```
    ///     fallback() external payable { LibZip.cdFallback(); }
    ///     receive() external payable {} // Silence compiler warning to add a `receive` function.
    /// ```
    /// For efficiency, this function will directly return the results, terminating the context.
    /// If called internally, it must be called at the end of the function.
    function cdFallback() internal {
        assembly {
            if iszero(calldatasize()) { return(calldatasize(), calldatasize()) }
            let o := 0
            let f := not(3) // For negating the first 4 bytes.
            for { let i := 0 } lt(i, calldatasize()) {} {
                let c := byte(0, xor(add(i, f), calldataload(i)))
                i := add(i, 1)
                if iszero(c) {
                    let d := byte(0, xor(add(i, f), calldataload(i)))
                    i := add(i, 1)
                    // Fill with either 0xff or 0x00.
                    mstore(o, not(0))
                    if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
                    o := add(o, add(and(d, 0x7f), 1))
                    continue
                }
                mstore8(o, c)
                o := add(o, 1)
            }
            let success := delegatecall(gas(), address(), 0x00, o, codesize(), 0x00)
            returndatacopy(0x00, 0x00, returndatasize())
            if iszero(success) { revert(0x00, returndatasize()) }
            return(0x00, returndatasize())
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 9 of 15 : ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

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

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

File 10 of 15 : BridgeAgentStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*///////////////////////////////////////////////////////////////
                            STRUCTS
//////////////////////////////////////////////////////////////*/

/// @notice Struct for storing the gas parameters for a cross-chain call.
/// @param gasLimit gas units allocated for a cross-chain call execution.
/// @param remoteBranchExecutionGas native token amount to request for destiantion branch usage.
struct GasParams {
    uint256 gasLimit;
    uint256 remoteBranchExecutionGas;
}

/// @notice Struct for storing information about a deposit in a Branch Bridge Agent's state.
/// @param status status of the deposit. Has 3 states - ready, done, retrieve.
/// @param isSigned indicates if the deposit has been signed allowing Virtual Account usage.
/// @param owner owner of the deposit.
/// @param hTokens array of local hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total deposited amounts.
/// @param deposits array of underlying token deposited amounts.
struct Deposit {
    uint8 status;
    uint88 isSigned;
    address owner;
    address[] hTokens;
    address[] tokens;
    uint256[] amounts;
    uint256[] deposits;
}

/// @notice Struct for inputting deposit information into a Branch Bridge Agent.
/// @param hToken local hToken address.
/// @param token underlying token address.
/// @param amount total amount to deposit.
/// @param deposit underlying token amount to deposit.
struct DepositInput {
    address hToken;
    address token;
    uint256 amount;
    uint256 deposit;
}

/// @notice Struct for inputting multiple asset deposit information into a Branch Bridge Agent.
/// @param hTokens array of local hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total amounts to deposit.
/// @param deposits array of underlying token amounts to deposit.
struct DepositMultipleInput {
    address[] hTokens;
    address[] tokens;
    uint256[] amounts;
    uint256[] deposits;
}

/// @notice Struct for encoding deposit information in a cross-chain message.
/// @param depositNonce deposit nonce.
/// @param hToken local hToken address.
/// @param token underlying token address.
/// @param amount total amount to deposit.
/// @param deposit underlying token amount to deposit.
struct DepositParams {
    uint32 depositNonce;
    address hToken;
    address token;
    uint256 amount;
    uint256 deposit;
}

/// @notice Struct for encoding multiple asset deposit information in a cross-chain message.
/// @param numberOfAssets number of assets to deposit.
/// @param depositNonce deposit nonce.
/// @param hTokens array of local hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total amounts to deposit.
/// @param deposits array of underlying token amounts to deposit.
struct DepositMultipleParams {
    uint8 numberOfAssets;
    uint32 depositNonce;
    address[] hTokens;
    address[] tokens;
    uint256[] amounts;
    uint256[] deposits;
}

/// @notice Struct for storing information about a settlement in a Root Bridge Agent's state.
/// @param dstChainId destination chain for interaction.
/// @param status status of the settlement.
/// @param owner owner of the settlement.
/// @param recipient recipient of the settlement.
/// @param hTokens array of global hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total settled amounts.
/// @param deposits array of underlying token settled amounts.
struct Settlement {
    uint16 dstChainId;
    uint80 status;
    address owner;
    address recipient;
    address[] hTokens;
    address[] tokens;
    uint256[] amounts;
    uint256[] deposits;
}

/// @notice Struct for inputting token settlement information into a Root Bridge Agent.
/// @param globalAddress global hToken address.
/// @param amount total amount to settle.
/// @param deposit underlying token amount to settle.
struct SettlementInput {
    address globalAddress;
    uint256 amount;
    uint256 deposit;
}

/// @notice Struct for inputting multiple asset settlement information into a Root Bridge Agent.
/// @param globalAddresses array of global hTokens addresses.
/// @param amounts array of total amounts to settle.
/// @param deposits array of underlying token amounts to settle.

struct SettlementMultipleInput {
    address[] globalAddresses;
    uint256[] amounts;
    uint256[] deposits;
}

/// @notice Struct for encoding settlement information in a cross-chain message.
/// @param settlementNonce settlement nonce.
/// @param recipient recipient of the settlement.
/// @param hToken destination local hToken address.
/// @param token destination underlying token address.
/// @param amount total amount to settle.
/// @param deposit underlying token amount to settle.
struct SettlementParams {
    uint32 settlementNonce;
    address recipient;
    address hToken;
    address token;
    uint256 amount;
    uint256 deposit;
}

/// @notice Struct for encoding multiple asset settlement information in a cross-chain message.
/// @param numberOfAssets number of assets to settle.
/// @param recipient recipient of the settlement.
/// @param settlementNonce settlement nonce.
/// @param hTokens array of destination local hTokens addresses.
/// @param tokens array of destination underlying token addresses.
/// @param amounts array of total amounts to settle.
/// @param deposits array of underlying token amounts to settle.
struct SettlementMultipleParams {
    uint8 numberOfAssets;
    address recipient;
    uint32 settlementNonce;
    address[] hTokens;
    address[] tokens;
    uint256[] amounts;
    uint256[] deposits;
}

File 11 of 15 : ILayerZeroReceiver.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.5.0;

interface ILayerZeroReceiver {
    /*///////////////////////////////////////////////////////////////
                            LAYER ZERO FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice LayerZero endpoint will invoke this function to deliver the message on the destination
     *  @param _srcChainId the source endpoint identifier
     *  @param _srcAddress the source sending contract address from the source chain
     *  @param _nonce the ordered message nonce
     *  @param _payload the signed payload is the UA bytes has encoded to be sent
     */
    function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload)
        external
        payable
        returns (bool);

    /**
     * @notice External function to receive cross-chain messages from LayerZero Endpoint Contract without blocking.
     *  @param _endpoint address of the LayerZero Endpoint Contract.
     *  @param _srcAddress address path of the recipient + sender.
     *  @param _payload Calldata for function call.
     */
    function lzReceiveNonBlocking(
        address _endpoint,
        uint16 _srcChainId,
        bytes calldata _srcAddress,
        bytes calldata _payload
    ) external payable;

    /**
     * @notice Only when the BridgeAgent needs to resume the message flow in blocking mode and clear the stored payload.
     *  @param _srcChainId the chainId of the source chain
     *  @param _srcAddress the contract address of the source contract at the source chain
     */
    function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
}

File 12 of 15 : IRootBridgeAgent.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {
    GasParams,
    DepositParams,
    DepositMultipleParams,
    Settlement,
    SettlementInput,
    SettlementMultipleInput,
    SettlementParams
} from "./BridgeAgentStructs.sol";

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

/*///////////////////////////////////////////////////////////////
                            ENUMS
//////////////////////////////////////////////////////////////*/

/**
 * @title  Root Bridge Agent Contract
 * @author MaiaDAO
 * @notice Contract responsible for interfacing with Users and Routers acting as a middleman to
 *         access LayerZero cross-chain messaging and Port communication for asset management.
 * @dev    Bridge Agents allow for the encapsulation of business logic as well as the standardized
 *         cross-chain communication, allowing for the creation of custom Routers to perform
 *         actions as a response to remote user requests. This contract is for deployment in the Root
 *         Chain Omnichain Environment based on Arbitrum.
 *         The Root Bridge Agent is responsible for sending/receiving requests to/from the LayerZero Messaging Layer for
 *         execution, as well as requests tokens clearances and tx execution from the `RootBridgeAgentExecutor`.
 *         Remote execution is "sandboxed" within 2 different layers/nestings:
 *         - 1: Upon receiving a request from LayerZero Messaging Layer to avoid blocking future requests due
 *              to execution reversion, ensuring our app is Non-Blocking.
 *              (See https://github.com/LayerZero-Labs/solidity-examples/blob/8e62ebc886407aafc89dbd2a778e61b7c0a25ca0/contracts/lzApp/NonblockingLzApp.sol)
 *         - 2: The call to `RootBridgeAgentExecutor` is in charge of requesting token deposits for each
 *              remote interaction as well as performing the Router calls if any of the calls initiated
 *              by the Router led to an invalid state change in both the token deposit clearances as well as
 *              the external interactions will be reverted and caught by the `RootBridgeAgent`.
 *
 *         **ROOT BRIDGE AGENT DEPOSIT FLAGS** Func IDs for calling these  functions through the messaging layer
 *
 *         | ID   | DESCRIPTION                                                                                           |
 *         |------|-------------------------------------------------------------------------------------------------------|
 *         | 0x01 | Call to Root Router without Deposit.                                                                  |
 *         | 0x02 | Call to Root Router with Deposit.                                                                     |
 *         | 0x03 | Call to Root Router with Deposit of Multiple Tokens.                                                  |
 *         | 0x04 | Call to Root Router without Deposit + signed message.                                                 |
 *         | 0x05 | Call to Root Router with Deposit + signed message.                                                    |
 *         | 0x06 | Call to Root Router with Deposit of Multiple Tokens + signed message.                                 |
 *         | 0x07 | Call to `retrySettlement()`. (retries sending a settlement w/ new calldata for execution + new gas)   |
 *         | 0x08 | Call to `retrieveDeposit()`. (clears a deposit that has not been executed yet triggering `_fallback`) |
 *         | 0x09 | Call to `_fallback()`. (reopens a settlement for asset redemption)                                    |
 *
 *
 *            Encoding Scheme for different Root Bridge Agent Deposit Flags:
 *
 *             - ht = hToken
 *             - t = Token
 *             - A = Amount
 *             - D = Deposit
 *             - b = bytes
 *             - n = number of assets
 *
 *         |            Flag               |        Deposit Info        |             Token Info             |   DATA   |
 *         |-------------------------------|----------------------------|------------------------------------|----------|
 *         |           1 byte              |         4-25 bytes         |       104 or (128 * n) bytes       |   ...	|
 *         |                               |                            |           hT - t - A - D           |          |
 *         | callOut = 0x01                |                 4b(nonce)  |                 ---                |   ...	|
 *         | callOutSingle = 0x02          |                 4b(nonce)  |        20b + 20b + 32b + 32b       |   ...	|
 *         | callOutMulti = 0x03           |         1b(n) + 4b(nonce)  |   	  32b + 32b + 32b + 32b      |   ...	|
 *         | callOutSigned = 0x04          |    20b(recip) + 4b(nonce)  |   	          ---                |   ...    |
 *         | callOutSignedSingle = 0x05    |           20b + 4b(nonce)  |        20b + 20b + 32b + 32b       |   ...	|
 *         | callOutSignedMultiple = 0x06  |   20b + 1b(n) + 4b(nonce)  |        32b + 32b + 32b + 32b       |   ...	|
 *
 *         **Generic Contract Interaction Flow:**
 *         BridgeAgent.lzReceive() -> BridgeAgentExecutor.execute() -> Router.execute()
 *
 */
interface IRootBridgeAgent is ILayerZeroReceiver {
    /*///////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Function that returns the current settlement nonce.
     *  @return nonce bridge agent's current settlement nonce
     *
     */
    function settlementNonce() external view returns (uint32 nonce);

    /**
     * @notice External function that returns a given settlement entry.
     *  @param _settlementNonce Identifier for token settlement.
     *
     */
    function getSettlementEntry(uint32 _settlementNonce) external view returns (Settlement memory);

    /**
     * @notice External function to get the Bridge Agent Executor Address.
     * @return address Bridge Agent Executor Address.
     */
    function bridgeAgentExecutorAddress() external view returns (address);

    /**
     * @notice External function to get the Root Bridge Agent's Factory Address.
     *  @return address Root Bridge Agent's Factory Address.
     */
    function factoryAddress() external view returns (address);

    /**
     * @notice External function to get the attached Branch Bridge Agent for a given chain.
     *  @param _chainId Chain ID of the Branch Bridge Agent.
     *  @return address Branch Bridge Agent Address.
     */
    function getBranchBridgeAgent(uint256 _chainId) external view returns (address);

    /**
     * @notice External function to verify a given chain has been allowed by the Root Bridge Agent's Manager
     *         for new Branch Bridge Agent creation.
     *  @param _chainId Chain ID of the Branch Bridge Agent.
     *  @return bool True if the chain has been allowed for new Branch Bridge Agent creation.
     */
    function isBranchBridgeAgentAllowed(uint256 _chainId) external view returns (bool);

    /*///////////////////////////////////////////////////////////////
                        ROOT ROUTER CALL FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice External function performs call to LayerZero Endpoint Contract for cross-chain messaging.
     *  @param _gasRefundee Address to return excess gas deposited in `msg.value` to.
     *  @param _recipient address to receive any outstanding gas on the destination chain.
     *  @param _dstChainId Chain to bridge to.
     *  @param _params Calldata for function call.
     *  @param _gParams Gas Parameters for cross-chain message.
     *  @dev Internal function performs call to LayerZero Endpoint Contract for cross-chain messaging.
     */
    function callOut(
        address payable _gasRefundee,
        address _recipient,
        uint16 _dstChainId,
        bytes memory _params,
        GasParams calldata _gParams
    ) external payable;

    /**
     * @notice External function to move assets from root chain to branch omnichain environment.
     *  @param _settlementOwnerAndGasRefundee the effective owner of the settlement this address receives
     *                    excess gas deposited on source chain for a cross-chain call and is allowed to redeeming
     *                    assets after a failed settlement fallback. This address' Virtual Account is also allowed.
     *  @param _recipient recipient of bridged tokens and any outstanding gas on the destination chain.
     *  @param _dstChainId chain to bridge to.
     *  @param _params parameters for function call on branch chain.
     *  @param _sParams settlement parameters for asset bridging to branch chains.
     *  @param _gParams Gas Parameters for cross-chain message.
     *  @param _hasFallbackToggled Flag to toggle fallback function.
     *
     */
    function callOutAndBridge(
        address payable _settlementOwnerAndGasRefundee,
        address _recipient,
        uint16 _dstChainId,
        bytes calldata _params,
        SettlementInput calldata _sParams,
        GasParams calldata _gParams,
        bool _hasFallbackToggled
    ) external payable;

    /**
     * @notice External function to move assets from branch chain to root omnichain environment.
     *  @param _settlementOwnerAndGasRefundee the effective owner of the settlement this address receives
     *                    excess gas deposited on source chain for a cross-chain call and is allowed to redeeming
     *                    assets after a failed settlement fallback. This address' Virtual Account is also allowed.
     *  @param _recipient recipient of bridged tokens.
     *  @param _dstChainId chain to bridge to.
     *  @param _params parameters for function call on branch chain.
     *  @param _sParams settlement parameters for asset bridging to branch chains.
     *  @param _gParams Gas Parameters for cross-chain message.
     *  @param _hasFallbackToggled Flag to toggle fallback function.
     *
     *
     */
    function callOutAndBridgeMultiple(
        address payable _settlementOwnerAndGasRefundee,
        address _recipient,
        uint16 _dstChainId,
        bytes calldata _params,
        SettlementMultipleInput calldata _sParams,
        GasParams calldata _gParams,
        bool _hasFallbackToggled
    ) external payable;

    /*///////////////////////////////////////////////////////////////
                        SETTLEMENT FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Function to retry a user's Settlement balance.
     *  @param _settlementOwnerAndGasRefundee owner of the settlement and gas refundee.
     *  @param _settlementNonce Identifier for token settlement.
     *  @param _recipient recipient of bridged tokens and any outstanding gas on the destination chain.
     *  @param _params Calldata for function call in branch chain.
     *  @param _gParams Gas Parameters for cross-chain message.
     *  @param _hasFallbackToggled Flag to toggle fallback function.
     */
    function retrySettlement(
        address _settlementOwnerAndGasRefundee,
        uint32 _settlementNonce,
        address _recipient,
        bytes calldata _params,
        GasParams calldata _gParams,
        bool _hasFallbackToggled
    ) external payable;

    /**
     * @notice Function that allows retrieval of failed Settlement's foricng fallback to be triggered.
     *  @param _settlementNonce Identifier for token settlement.
     *  @param _gParams Gas Parameters for cross-chain message.
     *
     */
    function retrieveSettlement(uint32 _settlementNonce, GasParams calldata _gParams) external payable;

    /**
     * @notice Function that allows redemption of failed Settlement's global tokens.
     *  @param _depositNonce Identifier for token deposit.
     *  @param _recipient recipient of redeemed root/global tokens.
     *
     */
    function redeemSettlement(uint32 _depositNonce, address _recipient) external;

    /*///////////////////////////////////////////////////////////////
                    TOKEN MANAGEMENT FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Function to move assets from branch chain to root omnichain environment.
     * @dev Called in response to Bridge Agent Executor.
     *  @param _recipient recipient of bridged token.
     *  @param _dParams Cross-Chain Deposit of Multiple Tokens Params.
     *  @param _srcChainId chain to bridge from.
     *
     */
    function bridgeIn(address _recipient, DepositParams memory _dParams, uint256 _srcChainId) external;

    /**
     * @notice Function to move assets from branch chain to root omnichain environment.
     * @dev Called in response to Bridge Agent Executor.
     *  @param _recipient recipient of bridged tokens.
     *  @param _dParams Cross-Chain Deposit of Multiple Tokens Params.
     *  @param _srcChainId chain to bridge from.
     *  @dev Since the input data is encodePacked we need to parse it:
     *     1. First byte is the number of assets to be bridged in. Equals length of all arrays.
     *     2. Next 4 bytes are the nonce of the deposit.
     *     3. Last 32 bytes after the token related information are the chain to bridge to.
     *     4. Token related information starts at index PARAMS_TKN_START is encoded as follows:
     *         1. N * 32 bytes for the hToken address.
     *         2. N * 32 bytes for the underlying token address.
     *         3. N * 32 bytes for the amount of hTokens to be bridged in.
     *         4. N * 32 bytes for the amount of underlying tokens to be bridged in.
     *     5. Each of the 4 token related arrays are of length N and start at the following indexes:
     *         1. PARAMS_TKN_START [hToken address has no offset from token information start].
     *         2. PARAMS_TKN_START + (PARAMS_ADDRESS_SIZE * N)
     *         3. PARAMS_TKN_START + (PARAMS_AMT_OFFSET * N)
     *         4. PARAMS_TKN_START + (PARAMS_DEPOSIT_OFFSET * N)
     *
     */
    function bridgeInMultiple(address _recipient, DepositMultipleParams calldata _dParams, uint256 _srcChainId)
        external;

    /*///////////////////////////////////////////////////////////////
                            ADMIN FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Adds a new branch bridge agent to a given branch chainId
     *  @param _branchChainId chainId of the branch chain
     */
    function approveBranchBridgeAgent(uint256 _branchChainId) external;

    /**
     * @notice Updates the address of the branch bridge agent
     *  @param _newBranchBridgeAgent address of the new branch bridge agent
     *  @param _branchChainId chainId of the branch chain
     */
    function syncBranchBridgeAgent(address _newBranchBridgeAgent, uint256 _branchChainId) external;

    /**
     * @notice Allows current bridge agent manager to allowlist a successor address.
     *  @param _newManager address of the new bridge agent manager.
     */
    function transferManagementRole(address _newManager) external;

    /**
     * @notice Used by the new bridge agent manager to accept the management role.
     */
    function acceptManagementRole() external;

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

    /// @notice Event emitted when a settlement is successfully and fully redeemed.
    /// @param settlementNonce Identifier for user settlement.
    /// @param recipient recipient of redeemed root/global tokens.
    event RedeemSettlement(uint256 indexed settlementNonce, address indexed recipient);

    /// @notice Event emitted when fallback is received for a failed deposit nonce.
    /// @param depositNonce Identifier for user deposit.
    /// @param srcChainId Chain ID of the source chain.
    event LogExecute(uint256 indexed depositNonce, uint256 indexed srcChainId);

    /// @notice Event emitted when a settlement nonce is executed successfully.
    /// @param settlementNonce Identifier for user settlement.
    /// @param dstChainId Chain ID of the destination chain.
    event LogFallback(uint256 indexed settlementNonce, uint256 indexed dstChainId);

    /// @notice Event emitted after a message is sent to the Layer Zero Endpoint.
    /// @param gasLimit gas limit for the cross-chain call.
    /// @param remoteBranchExecutionGas native gas tokens to be sent to the remote branch.
    event LogGasParams(uint256 indexed gasLimit, uint256 indexed remoteBranchExecutionGas);

    /*///////////////////////////////////////////////////////////////
                            ERRORS
    ///////////////////////////////////////////////////////////////*/

    /// @notice Error emitted when the provided Root Router Address is invalid.
    error InvalidRootRouterAddress();
    /// @notice Error emitted when the provided Branch Port Address is invalid.
    error InvalidRootPortAddress();
    /// @notice Error emitted when the provided Layer Zero Endpoint Address is invalid.
    error InvalidEndpointAddress();

    /// @notice Error emitted execution of a transaction fails.
    error ExecutionFailure();
    /// @notice Error emitted when the provided deposit nonce has already been executed.
    error AlreadyExecutedTransaction();

    /// @notice Error emitted when the Bridge Agent does not recognize the provided action flag.
    error UnknownFlag();

    /// @notice Error emitted when caller is not the DAO address.
    error NotDao();

    /// @notice Error emitted when caller is not the Layer Zero Endpoint.
    error LayerZeroUnauthorizedEndpoint();
    /// @notice Error emitted when remote caller is not the chain's connected Branch Bridge Agent.
    error LayerZeroUnauthorizedCaller();

    /// @notice Error emitted when the Root Bridge Agent already has a Branch Bridge Agent for a given chain.
    error AlreadyAddedBridgeAgent();

    /// @notice Error emitted when the caller is not the connected Root Bridge Agent Executor.
    error UnrecognizedExecutor();
    /// @notice Error emitted when the caller is not the Root Port.
    error UnrecognizedPort();
    /// @notice Error emitted when the caller is not the connected Root Bridge Agent.
    error UnrecognizedBridgeAgent();
    /// @notice Error emitted when the caller is not the connected Arbitrum Branch Bridge Agent.
    error UnrecognizedLocalBridgeAgent();
    /// @notice Error emitted when the caller is not the Root Bridge Agent's Manager.
    error UnrecognizedBridgeAgentManager();
    /// @notice Error emitted when the caller is not the connected Root Router.
    error UnrecognizedRouter();

    /// @notice Error emitted when the requested token does not have an underlying address is the destiantion chain.
    error UnrecognizedUnderlyingAddress();
    /// @notice Error emitted when the requested token is not recognized in the destination chain.
    error UnrecognizedLocalAddress();

    /// @notice Error emitted when the settlement is not available for retry.
    error SettlementRetryUnavailable();
    /// @notice Error emitted when the settlement action flag is no retryable.
    error SettlementRetryUnavailableUseCallout();
    /// @notice Error emitted when the settlement is not available for redemption.
    error SettlementRedeemUnavailable();
    /// @notice Error emitted when the settlement is not available for retrieval.
    error SettlementRetrieveUnavailable();

    /// @notice Error emitted when caller is not the settlement owner.
    error NotSettlementOwner();

    /// @notice Error emitted when Virtual Account caller is not the owner or the approved router.
    error ContractsVirtualAccountNotAllowed();

    /// @notice Error emitted when there is not enough balance to serve a request, meaning request is invalid.
    error InsufficientBalanceForSettlement();

    /// @notice Error emitted when the token info is invalid.
    error InvalidInputParams();
    /// @notice Error emitted when the token info are not of the same length.
    error InvalidInputParamsLength();
}

File 13 of 15 : IRootPort.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {GasParams} from "../interfaces/IRootBridgeAgent.sol";

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

/**
 * @title  Root Port - Omnichain Token Management Contract
 * @author MaiaDAO
 * @notice Ulysses `RootPort` implementation for Root Omnichain Environment deployment.
 *         This contract is used to manage the deposit and withdrawal of assets
 *         between the Root Omnichain Environment and every Branch Chain in response to
 *         Root Bridge Agents requests. Manages Bridge Agents and their factories as well as
 *         key governance enabled actions such as adding new chains and bridge agent factories.
 */
interface IRootPort {
    /*///////////////////////////////////////////////////////////////
                        VIEW FUNCTIONS
    ///////////////////////////////////////////////////////////////*/
    /**
     * @notice View Function returns True if the chain Id has been added to the system.
     *  @param _chainId The Layer Zero chainId of the chain.
     * @return bool True if the chain Id has been added to the system.
     */
    function isChainId(uint256 _chainId) external view returns (bool);

    /**
     * @notice View Function returns True if the bridge agent factory has been added to the system.
     *  @param _bridgeAgentFactory The address of the bridge agent factory.
     * @return bool True if the bridge agent factory has been added to the system.
     */
    function isBridgeAgentFactory(address _bridgeAgentFactory) external view returns (bool);

    /**
     * @notice View Function returns True if the address corresponds to a global token.
     *  @param _globalAddress The address of the token in the global chain.
     * @return bool True if the address corresponds to a global token.
     */
    function isGlobalAddress(address _globalAddress) external view returns (bool);

    /**
     * @notice View Function returns Token's Global Address from it's local address.
     *  @param _localAddress The address of the token in the local chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return address The address of the global token.
     */
    function getGlobalTokenFromLocal(address _localAddress, uint256 _srcChainId) external view returns (address);

    /**
     * @notice View Function returns Token's Local Address from it's global address.
     *  @param _globalAddress The address of the token in the global chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return address The address of the local token.
     */
    function getLocalTokenFromGlobal(address _globalAddress, uint256 _srcChainId) external view returns (address);

    /**
     * @notice View Function that returns the local token address from the underlying token address.
     *  @param _underlyingAddress The address of the underlying token.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return address The address of the local token.
     */
    function getLocalTokenFromUnderlying(address _underlyingAddress, uint256 _srcChainId)
        external
        view
        returns (address);

    /**
     * @notice Function that returns Local Token's Local Address on another chain.
     *  @param _localAddress The address of the token in the local chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     *  @param _dstChainId The chainId of the chain where the token is deployed.
     * @return address The address of the local token in the destination chain.
     */
    function getLocalToken(address _localAddress, uint256 _srcChainId, uint256 _dstChainId)
        external
        view
        returns (address);

    /**
     * @notice View Function returns a underlying token address from it's local address.
     *  @param _localAddress The address of the token in the local chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return address The address of the underlying token.
     */
    function getUnderlyingTokenFromLocal(address _localAddress, uint256 _srcChainId) external view returns (address);

    /**
     * @notice Returns the underlying token address given it's global address.
     *  @param _globalAddress The address of the token in the global chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return address The address of the underlying token.
     */
    function getUnderlyingTokenFromGlobal(address _globalAddress, uint256 _srcChainId)
        external
        view
        returns (address);

    /**
     * @notice View Function returns True if Global Token is already added in current chain, false otherwise.
     *  @param _globalAddress The address of the token in the global chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return bool True if Global Token is already added in current chain, false otherwise.
     */
    function isGlobalToken(address _globalAddress, uint256 _srcChainId) external view returns (bool);

    /**
     * @notice View Function returns True if Local Token is already added in current chain, false otherwise.
     *  @param _localAddress The address of the token in the local chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return bool True if Local Token is already added in current chain, false otherwise.
     */
    function isLocalToken(address _localAddress, uint256 _srcChainId) external view returns (bool);

    /**
     * @notice View Function returns True if Local Token is already added in destination chain, false otherwise.
     *  @param _localAddress The address of the token in the local chain.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     *  @param _dstChainId The chainId of the chain where the token is deployed.
     * @return bool True if Local Token is already added in current chain, false otherwise.
     */
    function isLocalToken(address _localAddress, uint256 _srcChainId, uint256 _dstChainId)
        external
        view
        returns (bool);

    /**
     * @notice View Function returns True if the underlying Token is already added in given chain, false otherwise.
     *  @param _underlyingToken The address of the underlying token.
     *  @param _srcChainId The chainId of the chain where the token is deployed.
     * @return bool True if the underlying Token is already added in given chain, false otherwise.
     */
    function isUnderlyingToken(address _underlyingToken, uint256 _srcChainId) external view returns (bool);

    /**
     * @notice View Function returns True if the router is approved by user request to use their virtual account.
     *  @param _userAccount The virtual account of the user.
     *  @param _router The address of the router.
     * @return bool True if the router is approved by user request to use their virtual account.
     */
    function isRouterApproved(VirtualAccount _userAccount, address _router) external returns (bool);

    /**
     * @notice View Function returns Virtual Account of a given user.
     *  @param _user The address of the user.
     * @return VirtualAccount user virtual account.
     */
    function getUserAccount(address _user) external view returns (VirtualAccount);

    /**
     * @notice View Function returns bridge agent manager for a given root bridge agent.
     *  @param _rootBridgeAgent address of the root bridge agent.
     * @return address address of the bridge agent manager.
     */
    function getBridgeAgentManager(address _rootBridgeAgent) external view returns (address);

    /*///////////////////////////////////////////////////////////////
                        BRIDGE AGENT MANAGER FUNCTIONS
    ///////////////////////////////////////////////////////////////*/
    /**
     * @notice Allows a root bridge agent to update it's bridge agent manager address.
     *  @param _newManager address of the new bridge agent manager.
     */
    function setBridgeAgentManager(address _newManager) external;

    /*///////////////////////////////////////////////////////////////
                        hTOKEN ACCOUTING FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Updates root port state to match a new deposit.
     *   @param _to recipient of bridged tokens.
     *   @param _hToken address of the hToken to bridge.
     *   @param _amount total amount of tokens to bridge.
     *   @param _deposit amount of underlying tokens to deposit.
     *   @param _srcChainId chainId of the chain where the tokens are being bridged from.
     */
    function bridgeToRoot(address _to, address _hToken, uint256 _amount, uint256 _deposit, uint256 _srcChainId)
        external;

    /**
     * @notice Updates root port state to match hTokens being bridged to branch.
     *   @param _from depositor of the hTokens to bridge.
     *   @param _hToken address of the hToken to bridge.
     *   @param _amount total amount of tokens to bridge.
     *   @param _deposit amount of underlying tokens to deposit.
     *   @param _dstChainId chainId of the chain where the tokens are being bridged to.
     */
    function bridgeToBranch(address _from, address _hToken, uint256 _amount, uint256 _deposit, uint256 _dstChainId)
        external;

    /**
     * @notice Bridges hTokens from the local arbitrum branch for usage in the root port.
     *   @param _from sender of the hTokens to bridge.
     *   @param _hToken address of the hToken to bridge.
     *   @param _amount amount of hTokens to bridge.
     */
    function bridgeToRootFromLocalBranch(address _from, address _hToken, uint256 _amount) external;

    /**
     * @notice Bridges hTokens from the root port to the local arbitrum branch.
     *   @param _to recipient of the bridged tokens.
     *   @param _hToken address of the hToken to bridge.
     *   @param _amount amount of hTokens to bridge.
     */
    function bridgeToLocalBranchFromRoot(address _to, address _hToken, uint256 _amount) external;

    /**
     * @notice Burns tokens from the Arbitrum Branch Port withdrawer address.
     *   @param _from sender of the tokens to burn.
     *   @param _hToken address of the hToken to burn.
     *   @param _amount amount of tokens to burn.
     */
    function burnFromLocalBranch(address _from, address _hToken, uint256 _amount) external;

    /**
     * @notice Mints new root hTokens to the recipient address in reflection of Artbitrum Branch Port deposit.
     *   @param _to recipient of the newly minted tokens.
     *   @param _hToken address of the hToken to mint.
     *   @param _amount amount of tokens to mint.
     */
    function mintToLocalBranch(address _to, address _hToken, uint256 _amount) external;

    /*///////////////////////////////////////////////////////////////
                        hTOKEN MANAGEMENT FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Setter function to add a new underlying token to the system. Includes the creation of a new local hToken
     *         and global hToken.
     *   @param _globalAddress new root hToken address to set.
     *   @param _localAddress new origin chain local hToken address to set.
     *   @param _underlyingAddress new underlying/native token address to set.
     *   @param _srcChainId chainId of the chain where the token is deployed.
     *
     */
    function setAddresses(
        address _globalAddress,
        address _localAddress,
        address _underlyingAddress,
        uint256 _srcChainId
    ) external;

    /**
     * @notice Setter function to update a Global hToken's Local hToken Address.
     *   @param _globalAddress new hToken address to update.
     *   @param _localAddress new underlying/native token address to set.
     *
     */
    function setLocalAddress(address _globalAddress, address _localAddress, uint256 _srcChainId) external;

    /*///////////////////////////////////////////////////////////////
                    VIRTUAL ACCOUNT MANAGEMENT FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Gets the virtual account given a user address. Creates a new virtual account if one does not exist.
     * @param _user address of the user to get the virtual account for.
     */
    function fetchVirtualAccount(address _user) external returns (VirtualAccount account);

    /**
     * @notice Toggles the approval of a router for virtual account usage.
     * @dev Allows for a router to interact/spend from a user's virtual account.
     * @param _userAccount virtual account to toggle the approval for.
     * @param _router router to toggle the approval for.
     */
    function toggleVirtualAccountApproved(VirtualAccount _userAccount, address _router) external;

    /*///////////////////////////////////////////////////////////////
                    BRIDGE AGENT MANAGEMENT FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Adds a new bridge agent to the system.
     * @param _manager address of the manager of the bridge agent.
     * @param _bridgeAgent address of the bridge agent to add.
     */
    function addBridgeAgent(address _manager, address _bridgeAgent) external;

    /**
     * @notice Sets the address of the branch bridge agent connected to a root bridge agent for a given chain.
     * @param _newBranchBridgeAgent address of the new branch bridge agent.
     * @param _rootBridgeAgent address of the root bridge agent.
     * @param _srcChainId chainId of the chain to set the bridge agent for.
     */
    function syncBranchBridgeAgentWithRoot(address _newBranchBridgeAgent, address _rootBridgeAgent, uint256 _srcChainId)
        external;

    /*///////////////////////////////////////////////////////////////
                            ADMIN FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Toggles the status of a bridge agent factory.
     * @param _bridgeAgentFactory address of the bridge agent factory to toggle.
     */
    function toggleBridgeAgentFactory(address _bridgeAgentFactory) external;

    /**
     * @notice Adds a new chain to the root port lists of chains and adds core branch contracts to system.
     * @param _coreBranchBridgeAgentAddress address of the core branch bridge agent
     * @param _chainId chainId of the new chain
     * @param _wrappedGasTokenName gas token name of the new chain
     * @param _wrappedGasTokenSymbol gas token symbol of the new chain
     * @param _wrappedGasTokenDecimals gas token decimals of the new chain
     * @param _newLocalBranchWrappedNativeTokenAddress address of the wrapped native local hToken of the new chain
     * @param _newUnderlyingBranchWrappedNativeTokenAddress new branch address of the underlying wrapped native token
     */
    function addNewChain(
        address _coreBranchBridgeAgentAddress,
        uint256 _chainId,
        string memory _wrappedGasTokenName,
        string memory _wrappedGasTokenSymbol,
        uint8 _wrappedGasTokenDecimals,
        address _newLocalBranchWrappedNativeTokenAddress,
        address _newUnderlyingBranchWrappedNativeTokenAddress
    ) external;

    /**
     * @notice Adds an ecosystem hToken to a branch chain
     * @param ecoTokenGlobalAddress ecosystem token global address
     */
    function addEcosystemToken(address ecoTokenGlobalAddress) external;

    /**
     * @notice Sets the core root router and bridge agent
     * @param _coreRootRouter address of the core root router
     * @param _coreRootBridgeAgent address of the core root bridge agent
     */
    function setCoreRootRouter(address _coreRootRouter, address _coreRootBridgeAgent) external;

    /**
     * @notice Sets the core branch router and bridge agent
     * @param _refundee address of the refundee
     * @param _coreBranchRouter address of the core branch router
     * @param _coreBranchBridgeAgent address of the core branch bridge agent
     * @param _dstChainId chainId of the destination chain
     * @param _gParams gas params for the transaction
     */
    function setCoreBranchRouter(
        address _refundee,
        address _coreBranchRouter,
        address _coreBranchBridgeAgent,
        uint16 _dstChainId,
        GasParams calldata _gParams
    ) external payable;

    /**
     * @notice Syncs a new core branch router and bridge agent.
     * @param _coreBranchRouter address of the core branch router
     * @param _coreBranchBridgeAgent address of the core branch bridge agent
     * @param _dstChainId chainId of the destination chain
     */
    function syncNewCoreBranchRouter(address _coreBranchRouter, address _coreBranchBridgeAgent, uint16 _dstChainId)
        external;

    /**
     * @notice Allows governance to withdraw any native tokens accumulated from failed transactions.
     *  @param _to address to transfer ETH to.
     */
    function sweep(address _to) external;

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

    /// @notice Emitted when a new chain is added to the system.
    event NewChainAdded(uint256 indexed chainId);

    /// @notice Emitted when a new bridge agent manager is set for a Root Bridge Agent.
    event BridgeAgentManagerSet(address indexed bridgeAgent, address indexed manager);

    /// @notice Emitted when a new bridge agent factory is added or removed.
    event BridgeAgentFactoryToggled(address indexed bridgeAgentFactory);
    /// @notice Emitted when a new bridge agent is added or removed.
    event BridgeAgentToggled(address indexed bridgeAgent);
    /// @notice Emitted when a new branch bridge agent is added to a root bridge agent.
    event BridgeAgentSynced(address indexed bridgeAgent, address indexed rootBridgeAgent, uint256 indexed srcChainId);

    /// @notice Emitted when a new Virtual Account is created.
    event VirtualAccountCreated(address indexed user, address indexed account);

    /// @notice Emitted when a new local token is added to the system.
    event LocalTokenAdded(
        address indexed underlyingAddress, address indexed localAddress, address indexed globalAddress, uint256 chainId
    );
    /// @notice Emitted when a new global token is added to the system.
    event GlobalTokenAdded(address indexed localAddress, address indexed globalAddress, uint256 indexed chainId);
    /// @notice Emitted when a new Ecosystem Token is added to the system.
    event EcosystemTokenAdded(address indexed ecoTokenGlobalAddress);

    /// @notice Emitted when the Core Root Router and Bridge Agent are set.
    event CoreRootSet(address indexed coreRootRouter, address indexed coreRootBridgeAgent);
    /// @notice Emitted when a new Core Branch Router and Bridge Agent are set.
    event CoreBranchSet(
        address indexed coreBranchRouter, address indexed coreBranchBridgeAgent, uint16 indexed dstChainId
    );
    /// @notice Emitted when a new Core Branch Router and Bridge Agent are synced with the root environment.
    event CoreBranchSynced(
        address indexed coreBranchRouter, address indexed coreBranchBridgeAgent, uint16 indexed dstChainId
    );

    /*///////////////////////////////////////////////////////////////
                            ERRORS  
    ///////////////////////////////////////////////////////////////*/

    /// @notice Error emitted when the owner tries to renounce ownership.
    error RenounceOwnershipNotAllowed();

    /// @notice Error emitted when Set Up period is over.
    error SetUpEnded();
    /// @notice Error emitted when Core Set Up period is over.
    error SetUpCoreEnded();

    /// @notice Error emitted when hToken minting fails.
    error UnableToMint();
    /// @notice Error emitted when hToken bridging fails due to insufficient balance.
    error InsufficientBalance();

    /// @notice Error emitted when an invalid global token address is provided.
    error InvalidGlobalAddress();
    /// @notice Error emitted when an invalid local token address is provided.
    error InvalidLocalAddress();
    /// @notice Error emitted when an invalid underlying token address is provided.
    error InvalidUnderlyingAddress();
    /// @notice Error emitted when zero address is provided for Virtual Account creation.
    error InvalidUserAddress();
    /// @notice Error emitted when zero address is provided for sweep recipient.
    error InvalidRecipientAddress();
    /// @notice Error emitted when zero address is provided for CoreRootRouter.
    error InvalidCoreRootRouter();
    /// @notice Error emitted when zero address is provided for CoreRootBridgeAgent.
    error InvalidCoreRootBridgeAgent();
    /// @notice Error emitted when zero address is provided for CoreBranchRouter.
    error InvalidCoreBranchRouter();
    /// @notice Error emitted when zero address is provided for CoreBranchBridgeAgent.
    error InvalidCoreBrancBridgeAgent();
    /// @notice Error emitted when zero address is provided for RootBridgeAgentFactory.
    error InvalidRootBridgeAgentFactory();
    /// @notice Error emitted when zero address is provided for Branch Port.
    error InvalidBranchPort();

    /// @notice Error emitted when caller is not a Bridge Agent Factory.
    error UnrecognizedBridgeAgentFactory();
    /// @notice Error emitted when caller is not a Bridge Agent.
    error UnrecognizedBridgeAgent();
    /// @notice Error emitted when caller is not the Core Root Router.
    error UnrecognizedCoreRootRouter();
    /// @notice Error emitted when caller is not the Arbitrum Branch
    error UnrecognizedLocalBranchPort();
    /// @notice Error emitted when Core Root Bridge Agent being added isn't added as Bridge Agent yet.
    error UnrecognizedCoreRootBridgeAgent();

    /// @notice Error emitted when trying to add a chain that already exists.
    error AlreadyAddedChain();
    /// @notice Error emitted when trying to add a token that already exists as an Ecosystem Token.
    error AlreadyAddedEcosystemToken();

    /// @notice Error emitted when trying to add a Bridge Agent that already exists.
    error AlreadyAddedBridgeAgent();
    /// @notice Error emitted when trying to add a Bridge Agent Factory that already exists.
    error AlreadyAddedBridgeAgentFactory();

    /// @notice Error emitted when trying to add a chain to a Root Bridge Agent without a Bridge Agent Manager allowing.
    error BridgeAgentNotAllowed();
}

File 14 of 15 : IVirtualAccount.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC721Receiver} from "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol";

/// @notice Call structure based off `Multicall2` contract for aggregating calls.
struct Call {
    address target;
    bytes callData;
}

/// @notice Payable call structure based off `Multicall3` contract for aggreagating calls with `msg.value`.
struct PayableCall {
    address target;
    bytes callData;
    uint256 value;
}

/**
 * @title  Virtual Account Contract
 * @author MaiaDAO
 * @notice A Virtual Account allows users to manage assets and perform interactions remotely while
 *         allowing dApps to keep encapsulated user balance for accounting purposes.
 * @dev    This contract is based off `Multicall2` and `Multicall3` contract, executes a set of `Call` or `PayableCall`
 *         objects if any of the performed calls is invalid the whole batch should revert.
 */
interface IVirtualAccount is IERC721Receiver {
    /*///////////////////////////////////////////////////////////////
                                VIEW FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the address of the user that owns the VirtualAccount.
     * @return The address of the user that owns the VirtualAccount.
     */
    function userAddress() external view returns (address);

    /**
     * @notice Returns the address of the local port.
     * @return The address of the local port.
     */
    function localPortAddress() external view returns (address);

    /*///////////////////////////////////////////////////////////////
                            WITHDRAW FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Withdraws native tokens from the VirtualAccount.
     * @param _amount The amount of tokens to withdraw.
     */
    function withdrawNative(uint256 _amount) external;

    /**
     * @notice Withdraws ERC20 tokens from the VirtualAccount.
     * @param _token The address of the ERC20 token to withdraw.
     * @param _amount The amount of tokens to withdraw.
     */
    function withdrawERC20(address _token, uint256 _amount) external;

    /**
     * @notice Withdraws ERC721 tokens from the VirtualAccount.
     * @param _token The address of the ERC721 token to withdraw.
     * @param _tokenId The id of the token to withdraw.
     */
    function withdrawERC721(address _token, uint256 _tokenId) external;

    /*///////////////////////////////////////////////////////////////
                              CALL FUNCTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Aggregate calls ensuring each call is successful. Inspired by `Multicall2` contract.
     * @param callInput The call to make.
     * @return The return data of the call.
     */
    function call(Call[] calldata callInput) external returns (bytes[] memory);

    /**
     * @notice Aggregate calls with a msg value ensuring each call is successful. Inspired by `Multicall3` contract.
     * @param calls The calls to make.
     * @return The return data of the calls.
     * @dev Reverts if msg.value is less than the sum of the call values.
     */
    function payableCall(PayableCall[] calldata calls) external payable returns (bytes[] memory);

    /*///////////////////////////////////////////////////////////////
                                ERRORS
    ///////////////////////////////////////////////////////////////*/

    /// @notice Error thrown when a call is not successfull.
    error CallFailed(bytes returnData);

    /// @notice Error thrown when caller is not an approved Virtual Account caller.
    error UnauthorizedCaller();
}

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

/**
 * @title  Address Code Size Library
 * @notice Library for checking the size of a contract's code.
 * @dev    Used for checking if an address is a contract or an EOA.
 */
library AddressCodeSize {
    /*///////////////////////////////////////////////////////////////
                   PAYLOAD DECODING POSITIONAL CONSTANTS
    ///////////////////////////////////////////////////////////////*/

    function isContract(address addr) internal view returns (bool) {
        uint256 size;
        assembly ("memory-safe") {
            size := extcodesize(addr)
        }
        return size > 0;
    }

    function isEOA(address addr) internal view returns (bool) {
        uint256 size;
        assembly ("memory-safe") {
            size := extcodesize(addr)
        }
        return size == 0;
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "bytecodeHash": "none"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_userAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"name":"CallFailed","type":"error"},{"inputs":[],"name":"UnauthorizedCaller","type":"error"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"call","outputs":[{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"localPortAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct PayableCall[]","name":"calls","type":"tuple[]"}],"name":"payableCall","outputs":[{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdrawERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

Deployed Bytecode

0x60806040526004361015610015575b36610b7b57005b60003560e01c806301ffc9a7146100c5578063150b7a02146100c05780635abfaf6c146100bb57806362ffbaa2146100b657806384276d81146100b157806393f29694146100ac578063a1db9782146100a7578063bc197c81146100a2578063e4128fb31461009d578063f23a6e61146100985763f3e414f80361000e57610a5d565b6109cc565b61095d565b610896565b610778565b610683565b610570565b610431565b610266565b6101d5565b346101845760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361018457807f4e2312e0000000000000000000000000000000000000000000000000000000006020921490811561015a575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150143861014f565b600080fd5b73ffffffffffffffffffffffffffffffffffffffff81160361018457565b9181601f840112156101845782359167ffffffffffffffff8311610184576020838186019501011161018457565b346101845760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101845761020f600435610189565b61021a602435610189565b60643567ffffffffffffffff81116101845761023a9036906004016101a7565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101845760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000800de4d33015feb4e344951ace8ea2f4f33ae529168152f35b9181601f840112156101845782359167ffffffffffffffff8311610184576020808501948460051b01011161018457565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610184576004359067ffffffffffffffff82116101845761034f916004016102d5565b9091565b919082519283825260005b84811061039d5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161035e565b602080820190808352835180925260408301928160408460051b8301019501936000915b8483106103e55750505050505090565b9091929394958480610421837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528a51610353565b98019301930191949392906103d5565b61043a36610306565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015290919073ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000800de4d33015feb4e344951ace8ea2f4f33ae52987165af190811561056b5760009161053d575b50156104e5575b6104e16104d584846110b0565b604051918291826103b1565b0390f35b7f00000000000000000000000053d4c1997cc0cc79a1d62227ae75b7fcc880ec6d16330361051357386104c8565b60046040517f5c427cd9000000000000000000000000000000000000000000000000000000008152fd5b61055e915060203d8111610564575b6105568183610c6e565b810190610cb4565b386104c1565b503d61054c565b610ccc565b346101845760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000800de4d33015feb4e344951ace8ea2f4f33ae52987165af190811561056b57600091610665575b5015610637575b610635600435610cd8565b005b7f00000000000000000000000053d4c1997cc0cc79a1d62227ae75b7fcc880ec6d163303610513573861062a565b61067d915060203d8111610564576105568183610c6e565b38610623565b346101845761069136610306565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015290919073ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000800de4d33015feb4e344951ace8ea2f4f33ae52987165af190811561056b5760009161075a575b501561072c575b6104e16104d58484610f67565b7f00000000000000000000000053d4c1997cc0cc79a1d62227ae75b7fcc880ec6d163303610513573861071f565b610772915060203d8111610564576105568183610c6e565b38610718565b346101845760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576004356107b381610189565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000800de4d33015feb4e344951ace8ea2f4f33ae52987165af190811561056b57600091610878575b501561084a575b61063560243583610cf7565b7f00000000000000000000000053d4c1997cc0cc79a1d62227ae75b7fcc880ec6d163303610513573861083e565b610890915060203d8111610564576105568183610c6e565b38610837565b346101845760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576108d0600435610189565b6108db602435610189565b67ffffffffffffffff604435818111610184576108fc9036906004016102d5565b5050606435818111610184576109169036906004016102d5565b5050608435908111610184576109309036906004016101a7565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b346101845760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000053d4c1997cc0cc79a1d62227ae75b7fcc880ec6d168152f35b346101845760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457610a06600435610189565b610a11602435610189565b60843567ffffffffffffffff811161018457610a319036906004016101a7565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b346101845760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457600435610a9881610189565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000800de4d33015feb4e344951ace8ea2f4f33ae52987165af190811561056b57600091610b5d575b5015610b2f575b61063560243583610d41565b7f00000000000000000000000053d4c1997cc0cc79a1d62227ae75b7fcc880ec6d1633036105135738610b23565b610b75915060203d8111610564576105568183610c6e565b38610b1c565b3615610c3b576000805b368110610ba8576000388382305af43d6000803e15610ba3573d6000f35b3d6000fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9181358383011860001a600180809401948215610bec5750508153015b90610b85565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845290850194607f9250803591011860001a81811115610c30575b160101610be6565b838101388439610c28565b3636f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610caf57604052565b610c3f565b90816020910312610184575180151581036101845790565b6040513d6000823e3d90fd5b60009081803892335af115610ce957565b63b12d13eb6000526004601cfd5b6020906010923360145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615610d3457603452565b6390b8ec1890526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff1690813b15610184576000916064839260405194859384927f23b872dd00000000000000000000000000000000000000000000000000000000845230600485015233602485015260448401525af1801561056b57610db15750565b67ffffffffffffffff8111610caf57604052565b67ffffffffffffffff8111610caf5760051b60200190565b90610de782610dc5565b610df46040519182610c6e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610e228294610dc5565b019060005b828110610e3357505050565b806060602080938501015201610e27565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b35610e7d81610189565b90565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610184570180359067ffffffffffffffff82116101845760200191813603831361018457565b908092918237016000815290565b3d15610f38573d9067ffffffffffffffff8211610caf5760405191610f2c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610c6e565b82523d6000602084013e565b606090565b8051821015610f515760209160051b010190565b610e44565b906020610e7d928181520190610353565b9190610f7281610ddd565b9260005b828110610f8257505050565b60008160051b8301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18436030181121561106c5781908401610fcd610fc782610e73565b3b151590565b611025575b505015610fe157600101610f76565b610fee6110219186610f3d565b516040519182917fa5fa8d2b00000000000000000000000000000000000000000000000000000000835260048301610f56565b0390fd5b8161103d61103283610e73565b926020810190610e80565b919061104e60405180948193610ed1565b03925af161105a610edf565b6110648388610f3d565b528038610fd2565b5080fd5b9190811015610f515760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215610184570190565b9190916000906110bf84610ddd565b93825b81811061113a5750505034036110d457565b6040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152806110216004820160609060208152600460208201527f5c63102a0000000000000000000000000000000000000000000000000000000060408201520190565b611145818385611070565b60409081810135809601956000918291611161610fc783610e73565b6111b6575b5050501561117757506001016110c2565b6111846110219288610f3d565b5190519182917fa5fa8d2b00000000000000000000000000000000000000000000000000000000835260048301610f56565b6111c261103283610e73565b91906111d2875180948193610ed1565b03925af16111de610edf565b6111e8848a610f3d565b5280388061116656fea164736f6c6343000813000a

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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