Source Code
Overview
ETH Balance
0 ETH
Token Holdings
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 88 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Call | 6322645 | 148 days ago | IN | 0 ETH | 0.00030834 | ||||
Call | 6284533 | 154 days ago | IN | 0 ETH | 0.00547406 | ||||
Call | 6284495 | 154 days ago | IN | 0 ETH | 0.00077933 | ||||
Call | 6284447 | 154 days ago | IN | 0 ETH | 0.00124948 | ||||
Call | 6283050 | 154 days ago | IN | 0 ETH | 0.00257236 | ||||
Call | 6282960 | 154 days ago | IN | 0 ETH | 0.04119595 | ||||
Call | 6282949 | 154 days ago | IN | 0 ETH | 0.04233923 | ||||
Call | 6282773 | 154 days ago | IN | 0 ETH | 0.09131289 | ||||
Call | 6282772 | 154 days ago | IN | 0 ETH | 0.08374362 | ||||
Call | 6282771 | 154 days ago | IN | 0 ETH | 0.10073524 | ||||
Call | 6282767 | 154 days ago | IN | 0 ETH | 0.14620657 | ||||
Call | 6282756 | 154 days ago | IN | 0 ETH | 0.15492445 | ||||
Call | 6282742 | 154 days ago | IN | 0 ETH | 0.11273307 | ||||
Call | 6278258 | 155 days ago | IN | 0 ETH | 0.00061089 | ||||
Call | 6278250 | 155 days ago | IN | 0 ETH | 0.00128193 | ||||
Call | 6278242 | 155 days ago | IN | 0 ETH | 0.00038302 | ||||
Call | 6278241 | 155 days ago | IN | 0 ETH | 0.00045439 | ||||
Call | 6278240 | 155 days ago | IN | 0 ETH | 0.00054461 | ||||
Call | 6278166 | 155 days ago | IN | 0 ETH | 0.00143013 | ||||
Call | 6278045 | 155 days ago | IN | 0 ETH | 0.00037535 | ||||
Call | 6277967 | 155 days ago | IN | 0 ETH | 0.00049964 | ||||
Call | 6277962 | 155 days ago | IN | 0 ETH | 0.00059353 | ||||
Call | 6277961 | 155 days ago | IN | 0 ETH | 0.00050193 | ||||
Call | 6277544 | 155 days ago | IN | 0 ETH | 0.00226007 | ||||
Call | 6277519 | 155 days ago | IN | 0 ETH | 0.00153486 |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
VirtualAccount
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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(); } } _; } }
// 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); }
// 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); } }
// 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); }
// 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; } }
// 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); }
// 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()) } } }
// 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) ) ) } } }
// 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; } }
// 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; }
// 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; }
// 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(); }
// 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(); }
// 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(); }
// 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; } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"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"}]
Contract Creation Code
60c0346100bd57601f6112d738819003918201601f19168301916001600160401b038311848410176100c2578084926020946040528339810103126100bd57516001600160a01b03811681036100bd573360a0526080526040516111fe90816100d982396080518181816104e7015281816106390152818161072e0152818161084c015281816109a80152610b31015260a0518181816102b10152818161048f015281816105f1015281816106e6015281816108050152610aea0152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610015575b36610b7b57005b60003560e01c806301ffc9a7146100c5578063150b7a02146100c05780635abfaf6c146100bb57806362ffbaa2146100b657806384276d81146100b157806393f29694146100ac578063a1db9782146100a7578063bc197c81146100a2578063e4128fb31461009d578063f23a6e61146100985763f3e414f80361000e57610a5d565b6109cc565b61095d565b610896565b610778565b610683565b610570565b610431565b610266565b6101d5565b346101845760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361018457807f4e2312e0000000000000000000000000000000000000000000000000000000006020921490811561015a575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150143861014f565b600080fd5b73ffffffffffffffffffffffffffffffffffffffff81160361018457565b9181601f840112156101845782359167ffffffffffffffff8311610184576020838186019501011161018457565b346101845760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101845761020f600435610189565b61021a602435610189565b60643567ffffffffffffffff81116101845761023a9036906004016101a7565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101845760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b9181601f840112156101845782359167ffffffffffffffff8311610184576020808501948460051b01011161018457565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610184576004359067ffffffffffffffff82116101845761034f916004016102d5565b9091565b919082519283825260005b84811061039d5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161035e565b602080820190808352835180925260408301928160408460051b8301019501936000915b8483106103e55750505050505090565b9091929394958480610421837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528a51610353565b98019301930191949392906103d5565b61043a36610306565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015290919073ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000000000000000000000000000000000000000000087165af190811561056b5760009161053d575b50156104e5575b6104e16104d584846110b0565b604051918291826103b1565b0390f35b7f000000000000000000000000000000000000000000000000000000000000000016330361051357386104c8565b60046040517f5c427cd9000000000000000000000000000000000000000000000000000000008152fd5b61055e915060203d8111610564575b6105568183610c6e565b810190610cb4565b386104c1565b503d61054c565b610ccc565b346101845760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000000000000000000000000000000000000000000087165af190811561056b57600091610665575b5015610637575b610635600435610cd8565b005b7f0000000000000000000000000000000000000000000000000000000000000000163303610513573861062a565b61067d915060203d8111610564576105568183610c6e565b38610623565b346101845761069136610306565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015290919073ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000000000000000000000000000000000000000000087165af190811561056b5760009161075a575b501561072c575b6104e16104d58484610f67565b7f0000000000000000000000000000000000000000000000000000000000000000163303610513573861071f565b610772915060203d8111610564576105568183610c6e565b38610718565b346101845760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576004356107b381610189565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000000000000000000000000000000000000000000087165af190811561056b57600091610878575b501561084a575b61063560243583610cf7565b7f0000000000000000000000000000000000000000000000000000000000000000163303610513573861083e565b610890915060203d8111610564576105568183610c6e565b38610837565b346101845760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576108d0600435610189565b6108db602435610189565b67ffffffffffffffff604435818111610184576108fc9036906004016102d5565b5050606435818111610184576109169036906004016102d5565b5050608435908111610184576109309036906004016101a7565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b346101845760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101845760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457610a06600435610189565b610a11602435610189565b60843567ffffffffffffffff811161018457610a319036906004016101a7565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b346101845760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457600435610a9881610189565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000000000000000000000000000000000000000000087165af190811561056b57600091610b5d575b5015610b2f575b61063560243583610d41565b7f00000000000000000000000000000000000000000000000000000000000000001633036105135738610b23565b610b75915060203d8111610564576105568183610c6e565b38610b1c565b3615610c3b576000805b368110610ba8576000388382305af43d6000803e15610ba3573d6000f35b3d6000fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9181358383011860001a600180809401948215610bec5750508153015b90610b85565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845290850194607f9250803591011860001a81811115610c30575b160101610be6565b838101388439610c28565b3636f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610caf57604052565b610c3f565b90816020910312610184575180151581036101845790565b6040513d6000823e3d90fd5b60009081803892335af115610ce957565b63b12d13eb6000526004601cfd5b6020906010923360145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615610d3457603452565b6390b8ec1890526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff1690813b15610184576000916064839260405194859384927f23b872dd00000000000000000000000000000000000000000000000000000000845230600485015233602485015260448401525af1801561056b57610db15750565b67ffffffffffffffff8111610caf57604052565b67ffffffffffffffff8111610caf5760051b60200190565b90610de782610dc5565b610df46040519182610c6e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610e228294610dc5565b019060005b828110610e3357505050565b806060602080938501015201610e27565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b35610e7d81610189565b90565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610184570180359067ffffffffffffffff82116101845760200191813603831361018457565b908092918237016000815290565b3d15610f38573d9067ffffffffffffffff8211610caf5760405191610f2c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610c6e565b82523d6000602084013e565b606090565b8051821015610f515760209160051b010190565b610e44565b906020610e7d928181520190610353565b9190610f7281610ddd565b9260005b828110610f8257505050565b60008160051b8301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18436030181121561106c5781908401610fcd610fc782610e73565b3b151590565b611025575b505015610fe157600101610f76565b610fee6110219186610f3d565b516040519182917fa5fa8d2b00000000000000000000000000000000000000000000000000000000835260048301610f56565b0390fd5b8161103d61103283610e73565b926020810190610e80565b919061104e60405180948193610ed1565b03925af161105a610edf565b6110648388610f3d565b528038610fd2565b5080fd5b9190811015610f515760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215610184570190565b9190916000906110bf84610ddd565b93825b81811061113a5750505034036110d457565b6040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152806110216004820160609060208152600460208201527f5c63102a0000000000000000000000000000000000000000000000000000000060408201520190565b611145818385611070565b60409081810135809601956000918291611161610fc783610e73565b6111b6575b5050501561117757506001016110c2565b6111846110219288610f3d565b5190519182917fa5fa8d2b00000000000000000000000000000000000000000000000000000000835260048301610f56565b6111c261103283610e73565b91906111d2875180948193610ed1565b03925af16111de610edf565b6111e8848a610f3d565b5280388061116656fea164736f6c6343000813000a000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791
Deployed Bytecode
0x60806040526004361015610015575b36610b7b57005b60003560e01c806301ffc9a7146100c5578063150b7a02146100c05780635abfaf6c146100bb57806362ffbaa2146100b657806384276d81146100b157806393f29694146100ac578063a1db9782146100a7578063bc197c81146100a2578063e4128fb31461009d578063f23a6e61146100985763f3e414f80361000e57610a5d565b6109cc565b61095d565b610896565b610778565b610683565b610570565b610431565b610266565b6101d5565b346101845760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361018457807f4e2312e0000000000000000000000000000000000000000000000000000000006020921490811561015a575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150143861014f565b600080fd5b73ffffffffffffffffffffffffffffffffffffffff81160361018457565b9181601f840112156101845782359167ffffffffffffffff8311610184576020838186019501011161018457565b346101845760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101845761020f600435610189565b61021a602435610189565b60643567ffffffffffffffff81116101845761023a9036906004016101a7565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101845760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b067c40f648d6b8811ef1334bff37d3465b67abd168152f35b9181601f840112156101845782359167ffffffffffffffff8311610184576020808501948460051b01011161018457565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610184576004359067ffffffffffffffff82116101845761034f916004016102d5565b9091565b919082519283825260005b84811061039d5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161035e565b602080820190808352835180925260408301928160408460051b8301019501936000915b8483106103e55750505050505090565b9091929394958480610421837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc086600196030187528a51610353565b98019301930191949392906103d5565b61043a36610306565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015290919073ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000b067c40f648d6b8811ef1334bff37d3465b67abd87165af190811561056b5760009161053d575b50156104e5575b6104e16104d584846110b0565b604051918291826103b1565b0390f35b7f000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d79116330361051357386104c8565b60046040517f5c427cd9000000000000000000000000000000000000000000000000000000008152fd5b61055e915060203d8111610564575b6105568183610c6e565b810190610cb4565b386104c1565b503d61054c565b610ccc565b346101845760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000b067c40f648d6b8811ef1334bff37d3465b67abd87165af190811561056b57600091610665575b5015610637575b610635600435610cd8565b005b7f000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791163303610513573861062a565b61067d915060203d8111610564576105568183610c6e565b38610623565b346101845761069136610306565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015290919073ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000b067c40f648d6b8811ef1334bff37d3465b67abd87165af190811561056b5760009161075a575b501561072c575b6104e16104d58484610f67565b7f000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791163303610513573861071f565b610772915060203d8111610564576105568183610c6e565b38610718565b346101845760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576004356107b381610189565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000b067c40f648d6b8811ef1334bff37d3465b67abd87165af190811561056b57600091610878575b501561084a575b61063560243583610cf7565b7f000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791163303610513573861083e565b610890915060203d8111610564576105568183610c6e565b38610837565b346101845760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610184576108d0600435610189565b6108db602435610189565b67ffffffffffffffff604435818111610184576108fc9036906004016102d5565b5050606435818111610184576109169036906004016102d5565b5050608435908111610184576109309036906004016101a7565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b346101845760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791168152f35b346101845760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457610a06600435610189565b610a11602435610189565b60843567ffffffffffffffff811161018457610a319036906004016101a7565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b346101845760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018457600435610a9881610189565b6040517f065b904100000000000000000000000000000000000000000000000000000000815230600482015233602482015273ffffffffffffffffffffffffffffffffffffffff9060208160448160007f000000000000000000000000b067c40f648d6b8811ef1334bff37d3465b67abd87165af190811561056b57600091610b5d575b5015610b2f575b61063560243583610d41565b7f000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d7911633036105135738610b23565b610b75915060203d8111610564576105568183610c6e565b38610b1c565b3615610c3b576000805b368110610ba8576000388382305af43d6000803e15610ba3573d6000f35b3d6000fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9181358383011860001a600180809401948215610bec5750508153015b90610b85565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff845290850194607f9250803591011860001a81811115610c30575b160101610be6565b838101388439610c28565b3636f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610caf57604052565b610c3f565b90816020910312610184575180151581036101845790565b6040513d6000823e3d90fd5b60009081803892335af115610ce957565b63b12d13eb6000526004601cfd5b6020906010923360145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615610d3457603452565b6390b8ec1890526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff1690813b15610184576000916064839260405194859384927f23b872dd00000000000000000000000000000000000000000000000000000000845230600485015233602485015260448401525af1801561056b57610db15750565b67ffffffffffffffff8111610caf57604052565b67ffffffffffffffff8111610caf5760051b60200190565b90610de782610dc5565b610df46040519182610c6e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610e228294610dc5565b019060005b828110610e3357505050565b806060602080938501015201610e27565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b35610e7d81610189565b90565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610184570180359067ffffffffffffffff82116101845760200191813603831361018457565b908092918237016000815290565b3d15610f38573d9067ffffffffffffffff8211610caf5760405191610f2c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610c6e565b82523d6000602084013e565b606090565b8051821015610f515760209160051b010190565b610e44565b906020610e7d928181520190610353565b9190610f7281610ddd565b9260005b828110610f8257505050565b60008160051b8301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18436030181121561106c5781908401610fcd610fc782610e73565b3b151590565b611025575b505015610fe157600101610f76565b610fee6110219186610f3d565b516040519182917fa5fa8d2b00000000000000000000000000000000000000000000000000000000835260048301610f56565b0390fd5b8161103d61103283610e73565b926020810190610e80565b919061104e60405180948193610ed1565b03925af161105a610edf565b6110648388610f3d565b528038610fd2565b5080fd5b9190811015610f515760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215610184570190565b9190916000906110bf84610ddd565b93825b81811061113a5750505034036110d457565b6040517fa5fa8d2b000000000000000000000000000000000000000000000000000000008152806110216004820160609060208152600460208201527f5c63102a0000000000000000000000000000000000000000000000000000000060408201520190565b611145818385611070565b60409081810135809601956000918291611161610fc783610e73565b6111b6575b5050501561117757506001016110c2565b6111846110219288610f3d565b5190519182917fa5fa8d2b00000000000000000000000000000000000000000000000000000000835260048301610f56565b6111c261103283610e73565b91906111d2875180948193610ed1565b03925af16111de610edf565b6111e8848a610f3d565b5280388061116656fea164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791
-----Decoded View---------------
Arg [0] : _userAddress (address): 0xC466af7ff16ef0f1A7fa4E23E095E47a4058D791
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000c466af7ff16ef0f1a7fa4e23e095e47a4058d791
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.