ETH Price: $3,995.22 (+0.06%)
Gas: 0.34 GWei

Contract

0x1E0e64677925eff0A3294A698ca6683d6A99F978
Transaction Hash
Method
Block
From
To
Execute Meta Txn117187562024-12-07 10:54:4417 hrs ago1733568884IN
0x1E0e6467...d6A99F978
0 ETH0.000062590.19095
Execute Meta Txn117118032024-12-07 4:25:0323 hrs ago1733545503IN
0x1E0e6467...d6A99F978
0 ETH0.00003810.05817898
Execute Meta Txn117117822024-12-07 4:23:5823 hrs ago1733545438IN
0x1E0e6467...d6A99F978
0 ETH0.000037340.05590427
Execute Meta Txn117117522024-12-07 4:22:2323 hrs ago1733545343IN
0x1E0e6467...d6A99F978
0 ETH0.00007370.06915205
Execute Meta Txn117117202024-12-07 4:20:4624 hrs ago1733545246IN
0x1E0e6467...d6A99F978
0 ETH0.000033850.06294006
Execute Meta Txn116963932024-12-06 14:45:2037 hrs ago1733496320IN
0x1E0e6467...d6A99F978
0 ETH0.000043060.07722534
Execute Meta Txn116963332024-12-06 14:42:2237 hrs ago1733496142IN
0x1E0e6467...d6A99F978
0 ETH0.000037190.0625
Execute Meta Txn116958972024-12-06 14:20:3638 hrs ago1733494836IN
0x1E0e6467...d6A99F978
0 ETH0.000104550.06
Execute Meta Txn116895302024-12-06 8:49:2143 hrs ago1733474961IN
0x1E0e6467...d6A99F978
0 ETH0.000035840.06653136
Execute Meta Txn116871112024-12-06 6:43:0045 hrs ago1733467380IN
0x1E0e6467...d6A99F978
0 ETH0.000062370.086354
Execute Meta Txn116868662024-12-06 6:29:4645 hrs ago1733466586IN
0x1E0e6467...d6A99F978
0 ETH0.000033840.04807154
Execute Meta Txn116865792024-12-06 6:14:4246 hrs ago1733465682IN
0x1E0e6467...d6A99F978
0 ETH0.000045650.08703049
Execute Meta Txn116815522024-12-06 1:34:272 days ago1733448867IN
0x1E0e6467...d6A99F978
0 ETH0.000030750.06061452
Execute Meta Txn116766522024-12-05 21:14:082 days ago1733433248IN
0x1E0e6467...d6A99F978
0 ETH0.000037870.05356311
Execute Meta Txn116764662024-12-05 21:03:572 days ago1733432637IN
0x1E0e6467...d6A99F978
0 ETH0.000074380.05096988
Execute Meta Txn116623112024-12-05 8:53:202 days ago1733388800IN
0x1E0e6467...d6A99F978
0 ETH0.0000310.05152193
Execute Meta Txn116490662024-12-04 21:19:233 days ago1733347163IN
0x1E0e6467...d6A99F978
0 ETH0.000195770.06377792
Execute Meta Txn116446772024-12-04 17:37:333 days ago1733333853IN
0x1E0e6467...d6A99F978
0 ETH0.00007360.05170016
Execute Meta Txn116409232024-12-04 14:28:323 days ago1733322512IN
0x1E0e6467...d6A99F978
0 ETH0.000040250.05962645
Execute Meta Txn116401712024-12-04 13:50:273 days ago1733320227IN
0x1E0e6467...d6A99F978
0 ETH0.000031620.06774538
Execute Meta Txn116401482024-12-04 13:49:183 days ago1733320158IN
0x1E0e6467...d6A99F978
0 ETH0.000031560.05640595
Execute Meta Txn116401072024-12-04 13:47:133 days ago1733320033IN
0x1E0e6467...d6A99F978
0 ETH0.000030590.05292524
Execute Meta Txn116400972024-12-04 13:46:413 days ago1733320001IN
0x1E0e6467...d6A99F978
0 ETH0.000028890.05479015
Execute Meta Txn116397532024-12-04 13:29:113 days ago1733318951IN
0x1E0e6467...d6A99F978
0 ETH0.000088150.09883761
Execute Meta Txn116397462024-12-04 13:28:493 days ago1733318929IN
0x1E0e6467...d6A99F978
0 ETH0.000114070.09763348
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
117363022024-12-08 4:09:1912 mins ago1733630959
0x1E0e6467...d6A99F978
0.00003392 ETH
117363022024-12-08 4:09:1912 mins ago1733630959
0x1E0e6467...d6A99F978
0.00003392 ETH
117361452024-12-08 3:58:5922 mins ago1733630339
0x1E0e6467...d6A99F978
0.00009731 ETH
117361452024-12-08 3:58:5922 mins ago1733630339
0x1E0e6467...d6A99F978
0.00009731 ETH
117254342024-12-07 17:00:0611 hrs ago1733590806
0x1E0e6467...d6A99F978
0.01782901 ETH
117254342024-12-07 17:00:0611 hrs ago1733590806
0x1E0e6467...d6A99F978
0.00011484 ETH
117254342024-12-07 17:00:0611 hrs ago1733590806
0x1E0e6467...d6A99F978
0.01794385 ETH
117187562024-12-07 10:54:4417 hrs ago1733568884
0x1E0e6467...d6A99F978
0.00114723 ETH
117187562024-12-07 10:54:4417 hrs ago1733568884
0x1E0e6467...d6A99F978
0.00114723 ETH
117118032024-12-07 4:25:0323 hrs ago1733545503
0x1E0e6467...d6A99F978
0.00378337 ETH
117118032024-12-07 4:25:0323 hrs ago1733545503
0x1E0e6467...d6A99F978
0.00008352 ETH
117118032024-12-07 4:25:0323 hrs ago1733545503
0x1E0e6467...d6A99F978
0.0038669 ETH
117117822024-12-07 4:23:5823 hrs ago1733545438
0x1E0e6467...d6A99F978
0.00388219 ETH
117117822024-12-07 4:23:5823 hrs ago1733545438
0x1E0e6467...d6A99F978
0.00008611 ETH
117117822024-12-07 4:23:5823 hrs ago1733545438
0x1E0e6467...d6A99F978
0.00396831 ETH
117117522024-12-07 4:22:2323 hrs ago1733545343
0x1E0e6467...d6A99F978
0.00387351 ETH
117117522024-12-07 4:22:2323 hrs ago1733545343
0x1E0e6467...d6A99F978
0.00015091 ETH
117117522024-12-07 4:22:2323 hrs ago1733545343
0x1E0e6467...d6A99F978
0.00402443 ETH
117117522024-12-07 4:22:2323 hrs ago1733545343
0x1E0e6467...d6A99F978
0.00011845 ETH
117117522024-12-07 4:22:2323 hrs ago1733545343
0x1E0e6467...d6A99F978
0.00011845 ETH
117117202024-12-07 4:20:4624 hrs ago1733545246
0x1E0e6467...d6A99F978
0.003947 ETH
117117202024-12-07 4:20:4624 hrs ago1733545246
0x1E0e6467...d6A99F978
0.00006948 ETH
117117202024-12-07 4:20:4624 hrs ago1733545246
0x1E0e6467...d6A99F978
0.00401649 ETH
116963932024-12-06 14:45:2037 hrs ago1733496320
0x1E0e6467...d6A99F978
0.05264919 ETH
116963932024-12-06 14:45:2037 hrs ago1733496320
0x1E0e6467...d6A99F978
0.00021144 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ScrollSettlerMetaTxn

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 10000 runs

Other Settings:
shanghai EvmVersion
File 1 of 1 : ScrollFlat.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 >=0.8.25 ^0.8.17 ^0.8.25;

// lib/forge-std/src/interfaces/IERC20.sol

/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
    /// is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Moves `amount` tokens from the caller's account to `to`.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` is allowed
    /// to spend on behalf of `owner`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
    /// `amount` is then deducted from the caller's allowance.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token.
    function symbol() external view returns (string memory);

    /// @notice Returns the decimals places of the token.
    function decimals() external view returns (uint8);
}

// lib/permit2/src/interfaces/IEIP712.sol

interface IEIP712 {
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// src/Context.sol

abstract contract AbstractContext {
    function _msgSender() internal view virtual returns (address);

    function _msgData() internal view virtual returns (bytes calldata);

    function _isForwarded() internal view virtual returns (bool);
}

abstract contract Context is AbstractContext {
    function _msgSender() internal view virtual override returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual override returns (bytes calldata) {
        return msg.data;
    }

    function _isForwarded() internal view virtual override returns (bool) {
        return false;
    }
}

// src/IERC721Owner.sol

interface IERC721Owner {
    function ownerOf(uint256) external view returns (address);
}

// src/allowanceholder/IAllowanceHolder.sol

interface IAllowanceHolder {
    /// @notice Executes against `target` with the `data` payload. Prior to execution, token permits
    ///         are temporarily stored for the duration of the transaction. These permits can be
    ///         consumed by the `operator` during the execution
    /// @notice `operator` consumes the funds during its operations by calling back into
    ///         `AllowanceHolder` with `transferFrom`, consuming a token permit.
    /// @dev Neither `exec` nor `transferFrom` check that `token` contains code.
    /// @dev msg.sender is forwarded to target appended to the msg data (similar to ERC-2771)
    /// @param operator An address which is allowed to consume the token permits
    /// @param token The ERC20 token the caller has authorised to be consumed
    /// @param amount The quantity of `token` the caller has authorised to be consumed
    /// @param target A contract to execute operations with `data`
    /// @param data The data to forward to `target`
    /// @return result The returndata from calling `target` with `data`
    /// @notice If calling `target` with `data` reverts, the revert is propagated
    function exec(address operator, address token, uint256 amount, address payable target, bytes calldata data)
        external
        payable
        returns (bytes memory result);

    /// @notice The counterpart to `exec` which allows for the consumption of token permits later
    ///         during execution
    /// @dev *DOES NOT* check that `token` contains code. This function vacuously succeeds if
    ///      `token` is empty.
    /// @dev can only be called by the `operator` previously registered in `exec`
    /// @param token The ERC20 token to transfer
    /// @param owner The owner of tokens to transfer
    /// @param recipient The destination/beneficiary of the ERC20 `transferFrom`
    /// @param amount The quantity of `token` to transfer`
    /// @return true
    function transferFrom(address token, address owner, address recipient, uint256 amount) external returns (bool);
}

// src/core/univ3forks/MetavaultV3.sol

address constant metavaultV3Factory = 0x9367c561915f9D062aFE3b57B18e30dEC62b8488;
uint8 constant metavaultV3ForkId = 17;

// src/core/univ3forks/SushiswapV3.sol

address constant sushiswapV3MainnetFactory = 0xbACEB8eC6b9355Dfc0269C18bac9d6E2Bdc29C4F;
address constant sushiswapV3Factory = 0xc35DADB65012eC5796536bD9864eD8773aBc74C4; // Base, Linea
address constant sushiswapV3ArbitrumFactory = 0x1af415a1EbA07a4986a52B6f2e7dE7003D82231e;
//address constant sushiswapV3AvalancheFactory = 0x3e603C14aF37EBdaD31709C4f848Fc6aD5BEc715;
//address constant sushiswapV3BlastFactory = 0x7680D4B43f3d1d54d6cfEeB2169463bFa7a6cf0d;
//address constant sushiswapV3BnbFactory = 0x126555dd55a39328F69400d6aE4F782Bd4C34ABb;
address constant sushiswapV3OptimismFactory = 0x9c6522117e2ed1fE5bdb72bb0eD5E3f2bdE7DBe0;
address constant sushiswapV3PolygonFactory = 0x917933899c6a5F8E37F31E19f92CdBFF7e8FF0e2;
address constant sushiswapV3ScrollFactory = 0x46B3fDF7b5CDe91Ac049936bF0bDb12c5d22202e;
//bytes32 constant sushiswapV3BlastInitHash = 0x8e13daee7f5a62e37e71bf852bcd44e7d16b90617ed2b17c24c2ee62411c5bae;
uint8 constant sushiswapV3ForkId = 2;

// src/core/univ3forks/UniswapV3.sol

address constant uniswapV3MainnetFactory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address constant uniswapV3SepoliaFactory = 0x0227628f3F023bb0B980b67D528571c95c6DaC1c;
address constant uniswapV3BaseFactory = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
address constant uniswapV3BnbFactory = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7;
address constant uniswapV3AvalancheFactory = 0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD;
address constant uniswapV3BlastFactory = 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd;
address constant uniswapV3ScrollFactory = 0x70C62C8b8e801124A4Aa81ce07b637A3e83cb919;
address constant uniswapV3LineaFactory = 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9;
address constant uniswapV3MantleFactory = 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9;
bytes32 constant uniswapV3InitHash = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
uint8 constant uniswapV3ForkId = 0;

interface IUniswapV3Callback {
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}

// src/core/univ3forks/ZebraV3.sol

address constant zebraV3Factory = 0x96a7F53f7636c93735bf85dE416A4Ace94B56Bd9;
bytes32 constant zebraV3InitHash = 0xcf0b3414328c2bd327a4f093539d0d7d82fb94f893a2965c75cb470289cb5ac7;
uint8 constant zebraV3ForkId = 12;

interface IZebraV3SwapCallback {
    function zebraV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}

// src/utils/FreeMemory.sol

abstract contract FreeMemory {
    modifier DANGEROUS_freeMemory() {
        uint256 freeMemPtr;
        assembly ("memory-safe") {
            freeMemPtr := mload(0x40)
        }
        _;
        assembly ("memory-safe") {
            mstore(0x40, freeMemPtr)
        }
    }
}

// src/utils/Panic.sol

library Panic {
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71) // selector for `Panic(uint256)`
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }

    // https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require
    uint8 internal constant GENERIC = 0x00;
    uint8 internal constant ASSERT_FAIL = 0x01;
    uint8 internal constant ARITHMETIC_OVERFLOW = 0x11;
    uint8 internal constant DIVISION_BY_ZERO = 0x12;
    uint8 internal constant ENUM_CAST = 0x21;
    uint8 internal constant CORRUPT_STORAGE_ARRAY = 0x22;
    uint8 internal constant POP_EMPTY_ARRAY = 0x31;
    uint8 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    uint8 internal constant OUT_OF_MEMORY = 0x41;
    uint8 internal constant ZERO_FUNCTION_POINTER = 0x51;
}

// src/utils/Revert.sol

library Revert {
    function _revert(bytes memory reason) internal pure {
        assembly ("memory-safe") {
            revert(add(reason, 0x20), mload(reason))
        }
    }

    function maybeRevert(bool success, bytes memory reason) internal pure {
        if (!success) {
            _revert(reason);
        }
    }
}

// src/utils/UnsafeMath.sol

library UnsafeMath {
    function unsafeInc(uint256 x) internal pure returns (uint256) {
        unchecked {
            return x + 1;
        }
    }

    function unsafeInc(int256 x) internal pure returns (int256) {
        unchecked {
            return x + 1;
        }
    }

    function unsafeNeg(int256 x) internal pure returns (int256) {
        unchecked {
            return -x;
        }
    }

    function unsafeDiv(uint256 numerator, uint256 denominator) internal pure returns (uint256 quotient) {
        assembly ("memory-safe") {
            quotient := div(numerator, denominator)
        }
    }

    function unsafeDiv(int256 numerator, int256 denominator) internal pure returns (int256 quotient) {
        assembly ("memory-safe") {
            quotient := sdiv(numerator, denominator)
        }
    }

    function unsafeMod(uint256 numerator, uint256 denominator) internal pure returns (uint256 remainder) {
        assembly ("memory-safe") {
            remainder := mod(numerator, denominator)
        }
    }

    function unsafeMod(int256 numerator, int256 denominator) internal pure returns (int256 remainder) {
        assembly ("memory-safe") {
            remainder := smod(numerator, denominator)
        }
    }

    function unsafeMulMod(uint256 a, uint256 b, uint256 m) internal pure returns (uint256 r) {
        assembly ("memory-safe") {
            r := mulmod(a, b, m)
        }
    }

    function unsafeAddMod(uint256 a, uint256 b, uint256 m) internal pure returns (uint256 r) {
        assembly ("memory-safe") {
            r := addmod(a, b, m)
        }
    }
}

// lib/permit2/src/interfaces/ISignatureTransfer.sol

/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
    /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
    /// @param maxAmount The maximum amount a spender can request to transfer
    error InvalidAmount(uint256 maxAmount);

    /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
    /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
    error LengthMismatch();

    /// @notice Emits an event when the owner successfully invalidates an unordered nonce.
    event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);

    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /// @notice The signed permit message for a single token transfer
    struct PermitTransferFrom {
        TokenPermissions permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested amount is greater than the permitted signed amount.
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /// @notice Used to reconstruct the signed permit message for multiple token transfers
    /// @dev Do not need to pass in spender address as it is required that it is msg.sender
    /// @dev Note that a user still signs over a spender address
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address, uint256) external view returns (uint256);

    /// @notice Transfers a token using a signed permit message
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers a token using a signed permit message
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Invalidates the bits specified in mask for the bitmap at the word position
    /// @dev The wordPos is maxed at type(uint248).max
    /// @param wordPos A number to index the nonceBitmap at
    /// @param mask A bitmap masked against msg.sender's current bitmap at the word position
    function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}

// src/core/SettlerErrors.sol

/// @notice Thrown when an offset is not the expected value
error InvalidOffset();

/// @notice Thrown when a validating a target contract to avoid certain types of targets
error ConfusedDeputy();

/// @notice Thrown when a target contract is invalid given the context
error InvalidTarget();

/// @notice Thrown when validating the caller against the expected caller
error InvalidSender();

/// @notice Thrown in cases when using a Trusted Forwarder / AllowanceHolder is not allowed
error ForwarderNotAllowed();

/// @notice Thrown when a signature length is not the expected length
error InvalidSignatureLen();

/// @notice Thrown when a slippage limit is exceeded
error TooMuchSlippage(IERC20 token, uint256 expected, uint256 actual);

/// @notice Thrown when a byte array that is supposed to encode a function from ISettlerActions is
///         not recognized in context.
error ActionInvalid(uint256 i, bytes4 action, bytes data);

/// @notice Thrown when the encoded fork ID as part of UniswapV3 fork path is not on the list of
///         recognized forks for this chain.
error UnknownForkId(uint8 forkId);

/// @notice Thrown when an AllowanceHolder transfer's permit is past its deadline
error SignatureExpired(uint256 deadline);

/// @notice An internal error that should never be thrown. Thrown when a callback reenters the
///         entrypoint and attempts to clobber the existing callback.
error ReentrantCallback(uint256 callbackInt);

/// @notice An internal error that should never be thrown. This error can only be thrown by
///         non-metatx-supporting Settler instances. Thrown when a callback-requiring liquidity
///         source is called, but Settler never receives the callback.
error CallbackNotSpent(uint256 callbackInt);

/// @notice Thrown when a metatransaction has reentrancy.
error ReentrantMetatransaction(bytes32 oldWitness);

/// @notice Thrown when any transaction has reentrancy, not just taker-submitted or metatransaction.
error ReentrantPayer(address oldPayer);

/// @notice An internal error that should never be thrown. Thrown when a metatransaction fails to
///         spend a coupon.
error WitnessNotSpent(bytes32 oldWitness);

/// @notice An internal error that should never be thrown. Thrown when the payer is unset
///         unexpectedly.
error PayerSpent();

// src/vendor/SafeTransferLib.sol

/// @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 {
    uint32 private constant _TRANSFER_FROM_FAILED_SELECTOR = 0x7939f424; // bytes4(keccak256("TransferFromFailed()"))
    uint32 private constant _TRANSFER_FAILED_SELECTOR = 0x90b8ec18; // bytes4(keccak256("TransferFailed()"))
    uint32 private constant _APPROVE_FAILED_SELECTOR = 0x3e3f8f73; // bytes4(keccak256("ApproveFailed()"))

    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address payable to, uint256 amount) internal {
        assembly ("memory-safe") {
            // Transfer the ETH and store if it succeeded or not.
            if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                let freeMemoryPointer := mload(0x40)
                returndatacopy(freeMemoryPointer, 0, returndatasize())
                revert(freeMemoryPointer, returndatasize())
            }
        }
    }

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

    function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
        assembly ("memory-safe") {
            // 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), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // 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.
            if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)) {
                returndatacopy(freeMemoryPointer, 0, returndatasize())
                revert(freeMemoryPointer, returndatasize())
            }
            // We check that the call either returned exactly 1 (can't just be non-zero data), or had no
            // return data.
            if iszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
                mstore(0, _TRANSFER_FROM_FAILED_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    function safeTransfer(IERC20 token, address to, uint256 amount) internal {
        assembly ("memory-safe") {
            // 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), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // 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.
            if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)) {
                returndatacopy(freeMemoryPointer, 0, returndatasize())
                revert(freeMemoryPointer, returndatasize())
            }
            // We check that the call either returned exactly 1 (can't just be non-zero data), or had no
            // return data.
            if iszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
                mstore(0, _TRANSFER_FAILED_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    function safeApprove(IERC20 token, address to, uint256 amount) internal {
        assembly ("memory-safe") {
            // 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), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // 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.
            if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)) {
                returndatacopy(freeMemoryPointer, 0, returndatasize())
                revert(freeMemoryPointer, returndatasize())
            }
            // We check that the call either returned exactly 1 (can't just be non-zero data), or had no
            // return data.
            if iszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
                mstore(0, _APPROVE_FAILED_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    function safeApproveIfBelow(IERC20 token, address spender, uint256 amount) internal {
        uint256 allowance = token.allowance(address(this), spender);
        if (allowance < amount) {
            if (allowance != 0) {
                safeApprove(token, spender, 0);
            }
            safeApprove(token, spender, type(uint256).max);
        }
    }
}

// src/ISettlerActions.sol

interface ISettlerActions {
    /// @dev Transfer funds from msg.sender Permit2.
    function TRANSFER_FROM(address recipient, ISignatureTransfer.PermitTransferFrom memory permit, bytes memory sig)
        external;

    /// @dev Transfer funds from metatransaction requestor into the Settler contract using Permit2. Only for use in `Settler.executeMetaTxn` where the signature is provided as calldata
    function METATXN_TRANSFER_FROM(address recipient, ISignatureTransfer.PermitTransferFrom memory permit) external;

    /// @dev Settle an RfqOrder between maker and taker transfering funds directly between the parties
    // Post-req: Payout if recipient != taker
    function RFQ_VIP(
        address recipient,
        ISignatureTransfer.PermitTransferFrom memory makerPermit,
        address maker,
        bytes memory makerSig,
        ISignatureTransfer.PermitTransferFrom memory takerPermit,
        bytes memory takerSig
    ) external;

    /// @dev Settle an RfqOrder between maker and taker transfering funds directly between the parties for the entire amount
    function METATXN_RFQ_VIP(
        address recipient,
        ISignatureTransfer.PermitTransferFrom memory makerPermit,
        address maker,
        bytes memory makerSig,
        ISignatureTransfer.PermitTransferFrom memory takerPermit
    ) external;

    /// @dev Settle an RfqOrder between Maker and Settler. Transfering funds from the Settler contract to maker.
    /// Retaining funds in the settler contract.
    // Pre-req: Funded
    // Post-req: Payout
    function RFQ(
        address recipient,
        ISignatureTransfer.PermitTransferFrom memory permit,
        address maker,
        bytes memory makerSig,
        address takerToken,
        uint256 maxTakerAmount
    ) external;

    /// @dev Trades against UniswapV3 using the contracts balance for funding
    // Pre-req: Funded
    // Post-req: Payout
    function UNISWAPV3(address recipient, uint256 bps, bytes memory path, uint256 amountOutMin) external;

    /// @dev Trades against UniswapV3 using user funds via Permit2 for funding
    function UNISWAPV3_VIP(
        address recipient,
        bytes memory path,
        ISignatureTransfer.PermitTransferFrom memory permit,
        bytes memory sig,
        uint256 amountOutMin
    ) external;

    function MAKERPSM(address recipient, address gemToken, uint256 bps, address psm, bool buyGem, uint256 amountOutMin)
        external;

    function CURVE_TRICRYPTO_VIP(
        address recipient,
        uint80 poolInfo,
        ISignatureTransfer.PermitTransferFrom memory permit,
        bytes memory sig,
        uint256 minBuyAmount
    ) external;
    function METATXN_CURVE_TRICRYPTO_VIP(
        address recipient,
        uint80 poolInfo,
        ISignatureTransfer.PermitTransferFrom memory permit,
        uint256 minBuyAmount
    ) external;

    function DODOV1(address sellToken, uint256 bps, address pool, bool quoteForBase, uint256 minBuyAmount) external;
    function DODOV2(
        address recipient,
        address sellToken,
        uint256 bps,
        address pool,
        bool quoteForBase,
        uint256 minBuyAmount
    ) external;

    function VELODROME(address recipient, uint256 bps, address pool, uint24 swapInfo, uint256 minBuyAmount) external;

    /// @dev Trades against UniswapV3 using user funds via Permit2 for funding. Metatransaction variant. Signature is over all actions.
    function METATXN_UNISWAPV3_VIP(
        address recipient,
        bytes memory path,
        ISignatureTransfer.PermitTransferFrom memory permit,
        uint256 amountOutMin
    ) external;

    /// @dev Trades against MaverickV2 using the contracts balance for funding
    /// This action does not use the MaverickV2 callback, so it takes an arbitrary pool address to make calls against.
    /// Passing `tokenAIn` as a parameter actually saves gas relative to introspecting the pool's `tokenA()` accessor.
    function MAVERICKV2(
        address recipient,
        address sellToken,
        uint256 bps,
        address pool,
        bool tokenAIn,
        uint256 minBuyAmount
    ) external;
    /// @dev Trades against MaverickV2, spending the taker's coupon inside the callback
    /// This action requires the use of the MaverickV2 callback, so we take the MaverickV2 CREATE2 salt as an argument to derive the pool address from the trusted factory and inithash.
    /// @param salt is formed as `keccak256(abi.encode(feeAIn, feeBIn, tickSpacing, lookback, tokenA, tokenB, kinds, address(0)))`
    function MAVERICKV2_VIP(
        address recipient,
        bytes32 salt,
        bool tokenAIn,
        ISignatureTransfer.PermitTransferFrom memory permit,
        bytes memory sig,
        uint256 minBuyAmount
    ) external;
    /// @dev Trades against MaverickV2, spending the taker's coupon inside the callback; metatransaction variant
    function METATXN_MAVERICKV2_VIP(
        address recipient,
        bytes32 salt,
        bool tokenAIn,
        ISignatureTransfer.PermitTransferFrom memory permit,
        uint256 minBuyAmount
    ) external;

    /// @dev Trades against UniswapV2 using the contracts balance for funding
    /// @param swapInfo is encoded as the upper 16 bits as the fee of the pool in bps, the second
    ///                 lowest bit as "sell token has transfer fee", and the lowest bit as the
    ///                 "token0 for token1" flag.
    function UNISWAPV2(
        address recipient,
        address sellToken,
        uint256 bps,
        address pool,
        uint24 swapInfo,
        uint256 amountOutMin
    ) external;

    function POSITIVE_SLIPPAGE(address recipient, address token, uint256 expectedAmount) external;

    /// @dev Trades against a basic AMM which follows the approval, transferFrom(msg.sender) interaction
    // Pre-req: Funded
    // Post-req: Payout
    function BASIC(address sellToken, uint256 bps, address pool, uint256 offset, bytes calldata data) external;
}

// src/allowanceholder/AllowanceHolderContext.sol

abstract contract AllowanceHolderContext is Context {
    IAllowanceHolder internal constant _ALLOWANCE_HOLDER = IAllowanceHolder(0x0000000000005E88410CcDFaDe4a5EfaE4b49562);

    function _isForwarded() internal view virtual override returns (bool) {
        return super._isForwarded() || super._msgSender() == address(_ALLOWANCE_HOLDER);
    }

    function _msgSender() internal view virtual override returns (address sender) {
        sender = super._msgSender();
        if (sender == address(_ALLOWANCE_HOLDER)) {
            // ERC-2771 like usage where the _trusted_ `AllowanceHolder` has appended the appropriate
            // msg.sender to the msg data
            assembly ("memory-safe") {
                sender := shr(0x60, calldataload(sub(calldatasize(), 0x14)))
            }
        }
    }

    // this is here to avoid foot-guns and make it very explicit that we intend
    // to pass the confused deputy check in AllowanceHolder
    function balanceOf(address) external pure {
        assembly ("memory-safe") {
            mstore8(0x00, 0x00)
            return(0x00, 0x01)
        }
    }
}

// src/utils/AddressDerivation.sol

library AddressDerivation {
    using UnsafeMath for uint256;

    uint256 internal constant _SECP256K1_P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
    uint256 internal constant _SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
    uint256 internal constant SECP256K1_GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
    uint256 internal constant SECP256K1_GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;

    error InvalidCurve(uint256 x, uint256 y);

    // keccak256(abi.encodePacked(ECMUL([x, y], k)))[12:]
    function deriveEOA(uint256 x, uint256 y, uint256 k) internal pure returns (address) {
        if (k == 0) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        if (k >= _SECP256K1_N || x >= _SECP256K1_P || y >= _SECP256K1_P) {
            Panic.panic(Panic.ARITHMETIC_OVERFLOW);
        }

        // +/-7 are neither square nor cube mod p, so we only have to check one
        // coordinate against 0. if it is 0, then the other is too (the point at
        // infinity) or the point is invalid
        if (
            x == 0
                || y.unsafeMulMod(y, _SECP256K1_P)
                    != x.unsafeMulMod(x, _SECP256K1_P).unsafeMulMod(x, _SECP256K1_P).unsafeAddMod(7, _SECP256K1_P)
        ) {
            revert InvalidCurve(x, y);
        }

        unchecked {
            // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384
            return ecrecover(
                bytes32(0), uint8(27 + (y & 1)), bytes32(x), bytes32(UnsafeMath.unsafeMulMod(x, k, _SECP256K1_N))
            );
        }
    }

    // keccak256(RLP([deployer, nonce]))[12:]
    function deriveContract(address deployer, uint64 nonce) internal pure returns (address result) {
        if (nonce == 0) {
            assembly ("memory-safe") {
                mstore(
                    0x00,
                    or(
                        0xd694000000000000000000000000000000000000000080,
                        shl(8, and(0xffffffffffffffffffffffffffffffffffffffff, deployer))
                    )
                )
                result := keccak256(0x09, 0x17)
            }
        } else if (nonce < 0x80) {
            assembly ("memory-safe") {
                // we don't care about dirty bits in `deployer`; they'll be overwritten later
                mstore(0x14, deployer)
                mstore(0x00, 0xd694)
                mstore8(0x34, nonce)
                result := keccak256(0x1e, 0x17)
            }
        } else {
            // compute ceil(log_256(nonce)) + 1
            uint256 nonceLength = 8;
            unchecked {
                if ((uint256(nonce) >> 32) != 0) {
                    nonceLength += 32;
                    if (nonce == type(uint64).max) {
                        Panic.panic(Panic.ARITHMETIC_OVERFLOW);
                    }
                }
                if ((uint256(nonce) >> 8) >= (1 << nonceLength)) {
                    nonceLength += 16;
                }
                if (uint256(nonce) >= (1 << nonceLength)) {
                    nonceLength += 8;
                }
                // ceil
                if ((uint256(nonce) << 8) >= (1 << nonceLength)) {
                    nonceLength += 8;
                }
                // bytes, not bits
                nonceLength >>= 3;
            }
            assembly ("memory-safe") {
                // we don't care about dirty bits in `deployer` or `nonce`. they'll be overwritten later
                mstore(nonceLength, nonce)
                mstore8(0x20, add(0x7f, nonceLength))
                mstore(0x00, deployer)
                mstore8(0x0a, add(0xd5, nonceLength))
                mstore8(0x0b, 0x94)
                result := keccak256(0x0a, add(0x16, nonceLength))
            }
        }
    }

    // keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initHash))[12:]
    function deriveDeterministicContract(address deployer, bytes32 salt, bytes32 initHash)
        internal
        pure
        returns (address result)
    {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // we don't care about dirty bits in `deployer`; they'll be overwritten later
            mstore(ptr, deployer)
            mstore8(add(ptr, 0x0b), 0xff)
            mstore(add(ptr, 0x20), salt)
            mstore(add(ptr, 0x40), initHash)
            result := keccak256(add(ptr, 0x0b), 0x55)
        }
    }
}

// src/vendor/FullMath.sol

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
library FullMath {
    using UnsafeMath for uint256;

    /// @notice 512-bit multiply [prod1 prod0] = a * b
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return prod0 Least significant 256 bits of the product
    /// @return prod1 Most significant 256 bits of the product
    /// @return remainder Remainder of full-precision division
    function _mulDivSetup(uint256 a, uint256 b, uint256 denominator)
        private
        pure
        returns (uint256 prod0, uint256 prod1, uint256 remainder)
    {
        // Compute the product mod 2**256 and mod 2**256 - 1 then use the Chinese
        // Remainder Theorem to reconstruct the 512 bit result. The result is stored
        // in two 256 variables such that product = prod1 * 2**256 + prod0
        assembly ("memory-safe") {
            // Full-precision multiplication
            {
                let mm := mulmod(a, b, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }
            remainder := mulmod(a, b, denominator)
        }
    }

    /// @notice 512-bit by 256-bit division.
    /// @param prod0 Least significant 256 bits of the product
    /// @param prod1 Most significant 256 bits of the product
    /// @param denominator The divisor
    /// @param remainder Remainder of full-precision division
    /// @return The 256-bit result
    /// @dev Overflow and division by zero aren't checked and are GIGO errors
    function _mulDivInvert(uint256 prod0, uint256 prod1, uint256 denominator, uint256 remainder)
        private
        pure
        returns (uint256)
    {
        uint256 inv;
        assembly ("memory-safe") {
            // Make division exact by rounding [prod1 prod0] down to a multiple of
            // denominator
            // Subtract 256 bit number from 512 bit number
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)

            // Factor powers of two out of denominator
            {
                // Compute largest power of two divisor of denominator.
                // Always >= 1.
                let twos := and(sub(0, denominator), denominator)

                // Divide denominator by power of two
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by the factors of two
                prod0 := div(prod0, twos)
                // Shift in bits from prod1 into prod0. For this we need to flip `twos`
                // such that it is 2**256 / twos.
                // If twos is zero, then it becomes one
                twos := add(div(sub(0, twos), twos), 1)
                prod0 := or(prod0, mul(prod1, twos))
            }

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse modulo 2**256
            // such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct correct for
            // four bits. That is, denominator * inv = 1 mod 2**4
            inv := xor(mul(3, denominator), 2)

            // Now use Newton-Raphson iteration to improve the precision.
            // Thanks to Hensel's lifting lemma, this also works in modular
            // arithmetic, doubling the correct bits in each step.
            inv := mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**8
            inv := mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**16
            inv := mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**32
            inv := mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**64
            inv := mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**128
            inv := mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**256
        }

        // Because the division is now exact we can divide by multiplying with the
        // modular inverse of denominator. This will give us the correct result
        // modulo 2**256. Since the precoditions guarantee that the outcome is less
        // than 2**256, this is the final result.  We don't need to compute the high
        // bits of the result and prod1 is no longer required.
        unchecked {
            return prod0 * inv;
        }
    }

    /// @notice Calculates a×b÷denominator with full precision then rounds towards 0. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return The 256-bit result
    function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256) {
        (uint256 prod0, uint256 prod1, uint256 remainder) = _mulDivSetup(a, b, denominator);
        // Make sure the result is less than 2**256.
        // Also prevents denominator == 0
        if (denominator <= prod1) {
            Panic.panic(denominator == 0 ? Panic.DIVISION_BY_ZERO : Panic.ARITHMETIC_OVERFLOW);
        }

        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            return prod0.unsafeDiv(denominator);
        }
        return _mulDivInvert(prod0, prod1, denominator, remainder);
    }

    /// @notice Calculates a×b÷denominator with full precision then rounds towards 0. Overflowing a uint256 or denominator == 0 are GIGO errors
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return The 256-bit result
    function unsafeMulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256) {
        (uint256 prod0, uint256 prod1, uint256 remainder) = _mulDivSetup(a, b, denominator);
        // Overflow and zero-division checks are skipped
        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            return prod0.unsafeDiv(denominator);
        }
        return _mulDivInvert(prod0, prod1, denominator, remainder);
    }
}

// src/core/Permit2PaymentAbstract.sol

abstract contract Permit2PaymentAbstract is AbstractContext {
    string internal constant TOKEN_PERMISSIONS_TYPE = "TokenPermissions(address token,uint256 amount)";

    function _isRestrictedTarget(address) internal view virtual returns (bool);

    function _operator() internal view virtual returns (address);

    function _permitToSellAmount(ISignatureTransfer.PermitTransferFrom memory permit)
        internal
        view
        virtual
        returns (uint256 sellAmount);

    function _permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient)
        internal
        view
        virtual
        returns (ISignatureTransfer.SignatureTransferDetails memory transferDetails, uint256 sellAmount);

    function _transferFromIKnowWhatImDoing(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        address from,
        bytes32 witness,
        string memory witnessTypeString,
        bytes memory sig,
        bool isForwarded
    ) internal virtual;

    function _transferFromIKnowWhatImDoing(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        address from,
        bytes32 witness,
        string memory witnessTypeString,
        bytes memory sig
    ) internal virtual;

    function _transferFrom(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        bytes memory sig,
        bool isForwarded
    ) internal virtual;

    function _transferFrom(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        bytes memory sig
    ) internal virtual;

    function _setOperatorAndCall(
        address target,
        bytes memory data,
        uint32 selector,
        function (bytes calldata) internal returns (bytes memory) callback
    ) internal virtual returns (bytes memory);

    modifier metaTx(address msgSender, bytes32 witness) virtual;

    modifier takerSubmitted() virtual;

    function _allowanceHolderTransferFrom(address token, address owner, address recipient, uint256 amount)
        internal
        virtual;
}

// src/SettlerAbstract.sol

abstract contract SettlerAbstract is Permit2PaymentAbstract {
    // Permit2 Witness for meta transactions
    string internal constant SLIPPAGE_AND_ACTIONS_TYPE =
        "SlippageAndActions(address recipient,address buyToken,uint256 minAmountOut,bytes[] actions)";
    bytes32 internal constant SLIPPAGE_AND_ACTIONS_TYPEHASH =
        0x615e8d716cef7295e75dd3f1f10d679914ad6d7759e8e9459f0109ef75241701;
    uint256 internal constant BASIS = 10_000;

    constructor() {
        assert(SLIPPAGE_AND_ACTIONS_TYPEHASH == keccak256(bytes(SLIPPAGE_AND_ACTIONS_TYPE)));
    }

    function _hasMetaTxn() internal pure virtual returns (bool);

    function _dispatch(uint256 i, bytes4 action, bytes calldata data) internal virtual returns (bool);
}

// src/core/UniswapV2.sol

interface IUniV2Pair {
    function token0() external view returns (address);

    function token1() external view returns (address);

    function getReserves() external view returns (uint112, uint112, uint32);

    function swap(uint256, uint256, address, bytes calldata) external;
}

abstract contract UniswapV2 is SettlerAbstract {
    using UnsafeMath for uint256;

    // bytes4(keccak256("getReserves()"))
    uint32 private constant UNI_PAIR_RESERVES_SELECTOR = 0x0902f1ac;
    // bytes4(keccak256("swap(uint256,uint256,address,bytes)"))
    uint32 private constant UNI_PAIR_SWAP_SELECTOR = 0x022c0d9f;
    // bytes4(keccak256("transfer(address,uint256)"))
    uint32 private constant ERC20_TRANSFER_SELECTOR = 0xa9059cbb;
    // bytes4(keccak256("balanceOf(address)"))
    uint32 private constant ERC20_BALANCEOF_SELECTOR = 0x70a08231;

    /// @dev Sell a token for another token using UniswapV2.
    function sellToUniswapV2(
        address recipient,
        address sellToken,
        uint256 bps,
        address pool,
        uint24 swapInfo,
        uint256 minBuyAmount
    ) internal {
        // Preventing calls to Permit2 or AH is not explicitly required as neither of these contracts implement the `swap` nor `transfer` selector

        // |7|6|5|4|3|2|1|0| - bit positions in swapInfo (uint8)
        // |0|0|0|0|0|0|F|Z| - Z: zeroForOne flag, F: sellTokenHasFee flag
        bool zeroForOne = (swapInfo & 1) == 1; // Extract the least significant bit (bit 0)
        bool sellTokenHasFee = (swapInfo & 2) >> 1 == 1; // Extract the second least significant bit (bit 1) and shift it right
        uint256 feeBps = swapInfo >> 8;

        uint256 sellAmount;
        uint256 buyAmount;
        // If bps is zero we assume there are no funds within this contract, skip the updating sellAmount.
        // This case occurs if the pool is being chained, in which the funds have been sent directly to the pool
        if (bps != 0) {
            // We don't care about phantom overflow here because reserves are
            // limited to 112 bits. Any token balance that would overflow here would
            // also break UniV2.
            // It is *possible* to set `bps` above the basis and therefore
            // cause an overflow on this multiplication. However, `bps` is
            // passed as authenticated calldata, so this is a GIGO error that we
            // do not attempt to fix.
            unchecked {
                sellAmount = (IERC20(sellToken).balanceOf(address(this)) * bps).unsafeDiv(BASIS);
            }
        }
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            // transfer sellAmount (a non zero amount) of sellToken to the pool
            if sellAmount {
                mstore(ptr, ERC20_TRANSFER_SELECTOR)
                mstore(add(ptr, 0x20), pool)
                mstore(add(ptr, 0x40), sellAmount)
                // ...||ERC20_TRANSFER_SELECTOR|pool|sellAmount|
                if iszero(call(gas(), sellToken, 0, add(ptr, 0x1c), 0x44, 0x00, 0x20)) { bubbleRevert(ptr) }
                if iszero(or(iszero(returndatasize()), and(iszero(lt(returndatasize(), 0x20)), eq(mload(0x00), 1)))) {
                    revert(0, 0)
                }
            }

            // get pool reserves
            let sellReserve
            let buyReserve
            mstore(0x00, UNI_PAIR_RESERVES_SELECTOR)
            // ||UNI_PAIR_RESERVES_SELECTOR|
            if iszero(staticcall(gas(), pool, 0x1c, 0x04, 0x00, 0x40)) { bubbleRevert(ptr) }
            if lt(returndatasize(), 0x40) { revert(0, 0) }
            {
                let r := shl(5, zeroForOne)
                buyReserve := mload(r)
                sellReserve := mload(xor(0x20, r))
            }

            // Update the sell amount in the following cases:
            //   the funds are in the pool already (flagged by sellAmount being 0)
            //   the sell token has a fee (flagged by sellTokenHasFee)
            if or(iszero(sellAmount), sellTokenHasFee) {
                // retrieve the sellToken balance of the pool
                mstore(0x00, ERC20_BALANCEOF_SELECTOR)
                mstore(0x20, and(0xffffffffffffffffffffffffffffffffffffffff, pool))
                // ||ERC20_BALANCEOF_SELECTOR|pool|
                if iszero(staticcall(gas(), sellToken, 0x1c, 0x24, 0x00, 0x20)) { bubbleRevert(ptr) }
                if lt(returndatasize(), 0x20) { revert(0, 0) }
                let bal := mload(0x00)

                // determine real sellAmount by comparing pool's sellToken balance to reserve amount
                if lt(bal, sellReserve) {
                    mstore(0x00, 0x4e487b71) // selector for `Panic(uint256)`
                    mstore(0x20, 0x11) // panic code for arithmetic underflow
                    revert(0x1c, 0x24)
                }
                sellAmount := sub(bal, sellReserve)
            }

            // compute buyAmount based on sellAmount and reserves
            let sellAmountWithFee := mul(sellAmount, sub(10000, feeBps))
            buyAmount := div(mul(sellAmountWithFee, buyReserve), add(sellAmountWithFee, mul(sellReserve, 10000)))
            let swapCalldata := add(ptr, 0x1c)
            // set up swap call selector and empty callback data
            mstore(ptr, UNI_PAIR_SWAP_SELECTOR)
            mstore(add(ptr, 0x80), 0x80) // offset to length of data
            mstore(add(ptr, 0xa0), 0) // length of data

            // set amount0Out and amount1Out
            {
                // If `zeroForOne`, offset is 0x24, else 0x04
                let offset := add(0x04, shl(5, zeroForOne))
                mstore(add(swapCalldata, offset), buyAmount)
                mstore(add(swapCalldata, xor(0x20, offset)), 0)
            }

            mstore(add(swapCalldata, 0x44), and(0xffffffffffffffffffffffffffffffffffffffff, recipient))
            // ...||UNI_PAIR_SWAP_SELECTOR|amount0Out|amount1Out|recipient|data|

            // perform swap at the pool sending bought tokens to the recipient
            if iszero(call(gas(), pool, 0, swapCalldata, 0xa4, 0, 0)) { bubbleRevert(swapCalldata) }

            // revert with the return data from the most recent call
            function bubbleRevert(p) {
                returndatacopy(p, 0, returndatasize())
                revert(p, returndatasize())
            }
        }
        if (buyAmount < minBuyAmount) {
            revert TooMuchSlippage(
                IERC20(zeroForOne ? IUniV2Pair(pool).token1() : IUniV2Pair(pool).token0()), minBuyAmount, buyAmount
            );
        }
    }
}

// src/core/Velodrome.sol

interface IVelodromePair {
    function metadata()
        external
        view
        returns (
            uint256 basis0,
            uint256 basis1,
            uint256 reserve0,
            uint256 reserve1,
            bool stable,
            IERC20 token0,
            IERC20 token1
        );
    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}

abstract contract Velodrome is SettlerAbstract {
    using UnsafeMath for uint256;
    using SafeTransferLib for IERC20;

    uint256 private constant _BASIS = 1 ether;

    // This is the `k = x^3 * y + y^3 * x` constant function
    function _k(uint256 x, uint256 y) private pure returns (uint256) {
        unchecked {
            return _k(x, y, x * x / _BASIS);
        }
    }

    function _k(uint256 x, uint256 y, uint256 x_squared) private pure returns (uint256) {
        unchecked {
            return _k(x, y, x_squared, y * y / _BASIS);
        }
    }

    function _k(uint256 x, uint256 y, uint256 x_squared, uint256 y_squared) private pure returns (uint256) {
        unchecked {
            return x * y / _BASIS * (x_squared + y_squared) / _BASIS;
        }
    }

    // For numerically approximating a solution to the `k = x^3 * y + y^3 * x` constant function
    // using Newton-Raphson, this is `∂k/∂y = 3 * x * y^2 + x^3`.
    function _d(uint256 y, uint256 three_x0, uint256 x0_cubed) private pure returns (uint256) {
        unchecked {
            return _d(y, three_x0, x0_cubed, y * y / _BASIS);
        }
    }

    function _d(uint256, uint256 three_x0, uint256 x0_cubed, uint256 y_squared) private pure returns (uint256) {
        unchecked {
            return y_squared * three_x0 / _BASIS + x0_cubed;
        }
    }

    error NotConverged();

    // Using Newton-Raphson iterations, compute the smallest `new_y` such that `_k(x0, new_y) >=
    // xy`. As a function of `y`, we find the root of `_k(x0, y) - xy`.
    function _get_y(uint256 x0, uint256 xy, uint256 y) private pure returns (uint256) {
        unchecked {
            uint256 three_x0 = 3 * x0;
            uint256 x0_squared = x0 * x0 / _BASIS;
            uint256 x0_cubed = x0_squared * x0 / _BASIS;
            for (uint256 i; i < 255; i++) {
                uint256 y_squared = y * y / _BASIS;
                uint256 k = _k(x0, y, x0_squared, y_squared);
                if (k < xy) {
                    // there are two cases where dy == 0
                    // case 1: The y is converged and we find the correct answer
                    // case 2: _d(x0, y) is too large compare to (xy - k) and the rounding error
                    //         screwed us.
                    //         In this case, we need to increase y by 1
                    uint256 dy = ((xy - k) * _BASIS).unsafeDiv(_d(y, three_x0, x0_cubed, y_squared));
                    if (dy == 0) {
                        if (k == xy) {
                            // We found the correct answer. Return y
                            return y;
                        }
                        if (_k(x0, y + 1, x0_squared) > xy) {
                            // If _k(x0, y + 1) > xy, then we are close to the correct answer.
                            // There's no closer answer than y + 1
                            return y + 1;
                        }
                        dy = 1;
                    }
                    y += dy;
                } else {
                    uint256 dy = ((k - xy) * _BASIS).unsafeDiv(_d(y, three_x0, x0_cubed, y_squared));
                    if (dy == 0) {
                        if (k == xy || _k(x0, y - 1, x0_squared) < xy) {
                            // Likewise, if k == xy, we found the correct answer.
                            // If _k(x0, y - 1) < xy, then we are close to the correct answer.
                            // There's no closer answer than "y"
                            // It's worth mentioning that we need to find y where _k(x0, y) >= xy
                            // As a result, we can't return y - 1 even it's closer to the correct answer
                            return y;
                        }
                        dy = 1;
                    }
                    y -= dy;
                }
            }
            revert NotConverged();
        }
    }

    function sellToVelodrome(address recipient, uint256 bps, IVelodromePair pair, uint24 swapInfo, uint256 minAmountOut)
        internal
    {
        // Preventing calls to Permit2 or AH is not explicitly required as neither of these contracts implement the `swap` nor `transfer` selector

        // |7|6|5|4|3|2|1|0| - bit positions in swapInfo (uint8)
        // |0|0|0|0|0|0|F|Z| - Z: zeroForOne flag, F: sellTokenHasFee flag
        bool zeroForOne = (swapInfo & 1) == 1; // Extract the least significant bit (bit 0)
        bool sellTokenHasFee = (swapInfo & 2) >> 1 == 1; // Extract the second least significant bit (bit 1) and shift it right
        uint256 feeBps = swapInfo >> 8;

        (
            uint256 sellBasis,
            uint256 buyBasis,
            uint256 sellReserve,
            uint256 buyReserve,
            bool stable,
            IERC20 sellToken,
            IERC20 buyToken
        ) = pair.metadata();
        assert(stable);
        if (!zeroForOne) {
            (sellBasis, buyBasis, sellReserve, buyReserve, sellToken, buyToken) =
                (buyBasis, sellBasis, buyReserve, sellReserve, buyToken, sellToken);
        }

        uint256 buyAmount;
        unchecked {
            // Compute sell amount in native units
            uint256 sellAmount;
            if (bps != 0) {
                // It must be possible to square the sell token balance of the pool, otherwise it
                // will revert with an overflow. Therefore, it can't be so large that multiplying by
                // a "reasonable" `bps` value could overflow. We don't care to protect against
                // unreasonable `bps` values because that just means the taker is griefing themself.
                sellAmount = (sellToken.balanceOf(address(this)) * bps).unsafeDiv(BASIS);
            }
            if (sellAmount != 0) {
                sellToken.safeTransfer(address(pair), sellAmount);
            }
            if (sellAmount == 0 || sellTokenHasFee) {
                sellAmount = sellToken.balanceOf(address(pair)) - sellReserve;
            }
            // Apply the fee
            sellAmount -= sellAmount * feeBps / 10_000; // can't overflow

            // Convert everything from native units to `_BASIS`
            sellReserve = (sellReserve * _BASIS).unsafeDiv(sellBasis);
            buyReserve = (buyReserve * _BASIS).unsafeDiv(buyBasis);
            sellAmount = (sellAmount * _BASIS).unsafeDiv(sellBasis);

            // Solve the constant function numerically to get `buyAmount` from `sellAmount`
            buyAmount = buyReserve - _get_y(sellAmount + sellReserve, _k(sellReserve, buyReserve), buyReserve);

            // Convert `buyAmount` from `_BASIS` to native units
            buyAmount = buyAmount * buyBasis / _BASIS;
        }
        if (buyAmount < minAmountOut) {
            revert TooMuchSlippage(sellToken, minAmountOut, buyAmount);
        }

        {
            (uint256 buyAmount0, uint256 buyAmount1) = zeroForOne ? (uint256(0), buyAmount) : (buyAmount, uint256(0));
            pair.swap(buyAmount0, buyAmount1, recipient, new bytes(0));
        }
    }
}

// src/core/RfqOrderSettlement.sol

abstract contract RfqOrderSettlement is SettlerAbstract {
    using SafeTransferLib for IERC20;
    using FullMath for uint256;

    struct Consideration {
        IERC20 token;
        uint256 amount;
        address counterparty;
        bool partialFillAllowed;
    }

    string internal constant CONSIDERATION_TYPE =
        "Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)";
    // `string.concat` isn't recognized by solc as compile-time constant, but `abi.encodePacked` is
    string internal constant CONSIDERATION_WITNESS =
        string(abi.encodePacked("Consideration consideration)", CONSIDERATION_TYPE, TOKEN_PERMISSIONS_TYPE));
    bytes32 internal constant CONSIDERATION_TYPEHASH =
        0x7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa;

    string internal constant RFQ_ORDER_TYPE =
        "RfqOrder(Consideration makerConsideration,Consideration takerConsideration)";
    string internal constant RFQ_ORDER_TYPE_RECURSIVE = string(abi.encodePacked(RFQ_ORDER_TYPE, CONSIDERATION_TYPE));
    bytes32 internal constant RFQ_ORDER_TYPEHASH = 0x49fa719b76f0f6b7e76be94b56c26671a548e1c712d5b13dc2874f70a7598276;

    function _hashConsideration(Consideration memory consideration) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let ptr := sub(consideration, 0x20)
            let oldValue := mload(ptr)
            mstore(ptr, CONSIDERATION_TYPEHASH)
            result := keccak256(ptr, 0xa0)
            mstore(ptr, oldValue)
        }
    }

    function _logRfqOrder(bytes32 makerConsiderationHash, bytes32 takerConsiderationHash, uint128 makerFilledAmount)
        private
    {
        assembly ("memory-safe") {
            mstore(0x00, RFQ_ORDER_TYPEHASH)
            mstore(0x20, makerConsiderationHash)
            let ptr := mload(0x40)
            mstore(0x40, takerConsiderationHash)
            let orderHash := keccak256(0x00, 0x60)
            mstore(0x40, ptr)
            mstore(0x10, makerFilledAmount)
            mstore(0x00, orderHash)
            log0(0x00, 0x30)
        }
    }

    constructor() {
        assert(CONSIDERATION_TYPEHASH == keccak256(bytes(CONSIDERATION_TYPE)));
        assert(RFQ_ORDER_TYPEHASH == keccak256(bytes(RFQ_ORDER_TYPE_RECURSIVE)));
    }

    /// @dev Settle an RfqOrder between maker and taker transfering funds directly between the counterparties. Either
    ///      two Permit2 signatures are consumed, with the maker Permit2 containing a witness of the RfqOrder, or
    ///      AllowanceHolder is supported for the taker payment. The Maker has signed the same order as the
    ///      Taker. Submission may be directly by the taker or via a third party with the Taker signing a witness.
    /// @dev if used, the taker's witness is not calculated nor verified here as calling function is trusted
    function fillRfqOrderVIP(
        address recipient,
        ISignatureTransfer.PermitTransferFrom memory makerPermit,
        address maker,
        bytes memory makerSig,
        ISignatureTransfer.PermitTransferFrom memory takerPermit,
        bytes memory takerSig
    ) internal {
        assert(makerPermit.permitted.amount <= type(uint256).max - BASIS);
        (ISignatureTransfer.SignatureTransferDetails memory makerTransferDetails, uint256 makerAmount) =
            _permitToTransferDetails(makerPermit, recipient);
        (ISignatureTransfer.SignatureTransferDetails memory takerTransferDetails, uint256 takerAmount) =
            _permitToTransferDetails(takerPermit, maker);

        bytes32 witness = _hashConsideration(
            Consideration({
                token: IERC20(takerPermit.permitted.token),
                amount: takerAmount,
                counterparty: _msgSender(),
                partialFillAllowed: false
            })
        );
        _transferFrom(takerPermit, takerTransferDetails, takerSig);
        _transferFromIKnowWhatImDoing(
            makerPermit, makerTransferDetails, maker, witness, CONSIDERATION_WITNESS, makerSig, false
        );

        _logRfqOrder(
            witness,
            _hashConsideration(
                Consideration({
                    token: IERC20(makerPermit.permitted.token),
                    amount: makerAmount,
                    counterparty: maker,
                    partialFillAllowed: false
                })
            ),
            uint128(makerAmount)
        );
    }

    /// @dev Settle an RfqOrder between maker and Settler retaining funds in this contract.
    /// @dev pre-condition: msgSender has been authenticated against the requestor
    /// One Permit2 signature is consumed, with the maker Permit2 containing a witness of the RfqOrder.
    // In this variant, Maker pays recipient and Settler pays Maker
    function fillRfqOrderSelfFunded(
        address recipient,
        ISignatureTransfer.PermitTransferFrom memory permit,
        address maker,
        bytes memory makerSig,
        IERC20 takerToken,
        uint256 maxTakerAmount
    ) internal {
        assert(permit.permitted.amount <= type(uint256).max - BASIS);
        // Compute witnesses. These are based on the quoted maximum amounts. We will modify them
        // later to adjust for the actual settled amount, which may be modified by encountered
        // slippage.
        (ISignatureTransfer.SignatureTransferDetails memory transferDetails, uint256 makerAmount) =
            _permitToTransferDetails(permit, recipient);

        bytes32 takerWitness = _hashConsideration(
            Consideration({
                token: IERC20(permit.permitted.token),
                amount: makerAmount,
                counterparty: maker,
                partialFillAllowed: true
            })
        );
        bytes32 makerWitness = _hashConsideration(
            Consideration({
                token: takerToken,
                amount: maxTakerAmount,
                counterparty: _msgSender(),
                partialFillAllowed: true
            })
        );

        // Now we adjust the transfer amounts to compensate for encountered slippage. Rounding is
        // performed in the maker's favor.
        uint256 takerAmount = takerToken.balanceOf(address(this));
        if (takerAmount > maxTakerAmount) {
            takerAmount = maxTakerAmount;
        }
        transferDetails.requestedAmount = makerAmount = makerAmount.unsafeMulDiv(takerAmount, maxTakerAmount);

        // Now that we have all the relevant information, make the transfers and log the order.
        takerToken.safeTransfer(maker, takerAmount);
        _transferFromIKnowWhatImDoing(
            permit, transferDetails, maker, makerWitness, CONSIDERATION_WITNESS, makerSig, false
        );

        _logRfqOrder(makerWitness, takerWitness, uint128(makerAmount));
    }
}

// src/core/DodoV2.sol

interface IDodoV2 {
    function sellBase(address to) external returns (uint256 receiveQuoteAmount);
    function sellQuote(address to) external returns (uint256 receiveBaseAmount);

    function _BASE_TOKEN_() external view returns (IERC20);
    function _QUOTE_TOKEN_() external view returns (IERC20);
}

abstract contract DodoV2 is SettlerAbstract {
    using FullMath for uint256;
    using SafeTransferLib for IERC20;

    function sellToDodoV2(
        address recipient,
        IERC20 sellToken,
        uint256 bps,
        IDodoV2 dodo,
        bool quoteForBase,
        uint256 minBuyAmount
    ) internal returns (uint256 buyAmount) {
        if (bps != 0) {
            uint256 sellAmount = sellToken.balanceOf(address(this)).mulDiv(bps, BASIS);
            sellToken.safeTransfer(address(dodo), sellAmount);
        }
        if (quoteForBase) {
            buyAmount = dodo.sellQuote(recipient);
            if (buyAmount < minBuyAmount) {
                revert TooMuchSlippage(dodo._BASE_TOKEN_(), minBuyAmount, buyAmount);
            }
        } else {
            buyAmount = dodo.sellBase(recipient);
            if (buyAmount < minBuyAmount) {
                revert TooMuchSlippage(dodo._QUOTE_TOKEN_(), minBuyAmount, buyAmount);
            }
        }
    }
}

// src/core/MaverickV2.sol

// Maverick AMM V2 is not open-source. The source code was disclosed to the
// developers of 0x Settler confidentially and recompiled privately. The
// deployed bytecode inithash matches the privately recompiled inithash.
bytes32 constant maverickV2InitHash = 0xbb7b783eb4b8ca46925c5384a6b9919df57cb83da8f76e37291f58d0dd5c439a;

// https://docs.mav.xyz/technical-reference/contract-addresses/v2-contract-addresses
// For chains: mainnet, base, bnb, arbitrum, scroll, sepolia
address constant maverickV2Factory = 0x0A7e848Aca42d879EF06507Fca0E7b33A0a63c1e;

interface IMaverickV2Pool {
    /**
     * @notice Parameters for swap.
     * @param amount Amount of the token that is either the input if exactOutput is false
     * or the output if exactOutput is true.
     * @param tokenAIn Boolean indicating whether tokenA is the input.
     * @param exactOutput Boolean indicating whether the amount specified is
     * the exact output amount (true).
     * @param tickLimit The furthest tick a swap will execute in. If no limit
     * is desired, value should be set to type(int32).max for a tokenAIn swap
     * and type(int32).min for a swap where tokenB is the input.
     */
    struct SwapParams {
        uint256 amount;
        bool tokenAIn;
        bool exactOutput;
        int32 tickLimit;
    }

    /**
     * @notice Swap tokenA/tokenB assets in the pool.  The swap user has two
     * options for funding their swap.
     * - The user can push the input token amount to the pool before calling
     * the swap function. In order to avoid having the pool call the callback,
     * the user should pass a zero-length `data` bytes object with the swap
     * call.
     * - The user can send the input token amount to the pool when the pool
     * calls the `maverickV2SwapCallback` function on the calling contract.
     * That callback has input parameters that specify the token address of the
     * input token, the input and output amounts, and the bytes data sent to
     * the swap function.
     * @dev  If the users elects to do a callback-based swap, the output
     * assets will be sent before the callback is called, allowing the user to
     * execute flash swaps.  However, the pool does have reentrancy protection,
     * so a swapper will not be able to interact with the same pool again
     * while they are in the callback function.
     * @param recipient The address to receive the output tokens.
     * @param params Parameters containing the details of the swap
     * @param data Bytes information that gets passed to the callback.
     */
    function swap(address recipient, SwapParams calldata params, bytes calldata data)
        external
        returns (uint256 amountIn, uint256 amountOut);

    /**
     * @notice Pool tokenA.  Address of tokenA is such that tokenA < tokenB.
     */
    function tokenA() external view returns (IERC20);

    /**
     * @notice Pool tokenB.
     */
    function tokenB() external view returns (IERC20);

    /**
     * @notice State of the pool.
     * @param reserveA Pool tokenA balanceOf at end of last operation
     * @param reserveB Pool tokenB balanceOf at end of last operation
     * @param lastTwaD8 Value of log time weighted average price at last block.
     * Value is 8-decimal scale and is in the fractional tick domain.  E.g. a
     * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th
     * tick.
     * @param lastLogPriceD8 Value of log price at last block. Value is
     * 8-decimal scale and is in the fractional tick domain.  E.g. a value of
     * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.
     * @param lastTimestamp Last block.timestamp value in seconds for latest
     * swap transaction.
     * @param activeTick Current tick position that contains the active bins.
     * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values
     * defined in Pool.sol.
     * @param binCounter Index of the last bin created.
     * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the
     * protocol.
     */
    struct State {
        uint128 reserveA;
        uint128 reserveB;
        int64 lastTwaD8;
        int64 lastLogPriceD8;
        uint40 lastTimestamp;
        int32 activeTick;
        bool isLocked;
        uint32 binCounter;
        uint8 protocolFeeRatioD3;
    }

    /**
     * @notice External function to get the state of the pool.
     */
    function getState() external view returns (State memory);
}

interface IMaverickV2SwapCallback {
    function maverickV2SwapCallback(IERC20 tokenIn, uint256 amountIn, uint256 amountOut, bytes calldata data)
        external;
}

abstract contract MaverickV2 is SettlerAbstract {
    using UnsafeMath for uint256;
    using SafeTransferLib for IERC20;

    function _encodeSwapCallback(ISignatureTransfer.PermitTransferFrom memory permit, bytes memory sig)
        internal
        view
        returns (bytes memory result)
    {
        bool isForwarded = _isForwarded();
        assembly ("memory-safe") {
            function mcopy(dst, src, len) {
                if or(xor(returndatasize(), len), iszero(staticcall(gas(), 0x04, src, len, dst, len))) { invalid() }
            }

            result := mload(0x40)
            mcopy(add(0x20, result), mload(permit), 0x40)
            mcopy(add(0x60, result), add(0x20, permit), 0x40)
            mstore8(add(0xa0, result), isForwarded)
            let sigLength := mload(sig)
            mcopy(add(0xa1, result), add(0x20, sig), sigLength)
            mstore(result, add(0x81, sigLength))
            mstore(0x40, add(sigLength, add(0xa1, result)))
        }
    }

    function sellToMaverickV2VIP(
        address recipient,
        bytes32 salt,
        bool tokenAIn,
        ISignatureTransfer.PermitTransferFrom memory permit,
        bytes memory sig,
        uint256 minBuyAmount
    ) internal returns (uint256 buyAmount) {
        bytes memory swapCallbackData = _encodeSwapCallback(permit, sig);
        address pool = AddressDerivation.deriveDeterministicContract(maverickV2Factory, salt, maverickV2InitHash);
        (, buyAmount) = abi.decode(
            _setOperatorAndCall(
                pool,
                abi.encodeCall(
                    IMaverickV2Pool.swap,
                    (
                        recipient,
                        IMaverickV2Pool.SwapParams({
                            amount: _permitToSellAmount(permit),
                            tokenAIn: tokenAIn,
                            exactOutput: false,
                            // TODO: actually set a tick limit so that we can partial fill
                            tickLimit: tokenAIn ? type(int32).max : type(int32).min
                        }),
                        swapCallbackData
                    )
                ),
                uint32(IMaverickV2SwapCallback.maverickV2SwapCallback.selector),
                _maverickV2Callback
            ),
            (uint256, uint256)
        );
        if (buyAmount < minBuyAmount) {
            IERC20 buyToken = tokenAIn ? IMaverickV2Pool(pool).tokenB() : IMaverickV2Pool(pool).tokenA();
            revert TooMuchSlippage(buyToken, minBuyAmount, buyAmount);
        }
    }

    function sellToMaverickV2(
        address recipient,
        IERC20 sellToken,
        uint256 bps,
        IMaverickV2Pool pool,
        bool tokenAIn,
        uint256 minBuyAmount
    ) internal returns (uint256 buyAmount) {
        uint256 sellAmount;
        if (bps != 0) {
            unchecked {
                // We don't care about phantom overflow here because reserves
                // are limited to 128 bits. Any token balance that would
                // overflow here would also break MaverickV2.
                sellAmount = (sellToken.balanceOf(address(this)) * bps).unsafeDiv(BASIS);
            }
        }
        if (sellAmount == 0) {
            sellAmount = sellToken.balanceOf(address(pool));
            IMaverickV2Pool.State memory poolState = pool.getState();
            unchecked {
                sellAmount -= tokenAIn ? poolState.reserveA : poolState.reserveB;
            }
        } else {
            sellToken.safeTransfer(address(pool), sellAmount);
        }
        (, buyAmount) = pool.swap(
            recipient,
            IMaverickV2Pool.SwapParams({
                amount: sellAmount,
                tokenAIn: tokenAIn,
                exactOutput: false,
                // TODO: actually set a tick limit so that we can partial fill
                tickLimit: tokenAIn ? type(int32).max : type(int32).min
            }),
            new bytes(0)
        );
        if (buyAmount < minBuyAmount) {
            revert TooMuchSlippage(tokenAIn ? pool.tokenB() : pool.tokenA(), minBuyAmount, buyAmount);
        }
    }

    function _maverickV2Callback(bytes calldata data) private returns (bytes memory) {
        require(data.length >= 0xa0);
        IERC20 tokenIn;
        uint256 amountIn;
        assembly ("memory-safe") {
            // we don't bother checking for dirty bits because we trust the
            // initcode (by its hash) to produce well-behaved bytecode that
            // produces strict ABI-encoded calldata
            tokenIn := calldataload(data.offset)
            amountIn := calldataload(add(0x20, data.offset))
            // likewise, we don't bother to perform the indirection to find the
            // nested data. we just index directly to it because we know that
            // the pool follows strict ABI encoding
            data.length := calldataload(add(0x80, data.offset))
            data.offset := add(0xa0, data.offset)
        }
        maverickV2SwapCallback(
            tokenIn,
            amountIn,
            // forgefmt: disable-next-line
            0 /* we didn't bother loading `amountOut` because we don't use it */,
            data
        );
        return new bytes(0);
    }

    // forgefmt: disable-next-line
    function maverickV2SwapCallback(IERC20 tokenIn, uint256 amountIn, uint256 /* amountOut */, bytes calldata data)
        private
    {
        ISignatureTransfer.PermitTransferFrom calldata permit;
        bool isForwarded;
        assembly ("memory-safe") {
            permit := data.offset
            isForwarded := and(0x01, calldataload(add(0x61, data.offset)))
            data.offset := add(0x81, data.offset)
            data.length := sub(data.length, 0x81)
        }
        assert(tokenIn == IERC20(permit.permitted.token));
        ISignatureTransfer.SignatureTransferDetails memory transferDetails =
            ISignatureTransfer.SignatureTransferDetails({to: msg.sender, requestedAmount: amountIn});
        _transferFrom(permit, transferDetails, data, isForwarded);
    }
}

// src/core/UniswapV3Fork.sol

interface IUniswapV3Pool {
    /// @notice Swap token0 for token1, or token1 for token0
    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
    /// @param recipient The address to receive the output of the swap
    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive),
    /// or exact output (negative)
    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
    /// @param data Any data to be passed through to the callback
    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);
}

abstract contract UniswapV3Fork is SettlerAbstract {
    using UnsafeMath for uint256;
    using SafeTransferLib for IERC20;

    /// @dev Minimum size of an encoded swap path:
    ///      sizeof(address(inputToken) | uint8(forkId) | uint24(poolId) | address(outputToken))
    uint256 private constant SINGLE_HOP_PATH_SIZE = 0x2c;
    /// @dev How many bytes to skip ahead in an encoded path to start at the next hop:
    ///      sizeof(address(inputToken) | uint8(forkId) | uint24(poolId))
    uint256 private constant PATH_SKIP_HOP_SIZE = 0x18;
    /// @dev The size of the swap callback prefix data before the Permit2 data.
    uint256 private constant SWAP_CALLBACK_PREFIX_DATA_SIZE = 0x28;
    /// @dev The offset from the pointer to the length of the swap callback prefix data to the start of the Permit2 data.
    uint256 private constant SWAP_CALLBACK_PERMIT2DATA_OFFSET = 0x48;
    uint256 private constant PERMIT_DATA_SIZE = 0x60;
    uint256 private constant ISFORWARDED_DATA_SIZE = 0x01;
    /// @dev Minimum tick price sqrt ratio.
    uint160 private constant MIN_PRICE_SQRT_RATIO = 4295128739;
    /// @dev Minimum tick price sqrt ratio.
    uint160 private constant MAX_PRICE_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
    /// @dev Mask of lower 20 bytes.
    uint256 private constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff;
    /// @dev Mask of lower 3 bytes.
    uint256 private constant UINT24_MASK = 0xffffff;

    /// @dev Sell a token for another token directly against uniswap v3.
    /// @param encodedPath Uniswap-encoded path.
    /// @param bps proportion of current balance of the first token in the path to sell.
    /// @param minBuyAmount Minimum amount of the last token in the path to buy.
    /// @param recipient The recipient of the bought tokens.
    /// @return buyAmount Amount of the last token in the path bought.
    function sellToUniswapV3(address recipient, uint256 bps, bytes memory encodedPath, uint256 minBuyAmount)
        internal
        returns (uint256 buyAmount)
    {
        buyAmount = _uniV3ForkSwap(
            recipient,
            encodedPath,
            // We don't care about phantom overflow here because reserves are
            // limited to 128 bits. Any token balance that would overflow here
            // would also break UniV3.
            (IERC20(address(bytes20(encodedPath))).balanceOf(address(this)) * bps).unsafeDiv(BASIS),
            minBuyAmount,
            address(this), // payer
            new bytes(SWAP_CALLBACK_PREFIX_DATA_SIZE)
        );
    }

    /// @dev Sell a token for another token directly against uniswap v3. Payment is using a Permit2 signature (or AllowanceHolder).
    /// @param encodedPath Uniswap-encoded path.
    /// @param minBuyAmount Minimum amount of the last token in the path to buy.
    /// @param recipient The recipient of the bought tokens.
    /// @param permit The PermitTransferFrom allowing this contract to spend the taker's tokens
    /// @param sig The taker's signature for Permit2
    /// @return buyAmount Amount of the last token in the path bought.
    function sellToUniswapV3VIP(
        address recipient,
        bytes memory encodedPath,
        ISignatureTransfer.PermitTransferFrom memory permit,
        bytes memory sig,
        uint256 minBuyAmount
    ) internal returns (uint256 buyAmount) {
        bytes memory swapCallbackData =
            new bytes(SWAP_CALLBACK_PREFIX_DATA_SIZE + PERMIT_DATA_SIZE + ISFORWARDED_DATA_SIZE + sig.length);
        _encodePermit2Data(swapCallbackData, permit, sig, _isForwarded());

        buyAmount = _uniV3ForkSwap(
            recipient,
            encodedPath,
            _permitToSellAmount(permit),
            minBuyAmount,
            address(0), // payer
            swapCallbackData
        );
    }

    // Executes successive swaps along an encoded uniswap path.
    function _uniV3ForkSwap(
        address recipient,
        bytes memory encodedPath,
        uint256 sellAmount,
        uint256 minBuyAmount,
        address payer,
        bytes memory swapCallbackData
    ) internal returns (uint256 buyAmount) {
        if (sellAmount > uint256(type(int256).max)) {
            Panic.panic(Panic.ARITHMETIC_OVERFLOW);
        }

        IERC20 outputToken;
        while (true) {
            bool isPathMultiHop = _isPathMultiHop(encodedPath);
            bool zeroForOne;
            IUniswapV3Pool pool;
            uint32 callbackSelector;
            {
                (IERC20 token0, uint8 forkId, uint24 poolId, IERC20 token1) = _decodeFirstPoolInfoFromPath(encodedPath);
                IERC20 sellToken = token0;
                outputToken = token1;
                if (!(zeroForOne = token0 < token1)) {
                    (token0, token1) = (token1, token0);
                }
                address factory;
                bytes32 initHash;
                (factory, initHash, callbackSelector) = _uniV3ForkInfo(forkId);
                pool = _toPool(factory, initHash, token0, token1, poolId);
                _updateSwapCallbackData(swapCallbackData, sellToken, payer);
            }

            int256 amount0;
            int256 amount1;
            if (isPathMultiHop) {
                uint256 freeMemPtr;
                assembly ("memory-safe") {
                    freeMemPtr := mload(0x40)
                }
                (amount0, amount1) = abi.decode(
                    _setOperatorAndCall(
                        address(pool),
                        abi.encodeCall(
                            pool.swap,
                            (
                                // Intermediate tokens go to this contract.
                                address(this),
                                zeroForOne,
                                int256(sellAmount),
                                zeroForOne ? MIN_PRICE_SQRT_RATIO + 1 : MAX_PRICE_SQRT_RATIO - 1,
                                swapCallbackData
                            )
                        ),
                        callbackSelector,
                        _uniV3ForkCallback
                    ),
                    (int256, int256)
                );
                assembly ("memory-safe") {
                    mstore(0x40, freeMemPtr)
                }
            } else {
                (amount0, amount1) = abi.decode(
                    _setOperatorAndCall(
                        address(pool),
                        abi.encodeCall(
                            pool.swap,
                            (
                                recipient,
                                zeroForOne,
                                int256(sellAmount),
                                zeroForOne ? MIN_PRICE_SQRT_RATIO + 1 : MAX_PRICE_SQRT_RATIO - 1,
                                swapCallbackData
                            )
                        ),
                        callbackSelector,
                        _uniV3ForkCallback
                    ),
                    (int256, int256)
                );
            }

            {
                int256 _buyAmount = -(zeroForOne ? amount1 : amount0);
                if (_buyAmount < 0) {
                    Panic.panic(Panic.ARITHMETIC_OVERFLOW);
                }
                buyAmount = uint256(_buyAmount);
            }
            if (!isPathMultiHop) {
                // Done.
                break;
            }
            // Continue with next hop.
            payer = address(this); // Subsequent hops are paid for by us.
            sellAmount = buyAmount;
            // Skip to next hop along path.
            encodedPath = _shiftHopFromPathInPlace(encodedPath);
            assembly ("memory-safe") {
                mstore(swapCallbackData, SWAP_CALLBACK_PREFIX_DATA_SIZE)
            }
        }
        if (buyAmount < minBuyAmount) {
            revert TooMuchSlippage(outputToken, minBuyAmount, buyAmount);
        }
    }

    // Return whether or not an encoded uniswap path contains more than one hop.
    function _isPathMultiHop(bytes memory encodedPath) private pure returns (bool) {
        return encodedPath.length > SINGLE_HOP_PATH_SIZE;
    }

    function _decodeFirstPoolInfoFromPath(bytes memory encodedPath)
        private
        pure
        returns (IERC20 inputToken, uint8 forkId, uint24 poolId, IERC20 outputToken)
    {
        if (encodedPath.length < SINGLE_HOP_PATH_SIZE) {
            Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
        }
        assembly ("memory-safe") {
            // Solidity cleans dirty bits automatically
            inputToken := mload(add(encodedPath, 0x14))
            forkId := mload(add(encodedPath, 0x15))
            poolId := mload(add(encodedPath, 0x18))
            outputToken := mload(add(encodedPath, SINGLE_HOP_PATH_SIZE))
        }
    }

    // Skip past the first hop of an encoded uniswap path in-place.
    function _shiftHopFromPathInPlace(bytes memory encodedPath) private pure returns (bytes memory) {
        if (encodedPath.length < PATH_SKIP_HOP_SIZE) {
            Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
        }
        assembly ("memory-safe") {
            let length := sub(mload(encodedPath), PATH_SKIP_HOP_SIZE)
            encodedPath := add(encodedPath, PATH_SKIP_HOP_SIZE)
            mstore(encodedPath, length)
        }
        return encodedPath;
    }

    function _encodePermit2Data(
        bytes memory swapCallbackData,
        ISignatureTransfer.PermitTransferFrom memory permit,
        bytes memory sig,
        bool isForwarded
    ) private view {
        assembly ("memory-safe") {
            function mcopy(dst, src, len) {
                if or(xor(returndatasize(), len), iszero(staticcall(gas(), 0x04, src, len, dst, len))) { invalid() }
            }
            mstore(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, swapCallbackData), mload(add(0x20, mload(permit))))
            mcopy(add(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, 0x20), swapCallbackData), add(0x20, permit), 0x40)
            mstore8(add(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, PERMIT_DATA_SIZE), swapCallbackData), isForwarded)
            mcopy(
                add(
                    add(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, PERMIT_DATA_SIZE), ISFORWARDED_DATA_SIZE),
                    swapCallbackData
                ),
                add(0x20, sig),
                mload(sig)
            )
        }
    }

    // Update `swapCallbackData` in place with new values.
    function _updateSwapCallbackData(bytes memory swapCallbackData, IERC20 sellToken, address payer) private pure {
        assembly ("memory-safe") {
            let length := mload(swapCallbackData)
            mstore(add(0x28, swapCallbackData), sellToken)
            mstore(add(0x14, swapCallbackData), payer)
            mstore(swapCallbackData, length)
        }
    }

    // Compute the pool address given two tokens and a poolId.
    function _toPool(address factory, bytes32 initHash, IERC20 token0, IERC20 token1, uint24 poolId)
        private
        pure
        returns (IUniswapV3Pool)
    {
        // address(keccak256(abi.encodePacked(
        //     hex"ff",
        //     factory,
        //     keccak256(abi.encode(token0, token1, poolId)),
        //     initHash
        // )))
        bytes32 salt;
        assembly ("memory-safe") {
            token0 := and(ADDRESS_MASK, token0)
            token1 := and(ADDRESS_MASK, token1)
            poolId := and(UINT24_MASK, poolId)
            let ptr := mload(0x40)
            mstore(0x00, token0)
            mstore(0x20, token1)
            mstore(0x40, poolId)
            salt := keccak256(0x00, sub(0x60, shl(0x05, iszero(poolId))))
            mstore(0x40, ptr)
        }
        return IUniswapV3Pool(AddressDerivation.deriveDeterministicContract(factory, salt, initHash));
    }

    function _uniV3ForkInfo(uint8 forkId) internal view virtual returns (address, bytes32, uint32);

    function _uniV3ForkCallback(bytes calldata data) private returns (bytes memory) {
        require(data.length >= 0x80);
        int256 amount0Delta;
        int256 amount1Delta;
        assembly ("memory-safe") {
            amount0Delta := calldataload(data.offset)
            amount1Delta := calldataload(add(0x20, data.offset))
            data.offset := add(data.offset, calldataload(add(0x40, data.offset)))
            data.length := calldataload(data.offset)
            data.offset := add(0x20, data.offset)
        }
        uniswapV3SwapCallback(amount0Delta, amount1Delta, data);
        return new bytes(0);
    }

    /// @dev The UniswapV3 pool swap callback which pays the funds requested
    ///      by the caller/pool to the pool. Can only be called by a valid
    ///      UniswapV3 pool.
    /// @param amount0Delta Token0 amount owed.
    /// @param amount1Delta Token1 amount owed.
    /// @param data Arbitrary data forwarded from swap() caller. A packed encoding of: payer, sellToken, (optionally: permit[0x20:], isForwarded, sig)
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) private {
        address payer = address(uint160(bytes20(data)));
        data = data[0x14:];
        uint256 sellAmount = amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
        _pay(payer, sellAmount, data);
    }

    function _pay(address payer, uint256 amount, bytes calldata permit2Data) private {
        if (payer == address(this)) {
            IERC20(address(uint160(bytes20(permit2Data)))).safeTransfer(msg.sender, amount);
        } else {
            assert(payer == address(0));
            ISignatureTransfer.PermitTransferFrom calldata permit;
            bool isForwarded;
            bytes calldata sig;
            assembly ("memory-safe") {
                // this is super dirty, but it works because although `permit` is aliasing in the
                // middle of `payer`, because `payer` is all zeroes, it's treated as padding for the
                // first word of `permit`, which is the sell token
                permit := sub(permit2Data.offset, 0x0c)
                isForwarded := and(0x01, calldataload(add(0x55, permit2Data.offset)))
                sig.offset := add(0x75, permit2Data.offset)
                sig.length := sub(permit2Data.length, 0x75)
            }
            ISignatureTransfer.SignatureTransferDetails memory transferDetails =
                ISignatureTransfer.SignatureTransferDetails({to: msg.sender, requestedAmount: amount});
            _transferFrom(permit, transferDetails, sig, isForwarded);
        }
    }
}

// src/core/Basic.sol

abstract contract Basic is SettlerAbstract {
    using SafeTransferLib for IERC20;
    using FullMath for uint256;
    using Revert for bool;

    IERC20 internal constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);

    /// @dev Sell to a pool with a generic approval, transferFrom interaction.
    /// offset in the calldata is used to update the sellAmount given a proportion of the sellToken balance
    function basicSellToPool(IERC20 sellToken, uint256 bps, address pool, uint256 offset, bytes memory data) internal {
        if (_isRestrictedTarget(pool)) {
            revert ConfusedDeputy();
        }

        bool success;
        bytes memory returnData;
        uint256 value;
        if (sellToken == IERC20(ETH_ADDRESS)) {
            value = address(this).balance.mulDiv(bps, BASIS);
            if (data.length == 0) {
                if (offset != 0) revert InvalidOffset();
                (success, returnData) = payable(pool).call{value: value}("");
                success.maybeRevert(returnData);
                return;
            } else {
                if ((offset += 32) > data.length) {
                    Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
                }
                assembly ("memory-safe") {
                    mstore(add(data, offset), value)
                }
            }
        } else if (address(sellToken) == address(0)) {
            // TODO: check for zero `bps`
            if (offset != 0) revert InvalidOffset();
        } else {
            uint256 amount = sellToken.balanceOf(address(this)).mulDiv(bps, BASIS);
            if ((offset += 32) > data.length) {
                Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
            }
            assembly ("memory-safe") {
                mstore(add(data, offset), amount)
            }
            if (address(sellToken) != pool) {
                sellToken.safeApproveIfBelow(pool, amount);
            }
        }
        (success, returnData) = payable(pool).call{value: value}(data);
        success.maybeRevert(returnData);
        // forbid sending data to EOAs
        if (returnData.length == 0 && pool.code.length == 0) revert InvalidTarget();
    }
}

// src/core/Permit2Payment.sol

library TransientStorage {
    // bytes32(uint256(keccak256("operator slot")) - 1)
    bytes32 private constant _OPERATOR_SLOT = 0x009355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe9;
    // bytes32(uint256(keccak256("witness slot")) - 1)
    bytes32 private constant _WITNESS_SLOT = 0x1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa;
    // bytes32(uint256(keccak256("payer slot")) - 1)
    bytes32 private constant _PAYER_SLOT = 0x46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893;

    // We assume (and our CI enforces) that internal function pointers cannot be
    // greater than 2 bytes. On chains not supporting the ViaIR pipeline, not
    // supporting EOF, and where the Spurious Dragon size limit is not enforced,
    // it might be possible to violate this assumption. However, our
    // `foundry.toml` enforces the use of the IR pipeline, so the point is moot.
    //
    // `operator` must not be `address(0)`. This is not checked.
    // `callback` must not be zero. This is checked in `_invokeCallback`.
    function setOperatorAndCallback(
        address operator,
        uint32 selector,
        function (bytes calldata) internal returns (bytes memory) callback
    ) internal {
        address currentSigner;
        assembly ("memory-safe") {
            currentSigner := sload(_PAYER_SLOT)
        }
        if (operator == currentSigner) {
            revert ConfusedDeputy();
        }
        uint256 callbackInt;
        assembly ("memory-safe") {
            callbackInt := sload(_OPERATOR_SLOT)
        }
        if (callbackInt != 0) {
            // It should be impossible to reach this error because the first thing the fallback does
            // is clear the operator. It's also not possible to reenter the entrypoint function
            // because `_PAYER_SLOT` is an implicit reentrancy guard.
            revert ReentrantCallback(callbackInt);
        }
        assembly ("memory-safe") {
            sstore(
                _OPERATOR_SLOT,
                or(
                    shl(0xe0, selector),
                    or(shl(0xa0, and(0xffff, callback)), and(0xffffffffffffffffffffffffffffffffffffffff, operator))
                )
            )
        }
    }

    function checkSpentOperatorAndCallback() internal view {
        uint256 callbackInt;
        assembly ("memory-safe") {
            callbackInt := sload(_OPERATOR_SLOT)
        }
        if (callbackInt != 0) {
            revert CallbackNotSpent(callbackInt);
        }
    }

    function getAndClearOperatorAndCallback()
        internal
        returns (bytes4 selector, function (bytes calldata) internal returns (bytes memory) callback, address operator)
    {
        assembly ("memory-safe") {
            selector := sload(_OPERATOR_SLOT)
            callback := and(0xffff, shr(0xa0, selector))
            operator := selector
            sstore(_OPERATOR_SLOT, 0x00)
        }
    }

    // `newWitness` must not be `bytes32(0)`. This is not checked.
    function setWitness(bytes32 newWitness) internal {
        bytes32 currentWitness;
        assembly ("memory-safe") {
            currentWitness := sload(_WITNESS_SLOT)
        }
        if (currentWitness != bytes32(0)) {
            // It should be impossible to reach this error because the first thing a metatransaction
            // does on entry is to spend the `witness` (either directly or via a callback)
            revert ReentrantMetatransaction(currentWitness);
        }
        assembly ("memory-safe") {
            sstore(_WITNESS_SLOT, newWitness)
        }
    }

    function checkSpentWitness() internal view {
        bytes32 currentWitness;
        assembly ("memory-safe") {
            currentWitness := sload(_WITNESS_SLOT)
        }
        if (currentWitness != bytes32(0)) {
            revert WitnessNotSpent(currentWitness);
        }
    }

    function getAndClearWitness() internal returns (bytes32 witness) {
        assembly ("memory-safe") {
            witness := sload(_WITNESS_SLOT)
            sstore(_WITNESS_SLOT, 0x00)
        }
    }

    function setPayer(address payer) internal {
        if (payer == address(0)) {
            revert ConfusedDeputy();
        }
        address oldPayer;
        assembly ("memory-safe") {
            oldPayer := sload(_PAYER_SLOT)
        }
        if (oldPayer != address(0)) {
            revert ReentrantPayer(oldPayer);
        }
        assembly ("memory-safe") {
            sstore(_PAYER_SLOT, and(0xffffffffffffffffffffffffffffffffffffffff, payer))
        }
    }

    function getPayer() internal view returns (address payer) {
        assembly ("memory-safe") {
            payer := sload(_PAYER_SLOT)
        }
    }

    function clearPayer(address expectedOldPayer) internal {
        address oldPayer;
        assembly ("memory-safe") {
            oldPayer := sload(_PAYER_SLOT)
        }
        if (oldPayer != expectedOldPayer) {
            revert PayerSpent();
        }
        assembly ("memory-safe") {
            sstore(_PAYER_SLOT, 0x00)
        }
    }
}

abstract contract Permit2PaymentBase is SettlerAbstract {
    using Revert for bool;

    /// @dev Permit2 address
    ISignatureTransfer internal constant _PERMIT2 = ISignatureTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);

    function _isRestrictedTarget(address target) internal pure virtual override returns (bool) {
        return target == address(_PERMIT2);
    }

    function _msgSender() internal view virtual override returns (address) {
        return TransientStorage.getPayer();
    }

    /// @dev You must ensure that `target` is derived by hashing trusted initcode or another
    ///      equivalent mechanism that guarantees "reasonable"ness. `target` must not be
    ///      user-supplied or attacker-controlled. This is required for security and is not checked
    ///      here. For example, it must not do something weird like modifying the spender (possibly
    ///      setting it to itself). If the callback is expected to relay a
    ///      `ISignatureTransfer.PermitTransferFrom` struct, then the computation of `target` using
    ///      the trusted initcode (or equivalent) must ensure that that calldata is relayed
    ///      unmodified. The library function `AddressDerivation.deriveDeterministicContract` is
    ///      recommended.
    function _setOperatorAndCall(
        address payable target,
        uint256 value,
        bytes memory data,
        uint32 selector,
        function (bytes calldata) internal returns (bytes memory) callback
    ) internal returns (bytes memory) {
        TransientStorage.setOperatorAndCallback(target, selector, callback);
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        success.maybeRevert(returndata);
        TransientStorage.checkSpentOperatorAndCallback();
        return returndata;
    }

    function _setOperatorAndCall(
        address target,
        bytes memory data,
        uint32 selector,
        function (bytes calldata) internal returns (bytes memory) callback
    ) internal override returns (bytes memory) {
        return _setOperatorAndCall(payable(target), 0, data, selector, callback);
    }

    function _invokeCallback(bytes calldata data) internal returns (bytes memory) {
        // Retrieve callback and perform call with untrusted calldata
        (bytes4 selector, function (bytes calldata) internal returns (bytes memory) callback, address operator) =
            TransientStorage.getAndClearOperatorAndCallback();
        require(bytes4(data) == selector);
        require(msg.sender == operator);
        return callback(data[4:]);
    }
}

abstract contract Permit2Payment is Permit2PaymentBase {
    using FullMath for uint256;

    fallback(bytes calldata data) external virtual returns (bytes memory) {
        return _invokeCallback(data);
    }

    function _permitToSellAmount(ISignatureTransfer.PermitTransferFrom memory permit)
        internal
        view
        override
        returns (uint256 sellAmount)
    {
        sellAmount = permit.permitted.amount;
        if (sellAmount > type(uint256).max - BASIS) {
            unchecked {
                sellAmount -= type(uint256).max - BASIS;
            }
            sellAmount = IERC20(permit.permitted.token).balanceOf(_msgSender()).mulDiv(sellAmount, BASIS);
        }
    }

    function _permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient)
        internal
        view
        override
        returns (ISignatureTransfer.SignatureTransferDetails memory transferDetails, uint256 sellAmount)
    {
        transferDetails.to = recipient;
        transferDetails.requestedAmount = sellAmount = _permitToSellAmount(permit);
    }

    // This function is provided *EXCLUSIVELY* for use here and in RfqOrderSettlement. Any other use
    // of this function is forbidden. You must use the version that does *NOT* take a `from` or
    // `witness` argument.
    function _transferFromIKnowWhatImDoing(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        address from,
        bytes32 witness,
        string memory witnessTypeString,
        bytes memory sig,
        bool isForwarded
    ) internal override {
        if (isForwarded) revert ForwarderNotAllowed();
        _PERMIT2.permitWitnessTransferFrom(permit, transferDetails, from, witness, witnessTypeString, sig);
    }

    // See comment in above overload; don't use this function
    function _transferFromIKnowWhatImDoing(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        address from,
        bytes32 witness,
        string memory witnessTypeString,
        bytes memory sig
    ) internal override {
        _transferFromIKnowWhatImDoing(permit, transferDetails, from, witness, witnessTypeString, sig, _isForwarded());
    }

    function _transferFrom(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        bytes memory sig
    ) internal override {
        _transferFrom(permit, transferDetails, sig, _isForwarded());
    }
}

abstract contract Permit2PaymentTakerSubmitted is AllowanceHolderContext, Permit2Payment {
    constructor() {
        assert(!_hasMetaTxn());
    }

    function _isRestrictedTarget(address target) internal pure virtual override returns (bool) {
        return target == address(_ALLOWANCE_HOLDER) || super._isRestrictedTarget(target);
    }

    function _operator() internal view override returns (address) {
        return AllowanceHolderContext._msgSender();
    }

    function _msgSender()
        internal
        view
        virtual
        override(Permit2PaymentBase, AllowanceHolderContext)
        returns (address)
    {
        return Permit2PaymentBase._msgSender();
    }

    function _transferFrom(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        bytes memory sig,
        bool isForwarded
    ) internal override {
        if (isForwarded) {
            if (sig.length != 0) revert InvalidSignatureLen();
            if (permit.nonce != 0) Panic.panic(Panic.ARITHMETIC_OVERFLOW);
            if (block.timestamp > permit.deadline) revert SignatureExpired(permit.deadline);
            // we don't check `requestedAmount` because it's checked by AllowanceHolder itself
            _allowanceHolderTransferFrom(
                permit.permitted.token, _msgSender(), transferDetails.to, transferDetails.requestedAmount
            );
        } else {
            _PERMIT2.permitTransferFrom(permit, transferDetails, _msgSender(), sig);
        }
    }

    function _allowanceHolderTransferFrom(address token, address owner, address recipient, uint256 amount)
        internal
        override
    {
        // `owner` is always `_msgSender()`
        _ALLOWANCE_HOLDER.transferFrom(token, owner, recipient, amount);
    }

    modifier takerSubmitted() override {
        address msgSender = _operator();
        TransientStorage.setPayer(msgSender);
        _;
        TransientStorage.clearPayer(msgSender);
    }

    modifier metaTx(address, bytes32) override {
        revert();
        _;
    }
}

abstract contract Permit2PaymentMetaTxn is Context, Permit2Payment {
    constructor() {
        assert(_hasMetaTxn());
    }

    function _operator() internal view override returns (address) {
        return Context._msgSender();
    }

    function _msgSender() internal view virtual override(Permit2PaymentBase, Context) returns (address) {
        return Permit2PaymentBase._msgSender();
    }

    // `string.concat` isn't recognized by solc as compile-time constant, but `abi.encodePacked` is
    // This is defined here as `private` and not in `SettlerAbstract` as `internal` because no other
    // contract/file should reference it. The *ONLY* approved way to make a transfer using this
    // witness string is by setting the witness with modifier `metaTx`
    string private constant _SLIPPAGE_AND_ACTIONS_WITNESS = string(
        abi.encodePacked("SlippageAndActions slippageAndActions)", SLIPPAGE_AND_ACTIONS_TYPE, TOKEN_PERMISSIONS_TYPE)
    );

    function _transferFrom(
        ISignatureTransfer.PermitTransferFrom memory permit,
        ISignatureTransfer.SignatureTransferDetails memory transferDetails,
        bytes memory sig,
        bool isForwarded // must be false
    ) internal override {
        bytes32 witness = TransientStorage.getAndClearWitness();
        if (witness == bytes32(0)) {
            revert ConfusedDeputy();
        }
        _transferFromIKnowWhatImDoing(
            permit, transferDetails, _msgSender(), witness, _SLIPPAGE_AND_ACTIONS_WITNESS, sig, isForwarded
        );
    }

    function _allowanceHolderTransferFrom(address, address, address, uint256) internal pure override {
        revert ConfusedDeputy();
    }

    modifier takerSubmitted() override {
        revert();
        _;
    }

    modifier metaTx(address msgSender, bytes32 witness) override {
        if (_isForwarded()) {
            revert ForwarderNotAllowed();
        }
        TransientStorage.setWitness(witness);
        TransientStorage.setPayer(msgSender);
        _;
        TransientStorage.clearPayer(msgSender);
        // It should not be possible for this check to revert because the very first thing that a
        // metatransaction does is spend the witness.
        TransientStorage.checkSpentWitness();
    }
}

// src/SettlerBase.sol

/// @dev This library's ABIDeocding is more lax than the Solidity ABIDecoder. This library omits index bounds/overflow
/// checking when accessing calldata arrays for gas efficiency. It also omits checks against `calldatasize()`. This
/// means that it is possible that `args` will run off the end of calldata and be implicitly padded with zeroes. That we
/// don't check for overflow means that offsets can be negative. This can also result in `args` that alias other parts
/// of calldata, or even the `actions` array itself.
library CalldataDecoder {
    function decodeCall(bytes[] calldata data, uint256 i)
        internal
        pure
        returns (bytes4 selector, bytes calldata args)
    {
        assembly ("memory-safe") {
            // initially, we set `args.offset` to the pointer to the length. this is 32 bytes before the actual start of data
            args.offset :=
                add(
                    data.offset,
                    // We allow the indirection/offset to `calls[i]` to be negative
                    calldataload(
                        add(shl(5, i), data.offset) // can't overflow; we assume `i` is in-bounds
                    )
                )
            // now we load `args.length` and set `args.offset` to the start of data
            args.length := calldataload(args.offset)
            args.offset := add(args.offset, 0x20)

            // slice off the first 4 bytes of `args` as the selector
            selector := calldataload(args.offset) // solidity cleans dirty bits automatically
            args.length := sub(args.length, 0x04)
            args.offset := add(args.offset, 0x04)
        }
    }
}

abstract contract SettlerBase is Basic, RfqOrderSettlement, UniswapV3Fork, UniswapV2, Velodrome {
    using SafeTransferLib for IERC20;
    using SafeTransferLib for address payable;

    receive() external payable {}

    event GitCommit(bytes20 indexed);

    constructor(bytes20 gitCommit, uint256 tokenId) {
        if (block.chainid != 31337) {
            emit GitCommit(gitCommit);
            assert(IERC721Owner(0x00000000000004533Fe15556B1E086BB1A72cEae).ownerOf(tokenId) == address(this));
        } else {
            assert(gitCommit == bytes20(0));
        }
    }

    struct AllowedSlippage {
        address recipient;
        IERC20 buyToken;
        uint256 minAmountOut;
    }

    function _checkSlippageAndTransfer(AllowedSlippage calldata slippage) internal {
        // This final slippage check effectively prohibits custody optimization on the
        // final hop of every swap. This is gas-inefficient. This is on purpose. Because
        // ISettlerActions.BASIC could interact with an intents-based settlement
        // mechanism, we must ensure that the user's want token increase is coming
        // directly from us instead of from some other form of exchange of value.
        (address recipient, IERC20 buyToken, uint256 minAmountOut) =
            (slippage.recipient, slippage.buyToken, slippage.minAmountOut);
        if (minAmountOut != 0 || address(buyToken) != address(0)) {
            if (buyToken == ETH_ADDRESS) {
                uint256 amountOut = address(this).balance;
                if (amountOut < minAmountOut) {
                    revert TooMuchSlippage(buyToken, minAmountOut, amountOut);
                }
                payable(recipient).safeTransferETH(amountOut);
            } else {
                uint256 amountOut = buyToken.balanceOf(address(this));
                if (amountOut < minAmountOut) {
                    revert TooMuchSlippage(buyToken, minAmountOut, amountOut);
                }
                buyToken.safeTransfer(recipient, amountOut);
            }
        }
    }

    function _dispatch(uint256, bytes4 action, bytes calldata data) internal virtual override returns (bool) {
        if (action == ISettlerActions.TRANSFER_FROM.selector) {
            (address recipient, ISignatureTransfer.PermitTransferFrom memory permit, bytes memory sig) =
                abi.decode(data, (address, ISignatureTransfer.PermitTransferFrom, bytes));
            (ISignatureTransfer.SignatureTransferDetails memory transferDetails,) =
                _permitToTransferDetails(permit, recipient);
            _transferFrom(permit, transferDetails, sig);
        } else if (action == ISettlerActions.RFQ.selector) {
            (
                address recipient,
                ISignatureTransfer.PermitTransferFrom memory permit,
                address maker,
                bytes memory makerSig,
                IERC20 takerToken,
                uint256 maxTakerAmount
            ) = abi.decode(data, (address, ISignatureTransfer.PermitTransferFrom, address, bytes, IERC20, uint256));

            fillRfqOrderSelfFunded(recipient, permit, maker, makerSig, takerToken, maxTakerAmount);
        } else if (action == ISettlerActions.UNISWAPV3.selector) {
            (address recipient, uint256 bps, bytes memory path, uint256 amountOutMin) =
                abi.decode(data, (address, uint256, bytes, uint256));

            sellToUniswapV3(recipient, bps, path, amountOutMin);
        } else if (action == ISettlerActions.UNISWAPV2.selector) {
            (address recipient, address sellToken, uint256 bps, address pool, uint24 swapInfo, uint256 amountOutMin) =
                abi.decode(data, (address, address, uint256, address, uint24, uint256));

            sellToUniswapV2(recipient, sellToken, bps, pool, swapInfo, amountOutMin);
        } else if (action == ISettlerActions.BASIC.selector) {
            (IERC20 sellToken, uint256 bps, address pool, uint256 offset, bytes memory _data) =
                abi.decode(data, (IERC20, uint256, address, uint256, bytes));

            basicSellToPool(sellToken, bps, pool, offset, _data);
        } else if (action == ISettlerActions.VELODROME.selector) {
            (address recipient, uint256 bps, IVelodromePair pool, uint24 swapInfo, uint256 minAmountOut) =
                abi.decode(data, (address, uint256, IVelodromePair, uint24, uint256));

            sellToVelodrome(recipient, bps, pool, swapInfo, minAmountOut);
        } else if (action == ISettlerActions.POSITIVE_SLIPPAGE.selector) {
            (address recipient, IERC20 token, uint256 expectedAmount) = abi.decode(data, (address, IERC20, uint256));
            if (token == IERC20(ETH_ADDRESS)) {
                uint256 balance = address(this).balance;
                if (balance > expectedAmount) {
                    unchecked {
                        payable(recipient).safeTransferETH(balance - expectedAmount);
                    }
                }
            } else {
                uint256 balance = token.balanceOf(address(this));
                if (balance > expectedAmount) {
                    unchecked {
                        token.safeTransfer(recipient, balance - expectedAmount);
                    }
                }
            }
        } else {
            return false;
        }
        return true;
    }
}

// src/Settler.sol

abstract contract Settler is Permit2PaymentTakerSubmitted, SettlerBase {
    using UnsafeMath for uint256;
    using CalldataDecoder for bytes[];

    // When/if you change this, you must make corresponding changes to
    // `sh/deploy_new_chain.sh` and 'sh/common_deploy_settler.sh' to set
    // `constructor_args`.
    constructor(bytes20 gitCommit) SettlerBase(gitCommit, 2) {}

    function _hasMetaTxn() internal pure override returns (bool) {
        return false;
    }

    function _msgSender()
        internal
        view
        virtual
        // Solidity inheritance is so stupid
        override(Permit2PaymentTakerSubmitted, AbstractContext)
        returns (address)
    {
        return super._msgSender();
    }

    function _isRestrictedTarget(address target)
        internal
        pure
        virtual
        // Solidity inheritance is so stupid
        override(Permit2PaymentTakerSubmitted, Permit2PaymentAbstract)
        returns (bool)
    {
        return super._isRestrictedTarget(target);
    }

    function _dispatchVIP(bytes4 action, bytes calldata data) internal virtual returns (bool) {
        if (action == ISettlerActions.RFQ_VIP.selector) {
            (
                address recipient,
                ISignatureTransfer.PermitTransferFrom memory makerPermit,
                address maker,
                bytes memory makerSig,
                ISignatureTransfer.PermitTransferFrom memory takerPermit,
                bytes memory takerSig
            ) = abi.decode(
                data,
                (
                    address,
                    ISignatureTransfer.PermitTransferFrom,
                    address,
                    bytes,
                    ISignatureTransfer.PermitTransferFrom,
                    bytes
                )
            );

            fillRfqOrderVIP(recipient, makerPermit, maker, makerSig, takerPermit, takerSig);
        } else if (action == ISettlerActions.UNISWAPV3_VIP.selector) {
            (
                address recipient,
                bytes memory path,
                ISignatureTransfer.PermitTransferFrom memory permit,
                bytes memory sig,
                uint256 amountOutMin
            ) = abi.decode(data, (address, bytes, ISignatureTransfer.PermitTransferFrom, bytes, uint256));

            sellToUniswapV3VIP(recipient, path, permit, sig, amountOutMin);
        } else {
            return false;
        }
        return true;
    }

    function execute(AllowedSlippage calldata slippage, bytes[] calldata actions, bytes32 /* zid & affiliate */ )
        public
        payable
        takerSubmitted
        returns (bool)
    {
        if (actions.length != 0) {
            (bytes4 action, bytes calldata data) = actions.decodeCall(0);
            if (!_dispatchVIP(action, data)) {
                if (!_dispatch(0, action, data)) {
                    revert ActionInvalid(0, action, data);
                }
            }
        }

        for (uint256 i = 1; i < actions.length; i = i.unsafeInc()) {
            (bytes4 action, bytes calldata data) = actions.decodeCall(i);
            if (!_dispatch(i, action, data)) {
                revert ActionInvalid(i, action, data);
            }
        }

        _checkSlippageAndTransfer(slippage);
        return true;
    }
}

// src/SettlerMetaTxn.sol

abstract contract SettlerMetaTxn is Permit2PaymentMetaTxn, SettlerBase {
    using UnsafeMath for uint256;
    using CalldataDecoder for bytes[];

    // When/if you change this, you must make corresponding changes to
    // `sh/deploy_new_chain.sh` and 'sh/common_deploy_settler.sh' to set
    // `constructor_args`.
    constructor(bytes20 gitCommit) SettlerBase(gitCommit, 3) {}

    function _hasMetaTxn() internal pure override returns (bool) {
        return true;
    }

    function _msgSender()
        internal
        view
        virtual
        // Solidity inheritance is so stupid
        override(Permit2PaymentMetaTxn, AbstractContext)
        returns (address)
    {
        return super._msgSender();
    }

    function _hashArrayOfBytes(bytes[] calldata actions) internal pure returns (bytes32 result) {
        // This function deliberately does no bounds checking on `actions` for
        // gas efficiency. We assume that `actions` will get used elsewhere in
        // this context and any OOB or other malformed calldata will result in a
        // revert later.
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            let hashesLength := shl(5, actions.length)
            for {
                let i := actions.offset
                let dst := ptr
                let end := add(i, hashesLength)
            } lt(i, end) {
                i := add(i, 0x20)
                dst := add(dst, 0x20)
            } {
                let src := add(actions.offset, calldataload(i))
                let length := calldataload(src)
                calldatacopy(dst, add(src, 0x20), length)
                mstore(dst, keccak256(dst, length))
            }
            result := keccak256(ptr, hashesLength)
        }
    }

    function _hashActionsAndSlippage(bytes[] calldata actions, AllowedSlippage calldata slippage)
        internal
        pure
        returns (bytes32 result)
    {
        // This function does not check for or clean any dirty bits that might
        // exist in `slippage`. We assume that `slippage` will be used elsewhere
        // in this context and that if there are dirty bits it will result in a
        // revert later.
        bytes32 arrayOfBytesHash = _hashArrayOfBytes(actions);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, SLIPPAGE_AND_ACTIONS_TYPEHASH)
            calldatacopy(add(ptr, 0x20), slippage, 0x60)
            mstore(add(ptr, 0x80), arrayOfBytesHash)
            result := keccak256(ptr, 0xa0)
        }
    }

    function _dispatchVIP(bytes4 action, bytes calldata data, bytes calldata sig) internal virtual returns (bool) {
        if (action == ISettlerActions.METATXN_RFQ_VIP.selector) {
            // An optimized path involving a maker/taker in a single trade
            // The RFQ order is signed by both maker and taker, validation is
            // performed inside the RfqOrderSettlement so there is no need to
            // validate `sig` against `actions` here
            (
                address recipient,
                ISignatureTransfer.PermitTransferFrom memory makerPermit,
                address maker,
                bytes memory makerSig,
                ISignatureTransfer.PermitTransferFrom memory takerPermit
            ) = abi.decode(
                data,
                (address, ISignatureTransfer.PermitTransferFrom, address, bytes, ISignatureTransfer.PermitTransferFrom)
            );

            fillRfqOrderVIP(recipient, makerPermit, maker, makerSig, takerPermit, sig);
        } else if (action == ISettlerActions.METATXN_TRANSFER_FROM.selector) {
            (address recipient, ISignatureTransfer.PermitTransferFrom memory permit) =
                abi.decode(data, (address, ISignatureTransfer.PermitTransferFrom));
            (ISignatureTransfer.SignatureTransferDetails memory transferDetails,) =
                _permitToTransferDetails(permit, recipient);

            // We simultaneously transfer-in the taker's tokens and authenticate the
            // metatransaction.
            _transferFrom(permit, transferDetails, sig);
        } else if (action == ISettlerActions.METATXN_UNISWAPV3_VIP.selector) {
            (
                address recipient,
                bytes memory path,
                ISignatureTransfer.PermitTransferFrom memory permit,
                uint256 amountOutMin
            ) = abi.decode(data, (address, bytes, ISignatureTransfer.PermitTransferFrom, uint256));

            sellToUniswapV3VIP(recipient, path, permit, sig, amountOutMin);
        } else {
            return false;
        }
        return true;
    }

    function executeMetaTxn(
        AllowedSlippage calldata slippage,
        bytes[] calldata actions,
        bytes32, /* zid & affiliate */
        address msgSender,
        bytes calldata sig
    ) public metaTx(msgSender, _hashActionsAndSlippage(actions, slippage)) returns (bool) {
        require(actions.length != 0);
        {
            (bytes4 action, bytes calldata data) = actions.decodeCall(0);

            // By forcing the first action to be one of the witness-aware
            // actions, we ensure that the entire sequence of actions is
            // authorized. `msgSender` is the signer of the metatransaction.
            if (!_dispatchVIP(action, data, sig)) {
                revert ActionInvalid(0, action, data);
            }
        }

        for (uint256 i = 1; i < actions.length; i = i.unsafeInc()) {
            (bytes4 action, bytes calldata data) = actions.decodeCall(i);
            if (!_dispatch(i, action, data)) {
                revert ActionInvalid(i, action, data);
            }
        }

        _checkSlippageAndTransfer(slippage);
        return true;
    }
}

// src/chains/Scroll.sol

// Solidity inheritance is stupid

abstract contract ScrollMixin is FreeMemory, SettlerBase, MaverickV2, DodoV2 {
    constructor() {
        assert(block.chainid == 534352 || block.chainid == 31337);
    }

    function _dispatch(uint256 i, bytes4 action, bytes calldata data)
        internal
        virtual
        override(SettlerBase, SettlerAbstract)
        DANGEROUS_freeMemory
        returns (bool)
    {
        if (super._dispatch(i, action, data)) {
            return true;
        } else if (action == ISettlerActions.MAVERICKV2.selector) {
            (
                address recipient,
                IERC20 sellToken,
                uint256 bps,
                IMaverickV2Pool pool,
                bool tokenAIn,
                uint256 minBuyAmount
            ) = abi.decode(data, (address, IERC20, uint256, IMaverickV2Pool, bool, uint256));

            sellToMaverickV2(recipient, sellToken, bps, pool, tokenAIn, minBuyAmount);
        } else if (action == ISettlerActions.DODOV2.selector) {
            (address recipient, IERC20 sellToken, uint256 bps, IDodoV2 dodo, bool quoteForBase, uint256 minBuyAmount) =
                abi.decode(data, (address, IERC20, uint256, IDodoV2, bool, uint256));

            sellToDodoV2(recipient, sellToken, bps, dodo, quoteForBase, minBuyAmount);
        } else {
            return false;
        }
        return true;
    }

    function _uniV3ForkInfo(uint8 forkId)
        internal
        pure
        override
        returns (address factory, bytes32 initHash, uint32 callbackSelector)
    {
        if (forkId == uniswapV3ForkId) {
            factory = uniswapV3ScrollFactory;
            initHash = uniswapV3InitHash;
            callbackSelector = uint32(IUniswapV3Callback.uniswapV3SwapCallback.selector);
        } else if (forkId == sushiswapV3ForkId) {
            factory = sushiswapV3ScrollFactory;
            initHash = uniswapV3InitHash;
            callbackSelector = uint32(IUniswapV3Callback.uniswapV3SwapCallback.selector);
        } else if (forkId == zebraV3ForkId) {
            factory = zebraV3Factory;
            initHash = zebraV3InitHash;
            callbackSelector = uint32(IZebraV3SwapCallback.zebraV3SwapCallback.selector);
        } else if (forkId == metavaultV3ForkId) {
            factory = metavaultV3Factory;
            initHash = uniswapV3InitHash;
            callbackSelector = uint32(IUniswapV3Callback.uniswapV3SwapCallback.selector);
        } else {
            revert UnknownForkId(forkId);
        }
    }
}

/// @custom:security-contact [email protected]
contract ScrollSettler is Settler, ScrollMixin {
    constructor(bytes20 gitCommit) Settler(gitCommit) {}

    function _dispatchVIP(bytes4 action, bytes calldata data) internal override DANGEROUS_freeMemory returns (bool) {
        if (super._dispatchVIP(action, data)) {
            return true;
        } else if (action == ISettlerActions.MAVERICKV2_VIP.selector) {
            (
                address recipient,
                bytes32 salt,
                bool tokenAIn,
                ISignatureTransfer.PermitTransferFrom memory permit,
                bytes memory sig,
                uint256 minBuyAmount
            ) = abi.decode(data, (address, bytes32, bool, ISignatureTransfer.PermitTransferFrom, bytes, uint256));

            sellToMaverickV2VIP(recipient, salt, tokenAIn, permit, sig, minBuyAmount);
        } else {
            return false;
        }
        return true;
    }

    // Solidity inheritance is stupid
    function _isRestrictedTarget(address target)
        internal
        pure
        override(Settler, Permit2PaymentAbstract)
        returns (bool)
    {
        return super._isRestrictedTarget(target);
    }

    function _dispatch(uint256 i, bytes4 action, bytes calldata data)
        internal
        override(SettlerAbstract, SettlerBase, ScrollMixin)
        returns (bool)
    {
        return super._dispatch(i, action, data);
    }

    function _msgSender() internal view override(Settler, AbstractContext) returns (address) {
        return super._msgSender();
    }
}

/// @custom:security-contact [email protected]
contract ScrollSettlerMetaTxn is SettlerMetaTxn, ScrollMixin {
    constructor(bytes20 gitCommit) SettlerMetaTxn(gitCommit) {}

    function _dispatchVIP(bytes4 action, bytes calldata data, bytes calldata sig)
        internal
        override
        DANGEROUS_freeMemory
        returns (bool)
    {
        if (super._dispatchVIP(action, data, sig)) {
            return true;
        } else if (action == ISettlerActions.METATXN_MAVERICKV2_VIP.selector) {
            (
                address recipient,
                bytes32 salt,
                bool tokenAIn,
                ISignatureTransfer.PermitTransferFrom memory permit,
                uint256 minBuyAmount
            ) = abi.decode(data, (address, bytes32, bool, ISignatureTransfer.PermitTransferFrom, uint256));

            sellToMaverickV2VIP(recipient, salt, tokenAIn, permit, sig, minBuyAmount);
        } else {
            return false;
        }
        return true;
    }

    // Solidity inheritance is stupid
    function _dispatch(uint256 i, bytes4 action, bytes calldata data)
        internal
        override(SettlerAbstract, SettlerBase, ScrollMixin)
        returns (bool)
    {
        return super._dispatch(i, action, data);
    }

    function _msgSender() internal view override(SettlerMetaTxn, AbstractContext) returns (address) {
        return super._msgSender();
    }
}

Settings
{
  "remappings": [
    "solmate/=lib/solmate/",
    "permit2/=lib/permit2/",
    "forge-std/=lib/forge-std/src/",
    "forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000,
    "details": {
      "constantOptimizer": true,
      "yul": true
    }
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"bytes20","name":"gitCommit","type":"bytes20"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"bytes4","name":"action","type":"bytes4"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"ActionInvalid","type":"error"},{"inputs":[{"internalType":"uint256","name":"callbackInt","type":"uint256"}],"name":"CallbackNotSpent","type":"error"},{"inputs":[],"name":"ConfusedDeputy","type":"error"},{"inputs":[],"name":"ForwarderNotAllowed","type":"error"},{"inputs":[],"name":"InvalidOffset","type":"error"},{"inputs":[],"name":"InvalidTarget","type":"error"},{"inputs":[],"name":"NotConverged","type":"error"},{"inputs":[],"name":"PayerSpent","type":"error"},{"inputs":[{"internalType":"uint256","name":"callbackInt","type":"uint256"}],"name":"ReentrantCallback","type":"error"},{"inputs":[{"internalType":"bytes32","name":"oldWitness","type":"bytes32"}],"name":"ReentrantMetatransaction","type":"error"},{"inputs":[{"internalType":"address","name":"oldPayer","type":"address"}],"name":"ReentrantPayer","type":"error"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"TooMuchSlippage","type":"error"},{"inputs":[{"internalType":"uint8","name":"forkId","type":"uint8"}],"name":"UnknownForkId","type":"error"},{"inputs":[{"internalType":"bytes32","name":"oldWitness","type":"bytes32"}],"name":"WitnessNotSpent","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes20","name":"","type":"bytes20"}],"name":"GitCommit","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"internalType":"struct SettlerBase.AllowedSlippage","name":"slippage","type":"tuple"},{"internalType":"bytes[]","name":"actions","type":"bytes[]"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"executeMetaTxn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080604090808252346102dd5780614a0e803803809161001f8285610336565b83396020928391810103126102dd57516001600160601b0319811691908290036102dd576100ef605b845161005381610307565b8181527f6e416d6f756e744f75742c62797465735b5d20616374696f6e732900000000006060858301927f536c697070616765416e64416374696f6e73286164647265737320726563697084527f69656e742c6164647265737320627579546f6b656e2c75696e74323536206d69898201520152207f615e8d716cef7295e75dd3f1f10d679914ad6d7759e8e9459f0109ef7524170114610359565b6101276100fa610374565b8281519101207f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa14610359565b6101f5835161013581610307565b604b81527f5266714f7264657228436f6e73696465726174696f6e206d616b6572436f6e73838201527f696465726174696f6e2c436f6e73696465726174696f6e2074616b6572436f6e858201526a73696465726174696f6e2960a81b60608201526101bd6101cb6101a5610374565b87519283916101b788840180976103f9565b906103f9565b03601f198101835282610336565b5190207f49fa719b76f0f6b7e76be94b56c26671a548e1c712d5b13dc2874f70a759827614610359565b46617a691491826102f2578351907f16fbd3a71aa6d159973eb9ff1e1199f9fe242767e6f30ac662a492f92ac704115f80a26331a9108f60e11b81526003600482015281816024816d04533fe15556b1e086bb1a72ceae5afa9182156102e8575f926102a0575b5061028b929161027791506001600160a01b03163014610359565b620827504614908115610298575b50610359565b516145eb90816104238239f35b90505f610285565b90809250813d83116102e1575b6102b78183610336565b810103126102dd5751906001600160a01b03821682036102dd579061027761028b61025c565b5f80fd5b503d6102ad565b84513d5f823e3d90fd5b61028b9291506103029015610359565b610277565b608081019081106001600160401b0382111761032257604052565b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761032257604052565b1561036057565b634e487b7160e01b5f52600160045260245ffd5b6040519061038182610307565b605882527f626f6f6c207061727469616c46696c6c416c6c6f7765642900000000000000006060837f436f6e73696465726174696f6e286164647265737320746f6b656e2c75696e7460208201527f32353620616d6f756e742c6164647265737320636f756e74657270617274792c60408201520152565b908151915f5b83811061040f575050015f815290565b80602080928401015181850152016103ff56fe608060405260043610801561036d575b361561036b573461020c577e9355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe9905f825492555f357fffffffff000000000000000000000000000000000000000000000000000000009081811692610356575b5080831691160361020c576001600160a01b03808216330361020c573660041161020c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36019160a01c61ffff16908160011461021057506002146100fa577f4e487b71000000000000000000000000000000000000000000000000000000005f52605160045260245ffd5b60801161020c5760043560443590816004013561011a81602485016145a7565b60601c918160141161020c575f81131561020257915b30810361018c575061016c6101749360387fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec33940191016145a7565b60601c612352565b60405161018081610b0d565b5f81525b602081519101f35b9161019b6101fd949315612408565b604051906101a882610a78565b33825260208201526101f76101c036602c8601610b4c565b92608d8501356001169460ad7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff773693019101610bc2565b91613bd2565b610174565b5060243591610130565b5f80fd5b9160a091501061020c57608435907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f82019160a4359180831680840361020c5761025e916004351614612408565b60405161026a81610a78565b33815260243560208201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c3601926080841261020c5760408051946102af86610ac1565b1261020c57604051906102c182610a78565b815260c4356020820152835260e43560208401526101043560408401526102e784610ba6565b916102f56040519384610b29565b8483523660a482011161020c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f5f91610342966101256020870137840101526101053560011692613bd2565b60405161034e81610b0d565b5f8152610184565b818093503660040360031b1b1616905f61006d565b005b5f3560e01c63fd3ad6d40361000f573461020c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360160e0811261020c5760601361020c5767ffffffffffffffff6064351161020c5736602360643501121561020c5767ffffffffffffffff606435600401351161020c573660246064356004013560051b60643501011161020c576001600160a01b0360a4351660a4350361020c5767ffffffffffffffff60c4351161020c5736602360c43501121561020c5767ffffffffffffffff60c435600401351161020c5736602460c4356004013560c43501011161020c5760806064356024015b60246064356004013560051b60643501018110610a145760a06064356004013560051b608020604051907f615e8d716cef7295e75dd3f1f10d679914ad6d7759e8e9459f0109ef7524170182526060600460208401376080820152207f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa54806109e357507f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa556001600160a01b0360a43516156109b9576001600160a01b037f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935416806109885760a4356001600160a01b03167f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde89355606435600401351561020c57602460643501356064350160448101359060487ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc602483013501910191604051926105db60c43560040135602460c43501858486610bf8565b84604052156109215760015b606435600401358110610853576004356001600160a01b03811680820361020c57602435906001600160a01b0382169283830361020c5760443593841580159061084a575b610722575b50505050506001600160a01b0360a435166001600160a01b037f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935416036106f8575f7f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893557f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa54806106c757602060405160018152f35b602490604051907fe25527c20000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517f5149e795000000000000000000000000000000000000000000000000000000008152fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81036107ab575050479183831061075d575061075392506123c8565b8080808080610631565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810193909352506044820152606490fd5b0390fd5b602493919250602090604051948580927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa92831561083f575f9361080b575b5083831061075d576108069350612352565b610753565b9092506020813d602011610837575b8161082760209383610b29565b8101031261020c575191846107f4565b3d915061081a565b6040513d5f823e3d90fd5b5080151561062c565b60246064358260051b010135606435019060448201359160487ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6024830135019101604051916108a4818387611a1d565b83604052156108ba5750505060019150016105e7565b7fffffffff00000000000000000000000000000000000000000000000000000000839584957f3c74eed6000000000000000000000000000000000000000000000000000000006107a796526004870152166024850152606060448501526064840191610a4b565b83927fffffffff000000000000000000000000000000000000000000000000000000006107a7937f3c74eed60000000000000000000000000000000000000000000000000000000086525f6004870152166024850152606060448501526064840191610a4b565b602490604051907f7407c0f80000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fe758b8d5000000000000000000000000000000000000000000000000000000008152fd5b602490604051907f9936cbab0000000000000000000000000000000000000000000000000000000082526004820152fd5b604481356064350160248101359182910184378220825260209182019101610461565b35906001600160a01b038216820361020c57565b601f8260209493601f1993818652868601375f8582860101520116010190565b3590811515820361020c57565b6040810190811067ffffffffffffffff821117610a9457604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6060810190811067ffffffffffffffff821117610a9457604052565b6080810190811067ffffffffffffffff821117610a9457604052565b67ffffffffffffffff8111610a9457604052565b6020810190811067ffffffffffffffff821117610a9457604052565b90601f601f19910116810190811067ffffffffffffffff821117610a9457604052565b80929103916080831261020c5760405190610b6682610ac1565b604082941261020c5760606040918251610b7f81610a78565b610b8882610a37565b81526020820135602082015284528281013560208501520135910152565b67ffffffffffffffff8111610a9457601f01601f191660200190565b929192610bce82610ba6565b91610bdc6040519384610b29565b82948184528183011161020c578281602093845f960137010152565b92919092610c0985848487856110ce565b15610c18575050505050600190565b7fffffffff00000000000000000000000000000000000000000000000000000000167f8d62436100000000000000000000000000000000000000000000000000000000036110a8578201906101008383031261020c57610c7783610a37565b604094602091610c95610c8b878901610a6b565b9560608801610b4c565b90610caf60e0880135956001600160a01b03943691610bc2565b968851928051988a60049a81878a01918d5afa158b3d18176110a6578a8560600181848a018d5afa158b3d18176110a657610d69605587947fbb7b783eb4b8ca46925c5384a6b9919df57cb83da8f76e37291f58d0dd5c439a60e18f730a7e848aca42d879ef06507fca0e7b33a0a63c1e8b610ddf988f610d3f905f8460a001538251928391018460a10161258b565b806081018252019160a18301809152528c60ac82019460ff8653013560c1820152015220926125bc565b891561108057637fffffff5b8c5191610d8183610add565b82528a1515898301525f8d83015260030b6060820152610dd18c51968792868b8501987f3eece7db000000000000000000000000000000000000000000000000000000008a5216602485016119a9565b03601f198101865285610b29565b16927f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde89354168314611058577e9355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe99182548061102957505f9182917f67ca7c910000000000000001000000000000000000000000000000000000000086178555519082865af190610e6b613fd5565b9115611021575480610ff25750808380610e8a935183010191016119f3565b949050838510610ea05750505050505050600190565b859697509082915f14610f76578751958680927f5f64b55b0000000000000000000000000000000000000000000000000000000082525afa908115610f6c575f91610f35575b506107a79350945b517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0390951693850193845260208401526040830152829160600190565b905083813d8311610f65575b610f4b8183610b29565b8101031261020c57610f5f6107a793611a09565b5f610ee6565b503d610f41565b86513d5f823e3d90fd5b8751958680927f0fc63d100000000000000000000000000000000000000000000000000000000082525afa908115610f6c575f91610fbb575b506107a7935094610eee565b905083813d8311610feb575b610fd18183610b29565b8101031261020c57610fe56107a793611a09565b5f610faf565b503d610fc7565b866024918951917fd66fcc38000000000000000000000000000000000000000000000000000000008352820152fd5b508281519101fd5b886024918b51917fab7646c4000000000000000000000000000000000000000000000000000000008352820152fd5b8688517fe758b8d5000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000610d75565bfe5b505050505f90565b9080601f8301121561020c578160206110cb93359101610bc2565b90565b9091907fffffffff00000000000000000000000000000000000000000000000000000000167f5228831d0000000000000000000000000000000000000000000000000000000081036112f9575081016101608282031261020c5761113182610a37565b9061113f8160208501610b4c565b9461114c60a08501610a37565b9460c08501359667ffffffffffffffff881161020c57611296866fffffffffffffffffffffffffffffffff966111f061119e8860e06111976112f49f9b6112ec9c6112859f016110b0565b9501610b4c565b9a6111b76001600160a01b039788809316993691610bc2565b926111e97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8ef6020895101511115612408565b16856123d7565b9990918b61127f88806112038c856123d7565b929094515116917f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde89354906040519361123a85610add565b845260208401521660408201525f6060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b9c613a5f565b898661128f61249b565b9286613e7b565b51511690604051916112a783610add565b825284602083015260408201525f6060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b911691613f0f565b600190565b929391927f0dfeb4190000000000000000000000000000000000000000000000000000000081036113705750820160a08382031261020c576112f4936113616001600160a01b0361135961136a94602061135289610a37565b9801610b4c565b9516856123d7565b50923691610bc2565b91613a5f565b92949193927f9ebf8e8d000000000000000000000000000000000000000000000000000000000361195a5784019360e08186031261020c576113b181610a37565b9260208201359067ffffffffffffffff821161020c576113e36113d9886113eb9486016110b0565b9760408501610b4c565b953691610bc2565b8051608901948560891161172157601f1961141e61140888610ba6565b97611416604051998a610b29565b808952610ba6565b013660208801378051602001518660480152604086606801818360200160045afa1560403d18176110a65761146561146a925f8860a801538051906020018860a90161258b565b6125bc565b5f937f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821161170f5795905b815190602c821061169557866014840151601585015197601886015190602c87015194838690879c6001600160a01b0389166001600160a01b038816101561194f575b60ff168061180757506001600160a01b0391611556917370c62c8b8e801124a4aa81ce07b637a3e83cb9197fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e33985b62ffffff16906040519387165f528616602052806040521560051b6060035f209160405261259d565b1691855190858760280152866014015285528c602c88115f1461177357506115f56001600160a01b03938785946115f0611604956115e26040519b8c9586938d8c8084169116105f14611755578d8c806401000276a4945b7f128acb080000000000000000000000000000000000000000000000000000000060208a0152169116103060248701613a29565b03601f198101845283610b29565b6126aa565b602080825183010191016119f3565b9590946040525b169116101561174e57505b7f80000000000000000000000000000000000000000000000000000000000000008114611721575f03945f861261170f57602c869211156116a757505030939060188151106116955780517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe80160189091019081526028865290611496565b634e487b715f5260326020526024601cfd5b939650935050925060c083013582106116c257505050600190565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260c0929092013560248301526044820152606490fd5b634e487b715f5260116020526024601cfd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9050611616565b8d8c8073fffd8963efd1fc6a506488495d951d5263988d25946115ae565b926001600160a01b036117e3936115f0896115e2849b9a8a6115f598879b888616898416105f146117eb57886401000276a4935b816040519b8c997f128acb080000000000000000000000000000000000000000000000000000000060208c015216911610911660248701613a29565b94909361160b565b8873fffd8963efd1fc6a506488495d951d5263988d25936117a7565b6002810361185f57506001600160a01b0391611556917346b3fdf7b5cde91ac049936bf0bdb12c5d22202e7fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b600c81036118b757506001600160a01b0391611556917396a7f53f7636c93735bf85de416a4ace94b56bd97fcf0b3414328c2bd327a4f093539d0d7d82fb94f893a2965c75cb470289cb5ac792639feb758b9861152d565b9091929394959650601181145f1461191e57506115568d96959493926001600160a01b0392739367c561915f9d062afe3b57b18e30dec62b84887fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b602490604051907fd3b1276d0000000000000000000000000000000000000000000000000000000082526004820152fd5b8692508891506114d9565b50925050505f90565b5f5b8381106119745750505f910152565b8181015183820152602001611965565b90601f19601f6020936119a281518092818752878088019101611963565b0116010190565b60606110cb94936001600160a01b0360c094168352805160208401526020810151151560408401526040810151151582840152015160030b60808201528160a08201520190611984565b919082604091031261020c576020825192015190565b51906001600160a01b038216820361020c57565b919091611a2b8284836127ca565b15611a3857505050600190565b82907fffffffff00000000000000000000000000000000000000000000000000000000167f67848fe7000000000000000000000000000000000000000000000000000000008103611fc6575060c0918101031261020c57611a9881610a37565b602090611aa6828401610a37565b9160409283850135946060810135916001600160a01b039081841680940361020c5760a0611ad660808501610a6b565b930135975f9080611f46575b508781611f3157869150916024849351809481937f70a08231000000000000000000000000000000000000000000000000000000008352896004840152165afa908115611f27575f91611efa575b5086517f1865c57d0000000000000000000000000000000000000000000000000000000081526101208082600481895afa918215611ef0579089949392915f92611df2575b50506fffffffffffffffffffffffffffffffff809188875f14611dea575051165b169003955b8315611dc457637fffffff5b835197611bb389610add565b8852841515878901525f8489015260030b6060880152611c0e8351611bd781610b0d565b5f8152845198899485947f3eece7db00000000000000000000000000000000000000000000000000000000865216600485016119a9565b03815f865af1938415611dba575f94611d89575b50858410611c3557505050505050600190565b6004939291829115611d0d578551948580927f5f64b55b0000000000000000000000000000000000000000000000000000000082525afa908115611d03575f91611ccc575b506107a79250925b519384937f97a6f3b9000000000000000000000000000000000000000000000000000000008552600485016040919493926001600160a01b03606083019616825260208201520152565b905082813d8311611cfc575b611ce28183610b29565b8101031261020c57611cf66107a792611a09565b85611c7a565b503d611cd8565b84513d5f823e3d90fd5b8551948580927f0fc63d100000000000000000000000000000000000000000000000000000000082525afa908115611d03575f91611d52575b506107a7925092611c82565b905082813d8311611d82575b611d688183610b29565b8101031261020c57611d7c6107a792611a09565b85611d46565b503d611d5e565b611daa919450853d8711611db3575b611da28183610b29565b8101906119f3565b9050925f611c22565b503d611d98565b85513d5f823e3d90fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000611ba7565b015116611b96565b818096508193503d8311611ee9575b611e0b8183610b29565b8101031261020c57885193840184811067ffffffffffffffff821117610a94578952611e36816139f1565b8452611e438782016139f1565b87850152611e52898201613a0e565b89850152611e6260608201613a0e565b6060850152608081015164ffffffffff8116810361020c57608085015260a08101518060030b810361020c5760a0850152611e9f60c08201613a1c565b60c085015260e081015163ffffffff8116810361020c5760e0850152610100809101519060ff8216820361020c5784015287926fffffffffffffffffffffffffffffffff80611b75565b503d611e01565b89513d5f823e3d90fd5b90508481813d8311611f20575b611f118183610b29565b8101031261020c57515f611b30565b503d611f07565b87513d5f823e3d90fd5b92918186611f4192999399612352565b611b9b565b88517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152915086826024818688165afa8015611ef0575f90611f97575b612710925002045f611ae2565b508682813d8311611fbf575b611fad8183610b29565b8101031261020c576127109151611f8a565b503d611fa3565b7fca9e5d0f000000000000000000000000000000000000000000000000000000001415905061234c578160c0918101031261020c5761200481610a37565b906020612012818301610a37565b9160409283820135916060810135916001600160a01b039384841680940361020c578460a061204360808601610a6b565b94013598169486826122a6575b505050505f1461219d578351917fdd93f59a000000000000000000000000000000000000000000000000000000008352600483015282826024815f855af1918215611d03575f9261216e575b508482106120ae575050505050600190565b826004918551928380927f4a248d2a0000000000000000000000000000000000000000000000000000000082525afa928315611d03575f93612135575b505091517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260248101929092526044820152606490fd5b90809350813d8311612167575b61214c8183610b29565b8101031261020c576121606107a792611a09565b91856120eb565b503d612142565b9091508281813d8311612196575b6121868183610b29565b8101031261020c5751905f61209c565b503d61217c565b8351917fbd6015b4000000000000000000000000000000000000000000000000000000008352600483015282826024815f855af1918215611d03575f92612277575b508482106121f1575050505050600190565b826004918551928380927fd4b970460000000000000000000000000000000000000000000000000000000082525afa928315611d03575f9361213557505091517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260248101929092526044820152606490fd5b9091508281813d831161229f575b61228f8183610b29565b8101031261020c5751905f6121df565b503d612285565b6024918951928380927f70a0823100000000000000000000000000000000000000000000000000000000825230600483015287165afa9081156123425790859392915f9161230d575b5061230493916122fe91613f52565b91612352565b5f808086612050565b809450888092503d831161233b575b6123268183610b29565b8101031261020c5791518492906123046122ef565b503d61231c565b88513d5f823e3d90fd5b50505f90565b60446020925f80936001600160a01b03966040519788947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af1156123c057503d15601f3d1160015f51141617156123b357565b6390b8ec185f526004601cfd5b3d5f823e3d90fd5b5f80809381935af11561083f57565b9091612403604051926123e984610a78565b60208401935f85526001600160a01b0381961690526125bc565b809252565b1561240f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b6040519061244982610ac1565b602e82527f696e7432353620616d6f756e74290000000000000000000000000000000000006040837f546f6b656e5065726d697373696f6e73286164647265737320746f6b656e2c7560208201520152565b6125676110cb603c6040516124af81610add565b60588152602081017f436f6e73696465726174696f6e286164647265737320746f6b656e2c75696e7481527f32353620616d6f756e742c6164647265737320636f756e74657270617274792c60408301527f626f6f6c207061727469616c46696c6c416c6c6f776564290000000000000000606083015261252e61243c565b906040519586937f436f6e73696465726174696f6e20636f6e73696465726174696f6e2900000000602086015251809286860190611963565b820161257c8251809360208785019101611963565b0103601c810184520182610b29565b82809260045afa15903d18176110a657565b9160406055938151908152600b81019360ff8553602082015201522090565b51906020820151917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8ef83116125ee5750565b909160206001600160a01b03809351169260247f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935460405195869384927f70a082310000000000000000000000000000000000000000000000000000000084521660048301525afa91821561083f575f92612674575b50906127116110cb920190613f52565b91506020823d6020116126a2575b8161268f60209383610b29565b8101031261020c57905190612711612664565b3d9150612682565b6001600160a01b03809116907f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893541681146109b9577e9355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe99283548061278957509181740200000000000000000000000000000000000000005f9493859460e01b1717855582602083519301915af19061273f613fd5565b911561278057548061274f575090565b602490604051907fd66fcc380000000000000000000000000000000000000000000000000000000082526004820152fd5b50602081519101fd5b602490604051907fab7646c40000000000000000000000000000000000000000000000000000000082526004820152fd5b359062ffffff8216820361020c57565b9091907fffffffff00000000000000000000000000000000000000000000000000000000167fc1fb425e0000000000000000000000000000000000000000000000000000000081036128785750810160c08282031261020c5761282c82610a37565b9061283a8160208501610b4c565b9060a084013567ffffffffffffffff811161020c57612869612871926001600160a01b03926112f497016110b0565b9316826123d7565b5090613a5f565b7fd92aadfb000000000000000000000000000000000000000000000000000000008103612b1f575081016101208282031261020c576128b682610a37565b916020926128c683858401610b4c565b6128d260a08401610a37565b9360c08401359067ffffffffffffffff821161020c576128f39185016110b0565b9161010061290360e08601610a37565b9401359361294e6001600160a01b0380809816936129477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8ef8b885101511115612408565b16846123d7565b9590918785515116916129ae60409384519061296982610add565b8152898c820152868582015260016060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b9760248b612a2e7f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893549c8751906129e482610add565b8088169e8f83528785840152168882015260016060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b9b8651928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa948515612b1657505f94612ad3575b5093612ab484878c95856112f49f9b9899612ac29b976fffffffffffffffffffffffffffffffff9e9b9511612ac9575b9084612aaa92614518565b809b860152612352565b612abc61249b565b93613e7b565b1691613f0f565b9093508390612a9f565b95919a809794508681999692993d8311612b0f575b612af28183610b29565b8101031261020c5794519599939692959094939092612ab4612a6f565b503d612ae8565b513d5f823e3d90fd5b9192917f8d68a15600000000000000000000000000000000000000000000000000000000810361308f575082019060808383031261020c57612b6083610a37565b9060208401359260408501359067ffffffffffffffff821161020c57612b879186016110b0565b9260246020855181870151907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000091828116916014811061307a575b50509050604051928380927f70a0823100000000000000000000000000000000000000000000000000000000825230600483015260601c5afa90811561083f575f91613048575b508181029181830414901517156117215761271090049360405193612c2d85610ac1565b6028855260403660208701379430937f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821161170f5795905b815190602c821061169557866014840151601585015197601886015190602c87015194838690879c6001600160a01b0389166001600160a01b038816101561303d575b60ff1680612f2657506001600160a01b0391612d25917370c62c8b8e801124a4aa81ce07b637a3e83cb9197fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339862ffffff16906040519387165f528616602052806040521560051b6060035f209160405261259d565b1691855190858760280152866014015285528c602c88115f14612eaf57506115f56001600160a01b03938785946115f0612daf956115e26040519b8c9586938d8c8084169116105f14611755577f128acb080000000000000000000000000000000000000000000000000000000060208601526401000276a4918c168c8f16103060248701613a29565b9590946040525b1691161015612ea857505b7f80000000000000000000000000000000000000000000000000000000000000008114611721575f03945f861261170f57602c86921115612e4057505030939060188151106116955780517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe80160189091019081526028865290612c66565b939650935050925060608301358210612e5b57505050600190565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526060929092013560248301526044820152606490fd5b9050612dc1565b926001600160a01b03612f1e936115f0896115e2849b9a8a6115f598879b888616898416105f146117eb57886401000276a493816040519b8c997f128acb080000000000000000000000000000000000000000000000000000000060208c015216911610911660248701613a29565b949093612db6565b60028103612f7e57506001600160a01b0391612d25917346b3fdf7b5cde91ac049936bf0bdb12c5d22202e7fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b600c8103612fd657506001600160a01b0391612d25917396a7f53f7636c93735bf85de416a4ace94b56bd97fcf0b3414328c2bd327a4f093539d0d7d82fb94f893a2965c75cb470289cb5ac792639feb758b9861152d565b9091929394959650601181145f1461191e5750612d258d96959493926001600160a01b0392739367c561915f9d062afe3b57b18e30dec62b84887fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b869250889150612ca9565b90506020813d602011613072575b8161306360209383610b29565b8101031261020c57515f612c09565b3d9150613056565b8391925060140360031b1b1616805f80612bc2565b92837f103b48be00000000000000000000000000000000000000000000000000000000829495145f14613418575060c0918101031261020c576130d181610a37565b6020916130df838201610a37565b9160409182810135906130f460608201610a37565b9060a0613103608083016127ba565b910135926001600160a01b03808098169316926001908180851614935f928461338c575b839450895193613356575b630902f1ac5f52895f6004601c8a5afa1561334d57893d1061020c578b8b928760051b9687519288811851948084811c1614821517613300575b506060955061ffff6127109260081c1682030292028201910204975f601c8d8187019763022c0d9f88526080808901528360a08901528c82828a0101526004011886010152169101525f8060a48382875af1156123c057508284106131d75750505050505050600190565b60049187911561329c578551928380927fd21220a70000000000000000000000000000000000000000000000000000000082525afa908115611d03576107a79495965f9261326f575b5050935b519485947f97a6f3b900000000000000000000000000000000000000000000000000000000865216600485016040919493926001600160a01b03606083019616825260208201520152565b61328e9250803d10613295575b6132868183610b29565b8101906143ba565b8680613220565b503d61327c565b8551928380927f0dfe16810000000000000000000000000000000000000000000000000000000082525afa908115611d03576107a79495965f926132e3575b505093613224565b6132f99250803d10613295576132868183610b29565b86806132db565b9294505091936024601c5f926370a0823184528b85525afa1561334d578b3d1061020c575f5183811061333c579183916060948d94038e61316c565b60118d634e487b715f52526024601cfd5b833d5f823e3d90fd5b63a9059cbb8452868c850152848a8501528b5f6044601c870182875af11561334d57805f51148c3d1015163d1517613132575f80fd5b9250928851907f70a082310000000000000000000000000000000000000000000000000000000082523060048301528b82602481865afa801561340e575f906133df575b61271092500204918390613127565b508b82813d8311613407575b6133f58183610b29565b8101031261020c5761271091516133d0565b503d6133eb565b8a513d5f823e3d90fd5b7f38c9c1470000000000000000000000000000000000000000000000000000000081036134a15750019060a08183031261020c5761345581610a37565b9061346260408201610a37565b90608081013567ffffffffffffffff811161020c576112f4946134869183016110b0565b9260206001600160a01b036060840135941692013590614004565b7ff5b991890000000000000000000000000000000000000000000000000000000081036138ad575060a0918101031261020c576134dd81610a37565b906040810135916001600160a01b039283811680910361020c57613503606084016127ba565b6040517f392f37e9000000000000000000000000000000000000000000000000000000008152936001808316149160e086600481875afa90811561083f575f91885f985f935f945f9b5f935f96613832575b50878197839781969f61356790612408565b8c1561381d575b50505050505f91602088013561378a575b8983158d8582159384613779575b5050509061376b575b6136e1575b6135e393508686670de0b6b3a76400008099818099020493020495869461271061ffff838089800204818880020401818a89020402049660081c1682020490030204016143d9565b9003020494608082013586106136925750501561368c575f925b60405161360981610b0d565b5f8152823b1561020c575f946136688692604051988997889687957f022c0d9f00000000000000000000000000000000000000000000000000000000875260048701526024860152166044840152608060648401526084830190611984565b03925af1801561083f5761367d575b50600190565b61368690610af9565b5f613677565b5f6135fd565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260809190910135602482015260448101859052606490fd5b915060208b60248b60405196879384927f70a082310000000000000000000000000000000000000000000000000000000084526004840152165afa801561083f5784905f90613735575b8d9450039161359b565b50506020833d602011613763575b8161375060209383610b29565b8101031261020c57836135e3935161372b565b3d9150613743565b5060018083811c1614613596565b61378292612352565b8d858d61358d565b915060208b6024604051809681937f70a08231000000000000000000000000000000000000000000000000000000008352306004840152165afa801561083f578c935f916137e5575b5061271090602089013502049161357f565b9350506020833d602011613815575b8161380160209383610b29565b8101031261020c576127108c9351906137d3565b3d91506137f4565b9e509198509650945091505f8080808061356e565b9c50505094505050505060e0863d60e0116138a5575b8161385560e09383610b29565b8101031261020c578580519188602083015193604084015192606085015161389760c061389060a061388960808b01613a1c565b9901611a09565b9d01611a09565b92969490959b92945f613555565b3d9150613848565b7fc876d21d000000000000000000000000000000000000000000000000000000001415905061234c57816060918101031261020c576138eb81610a37565b60406138f960208401610a37565b920135906001600160a01b038091169083169273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee84145f1461395157504792509080831161393e575b505050600190565b6139499203906123c8565b5f8080613936565b916020602494604051958680927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa93841561083f575f946139bd575b508084116139a9575b50505050600190565b6139b4930391612352565b5f8080806139a0565b9093506020813d6020116139e9575b816139d960209383610b29565b8101031261020c5751925f613997565b3d91506139cc565b51906fffffffffffffffffffffffffffffffff8216820361020c57565b51908160070b820361020c57565b5190811515820361020c57565b919360a0936110cb96956001600160a01b0380941685521515602085015260408401521660608201528160808201520190611984565b91907f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa5f8154915580156109b957613bd093613ba7927f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935491613bcb6046604051613ac981610add565b605b8152602081017f536c697070616765416e64416374696f6e73286164647265737320726563697081527f69656e742c6164647265737320627579546f6b656e2c75696e74323536206d6960408301527f6e416d6f756e744f75742c62797465735b5d20616374696f6e732900000000006060830152613b4861243c565b90604051998a937f536c697070616765416e64416374696f6e7320736c697070616765416e64416360208601527f74696f6e73290000000000000000000000000000000000000000000000000000604086015251809286860190611963565b8201613bbc8251809360208785019101611963565b01036026810188520186610b29565b613e7b565b565b9291907f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa935f8554955584156109b9577f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893547f74696f6e73290000000000000000000000000000000000000000000000000000604095613d396046888051613c5981610add565b605b8152602081017f536c697070616765416e64416374696f6e73286164647265737320726563697081527f69656e742c6164647265737320627579546f6b656e2c75696e74323536206d69838301527f6e416d6f756e744f75742c62797465735b5d20616374696f6e732900000000006060830152613d15613cda61243c565b9284519889957f536c697070616765416e64416374696f6e7320736c697070616765416e644163602088015286015251809286860190611963565b8201613d2a8251809360208785019101611963565b01036026810185520183610b29565b613e52576e22d473030f116ddee9f6b43ac78ba392833b1561020c57613e365f96613e058894613de69b6001600160a01b038c9889519e8f9c8d9b8c9a7f137c29fe000000000000000000000000000000000000000000000000000000008c52613dba60048d018251602080916001600160a01b0381511684520151910152565b60208181015160448e015291015160648c015281516001600160a01b031660848c0152015160a48a0152565b1660c487015260e4860152610140610104860152610144850190611984565b907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc84830301610124850152611984565b03925af1908115612b165750613e495750565b613bd090610af9565b600486517f1c500e5c000000000000000000000000000000000000000000000000000000008152fd5b91909294936e22d473030f116ddee9f6b43ac78ba392833b1561020c57613efe5f96613e058894613de69a6001600160a01b0360409889519d8e9c8d9b8c9a7f137c29fe000000000000000000000000000000000000000000000000000000008c52613dba60048d018251602080916001600160a01b0381511684520151910152565b03925af1801561083f57613e495750565b9190917f49fa719b76f0f6b7e76be94b56c26671a548e1c712d5b13dc2874f70a75982765f526020526040519160405260605f20916040526010525f5260305fa0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82820982820291828083109203918083039161271095869109928286111561170f5714613fcf577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e919350828211900360fc1b910360041c170290565b50500490565b3d15613fff573d90613fe682610ba6565b91613ff46040519384610b29565b82523d5f602084013e565b606090565b9291906001600160a01b03808316946e22d473030f116ddee9f6b43ac78ba386146109b9575f9181169073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee820361412d575050506140569047613f52565b938051155f146140b1575050614087575f80809381935af1614076613fd5565b901561407f5750565b602081519101fd5b60046040517f01da1572000000000000000000000000000000000000000000000000000000008152fd5b909392602083018084116117215782511061169557835f9460208695850101525b602083519301915af16140e3613fd5565b901561407f5751159081614123575b506140f957565b60046040517f82d5d76a000000000000000000000000000000000000000000000000000000008152fd5b90503b155f6140f2565b8192959391979497155f1461414c57505050614087575f9283926140d2565b6040969594969392938051937f70a08231000000000000000000000000000000000000000000000000000000008552600495308787015260209560249587828881865afa8015611dba575f9061438b575b6141a79250613f52565b91868101808211614360578b5110614350578683918c0101528881036141da575b50505050505050915f929183926140d2565b90856044928451938480927fdd62ed3e000000000000000000000000000000000000000000000000000000008252308c8301528d8a8301525afa918215614346575f92614317575b508110614230575b806141c8565b6142bc575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93925f8093604493519687937f095ea7b30000000000000000000000000000000000000000000000000000000085528b8a8601528401525af1156123c057503d15601f3d1160015f51141617156142b157808080808061422a565b633e3f8f735f52601cfd5b80517f095ea7b300000000000000000000000000000000000000000000000000000000815287868201525f84820152845f60448382875af1156123c057503d15601f3d1160015f511416176142355784633e3f8f735f52601cfd5b9091508581813d831161433f575b61432f8183610b29565b8101031261020c5751905f614222565b503d614325565b83513d5f823e3d90fd5b85603288634e487b715f5252601cfd5b8660118a7f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b508782813d83116143b3575b6143a18183610b29565b8101031261020c576141a7915161419d565b503d614397565b9081602091031261020c57516001600160a01b038116810361020c5790565b929091670de0b6b3a7640000918285800204948381870204955f935b60ff85106144275760046040517f481b61af000000000000000000000000000000000000000000000000000000008152fd5b85818002048789886003878286890181898402040204958587105f146144a95702020401888383030204918215614468575b505060019101945b01936143f5565b14905061449e57600180820188888083800204860181848902040204116144915750875f614459565b9750505050509350505090565b955050509350505090565b020204018882840302049182156144c7575b50506001910394614461565b14905080156144de575b61449e576001875f6144bb565b5086867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83018180828002048601918702040204106144d1565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff828409818385029384808410930395099080851461459f5782805f0316809304600280826003021880830282030280830282030280830282030280830282030280830282030280920290030294600184805f0304019185841191030302920304170290565b505091500490565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090358181169392601481106145dc57505050565b60140360031b82901b1616915056fcb4511565e07d5302ca7efd9b70aebacc150fe2000000000000000000000000

Deployed Bytecode

0x608060405260043610801561036d575b361561036b573461020c577e9355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe9905f825492555f357fffffffff000000000000000000000000000000000000000000000000000000009081811692610356575b5080831691160361020c576001600160a01b03808216330361020c573660041161020c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36019160a01c61ffff16908160011461021057506002146100fa577f4e487b71000000000000000000000000000000000000000000000000000000005f52605160045260245ffd5b60801161020c5760043560443590816004013561011a81602485016145a7565b60601c918160141161020c575f81131561020257915b30810361018c575061016c6101749360387fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec33940191016145a7565b60601c612352565b60405161018081610b0d565b5f81525b602081519101f35b9161019b6101fd949315612408565b604051906101a882610a78565b33825260208201526101f76101c036602c8601610b4c565b92608d8501356001169460ad7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff773693019101610bc2565b91613bd2565b610174565b5060243591610130565b5f80fd5b9160a091501061020c57608435907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f82019160a4359180831680840361020c5761025e916004351614612408565b60405161026a81610a78565b33815260243560208201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c3601926080841261020c5760408051946102af86610ac1565b1261020c57604051906102c182610a78565b815260c4356020820152835260e43560208401526101043560408401526102e784610ba6565b916102f56040519384610b29565b8483523660a482011161020c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f5f91610342966101256020870137840101526101053560011692613bd2565b60405161034e81610b0d565b5f8152610184565b818093503660040360031b1b1616905f61006d565b005b5f3560e01c63fd3ad6d40361000f573461020c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360160e0811261020c5760601361020c5767ffffffffffffffff6064351161020c5736602360643501121561020c5767ffffffffffffffff606435600401351161020c573660246064356004013560051b60643501011161020c576001600160a01b0360a4351660a4350361020c5767ffffffffffffffff60c4351161020c5736602360c43501121561020c5767ffffffffffffffff60c435600401351161020c5736602460c4356004013560c43501011161020c5760806064356024015b60246064356004013560051b60643501018110610a145760a06064356004013560051b608020604051907f615e8d716cef7295e75dd3f1f10d679914ad6d7759e8e9459f0109ef7524170182526060600460208401376080820152207f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa54806109e357507f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa556001600160a01b0360a43516156109b9576001600160a01b037f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935416806109885760a4356001600160a01b03167f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde89355606435600401351561020c57602460643501356064350160448101359060487ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc602483013501910191604051926105db60c43560040135602460c43501858486610bf8565b84604052156109215760015b606435600401358110610853576004356001600160a01b03811680820361020c57602435906001600160a01b0382169283830361020c5760443593841580159061084a575b610722575b50505050506001600160a01b0360a435166001600160a01b037f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935416036106f8575f7f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893557f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa54806106c757602060405160018152f35b602490604051907fe25527c20000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517f5149e795000000000000000000000000000000000000000000000000000000008152fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81036107ab575050479183831061075d575061075392506123c8565b8080808080610631565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810193909352506044820152606490fd5b0390fd5b602493919250602090604051948580927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa92831561083f575f9361080b575b5083831061075d576108069350612352565b610753565b9092506020813d602011610837575b8161082760209383610b29565b8101031261020c575191846107f4565b3d915061081a565b6040513d5f823e3d90fd5b5080151561062c565b60246064358260051b010135606435019060448201359160487ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6024830135019101604051916108a4818387611a1d565b83604052156108ba5750505060019150016105e7565b7fffffffff00000000000000000000000000000000000000000000000000000000839584957f3c74eed6000000000000000000000000000000000000000000000000000000006107a796526004870152166024850152606060448501526064840191610a4b565b83927fffffffff000000000000000000000000000000000000000000000000000000006107a7937f3c74eed60000000000000000000000000000000000000000000000000000000086525f6004870152166024850152606060448501526064840191610a4b565b602490604051907f7407c0f80000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fe758b8d5000000000000000000000000000000000000000000000000000000008152fd5b602490604051907f9936cbab0000000000000000000000000000000000000000000000000000000082526004820152fd5b604481356064350160248101359182910184378220825260209182019101610461565b35906001600160a01b038216820361020c57565b601f8260209493601f1993818652868601375f8582860101520116010190565b3590811515820361020c57565b6040810190811067ffffffffffffffff821117610a9457604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6060810190811067ffffffffffffffff821117610a9457604052565b6080810190811067ffffffffffffffff821117610a9457604052565b67ffffffffffffffff8111610a9457604052565b6020810190811067ffffffffffffffff821117610a9457604052565b90601f601f19910116810190811067ffffffffffffffff821117610a9457604052565b80929103916080831261020c5760405190610b6682610ac1565b604082941261020c5760606040918251610b7f81610a78565b610b8882610a37565b81526020820135602082015284528281013560208501520135910152565b67ffffffffffffffff8111610a9457601f01601f191660200190565b929192610bce82610ba6565b91610bdc6040519384610b29565b82948184528183011161020c578281602093845f960137010152565b92919092610c0985848487856110ce565b15610c18575050505050600190565b7fffffffff00000000000000000000000000000000000000000000000000000000167f8d62436100000000000000000000000000000000000000000000000000000000036110a8578201906101008383031261020c57610c7783610a37565b604094602091610c95610c8b878901610a6b565b9560608801610b4c565b90610caf60e0880135956001600160a01b03943691610bc2565b968851928051988a60049a81878a01918d5afa158b3d18176110a6578a8560600181848a018d5afa158b3d18176110a657610d69605587947fbb7b783eb4b8ca46925c5384a6b9919df57cb83da8f76e37291f58d0dd5c439a60e18f730a7e848aca42d879ef06507fca0e7b33a0a63c1e8b610ddf988f610d3f905f8460a001538251928391018460a10161258b565b806081018252019160a18301809152528c60ac82019460ff8653013560c1820152015220926125bc565b891561108057637fffffff5b8c5191610d8183610add565b82528a1515898301525f8d83015260030b6060820152610dd18c51968792868b8501987f3eece7db000000000000000000000000000000000000000000000000000000008a5216602485016119a9565b03601f198101865285610b29565b16927f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde89354168314611058577e9355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe99182548061102957505f9182917f67ca7c910000000000000001000000000000000000000000000000000000000086178555519082865af190610e6b613fd5565b9115611021575480610ff25750808380610e8a935183010191016119f3565b949050838510610ea05750505050505050600190565b859697509082915f14610f76578751958680927f5f64b55b0000000000000000000000000000000000000000000000000000000082525afa908115610f6c575f91610f35575b506107a79350945b517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0390951693850193845260208401526040830152829160600190565b905083813d8311610f65575b610f4b8183610b29565b8101031261020c57610f5f6107a793611a09565b5f610ee6565b503d610f41565b86513d5f823e3d90fd5b8751958680927f0fc63d100000000000000000000000000000000000000000000000000000000082525afa908115610f6c575f91610fbb575b506107a7935094610eee565b905083813d8311610feb575b610fd18183610b29565b8101031261020c57610fe56107a793611a09565b5f610faf565b503d610fc7565b866024918951917fd66fcc38000000000000000000000000000000000000000000000000000000008352820152fd5b508281519101fd5b886024918b51917fab7646c4000000000000000000000000000000000000000000000000000000008352820152fd5b8688517fe758b8d5000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000610d75565bfe5b505050505f90565b9080601f8301121561020c578160206110cb93359101610bc2565b90565b9091907fffffffff00000000000000000000000000000000000000000000000000000000167f5228831d0000000000000000000000000000000000000000000000000000000081036112f9575081016101608282031261020c5761113182610a37565b9061113f8160208501610b4c565b9461114c60a08501610a37565b9460c08501359667ffffffffffffffff881161020c57611296866fffffffffffffffffffffffffffffffff966111f061119e8860e06111976112f49f9b6112ec9c6112859f016110b0565b9501610b4c565b9a6111b76001600160a01b039788809316993691610bc2565b926111e97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8ef6020895101511115612408565b16856123d7565b9990918b61127f88806112038c856123d7565b929094515116917f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde89354906040519361123a85610add565b845260208401521660408201525f6060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b9c613a5f565b898661128f61249b565b9286613e7b565b51511690604051916112a783610add565b825284602083015260408201525f6060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b911691613f0f565b600190565b929391927f0dfeb4190000000000000000000000000000000000000000000000000000000081036113705750820160a08382031261020c576112f4936113616001600160a01b0361135961136a94602061135289610a37565b9801610b4c565b9516856123d7565b50923691610bc2565b91613a5f565b92949193927f9ebf8e8d000000000000000000000000000000000000000000000000000000000361195a5784019360e08186031261020c576113b181610a37565b9260208201359067ffffffffffffffff821161020c576113e36113d9886113eb9486016110b0565b9760408501610b4c565b953691610bc2565b8051608901948560891161172157601f1961141e61140888610ba6565b97611416604051998a610b29565b808952610ba6565b013660208801378051602001518660480152604086606801818360200160045afa1560403d18176110a65761146561146a925f8860a801538051906020018860a90161258b565b6125bc565b5f937f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821161170f5795905b815190602c821061169557866014840151601585015197601886015190602c87015194838690879c6001600160a01b0389166001600160a01b038816101561194f575b60ff168061180757506001600160a01b0391611556917370c62c8b8e801124a4aa81ce07b637a3e83cb9197fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e33985b62ffffff16906040519387165f528616602052806040521560051b6060035f209160405261259d565b1691855190858760280152866014015285528c602c88115f1461177357506115f56001600160a01b03938785946115f0611604956115e26040519b8c9586938d8c8084169116105f14611755578d8c806401000276a4945b7f128acb080000000000000000000000000000000000000000000000000000000060208a0152169116103060248701613a29565b03601f198101845283610b29565b6126aa565b602080825183010191016119f3565b9590946040525b169116101561174e57505b7f80000000000000000000000000000000000000000000000000000000000000008114611721575f03945f861261170f57602c869211156116a757505030939060188151106116955780517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe80160189091019081526028865290611496565b634e487b715f5260326020526024601cfd5b939650935050925060c083013582106116c257505050600190565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260c0929092013560248301526044820152606490fd5b634e487b715f5260116020526024601cfd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9050611616565b8d8c8073fffd8963efd1fc6a506488495d951d5263988d25946115ae565b926001600160a01b036117e3936115f0896115e2849b9a8a6115f598879b888616898416105f146117eb57886401000276a4935b816040519b8c997f128acb080000000000000000000000000000000000000000000000000000000060208c015216911610911660248701613a29565b94909361160b565b8873fffd8963efd1fc6a506488495d951d5263988d25936117a7565b6002810361185f57506001600160a01b0391611556917346b3fdf7b5cde91ac049936bf0bdb12c5d22202e7fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b600c81036118b757506001600160a01b0391611556917396a7f53f7636c93735bf85de416a4ace94b56bd97fcf0b3414328c2bd327a4f093539d0d7d82fb94f893a2965c75cb470289cb5ac792639feb758b9861152d565b9091929394959650601181145f1461191e57506115568d96959493926001600160a01b0392739367c561915f9d062afe3b57b18e30dec62b84887fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b602490604051907fd3b1276d0000000000000000000000000000000000000000000000000000000082526004820152fd5b8692508891506114d9565b50925050505f90565b5f5b8381106119745750505f910152565b8181015183820152602001611965565b90601f19601f6020936119a281518092818752878088019101611963565b0116010190565b60606110cb94936001600160a01b0360c094168352805160208401526020810151151560408401526040810151151582840152015160030b60808201528160a08201520190611984565b919082604091031261020c576020825192015190565b51906001600160a01b038216820361020c57565b919091611a2b8284836127ca565b15611a3857505050600190565b82907fffffffff00000000000000000000000000000000000000000000000000000000167f67848fe7000000000000000000000000000000000000000000000000000000008103611fc6575060c0918101031261020c57611a9881610a37565b602090611aa6828401610a37565b9160409283850135946060810135916001600160a01b039081841680940361020c5760a0611ad660808501610a6b565b930135975f9080611f46575b508781611f3157869150916024849351809481937f70a08231000000000000000000000000000000000000000000000000000000008352896004840152165afa908115611f27575f91611efa575b5086517f1865c57d0000000000000000000000000000000000000000000000000000000081526101208082600481895afa918215611ef0579089949392915f92611df2575b50506fffffffffffffffffffffffffffffffff809188875f14611dea575051165b169003955b8315611dc457637fffffff5b835197611bb389610add565b8852841515878901525f8489015260030b6060880152611c0e8351611bd781610b0d565b5f8152845198899485947f3eece7db00000000000000000000000000000000000000000000000000000000865216600485016119a9565b03815f865af1938415611dba575f94611d89575b50858410611c3557505050505050600190565b6004939291829115611d0d578551948580927f5f64b55b0000000000000000000000000000000000000000000000000000000082525afa908115611d03575f91611ccc575b506107a79250925b519384937f97a6f3b9000000000000000000000000000000000000000000000000000000008552600485016040919493926001600160a01b03606083019616825260208201520152565b905082813d8311611cfc575b611ce28183610b29565b8101031261020c57611cf66107a792611a09565b85611c7a565b503d611cd8565b84513d5f823e3d90fd5b8551948580927f0fc63d100000000000000000000000000000000000000000000000000000000082525afa908115611d03575f91611d52575b506107a7925092611c82565b905082813d8311611d82575b611d688183610b29565b8101031261020c57611d7c6107a792611a09565b85611d46565b503d611d5e565b611daa919450853d8711611db3575b611da28183610b29565b8101906119f3565b9050925f611c22565b503d611d98565b85513d5f823e3d90fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000611ba7565b015116611b96565b818096508193503d8311611ee9575b611e0b8183610b29565b8101031261020c57885193840184811067ffffffffffffffff821117610a94578952611e36816139f1565b8452611e438782016139f1565b87850152611e52898201613a0e565b89850152611e6260608201613a0e565b6060850152608081015164ffffffffff8116810361020c57608085015260a08101518060030b810361020c5760a0850152611e9f60c08201613a1c565b60c085015260e081015163ffffffff8116810361020c5760e0850152610100809101519060ff8216820361020c5784015287926fffffffffffffffffffffffffffffffff80611b75565b503d611e01565b89513d5f823e3d90fd5b90508481813d8311611f20575b611f118183610b29565b8101031261020c57515f611b30565b503d611f07565b87513d5f823e3d90fd5b92918186611f4192999399612352565b611b9b565b88517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152915086826024818688165afa8015611ef0575f90611f97575b612710925002045f611ae2565b508682813d8311611fbf575b611fad8183610b29565b8101031261020c576127109151611f8a565b503d611fa3565b7fca9e5d0f000000000000000000000000000000000000000000000000000000001415905061234c578160c0918101031261020c5761200481610a37565b906020612012818301610a37565b9160409283820135916060810135916001600160a01b039384841680940361020c578460a061204360808601610a6b565b94013598169486826122a6575b505050505f1461219d578351917fdd93f59a000000000000000000000000000000000000000000000000000000008352600483015282826024815f855af1918215611d03575f9261216e575b508482106120ae575050505050600190565b826004918551928380927f4a248d2a0000000000000000000000000000000000000000000000000000000082525afa928315611d03575f93612135575b505091517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260248101929092526044820152606490fd5b90809350813d8311612167575b61214c8183610b29565b8101031261020c576121606107a792611a09565b91856120eb565b503d612142565b9091508281813d8311612196575b6121868183610b29565b8101031261020c5751905f61209c565b503d61217c565b8351917fbd6015b4000000000000000000000000000000000000000000000000000000008352600483015282826024815f855af1918215611d03575f92612277575b508482106121f1575050505050600190565b826004918551928380927fd4b970460000000000000000000000000000000000000000000000000000000082525afa928315611d03575f9361213557505091517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260248101929092526044820152606490fd5b9091508281813d831161229f575b61228f8183610b29565b8101031261020c5751905f6121df565b503d612285565b6024918951928380927f70a0823100000000000000000000000000000000000000000000000000000000825230600483015287165afa9081156123425790859392915f9161230d575b5061230493916122fe91613f52565b91612352565b5f808086612050565b809450888092503d831161233b575b6123268183610b29565b8101031261020c5791518492906123046122ef565b503d61231c565b88513d5f823e3d90fd5b50505f90565b60446020925f80936001600160a01b03966040519788947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af1156123c057503d15601f3d1160015f51141617156123b357565b6390b8ec185f526004601cfd5b3d5f823e3d90fd5b5f80809381935af11561083f57565b9091612403604051926123e984610a78565b60208401935f85526001600160a01b0381961690526125bc565b809252565b1561240f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b6040519061244982610ac1565b602e82527f696e7432353620616d6f756e74290000000000000000000000000000000000006040837f546f6b656e5065726d697373696f6e73286164647265737320746f6b656e2c7560208201520152565b6125676110cb603c6040516124af81610add565b60588152602081017f436f6e73696465726174696f6e286164647265737320746f6b656e2c75696e7481527f32353620616d6f756e742c6164647265737320636f756e74657270617274792c60408301527f626f6f6c207061727469616c46696c6c416c6c6f776564290000000000000000606083015261252e61243c565b906040519586937f436f6e73696465726174696f6e20636f6e73696465726174696f6e2900000000602086015251809286860190611963565b820161257c8251809360208785019101611963565b0103601c810184520182610b29565b82809260045afa15903d18176110a657565b9160406055938151908152600b81019360ff8553602082015201522090565b51906020820151917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8ef83116125ee5750565b909160206001600160a01b03809351169260247f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935460405195869384927f70a082310000000000000000000000000000000000000000000000000000000084521660048301525afa91821561083f575f92612674575b50906127116110cb920190613f52565b91506020823d6020116126a2575b8161268f60209383610b29565b8101031261020c57905190612711612664565b3d9150612682565b6001600160a01b03809116907f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893541681146109b9577e9355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe99283548061278957509181740200000000000000000000000000000000000000005f9493859460e01b1717855582602083519301915af19061273f613fd5565b911561278057548061274f575090565b602490604051907fd66fcc380000000000000000000000000000000000000000000000000000000082526004820152fd5b50602081519101fd5b602490604051907fab7646c40000000000000000000000000000000000000000000000000000000082526004820152fd5b359062ffffff8216820361020c57565b9091907fffffffff00000000000000000000000000000000000000000000000000000000167fc1fb425e0000000000000000000000000000000000000000000000000000000081036128785750810160c08282031261020c5761282c82610a37565b9061283a8160208501610b4c565b9060a084013567ffffffffffffffff811161020c57612869612871926001600160a01b03926112f497016110b0565b9316826123d7565b5090613a5f565b7fd92aadfb000000000000000000000000000000000000000000000000000000008103612b1f575081016101208282031261020c576128b682610a37565b916020926128c683858401610b4c565b6128d260a08401610a37565b9360c08401359067ffffffffffffffff821161020c576128f39185016110b0565b9161010061290360e08601610a37565b9401359361294e6001600160a01b0380809816936129477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8ef8b885101511115612408565b16846123d7565b9590918785515116916129ae60409384519061296982610add565b8152898c820152868582015260016060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b9760248b612a2e7f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893549c8751906129e482610add565b8088169e8f83528785840152168882015260016060820152601f19019081517f7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa835260a083209252565b9b8651928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa948515612b1657505f94612ad3575b5093612ab484878c95856112f49f9b9899612ac29b976fffffffffffffffffffffffffffffffff9e9b9511612ac9575b9084612aaa92614518565b809b860152612352565b612abc61249b565b93613e7b565b1691613f0f565b9093508390612a9f565b95919a809794508681999692993d8311612b0f575b612af28183610b29565b8101031261020c5794519599939692959094939092612ab4612a6f565b503d612ae8565b513d5f823e3d90fd5b9192917f8d68a15600000000000000000000000000000000000000000000000000000000810361308f575082019060808383031261020c57612b6083610a37565b9060208401359260408501359067ffffffffffffffff821161020c57612b879186016110b0565b9260246020855181870151907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000091828116916014811061307a575b50509050604051928380927f70a0823100000000000000000000000000000000000000000000000000000000825230600483015260601c5afa90811561083f575f91613048575b508181029181830414901517156117215761271090049360405193612c2d85610ac1565b6028855260403660208701379430937f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821161170f5795905b815190602c821061169557866014840151601585015197601886015190602c87015194838690879c6001600160a01b0389166001600160a01b038816101561303d575b60ff1680612f2657506001600160a01b0391612d25917370c62c8b8e801124a4aa81ce07b637a3e83cb9197fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339862ffffff16906040519387165f528616602052806040521560051b6060035f209160405261259d565b1691855190858760280152866014015285528c602c88115f14612eaf57506115f56001600160a01b03938785946115f0612daf956115e26040519b8c9586938d8c8084169116105f14611755577f128acb080000000000000000000000000000000000000000000000000000000060208601526401000276a4918c168c8f16103060248701613a29565b9590946040525b1691161015612ea857505b7f80000000000000000000000000000000000000000000000000000000000000008114611721575f03945f861261170f57602c86921115612e4057505030939060188151106116955780517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe80160189091019081526028865290612c66565b939650935050925060608301358210612e5b57505050600190565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526060929092013560248301526044820152606490fd5b9050612dc1565b926001600160a01b03612f1e936115f0896115e2849b9a8a6115f598879b888616898416105f146117eb57886401000276a493816040519b8c997f128acb080000000000000000000000000000000000000000000000000000000060208c015216911610911660248701613a29565b949093612db6565b60028103612f7e57506001600160a01b0391612d25917346b3fdf7b5cde91ac049936bf0bdb12c5d22202e7fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b600c8103612fd657506001600160a01b0391612d25917396a7f53f7636c93735bf85de416a4ace94b56bd97fcf0b3414328c2bd327a4f093539d0d7d82fb94f893a2965c75cb470289cb5ac792639feb758b9861152d565b9091929394959650601181145f1461191e5750612d258d96959493926001600160a01b0392739367c561915f9d062afe3b57b18e30dec62b84887fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b549263fa461e339861152d565b869250889150612ca9565b90506020813d602011613072575b8161306360209383610b29565b8101031261020c57515f612c09565b3d9150613056565b8391925060140360031b1b1616805f80612bc2565b92837f103b48be00000000000000000000000000000000000000000000000000000000829495145f14613418575060c0918101031261020c576130d181610a37565b6020916130df838201610a37565b9160409182810135906130f460608201610a37565b9060a0613103608083016127ba565b910135926001600160a01b03808098169316926001908180851614935f928461338c575b839450895193613356575b630902f1ac5f52895f6004601c8a5afa1561334d57893d1061020c578b8b928760051b9687519288811851948084811c1614821517613300575b506060955061ffff6127109260081c1682030292028201910204975f601c8d8187019763022c0d9f88526080808901528360a08901528c82828a0101526004011886010152169101525f8060a48382875af1156123c057508284106131d75750505050505050600190565b60049187911561329c578551928380927fd21220a70000000000000000000000000000000000000000000000000000000082525afa908115611d03576107a79495965f9261326f575b5050935b519485947f97a6f3b900000000000000000000000000000000000000000000000000000000865216600485016040919493926001600160a01b03606083019616825260208201520152565b61328e9250803d10613295575b6132868183610b29565b8101906143ba565b8680613220565b503d61327c565b8551928380927f0dfe16810000000000000000000000000000000000000000000000000000000082525afa908115611d03576107a79495965f926132e3575b505093613224565b6132f99250803d10613295576132868183610b29565b86806132db565b9294505091936024601c5f926370a0823184528b85525afa1561334d578b3d1061020c575f5183811061333c579183916060948d94038e61316c565b60118d634e487b715f52526024601cfd5b833d5f823e3d90fd5b63a9059cbb8452868c850152848a8501528b5f6044601c870182875af11561334d57805f51148c3d1015163d1517613132575f80fd5b9250928851907f70a082310000000000000000000000000000000000000000000000000000000082523060048301528b82602481865afa801561340e575f906133df575b61271092500204918390613127565b508b82813d8311613407575b6133f58183610b29565b8101031261020c5761271091516133d0565b503d6133eb565b8a513d5f823e3d90fd5b7f38c9c1470000000000000000000000000000000000000000000000000000000081036134a15750019060a08183031261020c5761345581610a37565b9061346260408201610a37565b90608081013567ffffffffffffffff811161020c576112f4946134869183016110b0565b9260206001600160a01b036060840135941692013590614004565b7ff5b991890000000000000000000000000000000000000000000000000000000081036138ad575060a0918101031261020c576134dd81610a37565b906040810135916001600160a01b039283811680910361020c57613503606084016127ba565b6040517f392f37e9000000000000000000000000000000000000000000000000000000008152936001808316149160e086600481875afa90811561083f575f91885f985f935f945f9b5f935f96613832575b50878197839781969f61356790612408565b8c1561381d575b50505050505f91602088013561378a575b8983158d8582159384613779575b5050509061376b575b6136e1575b6135e393508686670de0b6b3a76400008099818099020493020495869461271061ffff838089800204818880020401818a89020402049660081c1682020490030204016143d9565b9003020494608082013586106136925750501561368c575f925b60405161360981610b0d565b5f8152823b1561020c575f946136688692604051988997889687957f022c0d9f00000000000000000000000000000000000000000000000000000000875260048701526024860152166044840152608060648401526084830190611984565b03925af1801561083f5761367d575b50600190565b61368690610af9565b5f613677565b5f6135fd565b6040517f97a6f3b90000000000000000000000000000000000000000000000000000000081526001600160a01b0391909116600482015260809190910135602482015260448101859052606490fd5b915060208b60248b60405196879384927f70a082310000000000000000000000000000000000000000000000000000000084526004840152165afa801561083f5784905f90613735575b8d9450039161359b565b50506020833d602011613763575b8161375060209383610b29565b8101031261020c57836135e3935161372b565b3d9150613743565b5060018083811c1614613596565b61378292612352565b8d858d61358d565b915060208b6024604051809681937f70a08231000000000000000000000000000000000000000000000000000000008352306004840152165afa801561083f578c935f916137e5575b5061271090602089013502049161357f565b9350506020833d602011613815575b8161380160209383610b29565b8101031261020c576127108c9351906137d3565b3d91506137f4565b9e509198509650945091505f8080808061356e565b9c50505094505050505060e0863d60e0116138a5575b8161385560e09383610b29565b8101031261020c578580519188602083015193604084015192606085015161389760c061389060a061388960808b01613a1c565b9901611a09565b9d01611a09565b92969490959b92945f613555565b3d9150613848565b7fc876d21d000000000000000000000000000000000000000000000000000000001415905061234c57816060918101031261020c576138eb81610a37565b60406138f960208401610a37565b920135906001600160a01b038091169083169273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee84145f1461395157504792509080831161393e575b505050600190565b6139499203906123c8565b5f8080613936565b916020602494604051958680927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa93841561083f575f946139bd575b508084116139a9575b50505050600190565b6139b4930391612352565b5f8080806139a0565b9093506020813d6020116139e9575b816139d960209383610b29565b8101031261020c5751925f613997565b3d91506139cc565b51906fffffffffffffffffffffffffffffffff8216820361020c57565b51908160070b820361020c57565b5190811515820361020c57565b919360a0936110cb96956001600160a01b0380941685521515602085015260408401521660608201528160808201520190611984565b91907f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa5f8154915580156109b957613bd093613ba7927f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde8935491613bcb6046604051613ac981610add565b605b8152602081017f536c697070616765416e64416374696f6e73286164647265737320726563697081527f69656e742c6164647265737320627579546f6b656e2c75696e74323536206d6960408301527f6e416d6f756e744f75742c62797465735b5d20616374696f6e732900000000006060830152613b4861243c565b90604051998a937f536c697070616765416e64416374696f6e7320736c697070616765416e64416360208601527f74696f6e73290000000000000000000000000000000000000000000000000000604086015251809286860190611963565b8201613bbc8251809360208785019101611963565b01036026810188520186610b29565b613e7b565b565b9291907f1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa935f8554955584156109b9577f46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893547f74696f6e73290000000000000000000000000000000000000000000000000000604095613d396046888051613c5981610add565b605b8152602081017f536c697070616765416e64416374696f6e73286164647265737320726563697081527f69656e742c6164647265737320627579546f6b656e2c75696e74323536206d69838301527f6e416d6f756e744f75742c62797465735b5d20616374696f6e732900000000006060830152613d15613cda61243c565b9284519889957f536c697070616765416e64416374696f6e7320736c697070616765416e644163602088015286015251809286860190611963565b8201613d2a8251809360208785019101611963565b01036026810185520183610b29565b613e52576e22d473030f116ddee9f6b43ac78ba392833b1561020c57613e365f96613e058894613de69b6001600160a01b038c9889519e8f9c8d9b8c9a7f137c29fe000000000000000000000000000000000000000000000000000000008c52613dba60048d018251602080916001600160a01b0381511684520151910152565b60208181015160448e015291015160648c015281516001600160a01b031660848c0152015160a48a0152565b1660c487015260e4860152610140610104860152610144850190611984565b907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc84830301610124850152611984565b03925af1908115612b165750613e495750565b613bd090610af9565b600486517f1c500e5c000000000000000000000000000000000000000000000000000000008152fd5b91909294936e22d473030f116ddee9f6b43ac78ba392833b1561020c57613efe5f96613e058894613de69a6001600160a01b0360409889519d8e9c8d9b8c9a7f137c29fe000000000000000000000000000000000000000000000000000000008c52613dba60048d018251602080916001600160a01b0381511684520151910152565b03925af1801561083f57613e495750565b9190917f49fa719b76f0f6b7e76be94b56c26671a548e1c712d5b13dc2874f70a75982765f526020526040519160405260605f20916040526010525f5260305fa0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82820982820291828083109203918083039161271095869109928286111561170f5714613fcf577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e919350828211900360fc1b910360041c170290565b50500490565b3d15613fff573d90613fe682610ba6565b91613ff46040519384610b29565b82523d5f602084013e565b606090565b9291906001600160a01b03808316946e22d473030f116ddee9f6b43ac78ba386146109b9575f9181169073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee820361412d575050506140569047613f52565b938051155f146140b1575050614087575f80809381935af1614076613fd5565b901561407f5750565b602081519101fd5b60046040517f01da1572000000000000000000000000000000000000000000000000000000008152fd5b909392602083018084116117215782511061169557835f9460208695850101525b602083519301915af16140e3613fd5565b901561407f5751159081614123575b506140f957565b60046040517f82d5d76a000000000000000000000000000000000000000000000000000000008152fd5b90503b155f6140f2565b8192959391979497155f1461414c57505050614087575f9283926140d2565b6040969594969392938051937f70a08231000000000000000000000000000000000000000000000000000000008552600495308787015260209560249587828881865afa8015611dba575f9061438b575b6141a79250613f52565b91868101808211614360578b5110614350578683918c0101528881036141da575b50505050505050915f929183926140d2565b90856044928451938480927fdd62ed3e000000000000000000000000000000000000000000000000000000008252308c8301528d8a8301525afa918215614346575f92614317575b508110614230575b806141c8565b6142bc575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93925f8093604493519687937f095ea7b30000000000000000000000000000000000000000000000000000000085528b8a8601528401525af1156123c057503d15601f3d1160015f51141617156142b157808080808061422a565b633e3f8f735f52601cfd5b80517f095ea7b300000000000000000000000000000000000000000000000000000000815287868201525f84820152845f60448382875af1156123c057503d15601f3d1160015f511416176142355784633e3f8f735f52601cfd5b9091508581813d831161433f575b61432f8183610b29565b8101031261020c5751905f614222565b503d614325565b83513d5f823e3d90fd5b85603288634e487b715f5252601cfd5b8660118a7f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b508782813d83116143b3575b6143a18183610b29565b8101031261020c576141a7915161419d565b503d614397565b9081602091031261020c57516001600160a01b038116810361020c5790565b929091670de0b6b3a7640000918285800204948381870204955f935b60ff85106144275760046040517f481b61af000000000000000000000000000000000000000000000000000000008152fd5b85818002048789886003878286890181898402040204958587105f146144a95702020401888383030204918215614468575b505060019101945b01936143f5565b14905061449e57600180820188888083800204860181848902040204116144915750875f614459565b9750505050509350505090565b955050509350505090565b020204018882840302049182156144c7575b50506001910394614461565b14905080156144de575b61449e576001875f6144bb565b5086867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83018180828002048601918702040204106144d1565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff828409818385029384808410930395099080851461459f5782805f0316809304600280826003021880830282030280830282030280830282030280830282030280830282030280920290030294600184805f0304019185841191030302920304170290565b505091500490565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090358181169392601481106145dc57505050565b60140360031b82901b1616915056

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

fcb4511565e07d5302ca7efd9b70aebacc150fe2000000000000000000000000

-----Decoded View---------------
Arg [0] : gitCommit (bytes20): 0xfcb4511565e07d5302ca7efd9b70aebacc150fe2

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : fcb4511565e07d5302ca7efd9b70aebacc150fe2000000000000000000000000


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

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

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