ETH Price: $1,971.29 (+0.13%)
Gas: 0.04 Gwei

Transaction Decoder

Block:
24499904 at Feb-20-2026 06:37:59 PM +UTC
Transaction Fee:
0.000031311659923164 ETH $0.06
Gas Used:
339,217 Gas / 0.092305692 Gwei

Emitted Events:

556 WalletSimple.Deposited( from=0x0be7a359982d9042cbd81a00b3fff8dac70723e2, value=33106550000000000, data=0x )
557 0x0be7a359982d9042cbd81a00b3fff8dac70723e2.0x69b31548dea9b3b707b4dff357d326e3e9348b24e7a6080a218a6edeeec48f9b( 0x69b31548dea9b3b707b4dff357d326e3e9348b24e7a6080a218a6edeeec48f9b, 00000000000000000000000009c30cdcdd971423cb3ba757a47d56c35d06d818, 00000000000000000000000000000000000000000000000000759e3b587d1c00, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x002155B9...1549e07Fb 0.0000026877306036 Eth0.0558322877306036 Eth0.0558296
0x238C0765...1818e2E00 2.164 Eth3.064 Eth0.9
0x2b3FeD49...355305DbB
(Revolut 4)
1,612.467292411606461446 Eth
Nonce: 1055844
1,608.075881789946538282 Eth
Nonce: 1055845
4.391410621659923164
0x35dcc2BE...A332B8f07 0.000022918828485 Eth0.033782728828485 Eth0.03375981
0x46f22eA2...050123f71 0.001433297266397214 Eth0.804639757266397214 Eth0.80320646
(Titan Builder)
12.432865935632672915 Eth12.432868654878574646 Eth0.000002719245901731
0x4E8EdcB1...c0B5AfB50 0.000000326894631 Eth0.063339256894631 Eth0.06333893
0x544F9369...0199E2Aec
(Binance Dep: 0x544f93692d84183099f53c7a8225c040199e2aec)
0.428862309484361 Eth0.507798729484361 Eth0.07893642
0x54FECf03...966FC19F6 0.000000374589768 Eth0.025938824589768 Eth0.02593845
0x58cB01E6...1857b4BF2 0.000000151644192 Eth0.064850471644192 Eth0.06485032
0x82105C06...f44f77025 0 Eth0.65981247 Eth0.65981247
0x86eb8d8b...DAefe4364 0.000000282386307 Eth0.051603382386307 Eth0.0516031
0x98b4c6d6...B596221eD 0.6792896707094385 Eth1.5285527307094385 Eth0.84926306
0xb0336d8b...77Eb5e5Ed 0.0214079 Eth0.02288976 Eth0.00148186
0xBCF0E947...f3924B6ff 0.000002214003498 Eth0.024191734003498 Eth0.02418952
0xcBb8374e...3945209C0 0.000000127757406 Eth0.004623157757406 Eth0.00462303
0xcbB945B1...9cB02553a 0.002986709205173802 Eth0.131466139205173802 Eth0.12847943
0xcc6902ef...c17899F64
(ByBit Dep: 0xcc6902efEe1BbDddca0aC8c7a4A6710c17899F64)
0 Eth
Nonce: 0
0.00345184 Eth
Nonce: 0
0.00345184From: 0 To: 0
0xcCC2B4F9...7FfF1f108 0 Eth0.01861141 Eth0.01861141
0xcFaD9499...3b663D5AA 0 Eth0.01419085 Eth0.01419085
0xcFc8EA70...32f6CC811 0.0000011002656 Eth0.0591495902656 Eth0.05914849
0xd2350Fd5...981bA2627 1.26996658 Eth1.38996658 Eth0.12
0xD8d76f49...3FbdA7Ca8 0.0000222165677685 Eth0.0236105665677685 Eth0.02358835
0xE763E04A...0eF488A86 2.192916618250407 Eth2.495978468250407 Eth0.30306185
0xEF07bAbF...82DE48333 0.000942429720572908 Eth0.038894879720572908 Eth0.03795245
0xf6F35B8D...eaC60c1eB 110.118453754160260985 Eth110.151560304160260985 Eth0.03310655
0xfAC7D446...3E595A180 0 Eth0.03295506 Eth0.03295506

Execution Trace

ETH 4.39137931 ERC1967Proxy.318adb8b( )
  • ETH 4.39137931 0x6d28bc0f114e3c629320ee692ede5bdd7155e6fe.318adb8b( )
    • ETH 0.03375981 0x35dcc2be98eeb32d70a6be3205e94e5a332b8f07.CALL( )
    • ETH 0.0558296 0x002155b91cf5659696e37ca66105d851549e07fb.CALL( )
    • ETH 0.02358835 0xd8d76f49a6391ed1e49fbf9177d22893fbda7ca8.CALL( )
    • ETH 0.00345184 ByBit Dep: 0xcc6902efEe1BbDddca0aC8c7a4A6710c17899F64.CALL( )
    • ETH 0.07893642 Binance Dep: 0x544f93692d84183099f53c7a8225c040199e2aec.CALL( )
    • ETH 0.03795245 0xef07babf9cdc00db039bbdf455d8d2a82de48333.CALL( )
    • ETH 0.02418952 0xbcf0e947833c09bc0a3206b67215722f3924b6ff.CALL( )
    • ETH 0.03310655 0x0be7a359982d9042cbd81a00b3fff8dac70723e2.CALL( )
      • ETH 0.03310655 Forwarder.DELEGATECALL( )
        • ETH 0.03310655 WalletSimple.CALL( )
          • ETH 0.03310655 WalletSimple.DELEGATECALL( )
          • ETH 0.30306185 0xe763e04a62f21fcd5ad7e7133db3bf50ef488a86.CALL( )
          • ETH 0.02593845 0x54fecf039000d1ae03af3c6c9582ad3966fc19f6.CALL( )
          • ETH 0.00462303 0xcbb8374e05cbfd32757573a49adb3cf3945209c0.CALL( )
          • ETH 0.80320646 0x46f22ea226654bd904a16c597f9b1a0050123f71.CALL( )
          • ETH 0.01419085 0xcfad9499d5fa661dbe0c535921388c13b663d5aa.CALL( )
          • ETH 0.00148186 0xb0336d8ba9acd0ad1f4754331ef5d6477eb5e5ed.CALL( )
          • ETH 0.0516031 0x86eb8d8b678bf750642ec037ecfd975daefe4364.CALL( )
          • ETH 0.12 0xd2350fd59565a5c0a9e53a2568af5b7981ba2627.CALL( )
          • ETH 0.01861141 0xccc2b4f92b1ffa9bcf2268c290802e87fff1f108.CALL( )
          • ETH 0.12847943 0xcbb945b15488ec2be8d73055c0eca8e9cb02553a.CALL( )
          • ETH 0.06333893 0x4e8edcb1de86b0b4ff88e74f839a2e1c0b5afb50.CALL( )
          • ETH 0.06485032 0x58cb01e633575e79b8a9959cc99b3451857b4bf2.CALL( )
          • ETH 0.84926306 0x98b4c6d62049c075577d0f41f535f3cb596221ed.CALL( )
          • ETH 0.9 0x238c076506b1f0de86b940f69b66c461818e2e00.CALL( )
          • ETH 0.03295506 0xfac7d446d9e779a95754e48e4076c353e595a180.CALL( )
          • ETH 0.65981247 0x82105c0623c5eceaebcc58c452427dcf44f77025.CALL( )
          • ETH 0.05914849 0xcfc8ea7090d92acfeebdf1da61b14a032f6cc811.CALL( )
            File 1 of 4: ERC1967Proxy
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
            pragma solidity ^0.8.20;
            import {Proxy} from "../Proxy.sol";
            import {ERC1967Utils} from "./ERC1967Utils.sol";
            /**
             * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
             * implementation address that can be changed. This address is stored in storage in the location specified by
             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
             * implementation behind the proxy.
             */
            contract ERC1967Proxy is Proxy {
                /**
                 * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
                 *
                 * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
                 * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
                 *
                 * Requirements:
                 *
                 * - If `data` is empty, `msg.value` must be zero.
                 */
                constructor(address implementation, bytes memory _data) payable {
                    ERC1967Utils.upgradeToAndCall(implementation, _data);
                }
                /**
                 * @dev Returns the current implementation address.
                 *
                 * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                 * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                 * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                 */
                function _implementation() internal view virtual override returns (address) {
                    return ERC1967Utils.getImplementation();
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
             * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
             * be specified by overriding the virtual {_implementation} function.
             *
             * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
             * different contract through the {_delegate} function.
             *
             * The success and return data of the delegated call will be returned back to the caller of the proxy.
             */
            abstract contract Proxy {
                /**
                 * @dev Delegates the current call to `implementation`.
                 *
                 * This function does not return to its internal call site, it will return directly to the external caller.
                 */
                function _delegate(address implementation) internal virtual {
                    assembly {
                        // Copy msg.data. We take full control of memory in this inline assembly
                        // block because it will not return to Solidity code. We overwrite the
                        // Solidity scratch pad at memory position 0.
                        calldatacopy(0, 0, calldatasize())
                        // Call the implementation.
                        // out and outsize are 0 because we don't know the size yet.
                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                        // Copy the returned data.
                        returndatacopy(0, 0, returndatasize())
                        switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                            revert(0, returndatasize())
                        }
                        default {
                            return(0, returndatasize())
                        }
                    }
                }
                /**
                 * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
                 * function and {_fallback} should delegate.
                 */
                function _implementation() internal view virtual returns (address);
                /**
                 * @dev Delegates the current call to the address returned by `_implementation()`.
                 *
                 * This function does not return to its internal call site, it will return directly to the external caller.
                 */
                function _fallback() internal virtual {
                    _delegate(_implementation());
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                 * function in the contract matches the call data.
                 */
                fallback() external payable virtual {
                    _fallback();
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
            pragma solidity ^0.8.20;
            import {IBeacon} from "../beacon/IBeacon.sol";
            import {Address} from "../../utils/Address.sol";
            import {StorageSlot} from "../../utils/StorageSlot.sol";
            /**
             * @dev This abstract contract provides getters and event emitting update functions for
             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
             */
            library ERC1967Utils {
                // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
                // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
                /**
                 * @dev Emitted when the implementation is upgraded.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @dev Emitted when the admin account has changed.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
                /**
                 * @dev Emitted when the beacon is changed.
                 */
                event BeaconUpgraded(address indexed beacon);
                /**
                 * @dev Storage slot with the address of the current implementation.
                 * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
                 */
                // solhint-disable-next-line private-vars-leading-underscore
                bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /**
                 * @dev The `implementation` of the proxy is invalid.
                 */
                error ERC1967InvalidImplementation(address implementation);
                /**
                 * @dev The `admin` of the proxy is invalid.
                 */
                error ERC1967InvalidAdmin(address admin);
                /**
                 * @dev The `beacon` of the proxy is invalid.
                 */
                error ERC1967InvalidBeacon(address beacon);
                /**
                 * @dev An upgrade function sees `msg.value > 0` that may be lost.
                 */
                error ERC1967NonPayable();
                /**
                 * @dev Returns the current implementation address.
                 */
                function getImplementation() internal view returns (address) {
                    return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
                }
                /**
                 * @dev Stores a new address in the EIP1967 implementation slot.
                 */
                function _setImplementation(address newImplementation) private {
                    if (newImplementation.code.length == 0) {
                        revert ERC1967InvalidImplementation(newImplementation);
                    }
                    StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
                }
                /**
                 * @dev Performs implementation upgrade with additional setup call if data is nonempty.
                 * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                 * to avoid stuck value in the contract.
                 *
                 * Emits an {IERC1967-Upgraded} event.
                 */
                function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                    if (data.length > 0) {
                        Address.functionDelegateCall(newImplementation, data);
                    } else {
                        _checkNonPayable();
                    }
                }
                /**
                 * @dev Storage slot with the admin of the contract.
                 * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                 */
                // solhint-disable-next-line private-vars-leading-underscore
                bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                /**
                 * @dev Returns the current admin.
                 *
                 * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                 * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                 * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                 */
                function getAdmin() internal view returns (address) {
                    return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
                }
                /**
                 * @dev Stores a new address in the EIP1967 admin slot.
                 */
                function _setAdmin(address newAdmin) private {
                    if (newAdmin == address(0)) {
                        revert ERC1967InvalidAdmin(address(0));
                    }
                    StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
                }
                /**
                 * @dev Changes the admin of the proxy.
                 *
                 * Emits an {IERC1967-AdminChanged} event.
                 */
                function changeAdmin(address newAdmin) internal {
                    emit AdminChanged(getAdmin(), newAdmin);
                    _setAdmin(newAdmin);
                }
                /**
                 * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                 * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
                 */
                // solhint-disable-next-line private-vars-leading-underscore
                bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                /**
                 * @dev Returns the current beacon.
                 */
                function getBeacon() internal view returns (address) {
                    return StorageSlot.getAddressSlot(BEACON_SLOT).value;
                }
                /**
                 * @dev Stores a new beacon in the EIP1967 beacon slot.
                 */
                function _setBeacon(address newBeacon) private {
                    if (newBeacon.code.length == 0) {
                        revert ERC1967InvalidBeacon(newBeacon);
                    }
                    StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
                    address beaconImplementation = IBeacon(newBeacon).implementation();
                    if (beaconImplementation.code.length == 0) {
                        revert ERC1967InvalidImplementation(beaconImplementation);
                    }
                }
                /**
                 * @dev Change the beacon and trigger a setup call if data is nonempty.
                 * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                 * to avoid stuck value in the contract.
                 *
                 * Emits an {IERC1967-BeaconUpgraded} event.
                 *
                 * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
                 * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
                 * efficiency.
                 */
                function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                    _setBeacon(newBeacon);
                    emit BeaconUpgraded(newBeacon);
                    if (data.length > 0) {
                        Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                    } else {
                        _checkNonPayable();
                    }
                }
                /**
                 * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
                 * if an upgrade doesn't perform an initialization call.
                 */
                function _checkNonPayable() private {
                    if (msg.value > 0) {
                        revert ERC1967NonPayable();
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev This is the interface that {BeaconProxy} expects of its beacon.
             */
            interface IBeacon {
                /**
                 * @dev Must return an address that can be used as a delegate call target.
                 *
                 * {UpgradeableBeacon} will check that this address is a contract.
                 */
                function implementation() external view returns (address);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
            pragma solidity ^0.8.20;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev The ETH balance of the account is not enough to perform the operation.
                 */
                error AddressInsufficientBalance(address account);
                /**
                 * @dev There's no code at `target` (it is not a contract).
                 */
                error AddressEmptyCode(address target);
                /**
                 * @dev A call to an address target failed. The target may have reverted.
                 */
                error FailedInnerCall();
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    if (address(this).balance < amount) {
                        revert AddressInsufficientBalance(address(this));
                    }
                    (bool success, ) = recipient.call{value: amount}("");
                    if (!success) {
                        revert FailedInnerCall();
                    }
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason or custom error, it is bubbled
                 * up by this function (like regular Solidity function calls). However, if
                 * the call reverted with no returned reason, this function reverts with a
                 * {FailedInnerCall} error.
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    if (address(this).balance < value) {
                        revert AddressInsufficientBalance(address(this));
                    }
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResultFromTarget(target, success, returndata);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResultFromTarget(target, success, returndata);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResultFromTarget(target, success, returndata);
                }
                /**
                 * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                 * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                 * unsuccessful call.
                 */
                function verifyCallResultFromTarget(
                    address target,
                    bool success,
                    bytes memory returndata
                ) internal view returns (bytes memory) {
                    if (!success) {
                        _revert(returndata);
                    } else {
                        // only check if target is a contract if the call was successful and the return data is empty
                        // otherwise we already know that it was a contract
                        if (returndata.length == 0 && target.code.length == 0) {
                            revert AddressEmptyCode(target);
                        }
                        return returndata;
                    }
                }
                /**
                 * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                 * revert reason or with a default {FailedInnerCall} error.
                 */
                function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                    if (!success) {
                        _revert(returndata);
                    } else {
                        return returndata;
                    }
                }
                /**
                 * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                 */
                function _revert(bytes memory returndata) private pure {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert FailedInnerCall();
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
            // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
            pragma solidity ^0.8.20;
            /**
             * @dev Library for reading and writing primitive types to specific storage slots.
             *
             * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
             * This library helps with reading and writing to such slots without the need for inline assembly.
             *
             * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
             *
             * Example usage to set ERC1967 implementation slot:
             * ```solidity
             * contract ERC1967 {
             *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
             *
             *     function _getImplementation() internal view returns (address) {
             *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
             *     }
             *
             *     function _setImplementation(address newImplementation) internal {
             *         require(newImplementation.code.length > 0);
             *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
             *     }
             * }
             * ```
             */
            library StorageSlot {
                struct AddressSlot {
                    address value;
                }
                struct BooleanSlot {
                    bool value;
                }
                struct Bytes32Slot {
                    bytes32 value;
                }
                struct Uint256Slot {
                    uint256 value;
                }
                struct StringSlot {
                    string value;
                }
                struct BytesSlot {
                    bytes value;
                }
                /**
                 * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                 */
                function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                 */
                function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                 */
                function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                 */
                function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `StringSlot` with member `value` located at `slot`.
                 */
                function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                 */
                function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := store.slot
                    }
                }
                /**
                 * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                 */
                function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                 */
                function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := store.slot
                    }
                }
            }
            

            File 2 of 4: WalletSimple
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import './TransferHelper.sol';
            import './ERC20Interface.sol';
            import './IForwarder.sol';
            /** ERC721, ERC1155 imports */
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            /**
             *
             * WalletSimple
             * ============
             *
             * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
             * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
             *
             * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken
             * The signer is determined by verifyMultiSig().
             *
             * The second signature is created by the submitter of the transaction and determined by msg.signer.
             *
             * Data Formats
             * ============
             *
             * The signature is created with ethereumjs-util.ecsign(operationHash).
             * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].
             * Unlike eth_sign, the message is not prefixed.
             *
             * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).
             * For ether transactions, `prefix` is "ETHER".
             * For token transaction, `prefix` is "ERC20" and `data` is the tokenContractAddress.
             *
             *
             */
            contract WalletSimple is IERC721Receiver, ERC1155Receiver {
              // Events
              event Deposited(address from, uint256 value, bytes data);
              event SafeModeActivated(address msgSender);
              event Transacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation, // Operation hash (see Data Formats)
                address toAddress, // The address the transaction was sent to
                uint256 value, // Amount of Wei sent to the address
                bytes data // Data sent when invoking the transaction
              );
              event BatchTransfer(address sender, address recipient, uint256 value);
              // this event shows the other signer and the operation hash that they signed
              // specific batch transfer events are emitted in Batcher
              event BatchTransacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation // Operation hash (see Data Formats)
              );
              // Public fields
              mapping(address => bool) public signers; // The addresses that can co-sign transactions on the wallet
              bool public safeMode = false; // When active, wallet may only send to signer addresses
              bool public initialized = false; // True if the contract has been initialized
              // Internal fields
              uint256 private constant MAX_SEQUENCE_ID_INCREASE = 10000;
              uint256 constant SEQUENCE_ID_WINDOW_SIZE = 10;
              uint256[SEQUENCE_ID_WINDOW_SIZE] recentSequenceIds;
              /**
               * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.
               * 2 signers will be required to send a transaction from this wallet.
               * Note: The sender is NOT automatically added to the list of signers.
               * Signers CANNOT be changed once they are set
               *
               * @param allowedSigners An array of signers on the wallet
               */
              function init(address[] calldata allowedSigners) external onlyUninitialized {
                require(allowedSigners.length == 3, 'Invalid number of signers');
                for (uint8 i = 0; i < allowedSigners.length; i++) {
                  require(allowedSigners[i] != address(0), 'Invalid signer');
                  signers[allowedSigners[i]] = true;
                }
                initialized = true;
              }
              /**
               * Get the network identifier that signers must sign over
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER';
              }
              /**
               * Get the network identifier that signers must sign over for token transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getTokenNetworkId() internal virtual pure returns (string memory) {
                return 'ERC20';
              }
              /**
               * Get the network identifier that signers must sign over for batch transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getBatchNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER-Batch';
              }
              /**
               * Determine if an address is a signer on this wallet
               * @param signer address to check
               * returns boolean indicating whether address is signer or not
               */
              function isSigner(address signer) public view returns (bool) {
                return signers[signer];
              }
              /**
               * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
               */
              modifier onlySigner {
                require(isSigner(msg.sender), 'Non-signer in onlySigner method');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(!initialized, 'Contract already initialized');
                _;
              }
              /**
               * Gets called when a transaction is received with data that does not match any other method
               */
              fallback() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  emit Deposited(msg.sender, msg.value, msg.data);
                }
              }
              /**
               * Gets called when a transaction is received with ether and no data
               */
              receive() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  // message data is always empty for receive. If there is data it is sent to fallback function.
                  emit Deposited(msg.sender, msg.value, '');
                }
              }
              /**
               * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in Wei to be sent
               * @param data the data to send to the toAddress when invoking the transaction
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSig(
                address toAddress,
                uint256 value,
                bytes calldata data,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getNetworkId(),
                    toAddress,
                    value,
                    data,
                    expireTime,
                    sequenceId
                  )
                );
                address otherSigner = verifyMultiSig(
                  toAddress,
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                // Success, send the transaction
                (bool success, ) = toAddress.call{ value: value }(data);
                require(success, 'Call execution failed');
                emit Transacted(
                  msg.sender,
                  otherSigner,
                  operationHash,
                  toAddress,
                  value,
                  data
                );
              }
              /**
               * Execute a batched multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               * The recipients and values to send are encoded in two arrays, where for index i, recipients[i] will be sent values[i].
               *
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigBatch(
                address[] calldata recipients,
                uint256[] calldata values,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                require(recipients.length != 0, 'Not enough recipients');
                require(
                  recipients.length == values.length,
                  'Unequal recipients and values'
                );
                require(recipients.length < 256, 'Too many recipients, max 255');
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getBatchNetworkId(),
                    recipients,
                    values,
                    expireTime,
                    sequenceId
                  )
                );
                // the first parameter (toAddress) is used to ensure transactions in safe mode only go to a signer
                // if in safe mode, we should use normal sendMultiSig to recover, so this check will always fail if in safe mode
                require(!safeMode, 'Batch in safe mode');
                address otherSigner = verifyMultiSig(
                  address(0x0),
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                batchTransfer(recipients, values);
                emit BatchTransacted(msg.sender, otherSigner, operationHash);
              }
              /**
               * Transfer funds in a batch to each of recipients
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to recipients.
               *  The recipient with index i in recipients array will be sent values[i].
               *  Thus, recipients and values must be the same length
               */
              function batchTransfer(
                address[] calldata recipients,
                uint256[] calldata values
              ) internal {
                for (uint256 i = 0; i < recipients.length; i++) {
                  require(address(this).balance >= values[i], 'Insufficient funds');
                  (bool success, ) = recipients[i].call{ value: values[i] }('');
                  require(success, 'Call failed');
                  emit BatchTransfer(msg.sender, recipients[i], values[i]);
                }
              }
              /**
               * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in tokens to be sent
               * @param tokenContractAddress the address of the erc20 token contract
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigToken(
                address toAddress,
                uint256 value,
                address tokenContractAddress,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getTokenNetworkId(),
                    toAddress,
                    value,
                    tokenContractAddress,
                    expireTime,
                    sequenceId
                  )
                );
                verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
                TransferHelper.safeTransfer(tokenContractAddress, toAddress, value);
              }
              /**
               * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushTokens(tokenContractAddress);
              }
              /**
               * Execute a ERC721 token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushERC721ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC721Token(tokenContractAddress, tokenId);
              }
              /**
               * Execute a ERC1155 batch token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               */
              function batchFlushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.batchFlushERC1155Tokens(tokenContractAddress, tokenIds);
              }
              /**
               * Execute a ERC1155 token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               * @param tokenId the token id associated with the ERC1155
               */
              function flushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC1155Tokens(tokenContractAddress, tokenId);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush721(autoFlush);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush1155(autoFlush);
              }
              /**
               * Do common multisig verification for both eth sends and erc20token transfers
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * returns address that has created the signature
               */
              function verifyMultiSig(
                address toAddress,
                bytes32 operationHash,
                bytes calldata signature,
                uint256 expireTime,
                uint256 sequenceId
              ) private returns (address) {
                address otherSigner = recoverAddressFromSignature(operationHash, signature);
                // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
                require(!safeMode || isSigner(toAddress), 'External transfer in safe mode');
                // Verify that the transaction has not expired
                require(expireTime >= block.timestamp, 'Transaction expired');
                // Try to insert the sequence ID. Will revert if the sequence id was invalid
                tryInsertSequenceId(sequenceId);
                require(isSigner(otherSigner), 'Invalid signer');
                require(otherSigner != msg.sender, 'Signers cannot be equal');
                return otherSigner;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered.
               *
               * @param _operator The address of the nft contract
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param _data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory _data
              ) external virtual override returns (bytes4) {
                return this.onERC721Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
               */
              function activateSafeMode() external onlySigner {
                safeMode = true;
                emit SafeModeActivated(msg.sender);
              }
              /**
               * Gets signer's address using ecrecover
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * returns address recovered from the signature
               */
              function recoverAddressFromSignature(
                bytes32 operationHash,
                bytes memory signature
              ) private pure returns (address) {
                require(signature.length == 65, 'Invalid signature - wrong length');
                // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign)
                bytes32 r;
                bytes32 s;
                uint8 v;
                // solhint-disable-next-line
                assembly {
                  r := mload(add(signature, 32))
                  s := mload(add(signature, 64))
                  v := and(mload(add(signature, 65)), 255)
                }
                if (v < 27) {
                  v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
                }
                // protect against signature malleability
                // S value must be in the lower half orader
                // reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/051d340171a93a3d401aaaea46b4b62fa81e5d7c/contracts/cryptography/ECDSA.sol#L53
                require(
                  uint256(s) <=
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "ECDSA: invalid signature 's' value"
                );
                // note that this returns 0 if the signature is invalid
                // Since 0x0 can never be a signer, when the recovered signer address
                // is checked against our signer list, that 0x0 will cause an invalid signer failure
                return ecrecover(operationHash, v, r, s);
              }
              /**
               * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
               * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
               * greater than the minimum element in the window.
               * @param sequenceId to insert into array of stored ids
               */
              function tryInsertSequenceId(uint256 sequenceId) private onlySigner {
                // Keep a pointer to the lowest value element in the window
                uint256 lowestValueIndex = 0;
                // fetch recentSequenceIds into memory for function context to avoid unnecessary sloads
                  uint256[SEQUENCE_ID_WINDOW_SIZE] memory _recentSequenceIds
                 = recentSequenceIds;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  require(_recentSequenceIds[i] != sequenceId, 'Sequence ID already used');
                  if (_recentSequenceIds[i] < _recentSequenceIds[lowestValueIndex]) {
                    lowestValueIndex = i;
                  }
                }
                // The sequence ID being used is lower than the lowest value in the window
                // so we cannot accept it as it may have been used before
                require(
                  sequenceId > _recentSequenceIds[lowestValueIndex],
                  'Sequence ID below window'
                );
                // Block sequence IDs which are much higher than the lowest value
                // This prevents people blocking the contract by using very large sequence IDs quickly
                require(
                  sequenceId <=
                    (_recentSequenceIds[lowestValueIndex] + MAX_SEQUENCE_ID_INCREASE),
                  'Sequence ID above maximum'
                );
                recentSequenceIds[lowestValueIndex] = sequenceId;
              }
              /**
               * Gets the next available sequence ID for signing when using executeAndConfirm
               * returns the sequenceId one higher than the highest currently stored
               */
              function getNextSequenceId() external view returns (uint256) {
                uint256 highestSequenceId = 0;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  if (recentSequenceIds[i] > highestSequenceId) {
                    highestSequenceId = recentSequenceIds[i];
                  }
                }
                return highestSequenceId + 1;
              }
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (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 `IERC721.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 (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 v4.4.1 (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // 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
            // OpenZeppelin Contracts v4.4.1 (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.
                    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. 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 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            

            File 3 of 4: Forwarder
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol';
            import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            import './ERC20Interface.sol';
            import './TransferHelper.sol';
            import './IForwarder.sol';
            /**
             * Contract that will forward any incoming Ether to the creator of the contract
             *
             */
            contract Forwarder is IERC721Receiver, ERC1155Receiver, IForwarder {
              // Address to which any funds sent to this contract will be forwarded
              address public parentAddress;
              bool public autoFlush721 = true;
              bool public autoFlush1155 = true;
              event ForwarderDeposited(address from, uint256 value, bytes data);
              /**
               * Initialize the contract, and sets the destination address to that of the creator
               */
              function init(
                address _parentAddress,
                bool _autoFlush721,
                bool _autoFlush1155
              ) external onlyUninitialized {
                parentAddress = _parentAddress;
                uint256 value = address(this).balance;
                // set whether we want to automatically flush erc721/erc1155 tokens or not
                autoFlush721 = _autoFlush721;
                autoFlush1155 = _autoFlush1155;
                if (value == 0) {
                  return;
                }
                (bool success, ) = parentAddress.call{ value: value }('');
                require(success, 'Flush failed');
                // NOTE: since we are forwarding on initialization,
                // we don't have the context of the original sender.
                // We still emit an event about the forwarding but set
                // the sender to the forwarder itself
                emit ForwarderDeposited(address(this), value, msg.data);
              }
              /**
               * Modifier that will execute internal code block only if the sender is the parent address
               */
              modifier onlyParent {
                require(msg.sender == parentAddress, 'Only Parent');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(parentAddress == address(0x0), 'Already initialized');
                _;
              }
              /**
               * Default function; Gets called when data is sent but does not match any other function
               */
              fallback() external payable {
                flush();
              }
              /**
               * Default function; Gets called when Ether is deposited with no data, and forwards it to the parent address
               */
              receive() external payable {
                flush();
              }
              /**
               * @inheritdoc IForwarder
               */
              function setAutoFlush721(bool autoFlush)
                external
                virtual
                override
                onlyParent
              {
                autoFlush721 = autoFlush;
              }
              /**
               * @inheritdoc IForwarder
               */
              function setAutoFlush1155(bool autoFlush)
                external
                virtual
                override
                onlyParent
              {
                autoFlush1155 = autoFlush;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered. The forwarder will send the nft
               * to the base wallet once the nft contract invokes this method after transfering the nft.
               *
               * @param _operator The address which called `safeTransferFrom` function
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory data
              ) external virtual override returns (bytes4) {
                if (autoFlush721) {
                  IERC721 instance = IERC721(msg.sender);
                  require(
                    instance.supportsInterface(type(IERC721).interfaceId),
                    'The caller does not support the ERC721 interface'
                  );
                  // this won't work for ERC721 re-entrancy
                  instance.safeTransferFrom(address(this), parentAddress, _tokenId, data);
                }
                return this.onERC721Received.selector;
              }
              function callFromParent(
                address target,
                uint256 value,
                bytes calldata data
              ) external onlyParent returns (bytes memory) {
                (bool success, bytes memory returnedData) = target.call{ value: value }(
                  data
                );
                require(success, 'Parent call execution failed');
                return returnedData;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                IERC1155 instance = IERC1155(msg.sender);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                if (autoFlush1155) {
                  instance.safeTransferFrom(address(this), parentAddress, id, value, data);
                }
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                IERC1155 instance = IERC1155(msg.sender);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                if (autoFlush1155) {
                  instance.safeBatchTransferFrom(
                    address(this),
                    parentAddress,
                    ids,
                    values,
                    data
                  );
                }
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushTokens(address tokenContractAddress)
                external
                virtual
                override
                onlyParent
              {
                ERC20Interface instance = ERC20Interface(tokenContractAddress);
                address forwarderAddress = address(this);
                uint256 forwarderBalance = instance.balanceOf(forwarderAddress);
                if (forwarderBalance == 0) {
                  return;
                }
                TransferHelper.safeTransfer(
                  tokenContractAddress,
                  parentAddress,
                  forwarderBalance
                );
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external
                virtual
                override
                onlyParent
              {
                IERC721 instance = IERC721(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC721).interfaceId),
                  'The tokenContractAddress does not support the ERC721 interface'
                );
                address ownerAddress = instance.ownerOf(tokenId);
                instance.transferFrom(ownerAddress, parentAddress, tokenId);
              }
              /**
               * @inheritdoc IForwarder
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external
                virtual
                override
                onlyParent
              {
                IERC1155 instance = IERC1155(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                address forwarderAddress = address(this);
                uint256 forwarderBalance = instance.balanceOf(forwarderAddress, tokenId);
                instance.safeTransferFrom(
                  forwarderAddress,
                  parentAddress,
                  tokenId,
                  forwarderBalance,
                  ''
                );
              }
              /**
               * @inheritdoc IForwarder
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external virtual override onlyParent {
                IERC1155 instance = IERC1155(tokenContractAddress);
                require(
                  instance.supportsInterface(type(IERC1155).interfaceId),
                  'The caller does not support the IERC1155 interface'
                );
                address forwarderAddress = address(this);
                uint256[] memory amounts = new uint256[](tokenIds.length);
                for (uint256 i = 0; i < tokenIds.length; i++) {
                  amounts[i] = instance.balanceOf(forwarderAddress, tokenIds[i]);
                }
                instance.safeBatchTransferFrom(
                  forwarderAddress,
                  parentAddress,
                  tokenIds,
                  amounts,
                  ''
                );
              }
              /**
               * Flush the entire balance of the contract to the parent address.
               */
              function flush() public {
                uint256 value = address(this).balance;
                if (value == 0) {
                  return;
                }
                (bool success, ) = parentAddress.call{ value: value }('');
                require(success, 'Flush failed');
                emit ForwarderDeposited(msg.sender, value, msg.data);
              }
              /**
               * @inheritdoc IERC165
               */
              function supportsInterface(bytes4 interfaceId)
                public
                virtual
                override(ERC1155Receiver, IERC165)
                view
                returns (bool)
              {
                return
                  interfaceId == type(IForwarder).interfaceId ||
                  super.supportsInterface(interfaceId);
              }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155 is IERC165 {
                /**
                 * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
                 */
                event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
                /**
                 * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
                 * transfers.
                 */
                event TransferBatch(
                    address indexed operator,
                    address indexed from,
                    address indexed to,
                    uint256[] ids,
                    uint256[] values
                );
                /**
                 * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
                 * `approved`.
                 */
                event ApprovalForAll(address indexed account, address indexed operator, bool approved);
                /**
                 * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
                 *
                 * If an {URI} event was emitted for `id`, the standard
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
                 * returned by {IERC1155MetadataURI-uri}.
                 */
                event URI(string value, uint256 indexed id);
                /**
                 * @dev Returns the amount of tokens of token type `id` owned by `account`.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) external view returns (uint256);
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `accounts` and `ids` must have the same length.
                 */
                function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                    external
                    view
                    returns (uint256[] memory);
                /**
                 * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
                 *
                 * Emits an {ApprovalForAll} event.
                 *
                 * Requirements:
                 *
                 * - `operator` cannot be the caller.
                 */
                function setApprovalForAll(address operator, bool approved) external;
                /**
                 * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
                 *
                 * See {setApprovalForAll}.
                 */
                function isApprovedForAll(address account, address operator) external view returns (bool);
                /**
                 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `to` cannot be the zero address.
                 * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
                 * - `from` must have a balance of tokens of type `id` of at least `amount`.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
                 * acceptance magic value.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    uint256 amount,
                    bytes calldata data
                ) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function safeBatchTransferFrom(
                    address from,
                    address to,
                    uint256[] calldata ids,
                    uint256[] calldata amounts,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
            pragma solidity ^0.8.0;
            import "../../utils/introspection/IERC165.sol";
            /**
             * @dev Required interface of an ERC721 compliant contract.
             */
            interface IERC721 is IERC165 {
                /**
                 * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                 */
                event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                /**
                 * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                 */
                event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                /**
                 * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                 */
                event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                /**
                 * @dev Returns the number of tokens in ``owner``'s account.
                 */
                function balanceOf(address owner) external view returns (uint256 balance);
                /**
                 * @dev Returns the owner of the `tokenId` token.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function ownerOf(uint256 tokenId) external view returns (address owner);
                /**
                 * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                 * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) external;
                /**
                 * @dev Transfers `tokenId` token from `from` to `to`.
                 *
                 * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must be owned by `from`.
                 * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                 *
                 * Emits a {Transfer} event.
                 */
                function transferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) external;
                /**
                 * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                 * The approval is cleared when the token is transferred.
                 *
                 * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                 *
                 * Requirements:
                 *
                 * - The caller must own the token or be an approved operator.
                 * - `tokenId` must exist.
                 *
                 * Emits an {Approval} event.
                 */
                function approve(address to, uint256 tokenId) external;
                /**
                 * @dev Returns the account approved for `tokenId` token.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function getApproved(uint256 tokenId) external view returns (address operator);
                /**
                 * @dev Approve or remove `operator` as an operator for the caller.
                 * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                 *
                 * Requirements:
                 *
                 * - The `operator` cannot be the caller.
                 *
                 * Emits an {ApprovalForAll} event.
                 */
                function setApprovalForAll(address operator, bool _approved) external;
                /**
                 * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                 *
                 * See {setApprovalForAll}
                 */
                function isApprovedForAll(address owner, address operator) external view returns (bool);
                /**
                 * @dev Safely transfers `tokenId` token from `from` to `to`.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes calldata data
                ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (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 `IERC721.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 (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: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // 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
            // OpenZeppelin Contracts v4.4.1 (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.
                    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. 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 (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/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            

            File 4 of 4: WalletSimple
            // SPDX-License-Identifier: Apache-2.0
            pragma solidity 0.8.10;
            import './TransferHelper.sol';
            import './ERC20Interface.sol';
            import './IForwarder.sol';
            /** ERC721, ERC1155 imports */
            import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
            import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol';
            /**
             *
             * WalletSimple
             * ============
             *
             * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
             * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
             *
             * The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken
             * The signer is determined by verifyMultiSig().
             *
             * The second signature is created by the submitter of the transaction and determined by msg.signer.
             *
             * Data Formats
             * ============
             *
             * The signature is created with ethereumjs-util.ecsign(operationHash).
             * Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].
             * Unlike eth_sign, the message is not prefixed.
             *
             * The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).
             * For ether transactions, `prefix` is "ETHER".
             * For token transaction, `prefix` is "ERC20" and `data` is the tokenContractAddress.
             *
             *
             */
            contract WalletSimple is IERC721Receiver, ERC1155Receiver {
              // Events
              event Deposited(address from, uint256 value, bytes data);
              event SafeModeActivated(address msgSender);
              event Transacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation, // Operation hash (see Data Formats)
                address toAddress, // The address the transaction was sent to
                uint256 value, // Amount of Wei sent to the address
                bytes data // Data sent when invoking the transaction
              );
              event BatchTransfer(address sender, address recipient, uint256 value);
              // this event shows the other signer and the operation hash that they signed
              // specific batch transfer events are emitted in Batcher
              event BatchTransacted(
                address msgSender, // Address of the sender of the message initiating the transaction
                address otherSigner, // Address of the signer (second signature) used to initiate the transaction
                bytes32 operation // Operation hash (see Data Formats)
              );
              // Public fields
              mapping(address => bool) public signers; // The addresses that can co-sign transactions on the wallet
              bool public safeMode = false; // When active, wallet may only send to signer addresses
              bool public initialized = false; // True if the contract has been initialized
              // Internal fields
              uint256 private constant MAX_SEQUENCE_ID_INCREASE = 10000;
              uint256 constant SEQUENCE_ID_WINDOW_SIZE = 10;
              uint256[SEQUENCE_ID_WINDOW_SIZE] recentSequenceIds;
              /**
               * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.
               * 2 signers will be required to send a transaction from this wallet.
               * Note: The sender is NOT automatically added to the list of signers.
               * Signers CANNOT be changed once they are set
               *
               * @param allowedSigners An array of signers on the wallet
               */
              function init(address[] calldata allowedSigners) external onlyUninitialized {
                require(allowedSigners.length == 3, 'Invalid number of signers');
                for (uint8 i = 0; i < allowedSigners.length; i++) {
                  require(allowedSigners[i] != address(0), 'Invalid signer');
                  signers[allowedSigners[i]] = true;
                }
                initialized = true;
              }
              /**
               * Get the network identifier that signers must sign over
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER';
              }
              /**
               * Get the network identifier that signers must sign over for token transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getTokenNetworkId() internal virtual pure returns (string memory) {
                return 'ERC20';
              }
              /**
               * Get the network identifier that signers must sign over for batch transfers
               * This provides protection signatures being replayed on other chains
               * This must be a virtual function because chain-specific contracts will need
               *    to override with their own network ids. It also can't be a field
               *    to allow this contract to be used by proxy with delegatecall, which will
               *    not pick up on state variables
               */
              function getBatchNetworkId() internal virtual pure returns (string memory) {
                return 'ETHER-Batch';
              }
              /**
               * Determine if an address is a signer on this wallet
               * @param signer address to check
               * returns boolean indicating whether address is signer or not
               */
              function isSigner(address signer) public view returns (bool) {
                return signers[signer];
              }
              /**
               * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
               */
              modifier onlySigner {
                require(isSigner(msg.sender), 'Non-signer in onlySigner method');
                _;
              }
              /**
               * Modifier that will execute internal code block only if the contract has not been initialized yet
               */
              modifier onlyUninitialized {
                require(!initialized, 'Contract already initialized');
                _;
              }
              /**
               * Gets called when a transaction is received with data that does not match any other method
               */
              fallback() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  emit Deposited(msg.sender, msg.value, msg.data);
                }
              }
              /**
               * Gets called when a transaction is received with ether and no data
               */
              receive() external payable {
                if (msg.value > 0) {
                  // Fire deposited event if we are receiving funds
                  // message data is always empty for receive. If there is data it is sent to fallback function.
                  emit Deposited(msg.sender, msg.value, '');
                }
              }
              /**
               * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in Wei to be sent
               * @param data the data to send to the toAddress when invoking the transaction
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSig(
                address toAddress,
                uint256 value,
                bytes calldata data,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getNetworkId(),
                    toAddress,
                    value,
                    data,
                    expireTime,
                    sequenceId
                  )
                );
                address otherSigner = verifyMultiSig(
                  toAddress,
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                // Success, send the transaction
                (bool success, ) = toAddress.call{ value: value }(data);
                require(success, 'Call execution failed');
                emit Transacted(
                  msg.sender,
                  otherSigner,
                  operationHash,
                  toAddress,
                  value,
                  data
                );
              }
              /**
               * Execute a batched multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               * The recipients and values to send are encoded in two arrays, where for index i, recipients[i] will be sent values[i].
               *
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigBatch(
                address[] calldata recipients,
                uint256[] calldata values,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                require(recipients.length != 0, 'Not enough recipients');
                require(
                  recipients.length == values.length,
                  'Unequal recipients and values'
                );
                require(recipients.length < 256, 'Too many recipients, max 255');
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getBatchNetworkId(),
                    recipients,
                    values,
                    expireTime,
                    sequenceId
                  )
                );
                // the first parameter (toAddress) is used to ensure transactions in safe mode only go to a signer
                // if in safe mode, we should use normal sendMultiSig to recover, so this check will always fail if in safe mode
                require(!safeMode, 'Batch in safe mode');
                address otherSigner = verifyMultiSig(
                  address(0x0),
                  operationHash,
                  signature,
                  expireTime,
                  sequenceId
                );
                batchTransfer(recipients, values);
                emit BatchTransacted(msg.sender, otherSigner, operationHash);
              }
              /**
               * Transfer funds in a batch to each of recipients
               * @param recipients The list of recipients to send to
               * @param values The list of values to send to recipients.
               *  The recipient with index i in recipients array will be sent values[i].
               *  Thus, recipients and values must be the same length
               */
              function batchTransfer(
                address[] calldata recipients,
                uint256[] calldata values
              ) internal {
                for (uint256 i = 0; i < recipients.length; i++) {
                  require(address(this).balance >= values[i], 'Insufficient funds');
                  (bool success, ) = recipients[i].call{ value: values[i] }('');
                  require(success, 'Call failed');
                  emit BatchTransfer(msg.sender, recipients[i], values[i]);
                }
              }
              /**
               * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
               * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param value the amount in tokens to be sent
               * @param tokenContractAddress the address of the erc20 token contract
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * @param signature see Data Formats
               */
              function sendMultiSigToken(
                address toAddress,
                uint256 value,
                address tokenContractAddress,
                uint256 expireTime,
                uint256 sequenceId,
                bytes calldata signature
              ) external onlySigner {
                // Verify the other signer
                bytes32 operationHash = keccak256(
                  abi.encodePacked(
                    getTokenNetworkId(),
                    toAddress,
                    value,
                    tokenContractAddress,
                    expireTime,
                    sequenceId
                  )
                );
                verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
                TransferHelper.safeTransfer(tokenContractAddress, toAddress, value);
              }
              /**
               * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushTokens(tokenContractAddress);
              }
              /**
               * Execute a ERC721 token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushERC721ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC721Token(tokenContractAddress, tokenId);
              }
              /**
               * Execute a ERC1155 batch token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               */
              function batchFlushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.batchFlushERC1155Tokens(tokenContractAddress, tokenIds);
              }
              /**
               * Execute a ERC1155 token flush from one of the forwarder addresses.
               * This transfer needs only a single signature and can be done by any signer.
               *
               * @param forwarderAddress the address of the forwarder address to flush the tokens from
               * @param tokenContractAddress the address of the erc1155 token contract
               * @param tokenId the token id associated with the ERC1155
               */
              function flushERC1155ForwarderTokens(
                address payable forwarderAddress,
                address tokenContractAddress,
                uint256 tokenId
              ) external onlySigner {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.flushERC1155Tokens(tokenContractAddress, tokenId);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush721(autoFlush);
              }
              /**
               * Sets the autoflush 721 parameter on the forwarder.
               *
               * @param forwarderAddress the address of the forwarder to toggle.
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(address forwarderAddress, bool autoFlush)
                external
                onlySigner
              {
                IForwarder forwarder = IForwarder(forwarderAddress);
                forwarder.setAutoFlush1155(autoFlush);
              }
              /**
               * Do common multisig verification for both eth sends and erc20token transfers
               *
               * @param toAddress the destination address to send an outgoing transaction
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * @param expireTime the number of seconds since 1970 for which this transaction is valid
               * @param sequenceId the unique sequence id obtainable from getNextSequenceId
               * returns address that has created the signature
               */
              function verifyMultiSig(
                address toAddress,
                bytes32 operationHash,
                bytes calldata signature,
                uint256 expireTime,
                uint256 sequenceId
              ) private returns (address) {
                address otherSigner = recoverAddressFromSignature(operationHash, signature);
                // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
                require(!safeMode || isSigner(toAddress), 'External transfer in safe mode');
                // Verify that the transaction has not expired
                require(expireTime >= block.timestamp, 'Transaction expired');
                // Try to insert the sequence ID. Will revert if the sequence id was invalid
                tryInsertSequenceId(sequenceId);
                require(isSigner(otherSigner), 'Invalid signer');
                require(otherSigner != msg.sender, 'Signers cannot be equal');
                return otherSigner;
              }
              /**
               * ERC721 standard callback function for when a ERC721 is transfered.
               *
               * @param _operator The address of the nft contract
               * @param _from The address of the sender
               * @param _tokenId The token id of the nft
               * @param _data Additional data with no specified format, sent in call to `_to`
               */
              function onERC721Received(
                address _operator,
                address _from,
                uint256 _tokenId,
                bytes memory _data
              ) external virtual override returns (bytes4) {
                return this.onERC721Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155Received(
                address _operator,
                address _from,
                uint256 id,
                uint256 value,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155Received.selector;
              }
              /**
               * @inheritdoc IERC1155Receiver
               */
              function onERC1155BatchReceived(
                address _operator,
                address _from,
                uint256[] calldata ids,
                uint256[] calldata values,
                bytes calldata data
              ) external virtual override returns (bytes4) {
                return this.onERC1155BatchReceived.selector;
              }
              /**
               * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
               */
              function activateSafeMode() external onlySigner {
                safeMode = true;
                emit SafeModeActivated(msg.sender);
              }
              /**
               * Gets signer's address using ecrecover
               * @param operationHash see Data Formats
               * @param signature see Data Formats
               * returns address recovered from the signature
               */
              function recoverAddressFromSignature(
                bytes32 operationHash,
                bytes memory signature
              ) private pure returns (address) {
                require(signature.length == 65, 'Invalid signature - wrong length');
                // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign)
                bytes32 r;
                bytes32 s;
                uint8 v;
                // solhint-disable-next-line
                assembly {
                  r := mload(add(signature, 32))
                  s := mload(add(signature, 64))
                  v := and(mload(add(signature, 65)), 255)
                }
                if (v < 27) {
                  v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
                }
                // protect against signature malleability
                // S value must be in the lower half orader
                // reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/051d340171a93a3d401aaaea46b4b62fa81e5d7c/contracts/cryptography/ECDSA.sol#L53
                require(
                  uint256(s) <=
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                  "ECDSA: invalid signature 's' value"
                );
                // note that this returns 0 if the signature is invalid
                // Since 0x0 can never be a signer, when the recovered signer address
                // is checked against our signer list, that 0x0 will cause an invalid signer failure
                return ecrecover(operationHash, v, r, s);
              }
              /**
               * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
               * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
               * greater than the minimum element in the window.
               * @param sequenceId to insert into array of stored ids
               */
              function tryInsertSequenceId(uint256 sequenceId) private onlySigner {
                // Keep a pointer to the lowest value element in the window
                uint256 lowestValueIndex = 0;
                // fetch recentSequenceIds into memory for function context to avoid unnecessary sloads
                  uint256[SEQUENCE_ID_WINDOW_SIZE] memory _recentSequenceIds
                 = recentSequenceIds;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  require(_recentSequenceIds[i] != sequenceId, 'Sequence ID already used');
                  if (_recentSequenceIds[i] < _recentSequenceIds[lowestValueIndex]) {
                    lowestValueIndex = i;
                  }
                }
                // The sequence ID being used is lower than the lowest value in the window
                // so we cannot accept it as it may have been used before
                require(
                  sequenceId > _recentSequenceIds[lowestValueIndex],
                  'Sequence ID below window'
                );
                // Block sequence IDs which are much higher than the lowest value
                // This prevents people blocking the contract by using very large sequence IDs quickly
                require(
                  sequenceId <=
                    (_recentSequenceIds[lowestValueIndex] + MAX_SEQUENCE_ID_INCREASE),
                  'Sequence ID above maximum'
                );
                recentSequenceIds[lowestValueIndex] = sequenceId;
              }
              /**
               * Gets the next available sequence ID for signing when using executeAndConfirm
               * returns the sequenceId one higher than the highest currently stored
               */
              function getNextSequenceId() external view returns (uint256) {
                uint256 highestSequenceId = 0;
                for (uint256 i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
                  if (recentSequenceIds[i] > highestSequenceId) {
                    highestSequenceId = recentSequenceIds[i];
                  }
                }
                return highestSequenceId + 1;
              }
            }
            // SPDX-License-Identifier: GPL-3.0-or-later
            // source: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol
            pragma solidity 0.8.10;
            import '@openzeppelin/contracts/utils/Address.sol';
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
              function safeTransfer(
                address token,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transfer(address,uint256)')));
                (bool success, bytes memory data) = token.call(
                  abi.encodeWithSelector(0xa9059cbb, to, value)
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  'TransferHelper::safeTransfer: transfer failed'
                );
              }
              function safeTransferFrom(
                address token,
                address from,
                address to,
                uint256 value
              ) internal {
                // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                (bool success, bytes memory returndata) = token.call(
                  abi.encodeWithSelector(0x23b872dd, from, to, value)
                );
                Address.verifyCallResult(
                  success,
                  returndata,
                  'TransferHelper::transferFrom: transferFrom failed'
                );
              }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity 0.8.10;
            /**
             * Contract that exposes the needed erc20 token functions
             */
            abstract contract ERC20Interface {
              // Send _value amount of tokens to address _to
              function transfer(address _to, uint256 _value)
                public
                virtual
                returns (bool success);
              // Get the account balance of another account with address _owner
              function balanceOf(address _owner)
                public
                virtual
                view
                returns (uint256 balance);
            }
            pragma solidity ^0.8.0;
            import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
            interface IForwarder is IERC165 {
              /**
               * Sets the autoflush721 parameter.
               *
               * @param autoFlush whether to autoflush erc721 tokens
               */
              function setAutoFlush721(bool autoFlush) external;
              /**
               * Sets the autoflush1155 parameter.
               *
               * @param autoFlush whether to autoflush erc1155 tokens
               */
              function setAutoFlush1155(bool autoFlush) external;
              /**
               * Execute a token transfer of the full balance from the forwarder token to the parent address
               *
               * @param tokenContractAddress the address of the erc20 token contract
               */
              function flushTokens(address tokenContractAddress) external;
              /**
               * Execute a nft transfer from the forwarder to the parent address
               *
               * @param tokenContractAddress the address of the ERC721 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC721Token(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenId The token id of the nft
               */
              function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId)
                external;
              /**
               * Execute a batch nft transfer from the forwarder to the parent address.
               *
               * @param tokenContractAddress the address of the ERC1155 NFT contract
               * @param tokenIds The token ids of the nfts
               */
              function batchFlushERC1155Tokens(
                address tokenContractAddress,
                uint256[] calldata tokenIds
              ) external;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (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 `IERC721.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 (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 v4.4.1 (utils/Address.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // 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
            // OpenZeppelin Contracts v4.4.1 (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.
                    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. 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 (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;
                }
            }