ETH Price: $2,136.77 (+8.40%)

Contract

0x0c6d0F7c8FE71Bc7c68D2cCba2DB3c67e9d59549
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions and > 10 Token Transfers found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Deposit206450632024-08-31 0:46:23551 days ago1725065183
0x0c6d0F7c...7e9d59549
55.66959078 ETH
Transfer206450632024-08-31 0:46:23551 days ago1725065183
0x0c6d0F7c...7e9d59549
55.66959078 ETH
Deposit206233692024-08-28 0:00:23554 days ago1724803223
0x0c6d0F7c...7e9d59549
0.01235257 ETH
Transfer206233692024-08-28 0:00:23554 days ago1724803223
0x0c6d0F7c...7e9d59549
0.01235257 ETH
Deposit206018932024-08-25 0:00:23557 days ago1724544023
0x0c6d0F7c...7e9d59549
0.02154299 ETH
Transfer206018932024-08-25 0:00:23557 days ago1724544023
0x0c6d0F7c...7e9d59549
0.02154299 ETH
Deposit205804312024-08-22 0:00:23560 days ago1724284823
0x0c6d0F7c...7e9d59549
0.00755693 ETH
Transfer205804312024-08-22 0:00:23560 days ago1724284823
0x0c6d0F7c...7e9d59549
0.00755693 ETH
Deposit205589592024-08-19 0:00:23563 days ago1724025623
0x0c6d0F7c...7e9d59549
0.02127236 ETH
Transfer205589592024-08-19 0:00:23563 days ago1724025623
0x0c6d0F7c...7e9d59549
0.02127236 ETH
Deposit205374642024-08-16 0:00:23566 days ago1723766423
0x0c6d0F7c...7e9d59549
0.02168297 ETH
Transfer205374642024-08-16 0:00:23566 days ago1723766423
0x0c6d0F7c...7e9d59549
0.02168297 ETH
Deposit205159822024-08-13 0:00:23569 days ago1723507223
0x0c6d0F7c...7e9d59549
0.0220106 ETH
Transfer205159822024-08-13 0:00:23569 days ago1723507223
0x0c6d0F7c...7e9d59549
0.0220106 ETH
Deposit204944862024-08-10 0:00:23572 days ago1723248023
0x0c6d0F7c...7e9d59549
0.02093508 ETH
Transfer204944862024-08-10 0:00:23572 days ago1723248023
0x0c6d0F7c...7e9d59549
0.02093508 ETH
Deposit204729722024-08-07 0:00:23575 days ago1722988823
0x0c6d0F7c...7e9d59549
0.02087202 ETH
Transfer204729722024-08-07 0:00:23575 days ago1722988823
0x0c6d0F7c...7e9d59549
0.02087202 ETH
Deposit204514612024-08-04 0:00:23578 days ago1722729623
0x0c6d0F7c...7e9d59549
0.18688666 ETH
Transfer204514612024-08-04 0:00:23578 days ago1722729623
0x0c6d0F7c...7e9d59549
0.18688666 ETH
Deposit204299752024-08-01 0:00:35581 days ago1722470435
0x0c6d0F7c...7e9d59549
0.01820656 ETH
Transfer204299752024-08-01 0:00:35581 days ago1722470435
0x0c6d0F7c...7e9d59549
0.01820656 ETH
Deposit204084992024-07-29 0:00:23584 days ago1722211223
0x0c6d0F7c...7e9d59549
0.01641224 ETH
Transfer204084992024-07-29 0:00:23584 days ago1722211223
0x0c6d0F7c...7e9d59549
0.01641224 ETH
Deposit203870022024-07-26 0:00:35587 days ago1721952035
0x0c6d0F7c...7e9d59549
0.01654539 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WethStrategyStargate

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion, GNU AGPLv3 license
/**
 *Submitted for verification at Etherscan.io on 2023-11-15
*/

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

library console {
    address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

    function _sendLogPayload(bytes memory payload) private view {
        uint256 payloadLength = payload.length;
        address consoleAddress = CONSOLE_ADDRESS;
        /// @solidity memory-safe-assembly
        assembly {
            let payloadStart := add(payload, 32)
            let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
        }
    }

    function log() internal view {
        _sendLogPayload(abi.encodeWithSignature("log()"));
    }

    function logInt(int p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(int)", p0));
    }

    function logUint(uint p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function logString(string memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function logBool(bool p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function logAddress(address p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function logBytes(bytes memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
    }

    function logBytes1(bytes1 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
    }

    function logBytes2(bytes2 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
    }

    function logBytes3(bytes3 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
    }

    function logBytes4(bytes4 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
    }

    function logBytes5(bytes5 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
    }

    function logBytes6(bytes6 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
    }

    function logBytes7(bytes7 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
    }

    function logBytes8(bytes8 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
    }

    function logBytes9(bytes9 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
    }

    function logBytes10(bytes10 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
    }

    function logBytes11(bytes11 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
    }

    function logBytes12(bytes12 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
    }

    function logBytes13(bytes13 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
    }

    function logBytes14(bytes14 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
    }

    function logBytes15(bytes15 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
    }

    function logBytes16(bytes16 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
    }

    function logBytes17(bytes17 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
    }

    function logBytes18(bytes18 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
    }

    function logBytes19(bytes19 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
    }

    function logBytes20(bytes20 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
    }

    function logBytes21(bytes21 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
    }

    function logBytes22(bytes22 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
    }

    function logBytes23(bytes23 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
    }

    function logBytes24(bytes24 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
    }

    function logBytes25(bytes25 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
    }

    function logBytes26(bytes26 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
    }

    function logBytes27(bytes27 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
    }

    function logBytes28(bytes28 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
    }

    function logBytes29(bytes29 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
    }

    function logBytes30(bytes30 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
    }

    function logBytes31(bytes31 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
    }

    function logBytes32(bytes32 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
    }

    function log(uint p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function log(string memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function log(bool p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function log(address p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function log(uint p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
    }

    function log(uint p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
    }

    function log(uint p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
    }

    function log(uint p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
    }

    function log(string memory p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
    }

    function log(string memory p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
    }

    function log(string memory p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
    }

    function log(string memory p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
    }

    function log(bool p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
    }

    function log(bool p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
    }

    function log(bool p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
    }

    function log(bool p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
    }

    function log(address p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
    }

    function log(address p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
    }

    function log(address p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
    }

    function log(address p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
    }

    function log(uint p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
    }

    function log(uint p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
    }

    function log(uint p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
    }

    function log(uint p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
    }

    function log(uint p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
    }

    function log(uint p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
    }

    function log(uint p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
    }

    function log(uint p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
    }

    function log(uint p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
    }

    function log(uint p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
    }

    function log(uint p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
    }

    function log(uint p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
    }

    function log(string memory p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
    }

    function log(string memory p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
    }

    function log(string memory p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
    }

    function log(string memory p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
    }

    function log(bool p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
    }

    function log(bool p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
    }

    function log(bool p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
    }

    function log(bool p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
    }

    function log(bool p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
    }

    function log(bool p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
    }

    function log(bool p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
    }

    function log(bool p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
    }

    function log(bool p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
    }

    function log(bool p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
    }

    function log(bool p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
    }

    function log(bool p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
    }

    function log(address p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
    }

    function log(address p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
    }

    function log(address p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
    }

    function log(address p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
    }

    function log(address p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
    }

    function log(address p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
    }

    function log(address p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
    }

    function log(address p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
    }

    function log(address p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
    }

    function log(address p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
    }

    function log(address p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
    }

    function log(address p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
    }

    function log(address p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
    }

    function log(address p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
    }

    function log(address p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
    }

    function log(address p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
    }

    function log(uint p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
    }

}

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

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

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

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

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

/// @notice Minimalist and modern Wrapped Ether implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
    using SafeTransferLib for address;

    event Deposit(address indexed from, uint256 amount);

    event Withdrawal(address indexed to, uint256 amount);

    function deposit() public payable virtual {
        _mint(msg.sender, msg.value);

        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 amount) public virtual {
        _burn(msg.sender, amount);

        emit Withdrawal(msg.sender, amount);

        msg.sender.safeTransferETH(amount);
    }

    receive() external payable virtual {
        deposit();
    }
}

/// https://etherscan.io/address/0xdf0770dF86a8034b3EFEf0A1Bb3c889B8332FF56#code
abstract contract LPTokenERC20 is ERC20 {
    function amountLPtoLD(uint256 _lpAmount) external view virtual returns (uint256);

    function totalLiquidity() external view virtual returns (uint256);
}

/// @dev
/// https://etherscan.io/address/0xB0D502E938ed5f4df2E681fE6E419ff29631d62b#code
/// basically a Goose MasterChef
interface ILPStaking {
    function poolInfo(uint256 _pid)
        external
        view
        returns (address lpToken, uint256 allocPoint, uint256 lastRewardBlock, uint256 accStargatePerShare);

    function userInfo(uint256 _pid, address _address) external view returns (uint256 amount, uint256 rewardDebt);

    function deposit(uint256 _pid, uint256 _amount) external;

    function withdraw(uint256 _pid, uint256 _amount) external;
}

/// @dev https://etherscan.io/address/0x8731d54E9D02c286767d56ac03e8037C07e01e98#code
interface IStargateRouter {
    struct lzTxObj {
        uint256 dstGasForCall;
        uint256 dstNativeAmount;
        bytes dstNativeAddr;
    }

    function addLiquidity(uint256 _poolId, uint256 _amountLD, address _to) external;

    function instantRedeemLocal(uint16 _srcPoolId, uint256 _amountLP, address _to) external returns (uint256);

    function redeemLocal(
        uint16 _dstChainId,
        uint256 _srcPoolId,
        uint256 _dstPoolId,
        address payable _refundAddress,
        uint256 _amountLP,
        bytes calldata _to,
        lzTxObj memory _lzTxParams
    ) external payable;

    function callDelta(uint256 _poolId, bool _fullMode) external;

    function quoteLayerZeroFee(
        uint16 _dstChainId,
        uint8 _functionType,
        bytes calldata _toAddress,
        bytes calldata _transferAndCallPayload,
        lzTxObj memory _lzTxParams
    ) external view returns (uint256, uint256);
}

// https://github.com/Uniswap/swap-router-contracts/blob/main/contracts/interfaces/ISwapRouter02.sol

interface ISwapRouter02 {
    function swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to)
        external
        payable
        returns (uint256 amountOut);

    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
    /// and swap the entire amount, enabling contracts to send tokens before calling this function.
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
    /// and swap the entire amount, enabling contracts to send tokens before calling this function.
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
}

// https://etherscan.io/address/0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f
// it's actually a UniswapV2Router02 but renamed for clarity vs actual uniswap

interface ISushiRouter {
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

/// https://etherscan.io/address/0xBA12222222228d8Ba445958a75a0704d566BF2C8#code

interface IAsset {}

interface IVault {
    struct JoinPoolRequest {
        IAsset[] assets;
        uint256[] maxAmountsIn;
        bytes userData;
        bool fromInternalBalance;
    }

    enum PoolSpecialization {
        GENERAL,
        MINIMAL_SWAP_INFO,
        TWO_TOKEN
    }

    function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);

    function getPoolTokens(bytes32 poolId)
        external
        view
        returns (address[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock);

    function getPoolTokenInfo(bytes32 poolId, address token)
        external
        view
        returns (uint256 cash, uint256 managed, uint256 lastChangedBlock, address assetManager);

    function queryExit(bytes32 poolId, address sender, address recipient, IVault.ExitPoolRequest memory request)
        external
        returns (uint256 bptIn, uint256[] memory amountsOut);

    enum SwapKind {
        GIVEN_IN,
        GIVEN_OUT
    }

    struct BatchSwapStep {
        bytes32 poolId;
        uint256 assetInIndex;
        uint256 assetOutIndex;
        uint256 amount;
        bytes userData;
    }

    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }

    function batchSwap(
        SwapKind kind,
        BatchSwapStep[] memory swaps,
        IAsset[] memory assets,
        FundManagement memory funds,
        int256[] memory limits,
        uint256 deadline
    ) external payable returns (int256[] memory);

    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        IAsset assetIn;
        IAsset assetOut;
        uint256 amount;
        bytes userData;
    }

    function swap(SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline)
        external
        returns (uint256 amountCalculated);

    function exitPool(bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request)
        external;

    struct ExitPoolRequest {
        IAsset[] assets;
        uint256[] minAmountsOut;
        bytes userData;
        bool toInternalBalance;
    }
}

abstract contract Ownable {
    address public owner;
    address public nominatedOwner;

    error Unauthorized();

    event OwnerChanged(address indexed previousOwner, address indexed newOwner);

    constructor() {
        owner = msg.sender;
    }

    // Public Functions

    function acceptOwnership() external {
        if (msg.sender != nominatedOwner) revert Unauthorized();
        emit OwnerChanged(owner, msg.sender);
        owner = msg.sender;
        nominatedOwner = address(0);
    }

    // Restricted Functions: onlyOwner

    /// @dev nominating zero address revokes a pending nomination
    function nominateOwnership(address _newOwner) external onlyOwner {
        nominatedOwner = _newOwner;
    }

    // Modifiers

    modifier onlyOwner() {
        if (msg.sender != owner) revert Unauthorized();
        _;
    }
}

// https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/Path.sol

//https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol

library BytesLib {
    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) {
        require(_bytes.length >= _start + 3, "toUint24_outOfBounds");
        uint24 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x3), _start))
        }

        return tempUint;
    }
}

/// @title Functions for manipulating path data for multihop swaps
library Path {
    using BytesLib for bytes;

    /// @dev The length of the bytes encoded address
    uint256 private constant ADDR_SIZE = 20;
    /// @dev The length of the bytes encoded fee
    uint256 private constant FEE_SIZE = 3;

    /// @dev The offset of a single token address and pool fee
    uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;
    /// @dev The offset of an encoded pool key
    uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;
    /// @dev The minimum length of an encoding that contains 2 or more pools
    uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET;

    /// @notice Returns true iff the path contains two or more pools
    /// @param path The encoded swap path
    /// @return True if path contains two or more pools, otherwise false
    function hasMultiplePools(bytes memory path) internal pure returns (bool) {
        return path.length >= MULTIPLE_POOLS_MIN_LENGTH;
    }

    /// @notice Returns the number of pools in the path
    /// @param path The encoded swap path
    /// @return The number of pools in the path
    function numPools(bytes memory path) internal pure returns (uint256) {
        // Ignore the first token address. From then on every fee and token offset indicates a pool.
        return ((path.length - ADDR_SIZE) / NEXT_OFFSET);
    }

    /// @notice Decodes the first pool in path
    /// @param path The bytes encoded swap path
    /// @return tokenA The first token of the given pool
    /// @return tokenB The second token of the given pool
    /// @return fee The fee level of the pool
    function decodeFirstPool(bytes memory path) internal pure returns (address tokenA, address tokenB, uint24 fee) {
        tokenA = path.toAddress(0);
        fee = path.toUint24(ADDR_SIZE);
        tokenB = path.toAddress(NEXT_OFFSET);
    }

    /// @notice Gets the segment corresponding to the first pool in the path
    /// @param path The bytes encoded swap path
    /// @return The segment containing all data necessary to target the first pool in the path
    function getFirstPool(bytes memory path) internal pure returns (bytes memory) {
        return path.slice(0, POP_OFFSET);
    }

    /// @notice Skips a token + fee element from the buffer and returns the remainder
    /// @param path The swap path
    /// @return The remaining token + fee elements in the path
    function skipToken(bytes memory path) internal pure returns (bytes memory) {
        return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);
    }
}

/**
 * @notice
 * Swap contract used by strategies to:
 * 1. swap strategy rewards to 'asset'
 * 2. zap similar tokens to asset (e.g. USDT to USDC)
 */
contract Swap is Ownable {
    using SafeTransferLib for ERC20;
    using Path for bytes;

    enum Route {
        Unsupported,
        UniswapV2,
        UniswapV3Direct,
        UniswapV3Path,
        SushiSwap,
        BalancerBatch,
        BalancerSingle
    }

    /**
     * @dev info depends on route:
     * 		UniswapV2: address[] path
     * 		UniswapV3Direct: uint24 fee
     * 		UniswapV3Path: bytes path (address, uint24 fee, address, uint24 fee, address)
     */
    struct RouteInfo {
        Route route;
        bytes info;
    }

    ISushiRouter internal constant sushiswap = ISushiRouter(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F);
    /// @dev single address which supports both uniswap v2 and v3 routes
    ISwapRouter02 internal constant uniswap = ISwapRouter02(0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45);

    IVault internal constant balancer = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);

    /// @dev tokenIn => tokenOut => routeInfo
    mapping(address => mapping(address => RouteInfo)) public routes;

    /*//////////////////
    /      Events      /
    //////////////////*/

    event RouteSet(address indexed tokenIn, address indexed tokenOut, RouteInfo routeInfo);
    event RouteRemoved(address indexed tokenIn, address indexed tokenOut);

    /*//////////////////
    /      Errors      /
    //////////////////*/

    error UnsupportedRoute(address tokenIn, address tokenOut);
    error InvalidRouteInfo();

    constructor() Ownable() {
        address CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;
        address CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
        address LDO = 0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32;

        address WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

        address STG = 0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6;
        address USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
        address USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;

        address BAL = 0xba100000625a3754423978a60c9317c58a424e3D;
        address AURA = 0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF;

        _setRoute(CRV, WETH, RouteInfo({route: Route.UniswapV3Direct, info: abi.encode(uint24(3_000))}));
        _setRoute(CVX, WETH, RouteInfo({route: Route.UniswapV3Direct, info: abi.encode(uint24(10_000))}));
        _setRoute(LDO, WETH, RouteInfo({route: Route.UniswapV3Direct, info: abi.encode(uint24(3_000))}));

        _setRoute(CRV, USDC, RouteInfo({route: Route.UniswapV3Direct, info: abi.encode(uint24(10_000))}));
        _setRoute(
            CVX,
            USDC,
            RouteInfo({route: Route.UniswapV3Path, info: abi.encodePacked(CVX, uint24(10_000), WETH, uint24(500), USDC)})
        );

        _setRoute(USDC, USDT, RouteInfo({route: Route.UniswapV3Direct, info: abi.encode(uint24(100))}));

        _setRoute(
            STG,
            USDC,
            RouteInfo({
                route: Route.BalancerSingle,
                info: abi.encode(0x3ff3a210e57cfe679d9ad1e9ba6453a716c56a2e0002000000000000000005d5)
            })
        );
        IAsset[] memory stgWethAssets = new IAsset[](4);
        stgWethAssets[0] = IAsset(STG);
        stgWethAssets[1] = IAsset(USDC);
        stgWethAssets[2] = IAsset(0x79c58f70905F734641735BC61e45c19dD9Ad60bC); // 3pool
        stgWethAssets[3] = IAsset(WETH);

        bytes32[] memory stgWethPoolIds = new bytes32[](3);
        stgWethPoolIds[0] = 0x3ff3a210e57cfe679d9ad1e9ba6453a716c56a2e0002000000000000000005d5; // STG/USDC
        stgWethPoolIds[1] = 0x79c58f70905f734641735bc61e45c19dd9ad60bc0000000000000000000004e7; // 3pool
        stgWethPoolIds[2] = 0x08775ccb6674d6bdceb0797c364c2653ed84f3840002000000000000000004f0; // 3pool/WETH

        IVault.BatchSwapStep[] memory stgWethSteps = _constructBalancerBatchSwapSteps(stgWethPoolIds);

        _setRoute(STG, WETH, RouteInfo({route: Route.BalancerBatch, info: abi.encode(stgWethSteps, stgWethAssets)}));

        bytes32 balWethPoolId = 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014;
        _setRoute(BAL, WETH, RouteInfo({route: Route.BalancerSingle, info: abi.encode(balWethPoolId)}));

        bytes32 auraWethPoolId = 0xcfca23ca9ca720b6e98e3eb9b6aa0ffc4a5c08b9000200000000000000000274;
        _setRoute(AURA, WETH, RouteInfo({route: Route.BalancerSingle, info: abi.encode(auraWethPoolId)}));
    }

    /*///////////////////////
    /      Public View      /
    ///////////////////////*/

    function getRoute(address _tokenIn, address _tokenOut) external view returns (RouteInfo memory routeInfo) {
        return routes[_tokenIn][_tokenOut];
    }

    /*////////////////////////////
    /      Public Functions      /
    ////////////////////////////*/

    function swapTokens(address _tokenIn, address _tokenOut, uint256 _amount, uint256 _minReceived)
        external
        returns (uint256 received)
    {
        RouteInfo memory routeInfo = routes[_tokenIn][_tokenOut];

        ERC20 tokenIn = ERC20(_tokenIn);
        tokenIn.safeTransferFrom(msg.sender, address(this), _amount);

        Route route = routeInfo.route;
        bytes memory info = routeInfo.info;

        if (route == Route.UniswapV2) {
            received = _uniswapV2(_amount, _minReceived, info);
        } else if (route == Route.UniswapV3Direct) {
            received = _uniswapV3Direct(_tokenIn, _tokenOut, _amount, _minReceived, info);
        } else if (route == Route.UniswapV3Path) {
            received = _uniswapV3Path(_amount, _minReceived, info);
        } else if (route == Route.SushiSwap) {
            received = _sushiswap(_amount, _minReceived, info);
        } else if (route == Route.BalancerBatch) {
            received = _balancerBatch(_amount, _minReceived, info);
        } else if (route == Route.BalancerSingle) {
            received = _balancerSingle(_tokenIn, _tokenOut, _amount, _minReceived, info);
        } else {
            revert UnsupportedRoute(_tokenIn, _tokenOut);
        }

        // return unswapped amount to sender
        uint256 balance = tokenIn.balanceOf(address(this));
        if (balance > 0) tokenIn.safeTransfer(msg.sender, balance);
    }

    /*///////////////////////////////////////////
    /      Restricted Functions: onlyOwner      /
    ///////////////////////////////////////////*/

    function setRoute(address _tokenIn, address _tokenOut, RouteInfo memory _routeInfo) external onlyOwner {
        _setRoute(_tokenIn, _tokenOut, _routeInfo);
    }

    function unsetRoute(address _tokenIn, address _tokenOut) external onlyOwner {
        delete routes[_tokenIn][_tokenOut];
        emit RouteRemoved(_tokenIn, _tokenOut);
    }

    /*//////////////////////////////
    /      Internal Functions      /
    //////////////////////////////*/

    function _setRoute(address _tokenIn, address _tokenOut, RouteInfo memory _routeInfo) internal {
        Route route = _routeInfo.route;
        bytes memory info = _routeInfo.info;

        if (route == Route.UniswapV2 || route == Route.SushiSwap) {
            address[] memory path = abi.decode(info, (address[]));

            if (path[0] != _tokenIn) revert InvalidRouteInfo();
            if (path[path.length - 1] != _tokenOut) revert InvalidRouteInfo();
        }

        // just check that this doesn't throw an error
        if (route == Route.UniswapV3Direct) abi.decode(info, (uint24));

        if (route == Route.UniswapV3Path) {
            bytes memory path = info;

            // check first tokenIn
            (address tokenIn,,) = path.decodeFirstPool();
            if (tokenIn != _tokenIn) revert InvalidRouteInfo();

            // check last tokenOut
            while (path.hasMultiplePools()) path = path.skipToken();
            (, address tokenOut,) = path.decodeFirstPool();
            if (tokenOut != _tokenOut) revert InvalidRouteInfo();
        }

        // just check that these don't throw an error, i.e. the poolId contains both _tokenIn
        if (route == Route.BalancerSingle) {
            bytes32 poolId = abi.decode(info, (bytes32));
            balancer.getPoolTokenInfo(poolId, _tokenIn);
            balancer.getPoolTokenInfo(poolId, _tokenOut);
        }

        address router = _getRouterAddress(route);

        ERC20(_tokenIn).safeApprove(router, 0);
        ERC20(_tokenIn).safeApprove(router, type(uint256).max);

        routes[_tokenIn][_tokenOut] = _routeInfo;
        emit RouteSet(_tokenIn, _tokenOut, _routeInfo);
    }

    function _uniswapV2(uint256 _amount, uint256 _minReceived, bytes memory _path) internal returns (uint256) {
        address[] memory path = abi.decode(_path, (address[]));

        return uniswap.swapExactTokensForTokens(_amount, _minReceived, path, msg.sender);
    }

    function _sushiswap(uint256 _amount, uint256 _minReceived, bytes memory _path) internal returns (uint256) {
        address[] memory path = abi.decode(_path, (address[]));

        uint256[] memory received =
            sushiswap.swapExactTokensForTokens(_amount, _minReceived, path, msg.sender, type(uint256).max);

        return received[received.length - 1];
    }

    function _uniswapV3Direct(
        address _tokenIn,
        address _tokenOut,
        uint256 _amount,
        uint256 _minReceived,
        bytes memory _info
    ) internal returns (uint256) {
        uint24 fee = abi.decode(_info, (uint24));

        return uniswap.exactInputSingle(
            ISwapRouter02.ExactInputSingleParams({
                tokenIn: _tokenIn,
                tokenOut: _tokenOut,
                fee: fee,
                recipient: msg.sender,
                amountIn: _amount,
                amountOutMinimum: _minReceived,
                sqrtPriceLimitX96: 0
            })
        );
    }

    function _uniswapV3Path(uint256 _amount, uint256 _minReceived, bytes memory _path) internal returns (uint256) {
        return uniswap.exactInput(
            ISwapRouter02.ExactInputParams({
                path: _path,
                recipient: msg.sender,
                amountIn: _amount,
                amountOutMinimum: _minReceived
            })
        );
    }

    function _balancerBatch(uint256 _amount, uint256 _minReceived, bytes memory _info) internal returns (uint256) {
        (IVault.BatchSwapStep[] memory steps, IAsset[] memory assets) =
            abi.decode(_info, (IVault.BatchSwapStep[], IAsset[]));

        steps[0].amount = _amount;

        int256[] memory limits = new int256[](assets.length);

        limits[0] = int256(_amount);
        limits[limits.length - 1] = -int256(_minReceived);

        int256[] memory received = balancer.batchSwap(
            IVault.SwapKind.GIVEN_IN,
            steps,
            assets,
            IVault.FundManagement({
                sender: address(this),
                fromInternalBalance: false,
                recipient: payable(address(msg.sender)),
                toInternalBalance: false
            }),
            limits,
            type(uint256).max
        );

        return uint256(-received[received.length - 1]);
    }

    function _balancerSingle(
        address _tokenIn,
        address _tokenOut,
        uint256 _amount,
        uint256 _minReceived,
        bytes memory _info
    ) internal returns (uint256) {
        bytes32 poolId = abi.decode(_info, (bytes32));

        return balancer.swap(
            IVault.SingleSwap({
                poolId: poolId,
                kind: IVault.SwapKind.GIVEN_IN,
                assetIn: IAsset(_tokenIn),
                assetOut: IAsset(_tokenOut),
                amount: _amount,
                userData: ""
            }),
            IVault.FundManagement({
                sender: address(this),
                fromInternalBalance: false,
                recipient: payable(address(msg.sender)),
                toInternalBalance: false
            }),
            _minReceived,
            type(uint256).max
        );
    }

    function _getRouterAddress(Route _route) internal pure returns (address) {
        if (_route == Route.SushiSwap) {
            return address(sushiswap);
        } else if (_route == Route.UniswapV2 || _route == Route.UniswapV3Direct || _route == Route.UniswapV3Path) {
            return address(uniswap);
        } else if (_route == Route.BalancerBatch || _route == Route.BalancerSingle) {
            return address(balancer);
        } else {
            revert InvalidRouteInfo();
        }
    }

    function _constructBalancerBatchSwapSteps(bytes32[] memory _poolIds)
        internal
        pure
        returns (IVault.BatchSwapStep[] memory steps)
    {
        uint256 length = _poolIds.length;
        steps = new IVault.BatchSwapStep[](length);

        for (uint8 i = 0; i < length; ++i) {
            steps[i] = IVault.BatchSwapStep({
                poolId: _poolIds[i],
                assetInIndex: i,
                assetOutIndex: i + 1,
                amount: 0,
                userData: ""
            });
        }
    }
}

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

abstract contract Ownership {
    address public owner;
    address public nominatedOwner;

    address public admin;

    mapping(address => bool) public authorized;

    event OwnerChanged(address indexed previousOwner, address indexed newOwner);
    event AuthAdded(address indexed newAuth);
    event AuthRemoved(address indexed oldAuth);

    error Unauthorized();
    error AlreadyRole();
    error NotRole();

    /// @param _authorized maximum of 256 addresses in constructor
    constructor(address _nominatedOwner, address _admin, address[] memory _authorized) {
        owner = msg.sender;
        nominatedOwner = _nominatedOwner;
        admin = _admin;
        for (uint8 i = 0; i < _authorized.length; ++i) {
            authorized[_authorized[i]] = true;
            emit AuthAdded(_authorized[i]);
        }
    }

    // Public Functions

    function acceptOwnership() external {
        if (msg.sender != nominatedOwner) revert Unauthorized();
        emit OwnerChanged(owner, msg.sender);
        owner = msg.sender;
        nominatedOwner = address(0);
    }

    // Restricted Functions: onlyOwner

    /// @dev nominating zero address revokes a pending nomination
    function nominateOwnership(address _newOwner) external onlyOwner {
        nominatedOwner = _newOwner;
    }

    function setAdmin(address _newAdmin) external onlyOwner {
        if (admin == _newAdmin) revert AlreadyRole();
        admin = _newAdmin;
    }

    // Restricted Functions: onlyAdmins

    function addAuthorized(address _authorized) external onlyAdmins {
        if (authorized[_authorized]) revert AlreadyRole();
        authorized[_authorized] = true;
        emit AuthAdded(_authorized);
    }

    function removeAuthorized(address _authorized) external onlyAdmins {
        if (!authorized[_authorized]) revert NotRole();
        authorized[_authorized] = false;
        emit AuthRemoved(_authorized);
    }

    // Modifiers

    modifier onlyOwner() {
        if (msg.sender != owner) revert Unauthorized();
        _;
    }

    modifier onlyAdmins() {
        if (msg.sender != owner && msg.sender != admin) revert Unauthorized();
        _;
    }

    modifier onlyAuthorized() {
        if (msg.sender != owner && msg.sender != admin && !authorized[msg.sender]) revert Unauthorized();
        _;
    }
}

abstract contract BlockDelay {
    /// @notice delay before functions with 'useBlockDelay' can be called by the same address
    /// @dev 0 means no delay
    uint256 public blockDelay;
    uint256 internal constant MAX_BLOCK_DELAY = 10;

    mapping(address => uint256) public lastBlock;

    error AboveMaxBlockDelay();
    error BeforeBlockDelay();

    constructor(uint8 _delay) {
        _setBlockDelay(_delay);
    }

    function _setBlockDelay(uint8 _newDelay) internal {
        if (_newDelay > MAX_BLOCK_DELAY) revert AboveMaxBlockDelay();
        blockDelay = _newDelay;
    }

    modifier useBlockDelay(address _address) {
        if (block.number < lastBlock[_address] + blockDelay) revert BeforeBlockDelay();
        lastBlock[_address] = block.number;
        _;
    }
}

/// @notice https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 {
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    );

    function asset() external view returns (ERC20);

    function totalAssets() external view returns (uint256 assets);

    function convertToShares(uint256 assets) external view returns (uint256 shares);

    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    function maxDeposit(address receiver) external view returns (uint256 assets);

    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    function maxMint(address receiver) external view returns (uint256 shares);

    function previewMint(uint256 shares) external view returns (uint256 assets);

    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    function maxWithdraw(address owner) external view returns (uint256 assets);

    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    function maxRedeem(address owner) external view returns (uint256 shares);

    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

contract Vault is ERC20, IERC4626, Ownership, BlockDelay {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /// @notice token which the vault uses and accumulates
    ERC20 public immutable asset;

    /// @notice whether deposits and withdrawals are paused
    bool public paused;

    uint256 private _lockedProfit;
    /// @notice timestamp of last report, used for locked profit calculations
    uint256 public lastReport;
    /// @notice period over which profits are gradually unlocked, defense against sandwich attacks
    uint256 public lockedProfitDuration = 24 hours;
    uint256 internal constant MAX_LOCKED_PROFIT_DURATION = 3 days;

    /// @dev maximum user can deposit in a single tx
    uint256 private _maxDeposit = type(uint256).max;

    struct StrategyParams {
        bool added;
        uint256 debt;
        uint256 debtRatio;
    }

    Strategy[] private _queue;
    mapping(Strategy => StrategyParams) public strategies;

    uint8 internal constant MAX_QUEUE_LENGTH = 20;

    uint256 public totalDebt;
    /// @dev proportion of funds kept in vault to facilitate user withdrawals
    uint256 public floatDebtRatio;
    uint256 public totalDebtRatio;
    uint256 internal constant MAX_TOTAL_DEBT_RATIO = 1_000;

    /*//////////////////
    /      Events      /
    //////////////////*/

    event Report(Strategy indexed strategy, uint256 harvested, uint256 gain, uint256 loss);
    event Lend(Strategy indexed strategy, uint256 assets, uint256 slippage);
    event Collect(Strategy indexed strategy, uint256 received, uint256 slippage, uint256 bonus);

    event StrategyAdded(Strategy indexed strategy, uint256 debtRatio);
    event StrategyDebtRatioChanged(Strategy indexed strategy, uint256 newDebtRatio);
    event StrategyRemoved(Strategy indexed strategy);
    event StrategyQueuePositionsSwapped(uint8 i, uint8 j, Strategy indexed newI, Strategy indexed newJ);

    event LockedProfitDurationChanged(uint256 newDuration);
    event MaxDepositChanged(uint256 newMaxDeposit);
    event FloatDebtRatioChanged(uint256 newFloatDebtRatio);

    /*//////////////////
    /      Errors      /
    //////////////////*/

    error Zero();
    error BelowMinimum(uint256);
    error AboveMaximum(uint256);

    error AboveMaxDeposit();

    error AlreadyStrategy();
    error NotStrategy();
    error StrategyDoesNotBelongToQueue();
    error StrategyQueueFull();

    error AlreadyValue();

    error Paused();

    /// @dev e.g. USDC becomes 'Unagii USD Coin Vault v3' and 'uUSDCv3'
    constructor(
        ERC20 _asset,
        uint8 _blockDelay,
        uint256 _floatDebtRatio,
        address _nominatedOwner,
        address _admin,
        address[] memory _authorized
    )
        ERC20(
            string(abi.encodePacked("Unagii ", _asset.name(), " Vault v3")),
            string(abi.encodePacked("u", _asset.symbol(), "v3")),
            _asset.decimals()
        )
        Ownership(_nominatedOwner, _admin, _authorized)
        BlockDelay(_blockDelay)
    {
        asset = _asset;
        _setFloatDebtRatio(_floatDebtRatio);
    }

    /*///////////////////////
    /      Public View      /
    ///////////////////////*/

    function queue() external view returns (Strategy[] memory) {
        return _queue;
    }

    function totalAssets() public view returns (uint256 assets) {
        return asset.balanceOf(address(this)) + totalDebt;
    }

    function lockedProfit() public view returns (uint256 lockedAssets) {
        uint256 last = lastReport;
        uint256 duration = lockedProfitDuration;

        unchecked {
            // won't overflow since time is nowhere near uint256.max
            if (block.timestamp >= last + duration) return 0;
            // can overflow if _lockedProfit * difference > uint256.max but in practice should never happen
            return _lockedProfit - _lockedProfit.mulDivDown(block.timestamp - last, duration);
        }
    }

    function freeAssets() public view returns (uint256 assets) {
        return totalAssets() - lockedProfit();
    }

    function convertToShares(uint256 _assets) public view returns (uint256 shares) {
        uint256 supply = totalSupply;
        return supply == 0 ? _assets : _assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 _shares) public view returns (uint256 assets) {
        uint256 supply = totalSupply;
        return supply == 0 ? _shares : _shares.mulDivDown(totalAssets(), supply);
    }

    function maxDeposit(address) external view returns (uint256 assets) {
        return _maxDeposit;
    }

    function previewDeposit(uint256 _assets) public view returns (uint256 shares) {
        return convertToShares(_assets);
    }

    function maxMint(address) external view returns (uint256 shares) {
        return convertToShares(_maxDeposit);
    }

    function previewMint(uint256 shares) public view returns (uint256 assets) {
        uint256 supply = totalSupply;
        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function maxWithdraw(address owner) external view returns (uint256 assets) {
        return convertToAssets(balanceOf[owner]);
    }

    function previewWithdraw(uint256 assets) public view returns (uint256 shares) {
        uint256 supply = totalSupply;
        return supply == 0 ? assets : assets.mulDivUp(supply, freeAssets());
    }

    function maxRedeem(address _owner) external view returns (uint256 shares) {
        return balanceOf[_owner];
    }

    function previewRedeem(uint256 shares) public view returns (uint256 assets) {
        uint256 supply = totalSupply;
        return supply == 0 ? shares : shares.mulDivDown(freeAssets(), supply);
    }

    /*////////////////////////////
    /      Public Functions      /
    ////////////////////////////*/

    function safeDeposit(uint256 _assets, address _receiver, uint256 _minShares) external returns (uint256 shares) {
        shares = deposit(_assets, _receiver);
        if (shares < _minShares) revert BelowMinimum(shares);
    }

    function safeMint(uint256 _shares, address _receiver, uint256 _maxAssets) external returns (uint256 assets) {
        assets = mint(_shares, _receiver);
        if (assets > _maxAssets) revert AboveMaximum(assets);
    }

    function safeWithdraw(uint256 _assets, address _receiver, address _owner, uint256 _maxShares)
        external
        returns (uint256 shares)
    {
        shares = withdraw(_assets, _receiver, _owner);
        if (shares > _maxShares) revert AboveMaximum(shares);
    }

    function safeRedeem(uint256 _shares, address _receiver, address _owner, uint256 _minAssets)
        external
        returns (uint256 assets)
    {
        assets = redeem(_shares, _receiver, _owner);
        if (assets < _minAssets) revert BelowMinimum(assets);
    }

    /*////////////////////////////////////
    /      ERC4626 Public Functions      /
    ////////////////////////////////////*/

    function deposit(uint256 _assets, address _receiver) public whenNotPaused returns (uint256 shares) {
        if ((shares = previewDeposit(_assets)) == 0) revert Zero();

        _deposit(_assets, shares, _receiver);
    }

    function mint(uint256 _shares, address _receiver) public whenNotPaused returns (uint256 assets) {
        if (_shares == 0) revert Zero();
        assets = previewMint(_shares);

        _deposit(assets, _shares, _receiver);
    }

    function withdraw(uint256 _assets, address _receiver, address _owner) public returns (uint256 shares) {
        if (_assets == 0) revert Zero();
        shares = previewWithdraw(_assets);

        _withdraw(_assets, shares, _owner, _receiver);
    }

    function redeem(uint256 _shares, address _receiver, address _owner) public returns (uint256 assets) {
        if ((assets = previewRedeem(_shares)) == 0) revert Zero();

        return _withdraw(assets, _shares, _owner, _receiver);
    }

    /*///////////////////////////////////////////
    /      Restricted Functions: onlyOwner      /
    ///////////////////////////////////////////*/

    function addStrategy(Strategy _strategy, uint256 _debtRatio) external onlyOwner {
        if (_strategy.vault() != this) revert StrategyDoesNotBelongToQueue();
        if (strategies[_strategy].added) revert AlreadyStrategy();
        if (_queue.length >= MAX_QUEUE_LENGTH) revert StrategyQueueFull();

        totalDebtRatio += _debtRatio;
        if (totalDebtRatio > MAX_TOTAL_DEBT_RATIO) revert AboveMaximum(totalDebtRatio);

        strategies[_strategy] = StrategyParams({added: true, debt: 0, debtRatio: _debtRatio});
        _queue.push(_strategy);

        emit StrategyAdded(_strategy, _debtRatio);
    }

    /*////////////////////////////////////////////
    /      Restricted Functions: onlyAdmins      /
    ////////////////////////////////////////////*/

    function removeStrategy(Strategy _strategy, bool _shouldHarvest, uint256 _minReceived)
        external
        onlyAdmins
        returns (uint256 received)
    {
        if (!strategies[_strategy].added) revert NotStrategy();

        _setDebtRatio(_strategy, 0);

        uint256 balanceBefore = asset.balanceOf(address(this));

        if (_shouldHarvest) _harvest(_strategy);
        else _report(_strategy, 0);

        received = asset.balanceOf(address(this)) - balanceBefore;

        if (received < _minReceived) revert BelowMinimum(received);

        // reorganize queue, filling in the empty strategy
        Strategy[] memory newQueue = new Strategy[](_queue.length - 1);

        bool found;
        uint8 length = uint8(newQueue.length);
        for (uint8 i = 0; i < length; ++i) {
            if (_queue[i] == _strategy) found = true;

            if (found) newQueue[i] = _queue[i + 1];
            else newQueue[i] = _queue[i];
        }

        delete strategies[_strategy];
        _queue = newQueue;

        emit StrategyRemoved(_strategy);
    }

    function swapQueuePositions(uint8 _i, uint8 _j) external onlyAdmins {
        Strategy s1 = _queue[_i];
        Strategy s2 = _queue[_j];

        _queue[_i] = s2;
        _queue[_j] = s1;

        emit StrategyQueuePositionsSwapped(_i, _j, s2, s1);
    }

    function setDebtRatio(Strategy _strategy, uint256 _newDebtRatio) external onlyAdmins {
        if (!strategies[_strategy].added) revert NotStrategy();
        _setDebtRatio(_strategy, _newDebtRatio);
    }

    /// @dev locked profit duration can be 0
    function setLockedProfitDuration(uint256 _newDuration) external onlyAdmins {
        if (_newDuration > MAX_LOCKED_PROFIT_DURATION) revert AboveMaximum(_newDuration);
        if (_newDuration == lockedProfitDuration) revert AlreadyValue();
        lockedProfitDuration = _newDuration;
        emit LockedProfitDurationChanged(_newDuration);
    }

    function setBlockDelay(uint8 _newDelay) external onlyAdmins {
        _setBlockDelay(_newDelay);
    }

    /*///////////////////////////////////////////////
    /      Restricted Functions: onlyAuthorized     /
    ///////////////////////////////////////////////*/

    function suspendStrategy(Strategy _strategy) external onlyAuthorized {
        if (!strategies[_strategy].added) revert NotStrategy();
        _setDebtRatio(_strategy, 0);
    }

    function collectFromStrategy(Strategy _strategy, uint256 _assets, uint256 _minReceived)
        external
        onlyAuthorized
        returns (uint256 received)
    {
        if (!strategies[_strategy].added) revert NotStrategy();
        (received,) = _collect(_strategy, _assets, address(this));
        if (received < _minReceived) revert BelowMinimum(received);
    }

    function pause() external onlyAuthorized {
        if (paused) revert AlreadyValue();
        paused = true;
    }

    function unpause() external onlyAuthorized {
        if (!paused) revert AlreadyValue();
        paused = false;
    }

    function setMaxDeposit(uint256 _newMaxDeposit) external onlyAuthorized {
        if (_maxDeposit == _newMaxDeposit) revert AlreadyValue();
        _maxDeposit = _newMaxDeposit;
        emit MaxDepositChanged(_newMaxDeposit);
    }

    /// @dev costs less gas than multiple harvests if active strategies > 1
    function harvestAll() external onlyAuthorized updateLastReport {
        uint8 length = uint8(_queue.length);
        for (uint8 i = 0; i < length; ++i) {
            _harvest(_queue[i]);
        }
    }

    /// @dev costs less gas than multiple reports if active strategies > 1
    function reportAll() external onlyAuthorized updateLastReport {
        uint8 length = uint8(_queue.length);
        for (uint8 i = 0; i < length; ++i) {
            _report(_queue[i], 0);
        }
    }

    function harvest(Strategy _strategy) external onlyAuthorized updateLastReport {
        if (!strategies[_strategy].added) revert NotStrategy();

        _harvest(_strategy);
    }

    function report(Strategy _strategy) external onlyAuthorized updateLastReport {
        if (!strategies[_strategy].added) revert NotStrategy();

        _report(_strategy, 0);
    }

    function setFloatDebtRatio(uint256 _floatDebtRatio) external onlyAuthorized {
        _setFloatDebtRatio(_floatDebtRatio);
    }

    /*///////////////////////////////////////////
    /      Internal Override: useBlockDelay     /
    ///////////////////////////////////////////*/

    /// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
    function _mint(address _to, uint256 _amount) internal override useBlockDelay(_to) {
        if (_to == address(0)) revert Zero();
        ERC20._mint(_to, _amount);
    }

    /// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
    function _burn(address _from, uint256 _amount) internal override useBlockDelay(_from) {
        ERC20._burn(_from, _amount);
    }

    /// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
    function transfer(address _to, uint256 _amount)
        public
        override
        useBlockDelay(msg.sender)
        useBlockDelay(_to)
        returns (bool)
    {
        return ERC20.transfer(_to, _amount);
    }

    /// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
    function transferFrom(address _from, address _to, uint256 _amount)
        public
        override
        useBlockDelay(_from)
        useBlockDelay(_to)
        returns (bool)
    {
        return ERC20.transferFrom(_from, _to, _amount);
    }

    /*//////////////////////////////
    /      Internal Functions      /
    //////////////////////////////*/

    function _deposit(uint256 _assets, uint256 _shares, address _receiver) internal {
        if (_assets > _maxDeposit) revert AboveMaxDeposit();

        asset.safeTransferFrom(msg.sender, address(this), _assets);
        _mint(_receiver, _shares);
        emit Deposit(msg.sender, _receiver, _assets, _shares);
    }

    function _withdraw(uint256 _assets, uint256 _shares, address _owner, address _receiver)
        internal
        returns (uint256 received)
    {
        if (msg.sender != _owner) {
            uint256 allowed = allowance[_owner][msg.sender];
            if (allowed != type(uint256).max) allowance[_owner][msg.sender] = allowed - _shares;
        }

        _burn(_owner, _shares);

        emit Withdraw(msg.sender, _receiver, _owner, _assets, _shares);

        // first, withdraw from balance
        uint256 balance = asset.balanceOf(address(this));

        if (balance > 0) {
            uint256 amount = _assets > balance ? balance : _assets;
            asset.safeTransfer(_receiver, amount);
            _assets -= amount;
            received += amount;
        }

        // next, withdraw from strategies
        uint8 length = uint8(_queue.length);
        for (uint8 i = 0; i < length; ++i) {
            if (_assets == 0) break;
            (uint256 receivedFromStrategy, uint256 slippage) = _collect(_queue[i], _assets, _receiver);
            _assets -= receivedFromStrategy + slippage; // user pays for slippage, if any
            received += receivedFromStrategy;
        }
    }

    /// @dev do not touch debt outside of _lend(), _collect() and _report()
    function _lend(Strategy _strategy, uint256 _assets) internal {
        uint256 balance = asset.balanceOf(address(this));
        uint256 amount = _assets > balance ? balance : _assets;

        asset.safeTransfer(address(_strategy), amount);
        _strategy.invest();

        uint256 debtBefore = strategies[_strategy].debt;
        uint256 debtAfter = _strategy.totalAssets();

        uint256 diff = debtAfter - debtBefore;

        uint256 slippage = amount > diff ? amount - diff : 0;

        // ignore bonus if diff > amount, safeguard against imprecise `strategy.totalAsset()` calculations that open vault to being drained
        uint256 debt = amount - slippage;

        strategies[_strategy].debt += debt;
        totalDebt += debt;

        emit Lend(_strategy, amount, slippage);
    }

    function _collect(Strategy _strategy, uint256 _assets, address _receiver)
        internal
        returns (uint256 received, uint256 slippage)
    {
        uint256 bonus;
        (received, slippage, bonus) = _strategy.withdraw(_assets);

        uint256 total = received + slippage;

        uint256 debt = strategies[_strategy].debt;

        uint256 amount = debt > total ? received : total;

        strategies[_strategy].debt -= amount;
        totalDebt -= amount;

        // do not pass bonuses on to users withdrawing, prevents exploits draining vault
        if (_receiver == address(this)) emit Collect(_strategy, received, slippage, bonus);
        else asset.safeTransfer(_receiver, received);
    }

    function _harvest(Strategy _strategy) internal {
        _report(_strategy, _strategy.harvest());
    }

    /// @dev do not touch debt outside of _lend(), _collect() and _report()
    function _report(Strategy _strategy, uint256 _harvested) internal {
        uint256 assets = _strategy.totalAssets();
        uint256 debt = strategies[_strategy].debt;

        strategies[_strategy].debt = assets; // update debt

        uint256 gain;
        uint256 loss;

        uint256 lockedProfitBefore = lockedProfit();

        if (assets > debt) {
            unchecked {
                gain = assets - debt;
            }
            totalDebt += gain;

            _lockedProfit = lockedProfitBefore + gain + _harvested;
        } else if (debt > assets) {
            unchecked {
                loss = debt - assets;
                totalDebt -= loss;

                _lockedProfit = lockedProfitBefore + _harvested > loss ? lockedProfitBefore + _harvested - loss : 0;
            }
        }

        uint256 possibleDebt =
            totalDebtRatio == 0 ? 0 : totalAssets().mulDivDown(strategies[_strategy].debtRatio, totalDebtRatio);

        if (possibleDebt > assets) _lend(_strategy, possibleDebt - assets);
        else if (assets > possibleDebt) _collect(_strategy, assets - possibleDebt, address(this));

        emit Report(_strategy, _harvested, gain, loss);
    }

    function _setDebtRatio(Strategy _strategy, uint256 _newDebtRatio) internal {
        uint256 currentDebtRatio = strategies[_strategy].debtRatio;
        if (_newDebtRatio == currentDebtRatio) revert AlreadyValue();

        uint256 newTotalDebtRatio = totalDebtRatio + _newDebtRatio - currentDebtRatio;
        if (newTotalDebtRatio > MAX_TOTAL_DEBT_RATIO) revert AboveMaximum(newTotalDebtRatio);

        strategies[_strategy].debtRatio = _newDebtRatio;
        totalDebtRatio = newTotalDebtRatio;

        emit StrategyDebtRatioChanged(_strategy, _newDebtRatio);
    }

    function _setFloatDebtRatio(uint256 _floatDebtRatio) internal {
        uint256 newTotalDebtRatio = totalDebtRatio + _floatDebtRatio - floatDebtRatio;
        if (newTotalDebtRatio > MAX_TOTAL_DEBT_RATIO) revert AboveMaximum(newTotalDebtRatio);

        floatDebtRatio = _floatDebtRatio;
        totalDebtRatio = newTotalDebtRatio;

        emit FloatDebtRatioChanged(_floatDebtRatio);
    }

    /*/////////////////////
    /      Modifiers      /
    /////////////////////*/

    modifier updateLastReport() {
        _;
        lastReport = block.timestamp;
    }

    modifier whenNotPaused() {
        if (paused) revert Paused();
        _;
    }
}

/**
 * @dev
 * Strategies have to implement the following virtual functions:
 *
 * totalAssets()
 * _withdraw(uint256, address)
 * _harvest()
 * _invest()
 */
abstract contract Strategy is Ownership {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    Vault public immutable vault;
    ERC20 public immutable asset;

    /// @notice address which performance fees are sent to
    address public treasury;
    /// @notice performance fee sent to treasury / FEE_BASIS of 10_000
    uint16 public fee = 1_000;
    uint16 internal constant MAX_FEE = 1_000;
    uint16 internal constant FEE_BASIS = 10_000;

    /// @notice used to calculate slippage / SLIP_BASIS of 10_000
    /// @dev default to 99% (or 1%)
    uint16 public slip = 9_900;
    uint16 internal constant SLIP_BASIS = 10_000;

    /*//////////////////
    /      Events      /
    //////////////////*/

    event FeeChanged(uint16 newFee);
    event SlipChanged(uint16 newSlip);
    event TreasuryChanged(address indexed newTreasury);

    /*//////////////////
    /      Errors      /
    //////////////////*/

    error Zero();
    error NotVault();
    error InvalidValue();
    error AlreadyValue();

    constructor(Vault _vault, address _treasury, address _nominatedOwner, address _admin, address[] memory _authorized)
        Ownership(_nominatedOwner, _admin, _authorized)
    {
        vault = _vault;
        asset = vault.asset();
        treasury = _treasury;
    }

    /*//////////////////////////
    /      Public Virtual      /
    //////////////////////////*/

    /// @notice amount of 'asset' currently managed by strategy
    function totalAssets() public view virtual returns (uint256);

    /*///////////////////////////////////////////
    /      Restricted Functions: onlyVault      /
    ///////////////////////////////////////////*/

    function withdraw(uint256 _assets) external onlyVault returns (uint256 received, uint256 slippage, uint256 bonus) {
        uint256 total = totalAssets();
        if (total == 0) revert Zero();

        uint256 assets = _assets > total ? total : _assets;

        received = _withdraw(assets);

        unchecked {
            if (assets > received) {
                slippage = assets - received;
            } else if (received > assets) {
                bonus = received - assets;
                // received cannot > assets for vault calcuations
                received = assets;
            }
        }
    }

    /*//////////////////////////////////////////////////
    /      Restricted Functions: onlyAdminOrVault      /
    //////////////////////////////////////////////////*/

    function harvest() external onlyAdminOrVault returns (uint256 received) {
        _harvest();

        received = asset.balanceOf(address(this));

        if (fee > 0) {
            uint256 feeAmount = _calculateFee(received);
            received -= feeAmount;
            asset.safeTransfer(treasury, feeAmount);
        }

        asset.safeTransfer(address(vault), received);
    }

    function invest() external onlyAdminOrVault {
        _invest();
    }

    /*///////////////////////////////////////////
    /      Restricted Functions: onlyOwner      /
    ///////////////////////////////////////////*/

    function setFee(uint16 _fee) external onlyOwner {
        if (_fee > MAX_FEE) revert InvalidValue();
        if (_fee == fee) revert AlreadyValue();
        fee = _fee;
        emit FeeChanged(_fee);
    }

    function setTreasury(address _treasury) external onlyOwner {
        if (_treasury == treasury) revert AlreadyValue();
        treasury = _treasury;
        emit TreasuryChanged(_treasury);
    }

    /*////////////////////////////////////////////
    /      Restricted Functions: onlyAdmins      /
    ////////////////////////////////////////////*/

    function setSlip(uint16 _slip) external onlyAdmins {
        if (_slip > SLIP_BASIS) revert InvalidValue();
        if (_slip == slip) revert AlreadyValue();
        slip = _slip;
        emit SlipChanged(_slip);
    }

    /*////////////////////////////
    /      Internal Virtual      /
    ////////////////////////////*/

    function _withdraw(uint256 _assets) internal virtual returns (uint256 received);

    /// @dev return harvested assets
    function _harvest() internal virtual;

    function _invest() internal virtual;

    /*//////////////////////////////
    /      Internal Functions      /
    //////////////////////////////*/

    function _calculateSlippage(uint256 _amount) internal view returns (uint256) {
        return _amount.mulDivDown(slip, SLIP_BASIS);
    }

    function _calculateFee(uint256 _amount) internal view returns (uint256) {
        return _amount.mulDivDown(fee, FEE_BASIS);
    }

    modifier onlyVault() {
        if (msg.sender != address(vault)) revert NotVault();
        _;
    }

    /*//////////////////////////////
    /      Internal Functions      /
    //////////////////////////////*/

    modifier onlyAdminOrVault() {
        if (msg.sender != owner && msg.sender != admin && msg.sender != address(vault)) revert Unauthorized();
        _;
    }
}

contract WethStrategyStargate is Strategy {
    using SafeTransferLib for ERC20;
    using SafeTransferLib for LPTokenERC20;
    using SafeTransferLib for WETH;
    using FixedPointMathLib for uint256;

    IStargateRouter internal constant router = IStargateRouter(0x8731d54E9D02c286767d56ac03e8037C07e01e98);
    ILPStaking internal constant staking = ILPStaking(0xB0D502E938ed5f4df2E681fE6E419ff29631d62b);
    ERC20 internal constant STG = ERC20(0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6);

    /// @dev Stargate's version of WETH that automatically unwraps on transfer. Annoyingly, not canonical WETH
    WETH internal constant SGETH = WETH(payable(0x72E2F4830b9E45d52F80aC08CB2bEC0FeF72eD9c));
    /// @dev canonical WETH
    WETH internal constant WETH9 = WETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));

    /// @dev pid of asset in their router
    uint16 public constant routerPoolId = 13;
    /// @dev pid of asset in their LP staking contract
    uint256 public constant stakingPoolId = 2;
    LPTokenERC20 public constant lpToken = LPTokenERC20(0x101816545F6bd2b1076434B54383a1E633390A2E);

    /// @notice contract used to swap STG rewards to asset
    Swap public swap;

    /*///////////////
    /     Errors    /
    ///////////////*/

    error NoRewards();
    error NothingToInvest();
    error BelowMinimum(uint256);
    error InvalidAsset();

    constructor(
        Vault _vault,
        address _treasury,
        address _nominatedOwner,
        address _admin,
        address[] memory _authorized,
        Swap _swap
    ) Strategy(_vault, _treasury, _nominatedOwner, _admin, _authorized) {
        swap = _swap;

        if (address(_vault.asset()) != address(WETH9)) revert InvalidAsset();

        _approve();
    }

    receive() external payable {
        if (msg.sender == address(WETH9)) return; // do nothing when unwrapping WETH

        // SGETH automatically unwraps to ETH upon transfer in `redeemLocal` and `instantRedeemLocal`. We wrap and send
        // WETH from other sources (namely router) to vault as ETH.
        WETH9.deposit{value: msg.value}();
        asset.safeTransfer(address(vault), msg.value);
    }

    /*///////////////////////
    /      Public View      /
    ///////////////////////*/

    function totalAssets() public view override returns (uint256 assets) {
        (uint256 stakedBalance,) = staking.userInfo(stakingPoolId, address(this));
        return lpToken.amountLPtoLD(stakedBalance);
    }

    /*///////////////////////////////////////////
    /      Restricted Functions: onlyOwner      /
    ///////////////////////////////////////////*/

    function changeSwap(Swap _swap) external onlyOwner {
        _unapproveSwap();
        swap = _swap;
        _approveSwap();
    }

    /*////////////////////////////////////////////////
    /      Restricted Functions: onlyAuthorized      /
    ////////////////////////////////////////////////*/

    function reapprove() external onlyAuthorized {
        _unapprove();
        _approve();
    }

    /**
     * @notice Safeguard to manually withdraw if insufficient delta in Stargate local pool.
     * 	@dev Use router.quoteLayerZeroFee to estimate 'msg.value' (excess will be refunded to `msg.sender`).
     * 	@param _dstChainId STG chainId, see https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet, ideally we want to use the chain with cheapest gas
     * 	@param _assets amount of LP to redeem, use type(uint256).max to withdraw everything
     * 	@param _lzTxObj usually can just be (0, 0, "0x")
     */
    function manualWithdraw(uint16 _dstChainId, uint256 _assets, IStargateRouter.lzTxObj calldata _lzTxObj)
        external
        payable
        onlyAuthorized
    {
        uint256 assets = totalAssets();

        uint256 amount = assets > _assets ? _assets : assets;
        uint256 lpAmount = _convertAssetToLP(amount);

        staking.withdraw(stakingPoolId, lpAmount);

        router.redeemLocal{value: msg.value}(
            _dstChainId,
            routerPoolId,
            routerPoolId,
            payable(msg.sender),
            lpAmount,
            abi.encodePacked(address(this)),
            _lzTxObj
        );
    }

    /*/////////////////////////////
    /      Internal Override      /
    /////////////////////////////*/

    function _withdraw(uint256 _assets) internal override returns (uint256 received) {
        uint256 lpAmount = _convertAssetToLP(_assets);

        // lpAmount can round down to 0 which will cause the withdraw to fail
        if (lpAmount == 0) return received;

        // 1. withdraw from staking contract
        staking.withdraw(stakingPoolId, lpAmount);

        // withdraw from stargate router
        received = router.instantRedeemLocal(routerPoolId, lpAmount, address(this));
        if (received < _calculateSlippage(_assets)) revert BelowMinimum(received);
    }

    function _harvest() internal override {
        // empty deposit/withdraw claims rewards withdraw as with all Goose clones
        staking.withdraw(stakingPoolId, 0);

        uint256 rewardBalance = STG.balanceOf(address(this));
        if (rewardBalance == 0) revert NoRewards(); // nothing to harvest

        swap.swapTokens(address(STG), address(asset), rewardBalance, 1);
    }

    function _invest() internal override {
        uint256 assetBalance = asset.balanceOf(address(this));
        if (assetBalance == 0) revert NothingToInvest();

        WETH9.withdraw(assetBalance);
        SGETH.deposit{value: assetBalance}();

        router.addLiquidity(routerPoolId, assetBalance, address(this));

        uint256 balance = lpToken.balanceOf(address(this));

        if (balance < _calculateSlippage(assetBalance)) revert BelowMinimum(balance);

        staking.deposit(stakingPoolId, balance);
    }

    /*//////////////////////////////
    /      Internal Functions      /
    //////////////////////////////*/

    function _approve() internal {
        // approve deposit SGETH into router
        SGETH.safeApprove(address(router), type(uint256).max);
        // approve deposit lpToken into staking contract
        lpToken.safeApprove(address(staking), type(uint256).max);

        _approveSwap();
    }

    function _unapprove() internal {
        SGETH.safeApprove(address(router), 0);
        lpToken.safeApprove(address(staking), 0);

        _unapproveSwap();
    }

    // approve swap rewards to asset
    function _unapproveSwap() internal {
        STG.safeApprove(address(swap), 0);
    }

    // approve swap rewards to asset
    function _approveSwap() internal {
        STG.safeApprove(address(swap), type(uint256).max);
    }

    function _convertAssetToLP(uint256 _amount) internal view returns (uint256) {
        return _amount.mulDivDown(lpToken.totalSupply(), lpToken.totalLiquidity());
    }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract Vault","name":"_vault","type":"address"},{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"address","name":"_nominatedOwner","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address[]","name":"_authorized","type":"address[]"},{"internalType":"contract Swap","name":"_swap","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyRole","type":"error"},{"inputs":[],"name":"AlreadyValue","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"BelowMinimum","type":"error"},{"inputs":[],"name":"InvalidAsset","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"NoRewards","type":"error"},{"inputs":[],"name":"NotRole","type":"error"},{"inputs":[],"name":"NotVault","type":"error"},{"inputs":[],"name":"NothingToInvest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Zero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAuth","type":"address"}],"name":"AuthAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAuth","type":"address"}],"name":"AuthRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newFee","type":"uint16"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newSlip","type":"uint16"}],"name":"SlipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasuryChanged","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorized","type":"address"}],"name":"addAuthorized","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"authorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Swap","name":"_swap","type":"address"}],"name":"changeSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[{"internalType":"uint256","name":"received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"invest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lpToken","outputs":[{"internalType":"contract LPTokenERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_dstChainId","type":"uint16"},{"internalType":"uint256","name":"_assets","type":"uint256"},{"components":[{"internalType":"uint256","name":"dstGasForCall","type":"uint256"},{"internalType":"uint256","name":"dstNativeAmount","type":"uint256"},{"internalType":"bytes","name":"dstNativeAddr","type":"bytes"}],"internalType":"struct IStargateRouter.lzTxObj","name":"_lzTxObj","type":"tuple"}],"name":"manualWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"nominateOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reapprove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorized","type":"address"}],"name":"removeAuthorized","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerPoolId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_fee","type":"uint16"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_slip","type":"uint16"}],"name":"setSlip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slip","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingPoolId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swap","outputs":[{"internalType":"contract Swap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract Vault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"received","type":"uint256"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"uint256","name":"bonus","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c06040526004805463ffffffff60a01b19166304d5807d60a31b1790553480156200002a57600080fd5b5060405162002b6138038062002b618339810160408190526200004d916200047d565b60008054336001600160a01b03199182161782556001805482166001600160a01b038881169190911790915560028054909216908616179055869086908690869086908390839083905b81518160ff1610156200016957600160036000848460ff1681518110620000c257620000c2620005ba565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550818160ff1681518110620001195762000119620005ba565b60200260200101516001600160a01b03167f5daa78f70ec2227622bb7db0a1d9e750860491853de15431e1e473d305381c2160405160405180910390a26200016181620005d0565b905062000097565b505050506001600160a01b0385166080819052604080516338d52e0f60e01b815290516338d52e0f91600480820192602092909190829003018186803b158015620001b357600080fd5b505afa158015620001c8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ee9190620005ff565b6001600160a01b0390811660a052600480549582166001600160a01b031996871617815560058054898416971696909617909555604080516338d52e0f60e01b8152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc29750918d16956338d52e0f955081810194506020935090829003018186803b1580156200027357600080fd5b505afa15801562000288573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002ae9190620005ff565b6001600160a01b031614620002d657604051636448d6e960e11b815260040160405180910390fd5b620002e0620002ec565b50505050505062000626565b6200032f7372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c738731d54e9d02c286767d56ac03e8037c07e01e986000196200037e602090811b6200160417901c565b6200037273101816545f6bd2b1076434b54383a1e633390a2e73b0d502e938ed5f4df2e681fe6e419ff29631d62b6000196200037e602090811b6200160417901c565b6200037c62000400565b565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080620003fa5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640160405180910390fd5b50505050565b6005546200037c9073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6906001600160a01b03166000196200037e602090811b6200160417901c565b6001600160a01b03811681146200045257600080fd5b50565b805162000462816200043c565b919050565b634e487b7160e01b600052604160045260246000fd5b60008060008060008060c087890312156200049757600080fd5b8651620004a4816200043c565b80965050602080880151620004b9816200043c565b6040890151909650620004cc816200043c565b6060890151909550620004df816200043c565b60808901519094506001600160401b0380821115620004fd57600080fd5b818a0191508a601f8301126200051257600080fd5b81518181111562000527576200052762000467565b8060051b604051601f19603f830116810181811085821117156200054f576200054f62000467565b60405291825284820192508381018501918d8311156200056e57600080fd5b938501935b828510156200059757620005878562000455565b8452938501939285019262000573565b809750505050505050620005ae60a0880162000455565b90509295509295509295565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff811415620005f657634e487b7160e01b600052601160045260246000fd5b60010192915050565b6000602082840312156200061257600080fd5b81516200061f816200043c565b9392505050565b60805160a0516124c862000699600039600081816102520152818161031901528181610bb001528181610c8401528181610cbc015281816119fc0152611da801526000818161027601528181610679015281816108ba01528181610b3a01528181610cde01526114e801526124c86000f3fe6080604052600436106101bb5760003560e01c80638da5cb5b116100ec578063d818f23a1161008a578063e8b5e51f11610064578063e8b5e51f14610612578063f0f4426014610627578063f851a44014610647578063fbfa77cf1461066757600080fd5b8063d818f23a146105aa578063d9380519146105bf578063ddca3f43146105df57600080fd5b8063a46c8505116100c6578063a46c850514610515578063b91816111461052a578063cf1c316a1461056a578063d59c58be1461058a57600080fd5b80638da5cb5b146104c05780638e005553146104e05780638fcb2be61461050057600080fd5b806353a47bb711610159578063704b6c0211610133578063704b6c0214610458578063793c85791461047857806379ba50971461048b5780638119c065146104a057600080fd5b806353a47bb7146103f05780635fcbd2851461041057806361d027b31461043857600080fd5b8063392c731f11610195578063392c731f1461035357806340cd23171461039b5780634641257d146103bb578063485d7d94146103d057600080fd5b806301e1d114146102a45780632e1a7d4d146102cc57806338d52e0f1461030757600080fd5b3661029f573373c02aaa39b223fe8d0a0e5c4f27ead9083c756cc214156101de57005b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561022d57600080fd5b505af1158015610241573d6000803e3d6000fd5b5061029d9350506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691507f000000000000000000000000000000000000000000000000000000000000000090503461069b565b005b600080fd5b3480156102b057600080fd5b506102b961075f565b6040519081526020015b60405180910390f35b3480156102d857600080fd5b506102ec6102e73660046121ad565b6108ab565b604080519384526020840192909252908201526060016102c3565b34801561031357600080fd5b5061033b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102c3565b34801561035f57600080fd5b5060045461038890760100000000000000000000000000000000000000000000900461ffff1681565b60405161ffff90911681526020016102c3565b3480156103a757600080fd5b5061029d6103b63660046121dd565b61099d565b3480156103c757600080fd5b506102b9610b01565b3480156103dc57600080fd5b5061029d6103eb366004612214565b610d06565b3480156103fc57600080fd5b5060015461033b906001600160a01b031681565b34801561041c57600080fd5b5061033b73101816545f6bd2b1076434b54383a1e633390a2e81565b34801561044457600080fd5b5060045461033b906001600160a01b031681565b34801561046457600080fd5b5061029d610473366004612214565b610e02565b61029d610486366004612231565b610eae565b34801561049757600080fd5b5061029d611091565b3480156104ac57600080fd5b5060055461033b906001600160a01b031681565b3480156104cc57600080fd5b5060005461033b906001600160a01b031681565b3480156104ec57600080fd5b5061029d6104fb3660046121dd565b61112b565b34801561050c57600080fd5b50610388600d81565b34801561052157600080fd5b506102b9600281565b34801561053657600080fd5b5061055a610545366004612214565b60036020526000908152604090205460ff1681565b60405190151581526020016102c3565b34801561057657600080fd5b5061029d610585366004612214565b61126b565b34801561059657600080fd5b5061029d6105a5366004612214565b61136b565b3480156105b657600080fd5b5061029d6113db565b3480156105cb57600080fd5b5061029d6105da366004612214565b61144c565b3480156105eb57600080fd5b506004546103889074010000000000000000000000000000000000000000900461ffff1681565b34801561061e57600080fd5b5061029d6114b0565b34801561063357600080fd5b5061029d610642366004612214565b611530565b34801561065357600080fd5b5060025461033b906001600160a01b031681565b34801561067357600080fd5b5061033b7f000000000000000000000000000000000000000000000000000000000000000081565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610759576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064015b60405180910390fd5b50505050565b6040517f93f1a40b00000000000000000000000000000000000000000000000000000000815260026004820152306024820152600090819073b0d502e938ed5f4df2e681fe6e419ff29631d62b906393f1a40b90604401604080518083038186803b1580156107cd57600080fd5b505afa1580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610805919061228f565b506040517ff6cd35ee0000000000000000000000000000000000000000000000000000000081526004810182905290915073101816545f6bd2b1076434b54383a1e633390a2e9063f6cd35ee9060240160206040518083038186803b15801561086d57600080fd5b505afa158015610881573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a591906122b3565b91505090565b60008080336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610911576040517f62df054500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061091b61075f565b905080610954576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008186116109635785610965565b815b9050610970816116bd565b945084811115610984578481039350610994565b8085111561099457938490039150835b50509193909250565b6000546001600160a01b031633148015906109c357506002546001600160a01b03163314155b156109e0576040516282b42960e81b815260040160405180910390fd5b61271061ffff82161115610a20576040517faa7feadc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045461ffff828116760100000000000000000000000000000000000000000000909204161415610a7d576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffff1676010000000000000000000000000000000000000000000061ffff8416908102919091179091556040519081527ffdb877bdd6097061fb8d18b884652721f0c42b47da21422277453d679c815e49906020015b60405180910390a150565b600080546001600160a01b03163314801590610b2857506002546001600160a01b03163314155b8015610b5d5750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b15610b7a576040516282b42960e81b815260040160405180910390fd5b610b82611853565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015610bfa57600080fd5b505afa158015610c0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3291906122b3565b60045490915074010000000000000000000000000000000000000000900461ffff1615610caf576000610c6482611a93565b9050610c7081836122cc565b600454909250610cad906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691168361069b565b505b610d036001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008361069b565b90565b6000546001600160a01b03163314801590610d2c57506002546001600160a01b03163314155b15610d49576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526003602052604090205460ff16610d9b576040517ffbb7077700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fea160e8ad4e620d61f1d6fc4a3eedd36336ed4c5fa149bbd9db0beab48a945c19190a250565b6000546001600160a01b03163314610e2c576040516282b42960e81b815260040160405180910390fd5b6002546001600160a01b0382811691161415610e74576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000546001600160a01b03163314801590610ed457506002546001600160a01b03163314155b8015610ef057503360009081526003602052604090205460ff16155b15610f0d576040516282b42960e81b815260040160405180910390fd5b6000610f1761075f565b90506000838211610f285781610f2a565b835b90506000610f3782611ac9565b6040517f441a3e70000000000000000000000000000000000000000000000000000000008152600260048201526024810182905290915073b0d502e938ed5f4df2e681fe6e419ff29631d62b9063441a3e7090604401600060405180830381600087803b158015610fa757600080fd5b505af1158015610fbb573d6000803e3d6000fd5b50505050738731d54e9d02c286767d56ac03e8037c07e01e986001600160a01b0316638f2e1d183488600d80338730604051602001611025919060609190911b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016815260140190565b6040516020818303038152906040528c6040518963ffffffff1660e01b815260040161105797969594939291906123cc565b6000604051808303818588803b15801561107057600080fd5b505af1158015611084573d6000803e3d6000fd5b5050505050505050505050565b6001546001600160a01b031633146110bb576040516282b42960e81b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600180549091169055565b6000546001600160a01b03163314611155576040516282b42960e81b815260040160405180910390fd5b6103e861ffff82161115611195576040517faa7feadc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045461ffff828116740100000000000000000000000000000000000000009092041614156111f0576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000061ffff8416908102919091179091556040519081527f931a978d852c1b6fa9dd97300fd89c773ff625b91f697b0911e943ba15f9e24390602001610af6565b6000546001600160a01b0316331480159061129157506002546001600160a01b03163314155b156112ae576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526003602052604090205460ff1615611301576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f5daa78f70ec2227622bb7db0a1d9e750860491853de15431e1e473d305381c219190a250565b6000546001600160a01b03163314611395576040516282b42960e81b815260040160405180910390fd5b61139d611be0565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790556113d8611c0d565b50565b6000546001600160a01b0316331480159061140157506002546001600160a01b03163314155b801561141d57503360009081526003602052604090205460ff16155b1561143a576040516282b42960e81b815260040160405180910390fd5b611442611c59565b61144a611cc9565b565b6000546001600160a01b03163314611476576040516282b42960e81b815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000546001600160a01b031633148015906114d657506002546001600160a01b03163314155b801561150b5750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b15611528576040516282b42960e81b815260040160405180910390fd5b61144a611d77565b6000546001600160a01b0316331461155a576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b03828116911614156115a2576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517fc714d22a2f08b695f81e7c707058db484aa5b4d6b4c9fd64beb10fe85832f60890600090a250565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610759576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610750565b6000806116c983611ac9565b9050806116d65750919050565b6040517f441a3e70000000000000000000000000000000000000000000000000000000008152600260048201526024810182905273b0d502e938ed5f4df2e681fe6e419ff29631d62b9063441a3e7090604401600060405180830381600087803b15801561174357600080fd5b505af1158015611757573d6000803e3d6000fd5b50506040517fc4de93a5000000000000000000000000000000000000000000000000000000008152600d600482015260248101849052306044820152738731d54e9d02c286767d56ac03e8037c07e01e98925063c4de93a59150606401602060405180830381600087803b1580156117ce57600080fd5b505af11580156117e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180691906122b3565b915061181183612143565b82101561184d576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101839052602401610750565b50919050565b6040517f441a3e70000000000000000000000000000000000000000000000000000000008152600260048201526000602482015273b0d502e938ed5f4df2e681fe6e419ff29631d62b9063441a3e7090604401600060405180830381600087803b1580156118c057600080fd5b505af11580156118d4573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000925073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd691506370a082319060240160206040518083038186803b15801561193e57600080fd5b505afa158015611952573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197691906122b3565b9050806119af576040517f3fb087f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546040517f8e18cdfc00000000000000000000000000000000000000000000000000000000815273af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd660048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152604482018490526001606483015290911690638e18cdfc90608401602060405180830381600087803b158015611a5757600080fd5b505af1158015611a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8f91906122b3565b5050565b600454600090611ac390839074010000000000000000000000000000000000000000900461ffff16612710612171565b92915050565b6000611ac373101816545f6bd2b1076434b54383a1e633390a2e6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611b1b57600080fd5b505afa158015611b2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5391906122b3565b73101816545f6bd2b1076434b54383a1e633390a2e6001600160a01b03166315770f926040518163ffffffff1660e01b815260040160206040518083038186803b158015611ba057600080fd5b505afa158015611bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd891906122b3565b849190612171565b60055461144a9073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6906001600160a01b03166000611604565b60055461144a9073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6906001600160a01b03167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611604565b611c8d7372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c738731d54e9d02c286767d56ac03e8037c07e01e986000611604565b611cc173101816545f6bd2b1076434b54383a1e633390a2e73b0d502e938ed5f4df2e681fe6e419ff29631d62b6000611604565b61144a611be0565b611d1c7372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c738731d54e9d02c286767d56ac03e8037c07e01e987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611604565b611d6f73101816545f6bd2b1076434b54383a1e633390a2e73b0d502e938ed5f4df2e681fe6e419ff29631d62b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611604565b61144a611c0d565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015611df257600080fd5b505afa158015611e06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e2a91906122b3565b905080611e63576040517f3c652dfd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810182905273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290632e1a7d4d90602401600060405180830381600087803b158015611ec957600080fd5b505af1158015611edd573d6000803e3d6000fd5b505050507372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c6001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611f3057600080fd5b505af1158015611f44573d6000803e3d6000fd5b50506040517f87b21efc000000000000000000000000000000000000000000000000000000008152600d600482015260248101859052306044820152738731d54e9d02c286767d56ac03e8037c07e01e9893506387b21efc92506064019050600060405180830381600087803b158015611fbd57600080fd5b505af1158015611fd1573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000925073101816545f6bd2b1076434b54383a1e633390a2e91506370a082319060240160206040518083038186803b15801561203b57600080fd5b505afa15801561204f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207391906122b3565b905061207e82612143565b8110156120ba576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101829052602401610750565b6040517fe2bbb158000000000000000000000000000000000000000000000000000000008152600260048201526024810182905273b0d502e938ed5f4df2e681fe6e419ff29631d62b9063e2bbb15890604401600060405180830381600087803b15801561212757600080fd5b505af115801561213b573d6000803e3d6000fd5b505050505050565b600454600090611ac3908390760100000000000000000000000000000000000000000000900461ffff166127105b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026121a657600080fd5b5091020490565b6000602082840312156121bf57600080fd5b5035919050565b803561ffff811681146121d857600080fd5b919050565b6000602082840312156121ef57600080fd5b6121f8826121c6565b9392505050565b6001600160a01b03811681146113d857600080fd5b60006020828403121561222657600080fd5b81356121f8816121ff565b60008060006060848603121561224657600080fd5b61224f846121c6565b925060208401359150604084013567ffffffffffffffff81111561227257600080fd5b84016060818703121561228457600080fd5b809150509250925092565b600080604083850312156122a257600080fd5b505080516020909101519092909150565b6000602082840312156122c557600080fd5b5051919050565b600082821015612305577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500390565b8035825260208101356020830152600060408201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe183360301811261234f57600080fd5b8201803567ffffffffffffffff81111561236857600080fd5b80360384131561237757600080fd5b606060408601528060608601528060208301608087013760006080828701015260807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168601019250505092915050565b600061ffff808a1683526020818a168185015281891660408501526001600160a01b038816606085015286608085015260e060a0850152855191508160e085015260005b8281101561242d5786810182015185820161010001528101612410565b8281111561244057600061010084870101525b5050601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820182810361010090810160c08501526124838183018661230a565b9b9a505050505050505050505056fea2646970667358221220bc727841b2b053a7d6b8a974d8ae96afa0d2e60493a50c2cd966fd54d564cf9164736f6c634300080900330000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d0000000000000000000000001c064ea662365c09c8e87242791dacbb90002605000000000000000000000000e2ceda90aa1e43647ef306810a903b32c9a3aa94000000000000000000000000f4e2007bb865b78bc0eac0cc242e974efd49c06d00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000df46950003eed5b40bd94fdf0efc4c148858eeb00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000a1f3a2c20a7e8e4470ffa52c01646e1ff4c759a0000000000000000000000001c595009b331fe85fd658aa0b6b7be95b6921021000000000000000000000000beca7b566fad28ccbe6dce59d42d37768ab3cd8b

Deployed Bytecode

0x6080604052600436106101bb5760003560e01c80638da5cb5b116100ec578063d818f23a1161008a578063e8b5e51f11610064578063e8b5e51f14610612578063f0f4426014610627578063f851a44014610647578063fbfa77cf1461066757600080fd5b8063d818f23a146105aa578063d9380519146105bf578063ddca3f43146105df57600080fd5b8063a46c8505116100c6578063a46c850514610515578063b91816111461052a578063cf1c316a1461056a578063d59c58be1461058a57600080fd5b80638da5cb5b146104c05780638e005553146104e05780638fcb2be61461050057600080fd5b806353a47bb711610159578063704b6c0211610133578063704b6c0214610458578063793c85791461047857806379ba50971461048b5780638119c065146104a057600080fd5b806353a47bb7146103f05780635fcbd2851461041057806361d027b31461043857600080fd5b8063392c731f11610195578063392c731f1461035357806340cd23171461039b5780634641257d146103bb578063485d7d94146103d057600080fd5b806301e1d114146102a45780632e1a7d4d146102cc57806338d52e0f1461030757600080fd5b3661029f573373c02aaa39b223fe8d0a0e5c4f27ead9083c756cc214156101de57005b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561022d57600080fd5b505af1158015610241573d6000803e3d6000fd5b5061029d9350506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21691507f0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d90503461069b565b005b600080fd5b3480156102b057600080fd5b506102b961075f565b6040519081526020015b60405180910390f35b3480156102d857600080fd5b506102ec6102e73660046121ad565b6108ab565b604080519384526020840192909252908201526060016102c3565b34801561031357600080fd5b5061033b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b0390911681526020016102c3565b34801561035f57600080fd5b5060045461038890760100000000000000000000000000000000000000000000900461ffff1681565b60405161ffff90911681526020016102c3565b3480156103a757600080fd5b5061029d6103b63660046121dd565b61099d565b3480156103c757600080fd5b506102b9610b01565b3480156103dc57600080fd5b5061029d6103eb366004612214565b610d06565b3480156103fc57600080fd5b5060015461033b906001600160a01b031681565b34801561041c57600080fd5b5061033b73101816545f6bd2b1076434b54383a1e633390a2e81565b34801561044457600080fd5b5060045461033b906001600160a01b031681565b34801561046457600080fd5b5061029d610473366004612214565b610e02565b61029d610486366004612231565b610eae565b34801561049757600080fd5b5061029d611091565b3480156104ac57600080fd5b5060055461033b906001600160a01b031681565b3480156104cc57600080fd5b5060005461033b906001600160a01b031681565b3480156104ec57600080fd5b5061029d6104fb3660046121dd565b61112b565b34801561050c57600080fd5b50610388600d81565b34801561052157600080fd5b506102b9600281565b34801561053657600080fd5b5061055a610545366004612214565b60036020526000908152604090205460ff1681565b60405190151581526020016102c3565b34801561057657600080fd5b5061029d610585366004612214565b61126b565b34801561059657600080fd5b5061029d6105a5366004612214565b61136b565b3480156105b657600080fd5b5061029d6113db565b3480156105cb57600080fd5b5061029d6105da366004612214565b61144c565b3480156105eb57600080fd5b506004546103889074010000000000000000000000000000000000000000900461ffff1681565b34801561061e57600080fd5b5061029d6114b0565b34801561063357600080fd5b5061029d610642366004612214565b611530565b34801561065357600080fd5b5060025461033b906001600160a01b031681565b34801561067357600080fd5b5061033b7f0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d81565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610759576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064015b60405180910390fd5b50505050565b6040517f93f1a40b00000000000000000000000000000000000000000000000000000000815260026004820152306024820152600090819073b0d502e938ed5f4df2e681fe6e419ff29631d62b906393f1a40b90604401604080518083038186803b1580156107cd57600080fd5b505afa1580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610805919061228f565b506040517ff6cd35ee0000000000000000000000000000000000000000000000000000000081526004810182905290915073101816545f6bd2b1076434b54383a1e633390a2e9063f6cd35ee9060240160206040518083038186803b15801561086d57600080fd5b505afa158015610881573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a591906122b3565b91505090565b60008080336001600160a01b037f0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d1614610911576040517f62df054500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061091b61075f565b905080610954576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008186116109635785610965565b815b9050610970816116bd565b945084811115610984578481039350610994565b8085111561099457938490039150835b50509193909250565b6000546001600160a01b031633148015906109c357506002546001600160a01b03163314155b156109e0576040516282b42960e81b815260040160405180910390fd5b61271061ffff82161115610a20576040517faa7feadc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045461ffff828116760100000000000000000000000000000000000000000000909204161415610a7d576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffff1676010000000000000000000000000000000000000000000061ffff8416908102919091179091556040519081527ffdb877bdd6097061fb8d18b884652721f0c42b47da21422277453d679c815e49906020015b60405180910390a150565b600080546001600160a01b03163314801590610b2857506002546001600160a01b03163314155b8015610b5d5750336001600160a01b037f0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d1614155b15610b7a576040516282b42960e81b815260040160405180910390fd5b610b82611853565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b158015610bfa57600080fd5b505afa158015610c0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3291906122b3565b60045490915074010000000000000000000000000000000000000000900461ffff1615610caf576000610c6482611a93565b9050610c7081836122cc565b600454909250610cad906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811691168361069b565b505b610d036001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2167f0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d8361069b565b90565b6000546001600160a01b03163314801590610d2c57506002546001600160a01b03163314155b15610d49576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526003602052604090205460ff16610d9b576040517ffbb7077700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fea160e8ad4e620d61f1d6fc4a3eedd36336ed4c5fa149bbd9db0beab48a945c19190a250565b6000546001600160a01b03163314610e2c576040516282b42960e81b815260040160405180910390fd5b6002546001600160a01b0382811691161415610e74576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000546001600160a01b03163314801590610ed457506002546001600160a01b03163314155b8015610ef057503360009081526003602052604090205460ff16155b15610f0d576040516282b42960e81b815260040160405180910390fd5b6000610f1761075f565b90506000838211610f285781610f2a565b835b90506000610f3782611ac9565b6040517f441a3e70000000000000000000000000000000000000000000000000000000008152600260048201526024810182905290915073b0d502e938ed5f4df2e681fe6e419ff29631d62b9063441a3e7090604401600060405180830381600087803b158015610fa757600080fd5b505af1158015610fbb573d6000803e3d6000fd5b50505050738731d54e9d02c286767d56ac03e8037c07e01e986001600160a01b0316638f2e1d183488600d80338730604051602001611025919060609190911b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016815260140190565b6040516020818303038152906040528c6040518963ffffffff1660e01b815260040161105797969594939291906123cc565b6000604051808303818588803b15801561107057600080fd5b505af1158015611084573d6000803e3d6000fd5b5050505050505050505050565b6001546001600160a01b031633146110bb576040516282b42960e81b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600180549091169055565b6000546001600160a01b03163314611155576040516282b42960e81b815260040160405180910390fd5b6103e861ffff82161115611195576040517faa7feadc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045461ffff828116740100000000000000000000000000000000000000009092041614156111f0576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000061ffff8416908102919091179091556040519081527f931a978d852c1b6fa9dd97300fd89c773ff625b91f697b0911e943ba15f9e24390602001610af6565b6000546001600160a01b0316331480159061129157506002546001600160a01b03163314155b156112ae576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526003602052604090205460ff1615611301576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f5daa78f70ec2227622bb7db0a1d9e750860491853de15431e1e473d305381c219190a250565b6000546001600160a01b03163314611395576040516282b42960e81b815260040160405180910390fd5b61139d611be0565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790556113d8611c0d565b50565b6000546001600160a01b0316331480159061140157506002546001600160a01b03163314155b801561141d57503360009081526003602052604090205460ff16155b1561143a576040516282b42960e81b815260040160405180910390fd5b611442611c59565b61144a611cc9565b565b6000546001600160a01b03163314611476576040516282b42960e81b815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000546001600160a01b031633148015906114d657506002546001600160a01b03163314155b801561150b5750336001600160a01b037f0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d1614155b15611528576040516282b42960e81b815260040160405180910390fd5b61144a611d77565b6000546001600160a01b0316331461155a576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b03828116911614156115a2576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517fc714d22a2f08b695f81e7c707058db484aa5b4d6b4c9fd64beb10fe85832f60890600090a250565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610759576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610750565b6000806116c983611ac9565b9050806116d65750919050565b6040517f441a3e70000000000000000000000000000000000000000000000000000000008152600260048201526024810182905273b0d502e938ed5f4df2e681fe6e419ff29631d62b9063441a3e7090604401600060405180830381600087803b15801561174357600080fd5b505af1158015611757573d6000803e3d6000fd5b50506040517fc4de93a5000000000000000000000000000000000000000000000000000000008152600d600482015260248101849052306044820152738731d54e9d02c286767d56ac03e8037c07e01e98925063c4de93a59150606401602060405180830381600087803b1580156117ce57600080fd5b505af11580156117e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180691906122b3565b915061181183612143565b82101561184d576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101839052602401610750565b50919050565b6040517f441a3e70000000000000000000000000000000000000000000000000000000008152600260048201526000602482015273b0d502e938ed5f4df2e681fe6e419ff29631d62b9063441a3e7090604401600060405180830381600087803b1580156118c057600080fd5b505af11580156118d4573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000925073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd691506370a082319060240160206040518083038186803b15801561193e57600080fd5b505afa158015611952573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197691906122b3565b9050806119af576040517f3fb087f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546040517f8e18cdfc00000000000000000000000000000000000000000000000000000000815273af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd660048201526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281166024830152604482018490526001606483015290911690638e18cdfc90608401602060405180830381600087803b158015611a5757600080fd5b505af1158015611a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8f91906122b3565b5050565b600454600090611ac390839074010000000000000000000000000000000000000000900461ffff16612710612171565b92915050565b6000611ac373101816545f6bd2b1076434b54383a1e633390a2e6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611b1b57600080fd5b505afa158015611b2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5391906122b3565b73101816545f6bd2b1076434b54383a1e633390a2e6001600160a01b03166315770f926040518163ffffffff1660e01b815260040160206040518083038186803b158015611ba057600080fd5b505afa158015611bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd891906122b3565b849190612171565b60055461144a9073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6906001600160a01b03166000611604565b60055461144a9073af5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6906001600160a01b03167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611604565b611c8d7372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c738731d54e9d02c286767d56ac03e8037c07e01e986000611604565b611cc173101816545f6bd2b1076434b54383a1e633390a2e73b0d502e938ed5f4df2e681fe6e419ff29631d62b6000611604565b61144a611be0565b611d1c7372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c738731d54e9d02c286767d56ac03e8037c07e01e987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611604565b611d6f73101816545f6bd2b1076434b54383a1e633390a2e73b0d502e938ed5f4df2e681fe6e419ff29631d62b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611604565b61144a611c0d565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b158015611df257600080fd5b505afa158015611e06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e2a91906122b3565b905080611e63576040517f3c652dfd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810182905273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290632e1a7d4d90602401600060405180830381600087803b158015611ec957600080fd5b505af1158015611edd573d6000803e3d6000fd5b505050507372e2f4830b9e45d52f80ac08cb2bec0fef72ed9c6001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611f3057600080fd5b505af1158015611f44573d6000803e3d6000fd5b50506040517f87b21efc000000000000000000000000000000000000000000000000000000008152600d600482015260248101859052306044820152738731d54e9d02c286767d56ac03e8037c07e01e9893506387b21efc92506064019050600060405180830381600087803b158015611fbd57600080fd5b505af1158015611fd1573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000925073101816545f6bd2b1076434b54383a1e633390a2e91506370a082319060240160206040518083038186803b15801561203b57600080fd5b505afa15801561204f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207391906122b3565b905061207e82612143565b8110156120ba576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101829052602401610750565b6040517fe2bbb158000000000000000000000000000000000000000000000000000000008152600260048201526024810182905273b0d502e938ed5f4df2e681fe6e419ff29631d62b9063e2bbb15890604401600060405180830381600087803b15801561212757600080fd5b505af115801561213b573d6000803e3d6000fd5b505050505050565b600454600090611ac3908390760100000000000000000000000000000000000000000000900461ffff166127105b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026121a657600080fd5b5091020490565b6000602082840312156121bf57600080fd5b5035919050565b803561ffff811681146121d857600080fd5b919050565b6000602082840312156121ef57600080fd5b6121f8826121c6565b9392505050565b6001600160a01b03811681146113d857600080fd5b60006020828403121561222657600080fd5b81356121f8816121ff565b60008060006060848603121561224657600080fd5b61224f846121c6565b925060208401359150604084013567ffffffffffffffff81111561227257600080fd5b84016060818703121561228457600080fd5b809150509250925092565b600080604083850312156122a257600080fd5b505080516020909101519092909150565b6000602082840312156122c557600080fd5b5051919050565b600082821015612305577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500390565b8035825260208101356020830152600060408201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe183360301811261234f57600080fd5b8201803567ffffffffffffffff81111561236857600080fd5b80360384131561237757600080fd5b606060408601528060608601528060208301608087013760006080828701015260807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168601019250505092915050565b600061ffff808a1683526020818a168185015281891660408501526001600160a01b038816606085015286608085015260e060a0850152855191508160e085015260005b8281101561242d5786810182015185820161010001528101612410565b8281111561244057600061010084870101525b5050601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820182810361010090810160c08501526124838183018661230a565b9b9a505050505050505050505056fea2646970667358221220bc727841b2b053a7d6b8a974d8ae96afa0d2e60493a50c2cd966fd54d564cf9164736f6c63430008090033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d0000000000000000000000001c064ea662365c09c8e87242791dacbb90002605000000000000000000000000e2ceda90aa1e43647ef306810a903b32c9a3aa94000000000000000000000000f4e2007bb865b78bc0eac0cc242e974efd49c06d00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000df46950003eed5b40bd94fdf0efc4c148858eeb00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000a1f3a2c20a7e8e4470ffa52c01646e1ff4c759a0000000000000000000000001c595009b331fe85fd658aa0b6b7be95b6921021000000000000000000000000beca7b566fad28ccbe6dce59d42d37768ab3cd8b

-----Decoded View---------------
Arg [0] : _vault (address): 0x6aBE5f87E3F4dC87301064F63CA5b244d119980d
Arg [1] : _treasury (address): 0x1C064EA662365c09c8E87242791dAcbb90002605
Arg [2] : _nominatedOwner (address): 0xe2CEDA90aa1E43647EF306810a903b32c9a3Aa94
Arg [3] : _admin (address): 0xf4E2007bb865B78bc0EAc0cC242e974Efd49C06d
Arg [4] : _authorized (address[]): 0x0a1F3A2c20a7e8E4470fFA52c01646E1ff4c759A,0x1C595009B331fE85FD658aA0B6B7bE95B6921021,0xBeCA7b566FAd28cCbe6dcE59d42D37768Ab3CD8B
Arg [5] : _swap (address): 0x0df46950003EEd5b40bd94FDf0EFC4c148858eeB

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 0000000000000000000000006abe5f87e3f4dc87301064f63ca5b244d119980d
Arg [1] : 0000000000000000000000001c064ea662365c09c8e87242791dacbb90002605
Arg [2] : 000000000000000000000000e2ceda90aa1e43647ef306810a903b32c9a3aa94
Arg [3] : 000000000000000000000000f4e2007bb865b78bc0eac0cc242e974efd49c06d
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [5] : 0000000000000000000000000df46950003eed5b40bd94fdf0efc4c148858eeb
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [7] : 0000000000000000000000000a1f3a2c20a7e8e4470ffa52c01646e1ff4c759a
Arg [8] : 0000000000000000000000001c595009b331fe85fd658aa0b6b7be95b6921021
Arg [9] : 000000000000000000000000beca7b566fad28ccbe6dce59d42d37768ab3cd8b


Deployed Bytecode Sourcemap

151079:7036:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;152933:10;151869:42;152933:28;152929:41;;;151079:7036;152929:41;151869:42;-1:-1:-1;;;;;153207:13:0;;153228:9;153207:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;153251:45:0;;-1:-1:-1;;;;;;;153251:5:0;:18;;-1:-1:-1;153278:5:0;;-1:-1:-1;153286:9:0;153251:18;:45::i;:::-;151079:7036;;;;;153407:214;;;;;;;;;;;;;:::i;:::-;;;160:25:1;;;148:2;133:18;153407:214:0;;;;;;;;147687:632;;;;;;;;;;-1:-1:-1;147687:632:0;;;;;:::i;:::-;;:::i;:::-;;;;583:25:1;;;639:2;624:18;;617:34;;;;667:18;;;660:34;571:2;556:18;147687:632:0;381:319:1;146097:28:0;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;883:55:1;;;865:74;;853:2;838:18;146097:28:0;705:240:1;146531:26:0;;;;;;;;;;-1:-1:-1;146531:26:0;;;;;;;;;;;;;;1124:6:1;1112:19;;;1094:38;;1082:2;1067:18;146531:26:0;950:188:1;149726:223:0;;;;;;;;;;-1:-1:-1;149726:223:0;;;;;:::i;:::-;;:::i;148503:397::-;;;;;;;;;;;;;:::i;121561:214::-;;;;;;;;;;-1:-1:-1;121561:214:0;;;;;:::i;:::-;;:::i;119857:29::-;;;;;;;;;;-1:-1:-1;119857:29:0;;;;-1:-1:-1;;;;;119857:29:0;;;152116:95;;;;;;;;;;;;152168:42;152116:95;;146194:23;;;;;;;;;;-1:-1:-1;146194:23:0;;;;-1:-1:-1;;;;;146194:23:0;;;121144:147;;;;;;;;;;-1:-1:-1;121144:147:0;;;;;:::i;:::-;;:::i;154753:657::-;;;;;;:::i;:::-;;:::i;120685:224::-;;;;;;;;;;;;;:::i;152280:16::-;;;;;;;;;;-1:-1:-1;152280:16:0;;;;-1:-1:-1;;;;;152280:16:0;;;119830:20;;;;;;;;;;-1:-1:-1;119830:20:0;;;;-1:-1:-1;;;;;119830:20:0;;;149143:210;;;;;;;;;;-1:-1:-1;149143:210:0;;;;;:::i;:::-;;:::i;151965:40::-;;;;;;;;;;;;152003:2;151965:40;;152068:41;;;;;;;;;;;;152108:1;152068:41;;119924:42;;;;;;;;;;-1:-1:-1;119924:42:0;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;3331:14:1;;3324:22;3306:41;;3294:2;3279:18;119924:42:0;3166:187:1;121342:211:0;;;;;;;;;;-1:-1:-1;121342:211:0;;;;;:::i;:::-;;:::i;153784:134::-;;;;;;;;;;-1:-1:-1;153784:134:0;;;;;:::i;:::-;;:::i;154096:97::-;;;;;;;;;;;;;:::i;121026:110::-;;;;;;;;;;-1:-1:-1;121026:110:0;;;;;:::i;:::-;;:::i;146296:25::-;;;;;;;;;;-1:-1:-1;146296:25:0;;;;;;;;;;;148908:72;;;;;;;;;;;;;:::i;149361:199::-;;;;;;;;;;-1:-1:-1;149361:199:0;;;;;:::i;:::-;;:::i;119895:20::-;;;;;;;;;;-1:-1:-1;119895:20:0;;;;-1:-1:-1;;;;;119895:20:0;;;146062:28;;;;;;;;;;;;;;;78070:1529;78187:12;78362:4;78356:11;78507:66;78488:17;78481:93;78622:2;78618:1;78599:17;78595:25;78588:37;78703:6;78698:2;78679:17;78675:26;78668:42;79515:2;79512:1;79508:2;79489:17;79486:1;79479:5;79472;79467:51;79031:16;79024:24;79018:2;79000:16;78997:24;78993:1;78989;78983:8;78980:15;78976:46;78973:76;78770:763;78759:774;;;79564:7;79556:35;;;;;;;4072:2:1;79556:35:0;;;4054:21:1;4111:2;4091:18;;;4084:30;4150:17;4130:18;;;4123:45;4185:18;;79556:35:0;;;;;;;;;78176:1423;78070:1529;;;:::o;153407:214::-;153514:46;;;;;152108:1;153514:46;;;4388:25:1;153554:4:0;4429:18:1;;;4422:83;153460:14:0;;;;151451:42;;153514:16;;4361:18:1;;153514:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;153578:35:0;;;;;;;;160:25:1;;;153487:73:0;;-1:-1:-1;152168:42:0;;153578:20;;133:18:1;;153578:35:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;153571:42;;;153407:214;:::o;147687:632::-;147750:16;;;150721:10;-1:-1:-1;;;;;150743:5:0;150721:28;;150717:51;;150758:10;;;;;;;;;;;;;;150717:51;147812:13:::1;147828;:11;:13::i;:::-;147812:29:::0;-1:-1:-1;147856:10:0;147852:29:::1;;147875:6;;;;;;;;;;;;;;147852:29;147894:14;147921:5;147911:7;:15;:33;;147937:7;147911:33;;;147929:5;147911:33;147894:50;;147968:17;147978:6;147968:9;:17::i;:::-;147957:28;;148036:8;148027:6;:17;148023:278;;;148085:8;148076:6;:17;148065:28;;148023:278;;;148130:6;148119:8;:17;148115:186;;;148176:6:::0;148165:17;;::::1;::::0;-1:-1:-1;148176:6:0;148115:186:::1;147801:518;;147687:632:::0;;;;;:::o;149726:223::-;121960:5;;-1:-1:-1;;;;;121960:5:0;121946:10;:19;;;;:42;;-1:-1:-1;121983:5:0;;-1:-1:-1;;;;;121983:5:0;121969:10;:19;;121946:42;121942:69;;;121997:14;;-1:-1:-1;;;121997:14:0;;;;;;;;;;;121942:69;146602:6:::1;149792:18;::::0;::::1;;149788:45;;;149819:14;;;;;;;;;;;;;;149788:45;149857:4;::::0;::::1;149848:13:::0;;::::1;149857:4:::0;;;::::1;;149848:13;149844:40;;;149870:14;;;;;;;;;;;;;;149844:40;149895:4;:12:::0;;;::::1;::::0;::::1;::::0;::::1;::::0;;::::1;::::0;;;::::1;::::0;;;149923:18:::1;::::0;1094:38:1;;;149923:18:0::1;::::0;1082:2:1;1067:18;149923::0::1;;;;;;;;149726:223:::0;:::o;148503:397::-;148557:16;150969:5;;-1:-1:-1;;;;;150969:5:0;150955:10;:19;;;;:42;;-1:-1:-1;150992:5:0;;-1:-1:-1;;;;;150992:5:0;150978:10;:19;;150955:42;:74;;;;-1:-1:-1;151001:10:0;-1:-1:-1;;;;;151023:5:0;151001:28;;;150955:74;150951:101;;;151038:14;;-1:-1:-1;;;151038:14:0;;;;;;;;;;;150951:101;148586:10:::1;:8;:10::i;:::-;148620:30;::::0;;;;148644:4:::1;148620:30;::::0;::::1;865:74:1::0;148620:5:0::1;-1:-1:-1::0;;;;;148620:15:0::1;::::0;::::1;::::0;838:18:1;;148620:30:0::1;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;148667:3;::::0;148609:41;;-1:-1:-1;148667:3:0;;::::1;;;:7:::0;148663:173:::1;;148691:17;148711:23;148725:8;148711:13;:23::i;:::-;148691:43:::0;-1:-1:-1;148749:21:0::1;148691:43:::0;148749:21;::::1;:::i;:::-;148804:8;::::0;148749:21;;-1:-1:-1;148785:39:0::1;::::0;-1:-1:-1;;;;;148785:5:0::1;:18:::0;::::1;::::0;148804:8:::1;148814:9:::0;148785:18:::1;:39::i;:::-;148676:160;148663:173;148848:44;-1:-1:-1::0;;;;;148848:5:0::1;:18;148875:5;148883:8:::0;148848:18:::1;:44::i;:::-;148503:397:::0;:::o;121561:214::-;121960:5;;-1:-1:-1;;;;;121960:5:0;121946:10;:19;;;;:42;;-1:-1:-1;121983:5:0;;-1:-1:-1;;;;;121983:5:0;121969:10;:19;;121946:42;121942:69;;;121997:14;;-1:-1:-1;;;121997:14:0;;;;;;;;;;;121942:69;-1:-1:-1;;;;;121644:23:0;::::1;;::::0;;;:10:::1;:23;::::0;;;;;::::1;;121639:46;;121676:9;;;;;;;;;;;;;;121639:46;-1:-1:-1::0;;;;;121696:23:0;::::1;121722:5;121696:23:::0;;;:10:::1;:23;::::0;;;;;:31;;;::::1;::::0;;121743:24;::::1;::::0;121722:5;121743:24:::1;121561:214:::0;:::o;121144:147::-;121853:5;;-1:-1:-1;;;;;121853:5:0;121839:10;:19;121835:46;;121867:14;;-1:-1:-1;;;121867:14:0;;;;;;;;;;;121835:46;121215:5:::1;::::0;-1:-1:-1;;;;;121215:18:0;;::::1;:5:::0;::::1;:18;121211:44;;;121242:13;;;;;;;;;;;;;;121211:44;121266:5;:17:::0;;;::::1;-1:-1:-1::0;;;;;121266:17:0;;;::::1;::::0;;;::::1;::::0;;121144:147::o;154753:657::-;122094:5;;-1:-1:-1;;;;;122094:5:0;122080:10;:19;;;;:42;;-1:-1:-1;122117:5:0;;-1:-1:-1;;;;;122117:5:0;122103:10;:19;;122080:42;:69;;;;-1:-1:-1;122138:10:0;122127:22;;;;:10;:22;;;;;;;;122126:23;122080:69;122076:96;;;122158:14;;-1:-1:-1;;;122158:14:0;;;;;;;;;;;122076:96;154932:14:::1;154949:13;:11;:13::i;:::-;154932:30;;154975:14;155001:7;154992:6;:16;:35;;155021:6;154992:35;;;155011:7;154992:35;154975:52;;155038:16;155057:25;155075:6;155057:17;:25::i;:::-;155095:41;::::0;;;;152108:1:::1;155095:41;::::0;::::1;5413:25:1::0;5454:18;;;5447:34;;;155038:44:0;;-1:-1:-1;151451:42:0::1;::::0;155095:16:::1;::::0;5386:18:1;;155095:41:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;151351:42;-1:-1:-1::0;;;;;155149:18:0::1;;155175:9;155200:11;152003:2;::::0;155288:10:::1;155314:8;155362:4;155337:31;;;;;;;5641:2:1::0;5637:15;;;;5654:66;5633:88;5621:101;;5747:2;5738:12;;5492:264;155337:31:0::1;;;;;;;;;;;;;155383:8;155149:253;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;154921:489;;;154753:657:::0;;;:::o;120685:224::-;120750:14;;-1:-1:-1;;;;;120750:14:0;120736:10;:28;120732:55;;120773:14;;-1:-1:-1;;;120773:14:0;;;;;;;;;;;120732:55;120816:5;;;120803:31;;120823:10;;-1:-1:-1;;;;;120816:5:0;;;;120803:31;;;120845:5;:18;;;;;;120853:10;120845:18;;;;;120874:27;;;;;;;120685:224::o;149143:210::-;121853:5;;-1:-1:-1;;;;;121853:5:0;121839:10;:19;121835:46;;121867:14;;-1:-1:-1;;;121867:14:0;;;;;;;;;;;121835:46;146363:5:::1;149206:14;::::0;::::1;;149202:41;;;149229:14;;;;;;;;;;;;;;149202:41;149266:3;::::0;::::1;149258:11:::0;;::::1;149266:3:::0;;;::::1;;149258:11;149254:38;;;149278:14;;;;;;;;;;;;;;149254:38;149303:3;:10:::0;;;::::1;::::0;::::1;::::0;::::1;::::0;;::::1;::::0;;;::::1;::::0;;;149329:16:::1;::::0;1094:38:1;;;149329:16:0::1;::::0;1082:2:1;1067:18;149329:16:0::1;950:188:1::0;121342:211:0;121960:5;;-1:-1:-1;;;;;121960:5:0;121946:10;:19;;;;:42;;-1:-1:-1;121983:5:0;;-1:-1:-1;;;;;121983:5:0;121969:10;:19;;121946:42;121942:69;;;121997:14;;-1:-1:-1;;;121997:14:0;;;;;;;;;;;121942:69;-1:-1:-1;;;;;121421:23:0;::::1;;::::0;;;:10:::1;:23;::::0;;;;;::::1;;121417:49;;;121453:13;;;;;;;;;;;;;;121417:49;-1:-1:-1::0;;;;;121477:23:0;::::1;;::::0;;;:10:::1;:23;::::0;;;;;:30;;;::::1;121503:4;121477:30;::::0;;121523:22;::::1;::::0;121477:23;121523:22:::1;121342:211:::0;:::o;153784:134::-;121853:5;;-1:-1:-1;;;;;121853:5:0;121839:10;:19;121835:46;;121867:14;;-1:-1:-1;;;121867:14:0;;;;;;;;;;;121835:46;153846:16:::1;:14;:16::i;:::-;153873:4;:12:::0;;;::::1;-1:-1:-1::0;;;;;153873:12:0;::::1;;::::0;;153896:14:::1;:12;:14::i;:::-;153784:134:::0;:::o;154096:97::-;122094:5;;-1:-1:-1;;;;;122094:5:0;122080:10;:19;;;;:42;;-1:-1:-1;122117:5:0;;-1:-1:-1;;;;;122117:5:0;122103:10;:19;;122080:42;:69;;;;-1:-1:-1;122138:10:0;122127:22;;;;:10;:22;;;;;;;;122126:23;122080:69;122076:96;;;122158:14;;-1:-1:-1;;;122158:14:0;;;;;;;;;;;122076:96;154152:12:::1;:10;:12::i;:::-;154175:10;:8;:10::i;:::-;154096:97::o:0;121026:110::-;121853:5;;-1:-1:-1;;;;;121853:5:0;121839:10;:19;121835:46;;121867:14;;-1:-1:-1;;;121867:14:0;;;;;;;;;;;121835:46;121102:14:::1;:26:::0;;;::::1;-1:-1:-1::0;;;;;121102:26:0;;;::::1;::::0;;;::::1;::::0;;121026:110::o;148908:72::-;150969:5;;-1:-1:-1;;;;;150969:5:0;150955:10;:19;;;;:42;;-1:-1:-1;150992:5:0;;-1:-1:-1;;;;;150992:5:0;150978:10;:19;;150955:42;:74;;;;-1:-1:-1;151001:10:0;-1:-1:-1;;;;;151023:5:0;151001:28;;;150955:74;150951:101;;;151038:14;;-1:-1:-1;;;151038:14:0;;;;;;;;;;;150951:101;148963:9:::1;:7;:9::i;149361:199::-:0;121853:5;;-1:-1:-1;;;;;121853:5:0;121839:10;:19;121835:46;;121867:14;;-1:-1:-1;;;121867:14:0;;;;;;;;;;;121835:46;149448:8:::1;::::0;-1:-1:-1;;;;;149435:21:0;;::::1;149448:8:::0;::::1;149435:21;149431:48;;;149465:14;;;;;;;;;;;;;;149431:48;149490:8;:20:::0;;;::::1;-1:-1:-1::0;;;;;149490:20:0;::::1;::::0;;::::1;::::0;;;149526:26:::1;::::0;::::1;::::0;-1:-1:-1;;149526:26:0::1;149361:199:::0;:::o;79607:1527::-;79723:12;79898:4;79892:11;80043:66;80024:17;80017:93;80158:2;80154:1;80135:17;80131:25;80124:37;80239:6;80234:2;80215:17;80211:26;80204:42;81051:2;81048:1;81044:2;81025:17;81022:1;81015:5;81008;81003:51;80567:16;80560:24;80554:2;80536:16;80533:24;80529:1;80525;80519:8;80516:15;80512:46;80509:76;80306:763;80295:774;;;81100:7;81092:34;;;;;;;8272:2:1;81092:34:0;;;8254:21:1;8311:2;8291:18;;;8284:30;8350:16;8330:18;;;8323:44;8384:18;;81092:34:0;8070:338:1;155531:585:0;155594:16;155623;155642:26;155660:7;155642:17;:26::i;:::-;155623:45;-1:-1:-1;155764:13:0;155760:34;;155779:15;155531:585;;;:::o;155760:34::-;155853:41;;;;;152108:1;155853:41;;;5413:25:1;5454:18;;;5447:34;;;151451:42:0;;155853:16;;5386:18:1;;155853:41:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;155960:64:0;;;;;152003:2;155960:64;;;8613:38:1;8667:18;;;8660:34;;;156018:4:0;8710:18:1;;;8703:83;151351:42:0;;-1:-1:-1;155960:25:0;;-1:-1:-1;8586:18:1;;155960:64:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;155949:75;;156050:27;156069:7;156050:18;:27::i;:::-;156039:8;:38;156035:73;;;156086:22;;;;;;;;160:25:1;;;133:18;;156086:22:0;14:177:1;156035:73:0;155612:504;155531:585;;;:::o;156124:391::-;156257:34;;;;;152108:1;156257:34;;;5413:25:1;156289:1:0;5454:18:1;;;5447:34;151451:42:0;;156257:16;;5386:18:1;;156257:34:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;156328:28:0;;;;;156350:4;156328:28;;;865:74:1;156304:21:0;;-1:-1:-1;151537:42:0;;-1:-1:-1;156328:13:0;;838:18:1;;156328:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;156304:52;-1:-1:-1;156371:18:0;156367:42;;156398:11;;;;;;;;;;;;;;156367:42;156444:4;;:63;;;;;151537:42;156444:63;;;9358:34:1;-1:-1:-1;;;;;156482:5:0;9428:15:1;;9408:18;;;9401:43;9460:18;;;9453:34;;;156444:4:0;9503:18:1;;;9496:34;156444:4:0;;;;:15;;9269:19:1;;156444:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;156162:353;156124:391::o;150545:132::-;150654:3;;150608:7;;150635:34;;:7;;150654:3;;;;;146412:6;150635:18;:34::i;:::-;150628:41;150545:132;-1:-1:-1;;150545:132:0:o;157943:169::-;158010:7;158037:67;152168:42;-1:-1:-1;;;;;158056:19:0;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;152168:42;-1:-1:-1;;;;;158079:22:0;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;158037:7;;:67;:18;:67::i;157701:87::-;157771:4;;157747:33;;151537:42;;-1:-1:-1;;;;;157771:4:0;;157747:15;:33::i;157834:101::-;157902:4;;157878:49;;151537:42;;-1:-1:-1;;;;;157902:4:0;157909:17;157878:15;:49::i;157488:167::-;157530:37;151745:42;151351;157565:1;157530:17;:37::i;:::-;157578:40;152168:42;151451;157616:1;157578:19;:40::i;:::-;157631:16;:14;:16::i;157181:299::-;157267:53;151745:42;151351;157302:17;157267;:53::i;:::-;157389:56;152168:42;151451;157427:17;157389:19;:56::i;:::-;157458:14;:12;:14::i;156523:534::-;156594:30;;;;;156618:4;156594:30;;;865:74:1;156571:20:0;;156594:5;-1:-1:-1;;;;;156594:15:0;;;;838:18:1;;156594:30:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;156571:53;-1:-1:-1;156639:17:0;156635:47;;156665:17;;;;;;;;;;;;;;156635:47;156695:28;;;;;;;;160:25:1;;;151869:42:0;;156695:14;;133:18:1;;156695:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;151745:42;-1:-1:-1;;;;;156734:13:0;;156755:12;156734:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;156783:62:0;;;;;152003:2;156783:62;;;8613:38:1;8667:18;;;8660:34;;;156839:4:0;8710:18:1;;;8703:83;151351:42:0;;-1:-1:-1;156783:19:0;;-1:-1:-1;8586:18:1;;;-1:-1:-1;156783:62:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;156876:32:0;;;;;156902:4;156876:32;;;865:74:1;156858:15:0;;-1:-1:-1;152168:42:0;;-1:-1:-1;156876:17:0;;838:18:1;;156876:32:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;156858:50;;156935:32;156954:12;156935:18;:32::i;:::-;156925:7;:42;156921:76;;;156976:21;;;;;;;;160:25:1;;;133:18;;156976:21:0;14:177:1;156921:76:0;157010:39;;;;;152108:1;157010:39;;;5413:25:1;5454:18;;;5447:34;;;151451:42:0;;157010:15;;5386:18:1;;157010:39:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;156560:497;;156523:534::o;150398:139::-;150512:4;;150466:7;;150493:36;;:7;;150512:4;;;;;146602:6;111083:541;111203:9;111455:1;111442:11;111438:19;111435:1;111432:26;111429:1;111425:34;111418:42;111405:11;111401:60;111391:118;;111492:1;111489;111482:12;111391:118;-1:-1:-1;111583:9:0;;111579:27;;111083:541::o;196:180:1:-;255:6;308:2;296:9;287:7;283:23;279:32;276:52;;;324:1;321;314:12;276:52;-1:-1:-1;347:23:1;;196:180;-1:-1:-1;196:180:1:o;1143:159::-;1210:20;;1270:6;1259:18;;1249:29;;1239:57;;1292:1;1289;1282:12;1239:57;1143:159;;;:::o;1307:184::-;1365:6;1418:2;1406:9;1397:7;1393:23;1389:32;1386:52;;;1434:1;1431;1424:12;1386:52;1457:28;1475:9;1457:28;:::i;:::-;1447:38;1307:184;-1:-1:-1;;;1307:184:1:o;1496:154::-;-1:-1:-1;;;;;1575:5:1;1571:54;1564:5;1561:65;1551:93;;1640:1;1637;1630:12;1655:247;1714:6;1767:2;1755:9;1746:7;1742:23;1738:32;1735:52;;;1783:1;1780;1773:12;1735:52;1822:9;1809:23;1841:31;1866:5;1841:31;:::i;2390:526::-;2493:6;2501;2509;2562:2;2550:9;2541:7;2537:23;2533:32;2530:52;;;2578:1;2575;2568:12;2530:52;2601:28;2619:9;2601:28;:::i;:::-;2591:38;;2676:2;2665:9;2661:18;2648:32;2638:42;;2731:2;2720:9;2716:18;2703:32;2758:18;2750:6;2747:30;2744:50;;;2790:1;2787;2780:12;2744:50;2813:22;;2869:2;2851:16;;;2847:25;2844:45;;;2885:1;2882;2875:12;2844:45;2908:2;2898:12;;;2390:526;;;;;:::o;4516:245::-;4595:6;4603;4656:2;4644:9;4635:7;4631:23;4627:32;4624:52;;;4672:1;4669;4662:12;4624:52;-1:-1:-1;;4695:16:1;;4751:2;4736:18;;;4730:25;4695:16;;4730:25;;-1:-1:-1;4516:245:1:o;4766:184::-;4836:6;4889:2;4877:9;4868:7;4864:23;4860:32;4857:52;;;4905:1;4902;4895:12;4857:52;-1:-1:-1;4928:16:1;;4766:184;-1:-1:-1;4766:184:1:o;4955:279::-;4995:4;5023:1;5020;5017:8;5014:188;;;5058:77;5055:1;5048:88;5159:4;5156:1;5149:15;5187:4;5184:1;5177:15;5014:188;-1:-1:-1;5219:9:1;;4955:279::o;5761:939::-;5863:5;5850:19;5845:3;5838:32;5926:4;5919:5;5915:16;5902:30;5895:4;5890:3;5886:14;5879:54;5820:3;5992:4;5985:5;5981:16;5968:30;6073:66;6065:5;6049:14;6045:26;6041:99;6021:18;6017:124;6007:152;;6155:1;6152;6145:12;6007:152;6183:30;;6236:21;;6280:18;6269:30;;6266:50;;;6312:1;6309;6302:12;6266:50;6359:6;6343:14;6339:27;6332:5;6328:39;6325:59;;;6380:1;6377;6370:12;6325:59;6416:4;6409;6404:3;6400:14;6393:28;6453:6;6446:4;6441:3;6437:14;6430:30;6517:6;6510:4;6501:7;6497:18;6491:3;6486;6482:13;6469:55;6568:1;6562:3;6553:6;6548:3;6544:16;6540:26;6533:37;6690:3;6620:66;6615:2;6607:6;6603:15;6599:88;6594:3;6590:98;6586:108;6579:115;;;;5761:939;;;;:::o;6705:1360::-;7047:4;7076:6;7121:2;7113:6;7109:15;7098:9;7091:34;7144:2;7194;7186:6;7182:15;7177:2;7166:9;7162:18;7155:43;7246:2;7238:6;7234:15;7229:2;7218:9;7214:18;7207:43;-1:-1:-1;;;;;7290:6:1;7286:55;7281:2;7270:9;7266:18;7259:83;7379:6;7373:3;7362:9;7358:19;7351:35;7423:3;7417;7406:9;7402:19;7395:32;7456:6;7450:13;7436:27;;7500:6;7494:3;7483:9;7479:19;7472:35;7525:1;7535:141;7549:6;7546:1;7543:13;7535:141;;;7645:14;;;7641:23;;7635:30;7610:17;;;7629:3;7606:27;7599:67;7564:10;;7535:141;;;7694:6;7691:1;7688:13;7685:92;;;7765:1;7759:3;7750:6;7739:9;7735:22;7731:32;7724:43;7685:92;-1:-1:-1;;7827:2:1;7815:15;7832:66;7811:88;7796:104;;7963:18;;;7919:3;7959:27;;;7953:3;7938:19;;7931:56;8004:55;8047:11;;;8039:6;8004:55;:::i;:::-;7996:63;6705:1360;-1:-1:-1;;;;;;;;;;;6705:1360:1:o

Swarm Source

ipfs://bc727841b2b053a7d6b8a974d8ae96afa0d2e60493a50c2cd966fd54d564cf91

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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