ETH Price: $2,062.72 (-0.66%)
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Execute244607342026-02-15 7:33:233 hrs ago1771140803IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000030520.05136648
Transfer244578062026-02-14 21:45:4713 hrs ago1771105547IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.00000110.05251653
Execute244578042026-02-14 21:45:2313 hrs ago1771105523IN
0xEbE0FA42...c19c2c0Ee
0.6 ETH0.000006310.04455049
Execute244435082026-02-12 21:53:592 days ago1770933239IN
0xEbE0FA42...c19c2c0Ee
0.2938 ETH0.000010070.05359666
Execute244362652026-02-11 21:37:473 days ago1770845867IN
0xEbE0FA42...c19c2c0Ee
0.3336 ETH0.000014580.08665794
Execute244362402026-02-11 21:32:473 days ago1770845567IN
0xEbE0FA42...c19c2c0Ee
0.3336 ETH0.000052150.08804515
Execute244322572026-02-11 8:12:474 days ago1770797567IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000011110.07329682
Execute244286592026-02-10 20:08:234 days ago1770754103IN
0xEbE0FA42...c19c2c0Ee
0.0073959 ETH0.000013330.09933536
Execute244278582026-02-10 17:27:114 days ago1770744431IN
0xEbE0FA42...c19c2c0Ee
0.0098612 ETH0.000016410.12516126
Execute244222092026-02-09 22:31:115 days ago1770676271IN
0xEbE0FA42...c19c2c0Ee
0.02280714 ETH0.000011250.08384837
Execute244195402026-02-09 13:34:595 days ago1770644099IN
0xEbE0FA42...c19c2c0Ee
0.00247903 ETH0.00000930.06929226
Execute244190282026-02-09 11:52:355 days ago1770637955IN
0xEbE0FA42...c19c2c0Ee
0.01239518 ETH0.000009160.061961
Execute244066922026-02-07 18:19:477 days ago1770488387IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000710030.11015423
Execute243588982026-02-01 2:01:5914 days ago1769911319IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000011660.07874943
Execute243381642026-01-29 4:36:5917 days ago1769661419IN
0xEbE0FA42...c19c2c0Ee
3 ETH0.00006450.09123414
Execute243289872026-01-27 21:54:4718 days ago1769550887IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000335630.09463219
Execute243284072026-01-27 19:57:4718 days ago1769543867IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.00003280.0638309
Execute243283932026-01-27 19:54:5918 days ago1769543699IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000202690.06984033
Execute243150922026-01-25 23:23:4720 days ago1769383427IN
0xEbE0FA42...c19c2c0Ee
0.00895 ETH0.00003470.11538634
Execute243149172026-01-25 22:48:3520 days ago1769381315IN
0xEbE0FA42...c19c2c0Ee
0.01813 ETH0.000103290.19608784
Execute242286432026-01-13 22:04:1132 days ago1768341851IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000142420.04662994
Execute241912702026-01-08 16:52:5937 days ago1767891179IN
0xEbE0FA42...c19c2c0Ee
0.8 ETH0.000216160.31565603
Execute241910602026-01-08 16:10:4737 days ago1767888647IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.001043270.30455406
Execute241792692026-01-07 0:38:1139 days ago1767746291IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000021930.03357932
Execute241782112026-01-06 21:05:4739 days ago1767733547IN
0xEbE0FA42...c19c2c0Ee
0 ETH0.000007620.05037015
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer244611642026-02-15 8:59:351 hr ago1771145975
0xEbE0FA42...c19c2c0Ee
0.00950911 ETH
Transfer244611642026-02-15 8:59:351 hr ago1771145975
0xEbE0FA42...c19c2c0Ee
0.00950911 ETH
Transfer244610542026-02-15 8:37:232 hrs ago1771144643
0xEbE0FA42...c19c2c0Ee
0.00024234 ETH
Transfer244610542026-02-15 8:37:232 hrs ago1771144643
0xEbE0FA42...c19c2c0Ee
0.00024234 ETH
Transfer244609922026-02-15 8:24:592 hrs ago1771143899
0xEbE0FA42...c19c2c0Ee
0.00023966 ETH
Transfer244609922026-02-15 8:24:592 hrs ago1771143899
0xEbE0FA42...c19c2c0Ee
0.00023966 ETH
Transfer244609232026-02-15 8:11:112 hrs ago1771143071
0xEbE0FA42...c19c2c0Ee
0.00018814 ETH
Transfer244609232026-02-15 8:11:112 hrs ago1771143071
0xEbE0FA42...c19c2c0Ee
0.00018814 ETH
Deposit244609152026-02-15 8:09:352 hrs ago1771142975
0xEbE0FA42...c19c2c0Ee
0.5949 ETH
Execute244609152026-02-15 8:09:352 hrs ago1771142975
0xEbE0FA42...c19c2c0Ee
0.5949 ETH
Transfer244608422026-02-15 7:54:593 hrs ago1771142099
0xEbE0FA42...c19c2c0Ee
0.0000717 ETH
Transfer244608422026-02-15 7:54:593 hrs ago1771142099
0xEbE0FA42...c19c2c0Ee
0.0000717 ETH
Transfer244607522026-02-15 7:36:593 hrs ago1771141019
0xEbE0FA42...c19c2c0Ee
0.00986161 ETH
Transfer244607522026-02-15 7:36:593 hrs ago1771141019
0xEbE0FA42...c19c2c0Ee
0.00986161 ETH
Deposit244607522026-02-15 7:36:593 hrs ago1771141019
0xEbE0FA42...c19c2c0Ee
0.00986161 ETH
Transfer244607522026-02-15 7:36:593 hrs ago1771141019
0xEbE0FA42...c19c2c0Ee
0.00986161 ETH
Transfer244603962026-02-15 6:25:354 hrs ago1771136735
0xEbE0FA42...c19c2c0Ee
0.00262551 ETH
Transfer244603962026-02-15 6:25:354 hrs ago1771136735
0xEbE0FA42...c19c2c0Ee
0.00262551 ETH
Deposit244603262026-02-15 6:11:354 hrs ago1771135895
0xEbE0FA42...c19c2c0Ee
0.00237378 ETH
Execute244603262026-02-15 6:11:354 hrs ago1771135895
0xEbE0FA42...c19c2c0Ee
0.00237378 ETH
Deposit244603232026-02-15 6:10:594 hrs ago1771135859
0xEbE0FA42...c19c2c0Ee
0.00204249 ETH
Execute244603232026-02-15 6:10:594 hrs ago1771135859
0xEbE0FA42...c19c2c0Ee
0.00204249 ETH
Transfer244597772026-02-15 4:21:356 hrs ago1771129295
0xEbE0FA42...c19c2c0Ee
0.21349196 ETH
Transfer244597772026-02-15 4:21:356 hrs ago1771129295
0xEbE0FA42...c19c2c0Ee
0.21349196 ETH
Deposit244594712026-02-15 3:19:597 hrs ago1771125599
0xEbE0FA42...c19c2c0Ee
0.00963738 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
UniversalRouter

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 44444444 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

// Command implementations
import {Dispatcher} from './base/Dispatcher.sol';
import {RouterParameters} from './types/RouterParameters.sol';
import {PaymentsImmutables, PaymentsParameters} from './modules/PaymentsImmutables.sol';
import {V4SwapRouter} from './modules/uniswap/v4/V4SwapRouter.sol';
import {Commands} from './libraries/Commands.sol';
import {IUniversalRouter} from './interfaces/IUniversalRouter.sol';
import {BalancerSwapRouter} from './modules/balancer/BalancerSwapRouter.sol';

contract UniversalRouter is IUniversalRouter, Dispatcher {
    constructor(RouterParameters memory params)
        V4SwapRouter(params.v4PoolManager)
        PaymentsImmutables(PaymentsParameters(params.permit2, params.weth9))
        BalancerSwapRouter(params.balancerVault)
    {}

    modifier checkDeadline(uint256 deadline) {
        if (block.timestamp > deadline) revert TransactionDeadlinePassed();
        _;
    }

    /// @notice To receive ETH from WETH
    receive() external payable {
        if (msg.sender != address(WETH9) && msg.sender != address(poolManager)) revert InvalidEthSender();
    }

    /// @inheritdoc IUniversalRouter
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline)
        external
        payable
        checkDeadline(deadline)
    {
        execute(commands, inputs);
    }

    /// @inheritdoc Dispatcher
    function execute(bytes calldata commands, bytes[] calldata inputs) public payable override isNotLocked {
        bool success;
        bytes memory output;
        uint256 numCommands = commands.length;
        if (inputs.length != numCommands) revert LengthMismatch();

        // loop through all given commands, execute them and pass along outputs as defined
        for (uint256 commandIndex = 0; commandIndex < numCommands; commandIndex++) {
            bytes1 command = commands[commandIndex];

            bytes calldata input = inputs[commandIndex];

            (success, output) = dispatch(command, input);

            if (!success && successRequired(command)) {
                revert ExecutionFailed({commandIndex: commandIndex, message: output});
            }
        }
    }

    function successRequired(bytes1 command) internal pure returns (bool) {
        return command & Commands.FLAG_ALLOW_REVERT == 0;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {V2SwapRouter} from '../modules/uniswap/v2/V2SwapRouter.sol';
import {V3SwapRouter} from '../modules/uniswap/v3/V3SwapRouter.sol';
import {V4SwapRouter} from '../modules/uniswap/v4/V4SwapRouter.sol';
import {BalancerSwapRouter} from '../modules/balancer/BalancerSwapRouter.sol';
import {CurveSwapRouter} from '../modules/curve/CurveSwapRouter.sol';
import {AerodromeSwapRouter} from '../modules/aerodrome/AerodromeSwapRouter.sol';
import {AerodromeV1SwapRouter} from '../modules/aerodrome/v1/AerodromeV1SwapRouter.sol';
import {SkyRouter} from '../modules/sky/SkyRouter.sol';
import {MaverickV1SwapRouter} from '../modules/maverick/v1/MaverickV1SwapRouter.sol';
import {MaverickV2SwapRouter} from '../modules/maverick/v2/MaverickV2SwapRouter.sol';
import {BytesLib} from '../modules/uniswap/v3/BytesLib.sol';
import {Payments} from '../modules/Payments.sol';
import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol';
import {Commands} from '../libraries/Commands.sol';
import {Lock} from './Lock.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol';
import {IAerodrome, AERODROME_ROUTER} from '../modules/aerodrome/interfaces/IAerodrome.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
import {BytesLibExt} from '../libraries/BytesLibExt.sol';

/// @title Decodes and Executes Commands
/// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command
abstract contract Dispatcher is
    Payments,
    V2SwapRouter,
    V3SwapRouter,
    V4SwapRouter,
    Lock,
    BalancerSwapRouter,
    CurveSwapRouter,
    AerodromeSwapRouter,
    SkyRouter,
    MaverickV1SwapRouter,
    MaverickV2SwapRouter,
    AerodromeV1SwapRouter
{
    using SafeTransferLib for ERC20;
    using BytesLib for bytes;
    using CalldataDecoder for bytes;

    error InvalidCommandType(uint256 commandType);
    error BalanceTooLow();

    /// @notice Executes encoded commands along with provided inputs.
    /// @param commands A set of concatenated commands, each 1 byte in length
    /// @param inputs An array of byte strings containing abi encoded inputs for each command
    function execute(bytes calldata commands, bytes[] calldata inputs) external payable virtual;

    /// @notice Public view function to be used instead of msg.sender, as the contract performs self-reentrancy and at
    /// times msg.sender == address(this). Instead msgSender() returns the initiator of the lock
    /// @dev overrides BaseActionsRouter.msgSender in V4Router
    function msgSender() public view override returns (address) {
        return _getLocker();
    }

    /// @notice Decodes and executes the given command with the given inputs
    /// @param commandType The command type to execute
    /// @param inputs The inputs to execute the command with
    /// @dev 2 masks are used to enable use of a nested-if statement in execution for efficiency reasons
    /// @return success True on success of the command, false on failure
    /// @return output The outputs or error messages, if any, from the command
    function dispatch(bytes1 commandType, bytes calldata inputs) internal returns (bool success, bytes memory output) {
        uint256 command = uint8(commandType & Commands.COMMAND_TYPE_MASK);

        success = true;

        // 0x00 <= command < 0x21
        if (command < Commands.BALANCER_SWAP_EXACT_IN) {
            // 0x00 <= command < 0x10
            if (command < Commands.V4_SWAP) {
                // 0x00 <= command < 0x08
                if (command < Commands.V2_SWAP_EXACT_IN) {
                    if (command == Commands.V3_SWAP_EXACT_IN) {
                        // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                        address recipient;
                        uint256 amountIn;
                        uint256 amountOutMin;
                        bool payerIsUser;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountIn := calldataload(add(inputs.offset, 0x20))
                            amountOutMin := calldataload(add(inputs.offset, 0x40))
                            // 0x60 offset is the path, decoded below
                            payerIsUser := calldataload(add(inputs.offset, 0x80))
                        }
                        bytes calldata path = inputs.toBytes(3);
                        address payer = payerIsUser ? msgSender() : address(this);
                        v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
                    } else if (command == Commands.PERMIT2_TRANSFER_FROM) {
                        // equivalent: abi.decode(inputs, (address, address, uint160))
                        address token;
                        address recipient;
                        uint160 amount;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            amount := calldataload(add(inputs.offset, 0x40))
                        }
                        permit2TransferFrom(token, msgSender(), map(recipient), amount);
                    } else if (command == Commands.PERMIT2_PERMIT_BATCH) {
                        IAllowanceTransfer.PermitBatch calldata permitBatch;
                        assembly {
                            // this is a variable length struct, so calldataload(inputs.offset) contains the
                            // offset from inputs.offset at which the struct begins
                            permitBatch := add(inputs.offset, calldataload(inputs.offset))
                        }
                        bytes calldata data = inputs.toBytes(1);
                        (success, output) = address(PERMIT2).call(
                            abi.encodeWithSignature(
                                'permit(address,((address,uint160,uint48,uint48)[],address,uint256),bytes)',
                                msgSender(),
                                permitBatch,
                                data
                            )
                        );
                    } else if (command == Commands.TRANSFER) {
                        // equivalent:  abi.decode(inputs, (address, address, uint256))
                        address token;
                        address recipient;
                        uint256 value;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            value := calldataload(add(inputs.offset, 0x40))
                        }
                        Payments.pay(token, map(recipient), value);
                    } else {
                        // command == Commands.TRANSFER_FROM
                        // equivalent:  abi.decode(inputs, (address, address, uint256))
                        address token;
                        address recipient;
                        uint256 value;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            value := calldataload(add(inputs.offset, 0x40))
                        }
                        Payments.payFrom(token, msgSender(), map(recipient), value);
                    }
                } else {
                    // 0x08 <= command < 0x10
                    if (command == Commands.V2_SWAP_EXACT_IN) {
                        // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                        address recipient;
                        uint256 amountIn;
                        uint256 amountOutMin;
                        bool payerIsUser;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountIn := calldataload(add(inputs.offset, 0x20))
                            amountOutMin := calldataload(add(inputs.offset, 0x40))
                            // 0x60 offset is the path, decoded below
                            payerIsUser := calldataload(add(inputs.offset, 0x80))
                        }
                        bytes calldata path = inputs.toBytes(3);
                        address payer = payerIsUser ? msgSender() : address(this);
                        v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
                    } else if (command == Commands.PERMIT2_PERMIT) {
                        // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes))
                        IAllowanceTransfer.PermitSingle calldata permitSingle;
                        assembly {
                            permitSingle := inputs.offset
                        }
                        bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5)
                        (success, output) = address(PERMIT2).call(
                            abi.encodeWithSignature(
                                'permit(address,((address,uint160,uint48,uint48),address,uint256),bytes)',
                                msgSender(),
                                permitSingle,
                                data
                            )
                        );
                    } else if (command == Commands.WRAP_ETH) {
                        // equivalent: abi.decode(inputs, (address, uint256))
                        address recipient;
                        uint256 amount;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amount := calldataload(add(inputs.offset, 0x20))
                        }
                        Payments.wrapETH(map(recipient), amount);
                    } else if (command == Commands.UNWRAP_WETH) {
                        // equivalent: abi.decode(inputs, (address, uint256))
                        address recipient;
                        uint256 amountMin;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountMin := calldataload(add(inputs.offset, 0x20))
                        }
                        Payments.unwrapWETH9(map(recipient), amountMin);
                    } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) {
                        IAllowanceTransfer.AllowanceTransferDetails[] calldata batchDetails;
                        (uint256 length, uint256 offset) = inputs.toLengthOffset(0);
                        assembly {
                            batchDetails.length := length
                            batchDetails.offset := offset
                        }
                        permit2TransferFrom(batchDetails, msgSender());
                    } else if (command == Commands.BALANCE_CHECK_ERC20) {
                        // equivalent: abi.decode(inputs, (address, address, uint256))
                        address owner;
                        address token;
                        uint256 minBalance;
                        assembly {
                            owner := calldataload(inputs.offset)
                            token := calldataload(add(inputs.offset, 0x20))
                            minBalance := calldataload(add(inputs.offset, 0x40))
                        }
                        success = (ERC20(token).balanceOf(owner) >= minBalance);
                        if (!success) output = abi.encodePacked(BalanceTooLow.selector);
                    } else {
                        // placeholder area for command 0x0f
                        revert InvalidCommandType(command);
                    }
                }
            } else {
                // 0x10 <= command < 0x21
                if (command == Commands.V4_SWAP) {
                    // pass the calldata provided to V4SwapRouter._executeActions (defined in BaseActionsRouter)
                    _executeActions(inputs);
                    // This contract MUST be approved to spend the token since its going to be doing the call on the position manager
                } else if (command == Commands.AERODROME_EXACT_INPUT_SINGLE) {
                    IAerodrome.ExactInputSingleParams memory params;
                    assembly {
                        let ptr := params
                        mstore(ptr, calldataload(inputs.offset)) // tokenIn
                        mstore(add(ptr, 0x20), calldataload(add(inputs.offset, 0x20))) // tokenOut
                        mstore(add(ptr, 0x40), calldataload(add(inputs.offset, 0x40))) // tickSpacing
                        mstore(add(ptr, 0x60), calldataload(add(inputs.offset, 0x60))) // recipient
                        // Jump one
                        mstore(add(ptr, 0xA0), calldataload(add(inputs.offset, 0x80))) // amountIn
                        mstore(add(ptr, 0xC0), calldataload(add(inputs.offset, 0xA0))) // amountOutMinimum
                    }
                    params.deadline = block.timestamp;
                    params.recipient = map(params.recipient);
                    // We can set the router as constant as there is only one deployment
                    aerodromeExactInputSingle(params);
                } else {
                    // placeholder area for commands 0x15-0x20
                    revert InvalidCommandType(command);
                }
            }
        } else {
            // 0x22 <= command
            if (command == Commands.BALANCER_SWAP_EXACT_IN) {
                // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes32, address, address))
                address recipient;
                uint256 amountIn;
                uint256 amountOutMin;
                bytes32 poolId;
                address assetIn;
                address assetOut;
                assembly {
                    recipient := calldataload(inputs.offset)
                    amountIn := calldataload(add(inputs.offset, 0x20))
                    amountOutMin := calldataload(add(inputs.offset, 0x40))
                    poolId := calldataload(add(inputs.offset, 0x60))
                    assetIn := calldataload(add(inputs.offset, 0x80))
                    assetOut := calldataload(add(inputs.offset, 0xa0))
                }
                balancerSwapExactIn(map(recipient), amountIn, amountOutMin, poolId, assetIn, assetOut);
            } else if (command == Commands.CURVE_SWAP_EXACT_IN) {
                CurveSwapRouter.SwapParams memory params;

                assembly {
                    let ptr := params
                    mstore(ptr, calldataload(inputs.offset)) // poolType
                    mstore(add(ptr, 0x20), calldataload(add(inputs.offset, 0x20))) // pool
                    mstore(add(ptr, 0x40), calldataload(add(inputs.offset, 0x40))) // tokenIn
                    mstore(add(ptr, 0x60), calldataload(add(inputs.offset, 0x60))) // tokenOut
                    mstore(add(ptr, 0x80), calldataload(add(inputs.offset, 0x80))) // amountIn
                    mstore(add(ptr, 0xa0), calldataload(add(inputs.offset, 0xa0))) // amountOutMin
                    mstore(add(ptr, 0xc0), calldataload(add(inputs.offset, 0xc0))) // indexIn
                    mstore(add(ptr, 0xe0), calldataload(add(inputs.offset, 0xe0))) // indexOut
                    mstore(add(ptr, 0x100), calldataload(add(inputs.offset, 0x100))) // recipient
                    mstore(add(ptr, 0x120), calldataload(add(inputs.offset, 0x120))) // useEth
                }
                params.recipient = map(params.recipient);
                curveSwapExactIn(params);
            } else if (command == Commands.SKY_TRADE_GEM) {
                SkyRouter.TradeType tradeType;
                address recipient;
                uint256 amountIn;
                uint256 amountOut;
                bool payerIsUser;

                assembly {
                    tradeType := calldataload(inputs.offset)
                    recipient := calldataload(add(inputs.offset, 0x20))
                    amountIn := calldataload(add(inputs.offset, 0x40))
                    amountOut := calldataload(add(inputs.offset, 0x60))
                    payerIsUser := calldataload(add(inputs.offset, 0x80))
                }
                recipient = map(recipient);
                address payer = payerIsUser ? msgSender() : address(this);
                tradeGem(tradeType, payer, recipient, amountIn, amountOut);
            } else if (command == Commands.MAVERICK_V1_SWAP_EXACT_IN) {
                // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                address recipient;
                uint256 amountIn;
                uint256 amountOutMin;
                bool payerIsUser;
                assembly {
                    recipient := calldataload(inputs.offset)
                    amountIn := calldataload(add(inputs.offset, 0x20))
                    amountOutMin := calldataload(add(inputs.offset, 0x40))
                    // 0x60 offset is the path, decoded below
                    payerIsUser := calldataload(add(inputs.offset, 0x80))
                }
                bytes calldata path = inputs.toBytes(3);
                address payer = payerIsUser ? msgSender() : address(this);
                maverickV1SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
            } else if (command == Commands.MAVERICK_V2_SWAP_EXACT_IN) {
                // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                address recipient;
                uint256 amountIn;
                uint256 amountOutMin;
                bool payerIsUser;
                assembly {
                    recipient := calldataload(inputs.offset)
                    amountIn := calldataload(add(inputs.offset, 0x20))
                    amountOutMin := calldataload(add(inputs.offset, 0x40))
                    // 0x60 offset is the path, decoded below
                    payerIsUser := calldataload(add(inputs.offset, 0x80))
                }
                bytes calldata path = inputs.toBytes(3);
                address payer = payerIsUser ? msgSender() : address(this);
                maverickV2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
            } else if (command == Commands.AERODROME_V1_SWAP_EXACT_IN) {
                // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                address recipient;
                uint256 amountIn;
                uint256 amountOutMin;
                bool payerIsUser;
                assembly {
                    recipient := calldataload(inputs.offset)
                    amountIn := calldataload(add(inputs.offset, 0x20))
                    amountOutMin := calldataload(add(inputs.offset, 0x40))
                    // 0x60 offset is the path, decoded below
                    payerIsUser := calldataload(add(inputs.offset, 0x80))
                }
                bytes calldata path = inputs.toBytes(3);
                address payer = payerIsUser ? msgSender() : address(this);
                aerodromeV1SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
            } else {
                // placeholder area for commands 0x28-0x3f
                revert InvalidCommandType(command);
            }
        }
    }
    /// @notice Calculates the recipient address for a command
    /// @param recipient The recipient or recipient-flag for the command
    /// @return output The resultant recipient for the command

    function map(address recipient) internal view returns (address) {
        if (recipient == ActionConstants.MSG_SENDER) {
            return msgSender();
        } else if (recipient == ActionConstants.ADDRESS_THIS) {
            return address(this);
        } else {
            return recipient;
        }
    }
}

File 3 of 81 : RouterParameters.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

struct RouterParameters {
    // Payment parameters
    address permit2;
    address weth9;
    // Uniswap swapping parameters
    address v4PoolManager;
    // Balancer parameters
    address balancerVault;
}

File 4 of 81 : PaymentsImmutables.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {IWETH9} from '@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol';
import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol';

struct PaymentsParameters {
    address permit2;
    address weth9;
}

contract PaymentsImmutables {
    /// @notice WETH9 address
    IWETH9 internal immutable WETH9;

    /// @notice Permit2 address
    IPermit2 internal immutable PERMIT2;

    constructor(PaymentsParameters memory params) {
        WETH9 = IWETH9(params.weth9);
        PERMIT2 = IPermit2(params.permit2);
    }
}

File 5 of 81 : V4SwapRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {Permit2Payments} from '../../Permit2Payments.sol';
import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';
import {Currency} from '@uniswap/v4-core/src/types/Currency.sol';
import {V4RouterOnlySwap} from './V4RouterOnlySwap.sol';

/// @title Router for Uniswap v4 Trades
abstract contract V4SwapRouter is V4RouterOnlySwap, Permit2Payments {
    constructor(address _poolManager) V4RouterOnlySwap(IPoolManager(_poolManager)) {}

    function _pay(Currency token, address payer, uint256 amount) internal override {
        payOrPermit2Transfer(Currency.unwrap(token), payer, address(poolManager), amount);
    }
}

File 6 of 81 : Commands.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

/// @title Commands
/// @notice Command Flags used to decode commands
library Commands {
    // Masks to extract certain bits of commands
    bytes1 internal constant FLAG_ALLOW_REVERT = 0x80;
    bytes1 internal constant COMMAND_TYPE_MASK = 0x3f;

    // Command Types. Maximum supported command at this moment is 0x3f.
    // The commands are executed in nested if blocks to minimise gas consumption

    // Command Types where value<=0x07, executed in the first nested-if block
    uint256 constant V3_SWAP_EXACT_IN = 0x00;
    uint256 constant PERMIT2_TRANSFER_FROM = 0x02;
    uint256 constant PERMIT2_PERMIT_BATCH = 0x03;
    uint256 constant TRANSFER = 0x05;
    uint256 constant TRANSFER_FROM = 0x07;

    // Command Types where 0x08<=value<=0x0f, executed in the second nested-if block
    uint256 constant V2_SWAP_EXACT_IN = 0x08;
    uint256 constant V2_SWAP_EXACT_OUT = 0x09;
    uint256 constant PERMIT2_PERMIT = 0x0a;
    uint256 constant WRAP_ETH = 0x0b;
    uint256 constant UNWRAP_WETH = 0x0c;
    uint256 constant PERMIT2_TRANSFER_FROM_BATCH = 0x0d;
    uint256 constant BALANCE_CHECK_ERC20 = 0x0e;
    // COMMAND_PLACEHOLDER = 0x0f;

    // Command Types where 0x10<=value<=0x20, executed in the third nested-if block
    uint256 constant V4_SWAP = 0x10;
    uint256 constant AERODROME_EXACT_INPUT_SINGLE = 0x11;
    // COMMAND_PLACEHOLDER = 0x15 -> 0x20

    // Command Types where 0x22<=value<=0x3f
    // Balancer commands
    uint256 constant BALANCER_SWAP_EXACT_IN = 0x22;

    // Curve commands
    uint256 constant CURVE_SWAP_EXACT_IN = 0x23;

    // Sky commands
    uint256 constant SKY_TRADE_GEM = 0x24;

    // Maverick commands
    uint256 constant MAVERICK_V1_SWAP_EXACT_IN = 0x25;
    uint256 constant MAVERICK_V2_SWAP_EXACT_IN = 0x26;

    // Aerodrome v1 command
    uint256 constant AERODROME_V1_SWAP_EXACT_IN = 0x27;

    // COMMAND_PLACEHOLDER for 0x28 to 0x3f
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

interface IUniversalRouter {
    /// @notice Thrown when a required command has failed
    error ExecutionFailed(uint256 commandIndex, bytes message);

    /// @notice Thrown when attempting to send ETH directly to the contract
    error ETHNotAccepted();

    /// @notice Thrown when executing commands with an expired deadline
    error TransactionDeadlinePassed();

    /// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
    error LengthMismatch();

    // @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata
    error InvalidEthSender();

    /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
    /// @param commands A set of concatenated commands, each 1 byte in length
    /// @param inputs An array of byte strings containing abi encoded inputs for each command
    /// @param deadline The deadline by which the transaction must be executed
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {IBalancerVault} from './interfaces/IBalancerVault.sol';

abstract contract BalancerSwapRouter {
    using SafeTransferLib for ERC20;

    address internal immutable balancerVault;

    constructor(address _vault) {
        balancerVault = _vault;
    }

    function balancerSwapExactIn(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes32 poolId,
        address assetIn,
        address assetOut
    ) internal {
        // use amountIn == ActionConstants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
        if (amountIn == ActionConstants.CONTRACT_BALANCE) {
            amountIn = ERC20(assetIn).balanceOf(address(this));
        }

        _approveExact(assetIn, amountIn);

        IBalancerVault(balancerVault).swap(
            IBalancerVault.SingleSwap({
                poolId: poolId,
                kind: IBalancerVault.SwapKind.GIVEN_IN,
                assetIn: assetIn,
                assetOut: assetOut,
                amount: amountIn,
                userData: new bytes(0)
            }),
            IBalancerVault.FundManagement({
                sender: address(this),
                fromInternalBalance: false,
                recipient: payable(recipient),
                toInternalBalance: false
            }),
            amountOutMin,
            block.timestamp
        );
    }

    function balancerBatchSwapExactIn(
        address recipient,
        IBalancerVault.BatchSwap[] memory swaps,
        address[] memory assets,
        int256[] memory limits
    ) internal {
        for (uint256 i = 0; i < assets.length; i++) {
            if (limits[i] > 0) {
                _approveExact(assets[i], uint256(limits[i]));
            }
        }

        IBalancerVault(balancerVault).batchSwap(
            IBalancerVault.SwapKind.GIVEN_IN,
            swaps,
            assets,
            IBalancerVault.FundManagement({
                sender: address(this),
                fromInternalBalance: false,
                recipient: payable(recipient),
                toInternalBalance: false
            }),
            limits,
            block.timestamp
        );
    }

    function _approveExact(address token, uint256 amount) private {
        ERC20(token).safeApprove(balancerVault, amount);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import {UniswapV2Library} from './UniswapV2Library.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {Constants} from '../../../libraries/Constants.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';

/// @title Router for Uniswap v2 Trades
abstract contract V2SwapRouter is Permit2Payments {
    using UniswapV2Library for bytes;

    error V2TooLittleReceived();
    error V2TooMuchRequested();
    error V2InvalidPath();

    function _v2Swap(bytes calldata path, address recipient) private {
        unchecked {
            (address input, uint16 feeNumerator, address pair, address output) = path.toPair();
            while (true) {
                (address token0,) = UniswapV2Library.sortTokens(input, output);
                (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves();
                (uint256 reserveInput, uint256 reserveOutput) =
                    input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput;
                uint256 amountOutput =
                    UniswapV2Library.getAmountOut(amountInput, feeNumerator, reserveInput, reserveOutput);
                (uint256 amount0Out, uint256 amount1Out) =
                    input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
                address nextRecipient;
                bool hasNext = path.hasMultiplePairs();
                if (hasNext) {
                    path = path.skipToken();
                    (input, feeNumerator, nextRecipient, output) = path.toPair();
                } else {
                    nextRecipient = recipient;
                }
                IUniswapV2Pair(pair).swap(amount0Out, amount1Out, nextRecipient, new bytes(0));
                if (!hasNext) break;
                pair = nextRecipient;
            }
        }
    }

    /// @notice Performs a Uniswap v2 exact input swap
    /// @param recipient The recipient of the output tokens
    /// @param amountIn The amount of input tokens for the trade
    /// @param amountOutMinimum The minimum desired amount of output tokens
    /// @param path The path of the trade as an array of token addresses
    /// @param payer The address that will be paying the input
    function v2SwapExactInput(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum,
        bytes calldata path,
        address payer
    ) internal {
        if (path.length < Constants.V2_POP_OFFSET) revert V2InvalidPath();

        if (
            amountIn != Constants.ALREADY_PAID // amountIn of 0 to signal that the pair already has the tokens
        ) {
            (address input,, address firstPair,) = path.toPair();
            payOrPermit2Transfer(input, payer, firstPair, amountIn);
        }

        if (amountOutMinimum == 0) {
            _v2Swap(path, recipient);
        } else {
            // The last 20 bytes always hold the address of the final token.
            ERC20 tokenOut = ERC20(path.decodeLastOutput());
            uint256 balanceBefore = tokenOut.balanceOf(recipient);

            _v2Swap(path, recipient);

            uint256 amountOut = tokenOut.balanceOf(recipient) - balanceBefore;
            if (amountOut < amountOutMinimum) revert V2TooLittleReceived();
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {V3Path} from './V3Path.sol';
import {BytesLib} from './BytesLib.sol';
import {SafeCast} from '@uniswap/v3-core/contracts/libraries/SafeCast.sol';
import {IUniswapV3Pool} from '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import {IUniswapV3SwapCallback} from '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {MaxInputAmount} from '../../../libraries/MaxInputAmount.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {Locker} from '../../../libraries/Locker.sol';

/// @title Router for Uniswap v3 Trades
abstract contract V3SwapRouter is Permit2Payments, IUniswapV3SwapCallback {
    using V3Path for bytes;
    using BytesLib for bytes;
    using CalldataDecoder for bytes;
    using SafeCast for uint256;

    error V3InvalidSwap();
    error V3TooLittleReceived();
    error V3TooMuchRequested();
    error V3InvalidAmountOut();
    error V3InvalidCaller();
    error V3InvalidPayer();

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;

    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) public {
        if (amount0Delta <= 0 && amount1Delta <= 0) revert V3InvalidSwap(); // swaps entirely within 0-liquidity regions are not supported
        (, address payer) = abi.decode(data, (bytes, address));
        if (payer != address(this) && payer != Locker.get()) revert V3InvalidPayer();

        bytes calldata path = data.toBytes(0);

        // because exact output swaps are executed in reverse order, in this case tokenOut is actually tokenIn
        (address tokenIn, address pool, address tokenOut) = path.decodeFirstPool();

        if (pool != msg.sender) revert V3InvalidCaller();

        (bool isExactInput, uint256 amountToPay) =
            amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));

        if (isExactInput) {
            // Pay the pool (msg.sender)
            payOrPermit2Transfer(tokenIn, payer, msg.sender, amountToPay);
        } else {
            // either initiate the next swap or pay
            if (path.hasMultiplePools()) {
                // this is an intermediate step so the payer is actually this contract
                path = path.skipToken();
                _swap(-amountToPay.toInt256(), msg.sender, path, payer, false);
            } else {
                if (amountToPay > MaxInputAmount.get()) revert V3TooMuchRequested();
                // note that because exact output swaps are executed in reverse order, tokenOut is actually tokenIn
                payOrPermit2Transfer(tokenOut, payer, msg.sender, amountToPay);
            }
        }
    }

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

    /// @notice Kyber callback signature.
    function swapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
        uniswapV3SwapCallback(amount0Delta, amount1Delta, data);
    }

    /// @notice Performs a Uniswap v3 exact input swap
    /// @param recipient The recipient of the output tokens
    /// @param amountIn The amount of input tokens for the trade
    /// @param amountOutMinimum The minimum desired amount of output tokens
    /// @param path The path of the trade as a bytes string
    /// @param payer The address that will be paying the input
    function v3SwapExactInput(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum,
        bytes calldata path,
        address payer
    ) internal {
        // use amountIn == ActionConstants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
        if (amountIn == ActionConstants.CONTRACT_BALANCE) {
            address tokenIn = path.decodeFirstToken();
            amountIn = ERC20(tokenIn).balanceOf(address(this));
        }

        uint256 amountOut;
        while (true) {
            bool hasMultiplePools = path.hasMultiplePools();

            // the outputs of prior swaps become the inputs to subsequent ones
            (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap(
                amountIn.toInt256(),
                hasMultiplePools ? address(this) : recipient, // for intermediate swaps, this contract custodies
                path.getFirstPool(), // only the first pool is needed
                payer, // for intermediate swaps, this contract custodies
                true
            );

            amountIn = uint256(-(zeroForOne ? amount1Delta : amount0Delta));

            // decide whether to continue or terminate
            if (hasMultiplePools) {
                payer = address(this);
                path = path.skipToken();
            } else {
                amountOut = amountIn;
                break;
            }
        }

        if (amountOut < amountOutMinimum) revert V3TooLittleReceived();
    }

    /// @dev Performs a single swap for both exactIn and exactOut
    /// For exactIn, `amount` is `amountIn`. For exactOut, `amount` is `-amountOut`
    function _swap(int256 amount, address recipient, bytes calldata path, address payer, bool isExactIn)
        private
        returns (int256 amount0Delta, int256 amount1Delta, bool zeroForOne)
    {
        (address tokenIn, address pool, address tokenOut) = path.decodeFirstPool();

        zeroForOne = isExactIn ? tokenIn < tokenOut : tokenOut < tokenIn;

        (amount0Delta, amount1Delta) = IUniswapV3Pool(pool).swap(
            recipient,
            zeroForOne,
            amount,
            (zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1),
            abi.encode(path, payer)
        );
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;

import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {Constants} from '../../libraries/Constants.sol';
import {Payments} from '../Payments.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';

interface IStableSwap {
    function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
    function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable;
    function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver)
        external
        payable
        returns (uint256);
    function exchange_received(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver)
        external
        payable
        returns (uint256);
    function calc_withdraw_one_coin(uint256 amount, int128 coinIndex) external view returns (uint256);
}

interface ILegacyStableSwap {
    function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
    function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable;
    function calc_withdraw_one_coin(uint256 amount, int128 coinIndex) external view returns (uint256);
}

interface ICryptoSwap {
    function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external payable;
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, address receiver) external payable;
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth, address receiver)
        external
        payable;
    function exchange_received(uint256 i, uint256 j, uint256 dx, uint256 min_dy, address receiver) external payable;
    function exchange_received(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external payable;
    function calc_withdraw_one_coin(uint256 amount, uint256 coinIndex) external view returns (uint256);
}

abstract contract CurveSwapRouter is Payments {
    error InvalidInput();

    using SafeTransferLib for ERC20;

    error UnsupportedPoolType(uint8 poolType);
    error SwapFailed();

    enum PoolType {
        LEGACY_CRYPTO,
        CRYPTO,
        OPTIMIZED_ETH_CRYPTO,
        LEGACY_STABLE,
        STABLE
    }

    struct SwapParams {
        PoolType poolType; // Type of the Curve pool
        address pool; // Pool address
        address tokenIn; // Input token address (address(0) for ETH)
        address tokenOut; // Output token address (address(0) for ETH)
        uint256 amountIn; // Amount of input tokens
        uint256 amountOutMin; // Minimum amount of output tokens
        uint256 indexIn; // Input token index
        uint256 indexOut; // Output token index
        address recipient; // Recipient of the output tokens
        bool useEth; // Whether to use ETH (only for OPTIMIZED_ETH_CRYPTO)
    }

    function curveSwapExactIn(SwapParams memory params) internal {
        // Only approve if token is not ETH and not ETH_ADDRESS
        bool useEth = params.tokenIn == Constants.ETH;
        if (useEth) {
            if (params.amountIn == ActionConstants.CONTRACT_BALANCE) {
                params.amountIn = address(this).balance;
            }
        } else {
            if (params.amountIn == ActionConstants.CONTRACT_BALANCE) {
                params.amountIn = ERC20(params.tokenIn).balanceOf(address(this));
            }
            ERC20(params.tokenIn).safeApprove(params.pool, params.amountIn);
        }

        // Handle different pool types
        if (params.poolType == PoolType.LEGACY_CRYPTO) {
            _handleLegacyCryptoSwap(params);
        } else if (params.poolType == PoolType.CRYPTO) {
            _handleCryptoSwap(params);
        } else if (params.poolType == PoolType.OPTIMIZED_ETH_CRYPTO) {
            _handleOptimizedEthCryptoSwap(params);
        } else if (params.poolType == PoolType.LEGACY_STABLE) {
            _handleLegacyStableSwap(params);
        } else if (params.poolType == PoolType.STABLE) {
            _handleStableSwap(params);
        } else {
            revert UnsupportedPoolType(uint8(params.poolType));
        }

        if (params.recipient != address(0) && params.recipient != address(this)) {
            if (params.poolType == PoolType.LEGACY_CRYPTO || params.poolType == PoolType.LEGACY_STABLE) {
                // For legacy pools that don't support recipient parameter, we need to transfer manually
                pay(params.tokenOut, params.recipient, ActionConstants.CONTRACT_BALANCE);
            }
        }
    }

    function _handleLegacyCryptoSwap(SwapParams memory params) private {
        ICryptoSwap(params.pool).exchange{value: params.tokenIn == Constants.ETH ? params.amountIn : 0}(
            params.indexIn, params.indexOut, params.amountIn, params.amountOutMin
        );
    }

    function _handleCryptoSwap(SwapParams memory params) private {
        if (params.recipient == address(this)) {
            ICryptoSwap(params.pool).exchange{value: params.tokenIn == Constants.ETH ? params.amountIn : 0}(
                params.indexIn, params.indexOut, params.amountIn, params.amountOutMin
            );
        } else {
            ICryptoSwap(params.pool).exchange{value: params.tokenIn == Constants.ETH ? params.amountIn : 0}(
                params.indexIn, params.indexOut, params.amountIn, params.amountOutMin, params.recipient
            );
        }
    }

    function _handleOptimizedEthCryptoSwap(SwapParams memory params) private {
        ICryptoSwap(params.pool).exchange{value: params.useEth ? params.amountIn : 0}(
            params.indexIn, params.indexOut, params.amountIn, params.amountOutMin, params.useEth, params.recipient
        );
    }

    function _handleLegacyStableSwap(SwapParams memory params) private {
        ILegacyStableSwap(params.pool).exchange{value: params.tokenIn == Constants.ETH ? params.amountIn : 0}(
            toInt128(params.indexIn), toInt128(params.indexOut), params.amountIn, params.amountOutMin
        );
    }

    function _handleStableSwap(SwapParams memory params) private {
        if (params.recipient == address(this)) {
            IStableSwap(params.pool).exchange{value: params.tokenIn == Constants.ETH ? params.amountIn : 0}(
                toInt128(params.indexIn), toInt128(params.indexOut), params.amountIn, params.amountOutMin
            );
        } else {
            IStableSwap(params.pool).exchange{value: params.tokenIn == Constants.ETH ? params.amountIn : 0}(
                toInt128(params.indexIn),
                toInt128(params.indexOut),
                params.amountIn,
                params.amountOutMin,
                params.recipient
            );
        }
    }

    function toInt128(uint256 input) private pure returns (int128) {
        require(input <= uint128(type(int128).max), InvalidInput());
        return int128(uint128(input));
    }
}

File 12 of 81 : AerodromeSwapRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;

import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {Constants} from '../../libraries/Constants.sol';
import {IAerodrome, AERODROME_ROUTER} from './interfaces/IAerodrome.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';

abstract contract AerodromeSwapRouter {
    using SafeTransferLib for ERC20;

    function aerodromeExactInputSingle(IAerodrome.ExactInputSingleParams memory params) internal {
        if (params.amountIn == ActionConstants.CONTRACT_BALANCE) {
            params.amountIn = ERC20(params.tokenIn).balanceOf(address(this));
        }
        ERC20(params.tokenIn).safeApprove(AERODROME_ROUTER, params.amountIn);
        IAerodrome(AERODROME_ROUTER).exactInputSingle(params);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {V3Path} from '../../uniswap/v3/V3Path.sol';
import {UniswapV2Library} from '../../uniswap/v2/UniswapV2Library.sol';
import {Constants} from '../../../libraries/Constants.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';

interface IAerodromeV1Pool {
    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
    function getReserves() external view returns (uint256 reserve0, uint256 reserve1, uint256 blockTimestampLast);
    function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256 amountOut);
}

abstract contract AerodromeV1SwapRouter is Permit2Payments {
    using V3Path for bytes;
    using SafeTransferLib for ERC20;

    function _swap(bytes calldata path, address recipient) private {
        unchecked {
            (address tokenIn, address pool, address tokenOut) = path.decodeFirstPool();
            while (true) {
                // 1. Determine the amount just received by the pool.
                (uint256 reserve0, uint256 reserve1,) = IAerodromeV1Pool(pool).getReserves();
                (address token0,) = tokenIn < tokenOut ? (tokenIn, tokenOut) : (tokenOut, tokenIn);
                (uint256 reserveInput,) = tokenIn == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                uint256 amountInput = ERC20(tokenIn).balanceOf(pool) - reserveInput;

                // 2. Query pool for output given this input.
                uint256 amountOutput = IAerodromeV1Pool(pool).getAmountOut(amountInput, tokenIn);

                // 3. Prepare swap parameters.
                (uint256 amount0Out, uint256 amount1Out) =
                    tokenIn == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));

                // 4. Determine next hop recipient.
                address nextRecipient;
                bool hasNext = path.hasMultiplePools();
                address nextPool;
                address nextTokenOut;
                if (hasNext) {
                    path = path.skipToken();
                    (, nextPool, nextTokenOut) = path.decodeFirstPool();
                    nextRecipient = nextPool;
                } else {
                    // Final hop – deliver directly to the user-supplied recipient.
                    nextRecipient = recipient;
                }

                // 5. Execute the swap.
                IAerodromeV1Pool(pool).swap(amount0Out, amount1Out, nextRecipient, new bytes(0));

                if (!hasNext) break;

                // Prepare variables for next iteration
                tokenIn = tokenOut;
                tokenOut = nextTokenOut;
                pool = nextPool;
            }
        }
    }

    function aerodromeV1SwapExactInput(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum,
        bytes calldata path,
        address payer
    ) internal {
        // Extract the first hop parameters (tokenIn + first pool) for upfront transfer handling.
        (address firstTokenIn, address firstPool,) = path.decodeFirstPool();

        // Transfer input tokens into the first pool if required.
        if (amountIn != Constants.ALREADY_PAID) {
            if (amountIn == ActionConstants.CONTRACT_BALANCE) {
                amountIn = ERC20(firstTokenIn).balanceOf(address(this));
            }
            payOrPermit2Transfer(firstTokenIn, payer, firstPool, amountIn);
        }

        if (amountOutMinimum == 0) {
            _swap(path, recipient);
        } else {
            // Derive the final output token of the path.
            // The path is encoded as: token | pool | token | [pool | token] ...
            // Hence the last 20 bytes always hold the address of the final token.
            address finalTokenOut = UniswapV2Library.decodeLastOutput(path);
            uint256 balanceBefore = ERC20(finalTokenOut).balanceOf(recipient);

            // Perform the swaps.
            _swap(path, recipient);

            // Slippage check only if a minimum was specified.
            uint256 amountOut = ERC20(finalTokenOut).balanceOf(recipient) - balanceBefore;
            if (amountOut < amountOutMinimum) revert();
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;

import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {Permit2Payments, SafeCast160} from '../Permit2Payments.sol';

interface IPsm {
    function sellGem(address recipient, uint256 gemAmount) external;
    function buyGem(address recipient, uint256 gemAmount) external;
    function tout() external view returns (uint256);
}

abstract contract SkyRouter is Permit2Payments {
    using SafeTransferLib for ERC20;
    using SafeCast160 for uint256;

    enum TradeType {
        SellUSDCForUSDS,
        SellUSDCForDAI,
        BuyUSDCWithUSDS,
        BuyUSDCWithDAI
    }

    IPsm private constant USDS_PSM_WRAPPER = IPsm(0xA188EEC8F81263234dA3622A406892F3D630f98c);
    IPsm private constant DSS_LITE_PSM = IPsm(0xf6e72Db5454dd049d0788e411b06CfAF16853042);
    ERC20 private constant USDC = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    ERC20 private constant USDS = ERC20(0xdC035D45d973E3EC169d2276DDab16f1e407384F);
    ERC20 private constant DAI = ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
    uint256 private WAD = 1e18;

    error SkyTooMuchRequested();

    function tradeGem(TradeType tradeType, address payer, address recipient, uint256 amountIn, uint256 amountOut)
        internal
    {
        ERC20 tokenIn;
        function (address, uint256) external targetFunc;
        bool isSell = true;
        if (tradeType == TradeType.SellUSDCForUSDS) {
            tokenIn = USDC;
            targetFunc = USDS_PSM_WRAPPER.sellGem;
        } else if (tradeType == TradeType.SellUSDCForDAI) {
            tokenIn = USDC;
            targetFunc = DSS_LITE_PSM.sellGem;
        } else if (tradeType == TradeType.BuyUSDCWithUSDS) {
            tokenIn = USDS;
            targetFunc = USDS_PSM_WRAPPER.buyGem;
            isSell = false;

            // The following part is only needed if fees are be enabled.
            // uint256 amount18 = amountOut * 1e12;
            // amount18 += amount18 * USDS_PSM_WRAPPER.tout() / WAD;
            // if (amountIn < amount18) revert SkyTooMuchRequested();
            // amountIn = amount18;
        } else {
            // tradeType == TradeType.BuyUSDCWithDAI
            tokenIn = DAI;
            targetFunc = DSS_LITE_PSM.buyGem;
            isSell = false;

            // The following part is only needed if fees are be enabled.
            // uint256 amount18 = amountOut * 1e12;
            // amount18 += amount18 * DSS_LITE_PSM.tout() / WAD;
            // if (amountIn < amount18) revert SkyTooMuchRequested();
            // amountIn = amount18;
        }
        if (payer != address(this)) {
            permit2TransferFrom(address(tokenIn), payer, address(this), amountIn.toUint160());
        } else if (amountIn == ActionConstants.CONTRACT_BALANCE) {
            amountIn = tokenIn.balanceOf(address(this));
            if (isSell == false) {
                amountOut = amountIn / 1e12;
            }
        }
        tokenIn.safeApprove(targetFunc.address, amountIn);
        targetFunc(recipient, isSell ? amountIn : amountOut);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {V3Path} from '../../uniswap/v3/V3Path.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {Locker} from '../../../libraries/Locker.sol';

interface IMaverickV1Pool {
    function swap(
        address recipient,
        uint256 amount,
        bool tokenAIn,
        bool exactOutput,
        uint256 sqrtPriceLimit,
        bytes calldata data
    ) external returns (uint256 amountIn, uint256 amountOut);
}

/// @title Router for Maverick v1 Trades
abstract contract MaverickV1SwapRouter is Permit2Payments {
    using V3Path for bytes;

    error MaverickV1InvalidSwap();
    error MaverickV1TooLittleReceived();
    error MaverickV1InvalidPayer();

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint256 private constant MIN_SQRT_RATIO = 4295128739;

    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint256 private constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    function swapCallback(uint256 amountIn, uint256 amountOut, bytes calldata data) external {
        if (amountIn == 0 || amountOut == 0) revert MaverickV1InvalidSwap();
        (address tokenIn, address payer) = abi.decode(data, (address, address));
        if (payer != address(this) && payer != Locker.get()) revert MaverickV1InvalidPayer();

        // Pay the pool (msg.sender)
        payOrPermit2Transfer(tokenIn, payer, msg.sender, amountIn);
    }

    /// @notice Performs a Maverick v1 exact input swap
    /// @param recipient The recipient of the output tokens
    /// @param amountIn The amount of input tokens for the trade
    /// @param amountOutMinimum The minimum desired amount of output tokens
    /// @param path The path of the trade as a bytes string
    /// @param payer The address that will be paying the input
    function maverickV1SwapExactInput(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum,
        bytes calldata path,
        address payer
    ) internal {
        (address tokenIn, address pool, address tokenOut) = path.decodeFirstPool();
        // use amountIn == ActionConstants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
        if (amountIn == ActionConstants.CONTRACT_BALANCE) {
            amountIn = ERC20(tokenIn).balanceOf(address(this));
        }

        uint256 amountOut = amountIn;
        while (true) {
            bool hasMultiplePools = path.hasMultiplePools();

            // the outputs of prior swaps become the inputs to subsequent ones
            amountOut = _swap(
                amountOut, // amountOut from last swap becomes amountIn
                hasMultiplePools ? address(this) : recipient, // for intermediate swaps, this contract custodies
                pool,
                tokenIn,
                tokenOut,
                payer
            );

            // decide whether to continue or terminate
            if (hasMultiplePools) {
                payer = address(this); // for intermediate swaps, this contract custodies
                path = path.skipToken();
                (tokenIn, pool, tokenOut) = path.decodeFirstPool();
            } else {
                break;
            }
        }

        if (amountOut < amountOutMinimum) revert MaverickV1TooLittleReceived();
    }

    /// @dev Performs a single swap for exactIn
    function _swap(uint256 amount, address recipient, address pool, address tokenIn, address tokenOut, address payer)
        private
        returns (uint256 amountOut)
    {
        bool tokenAIn = tokenIn < tokenOut;

        (, amountOut) = IMaverickV1Pool(pool).swap(
            recipient,
            amount,
            tokenAIn,
            false,
            (tokenAIn ? MAX_SQRT_RATIO - 1 : MIN_SQRT_RATIO + 1),
            abi.encode(tokenIn, payer)
        );

        return amountOut;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {V3Path} from '../../uniswap/v3/V3Path.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {Permit2Payments, SafeCast160} from '../../Permit2Payments.sol';

interface IMaverickV2Pool {
    struct SwapParams {
        uint256 amount;
        bool tokenAIn;
        bool exactOutput;
        int32 tickLimit;
    }

    function swap(address recipient, SwapParams memory params, bytes calldata data)
        external
        returns (uint256 amountIn, uint256 amountOut);
}

/// @title Router for Maverick v2 Trades
abstract contract MaverickV2SwapRouter is Permit2Payments {
    using V3Path for bytes;
    using SafeCast160 for uint256;

    error MaverickV2TooLittleReceived();

    /// @notice Performs a Maverick v2 exact input swap
    /// @param recipient The recipient of the output tokens
    /// @param amountIn The amount of input tokens for the trade
    /// @param amountOutMinimum The minimum desired amount of output tokens
    /// @param path The path of the trade as a bytes string
    /// @param payer The address that will be paying the input
    function maverickV2SwapExactInput(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMinimum,
        bytes calldata path,
        address payer
    ) internal {
        (address tokenIn, address pool, address tokenOut) = path.decodeFirstPool();
        if (payer == address(this)) {
            // use amountIn == ActionConstants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
            if (amountIn == ActionConstants.CONTRACT_BALANCE) {
                amountIn = ERC20(tokenIn).balanceOf(address(this));
            }
            pay(tokenIn, pool, amountIn);
        } else {
            permit2TransferFrom(tokenIn, payer, pool, amountIn.toUint160());
        }

        uint256 amountOut = amountIn;
        while (true) {
            bool hasMultiplePools = path.hasMultiplePools();
            address nextRecipient;
            address nextTokenOut;
            if (hasMultiplePools) {
                path = path.skipToken();
                (, nextRecipient, nextTokenOut) = path.decodeFirstPool();
            } else {
                nextRecipient = recipient;
            }

            // the outputs of prior swaps become the inputs to subsequent ones
            amountOut = _swap(
                amountOut, // amountOut from last swap becomes amountIn
                nextRecipient, // for intermediate swaps -> next pool
                pool,
                tokenIn < tokenOut
            );

            // decide whether to continue or terminate
            if (hasMultiplePools) {
                pool = nextRecipient;
                tokenIn = tokenOut;
                tokenOut = nextTokenOut;
            } else {
                break;
            }
        }

        if (amountOut < amountOutMinimum) revert MaverickV2TooLittleReceived();
    }

    /// @dev Performs a single swap for exactIn
    function _swap(uint256 amountIn, address recipient, address pool, bool tokenAIn)
        private
        returns (uint256 amountOut)
    {
        IMaverickV2Pool.SwapParams memory swapParams =
            IMaverickV2Pool.SwapParams(amountIn, tokenAIn, false, tokenAIn ? type(int32).max : type(int32).min);

        (, amountOut) = IMaverickV2Pool(pool).swap(recipient, swapParams, '');

        return amountOut;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

/// @title Library for Bytes Manipulation
pragma solidity ^0.8.0;

import {Constants} from '../../../libraries/Constants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';

library BytesLib {
    using CalldataDecoder for bytes;

    error SliceOutOfBounds();

    /// @notice Returns the address starting at byte 0
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return _address The address starting at byte 0
    function toAddress(bytes calldata _bytes) internal pure returns (address _address) {
        if (_bytes.length < Constants.ADDR_SIZE) revert SliceOutOfBounds();
        assembly {
            _address := shr(96, calldataload(_bytes.offset))
        }
    }

    /// @notice Returns the pool details starting at byte 0
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return token0 The address at byte 0
    /// @return pool The address starting at byte 20
    /// @return token1 The address at byte 40
    function toPool(bytes calldata _bytes) internal pure returns (address token0, address pool, address token1) {
        if (_bytes.length < Constants.V3_POP_OFFSET) revert SliceOutOfBounds();
        assembly {
            token0 := shr(96, calldataload(_bytes.offset))
            pool := shr(96, calldataload(add(_bytes.offset, 20)))
            token1 := shr(96, calldataload(add(_bytes.offset, 40)))
        }
    }

    /// @notice Decode the `_arg`-th element in `_bytes` as a dynamic array
    /// @dev The decoding of `length` and `offset` is universal,
    /// whereas the type declaration of `res` instructs the compiler how to read it.
    /// @param _bytes The input bytes string to slice
    /// @param _arg The index of the argument to extract
    /// @return length Length of the array
    /// @return offset Pointer to the data part of the array
    function toLengthOffset(bytes calldata _bytes, uint256 _arg)
        internal
        pure
        returns (uint256 length, uint256 offset)
    {
        uint256 relativeOffset;
        assembly {
            // The offset of the `_arg`-th element is `32 * arg`, which stores the offset of the length pointer.
            // shl(5, x) is equivalent to mul(32, x)
            let lengthPtr := add(_bytes.offset, calldataload(add(_bytes.offset, shl(5, _arg))))
            length := calldataload(lengthPtr)
            offset := add(lengthPtr, 0x20)
            relativeOffset := sub(offset, _bytes.offset)
        }
        if (_bytes.length < length + relativeOffset) revert SliceOutOfBounds();
    }

    /// @notice Decode the `_arg`-th element in `_bytes` as `address[]`
    /// @param _bytes The input bytes string to extract an address array from
    /// @param _arg The index of the argument to extract
    function toAddressArray(bytes calldata _bytes, uint256 _arg) internal pure returns (address[] calldata res) {
        (uint256 length, uint256 offset) = toLengthOffset(_bytes, _arg);
        assembly {
            res.length := length
            res.offset := offset
        }
    }

    /// @notice Equivalent to abi.decode(bytes, bytes[])
    /// @param _bytes The input bytes string to extract an parameters from
    function decodeCommandsAndInputs(bytes calldata _bytes) internal pure returns (bytes calldata, bytes[] calldata) {
        return _bytes.decodeActionsRouterParams();
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {Constants} from '../libraries/Constants.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {BipsLibrary} from '@uniswap/v4-periphery/src/libraries/BipsLibrary.sol';
import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';

/// @title Payments contract
/// @notice Performs various operations around the payment of ETH and tokens
abstract contract Payments is PaymentsImmutables {
    using SafeTransferLib for ERC20;
    using SafeTransferLib for address;
    using BipsLibrary for uint256;

    error InsufficientToken();
    error InsufficientETH();
    error CannotTransferFromETH();

    /// @notice Pays an amount of ETH or ERC20 to a recipient
    /// @param token The token to pay (can be ETH using Constants.ETH)
    /// @param recipient The address that will receive the payment
    /// @param value The amount to pay
    function pay(address token, address recipient, uint256 value) internal {
        if (token == Constants.ETH) {
            recipient.safeTransferETH(value);
        } else {
            if (value == ActionConstants.CONTRACT_BALANCE) {
                value = ERC20(token).balanceOf(address(this));
            }

            ERC20(token).safeTransfer(recipient, value);
        }
    }

    /// @notice Pays an amount of ERC20 from a sender to a recipient
    /// @param token The token to pay
    /// @param sender The address that will spend the payment, must be msg sender.
    /// @param recipient The address that will receive the payment
    /// @param value The amount to pay
    function payFrom(address token, address sender, address recipient, uint256 value) internal {
        require(token != Constants.ETH, CannotTransferFromETH());

        ERC20(token).safeTransferFrom(sender, recipient, value);
    }

    /// @notice Wraps an amount of ETH into WETH
    /// @param recipient The recipient of the WETH
    /// @param amount The amount to wrap (can be CONTRACT_BALANCE)
    function wrapETH(address recipient, uint256 amount) internal {
        if (amount == ActionConstants.CONTRACT_BALANCE) {
            amount = address(this).balance;
        } else if (amount > address(this).balance) {
            revert InsufficientETH();
        }
        if (amount > 0) {
            WETH9.deposit{value: amount}();
            if (recipient != address(this)) {
                WETH9.transfer(recipient, amount);
            }
        }
    }

    /// @notice Unwraps all of the contract's WETH into ETH
    /// @param recipient The recipient of the ETH
    /// @param amountMinimum The minimum amount of ETH desired
    function unwrapWETH9(address recipient, uint256 amountMinimum) internal {
        uint256 value = WETH9.balanceOf(address(this));
        if (value < amountMinimum) {
            revert InsufficientETH();
        }
        if (value > 0) {
            WETH9.withdraw(value);
            if (recipient != address(this)) {
                recipient.safeTransferETH(value);
            }
        }
    }
}

File 19 of 81 : Lock.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {Locker} from '../libraries/Locker.sol';

/// @title Lock
/// @notice A contract that provides a reentrancy lock for external calls
contract Lock {
    /// @notice Thrown when attempting to reenter a locked function from an external caller
    error ContractLocked();

    /// @notice Modifier enforcing a reentrancy lock that allows self-reentrancy
    /// @dev If the contract is not locked, use msg.sender as the locker
    modifier isNotLocked() {
        // Apply a reentrancy lock for all external callers
        if (msg.sender != address(this)) {
            if (Locker.isLocked()) revert ContractLocked();
            Locker.set(msg.sender);
            _;
            Locker.set(address(0));
        } else {
            // The contract is allowed to reenter itself, so the lock is not checked
            _;
        }
    }

    /// @notice return the current locker of the contract
    function _getLocker() internal view returns (address) {
        return Locker.get();
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.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 {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

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

        require(success, "ETH_TRANSFER_FAILED");
    }

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

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

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

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

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

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

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

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

        require(success, "TRANSFER_FAILED");
    }

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

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

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

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

        require(success, "APPROVE_FAILED");
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer is IEIP712 {
    /// @notice Thrown when an allowance on a token has expired.
    /// @param deadline The timestamp at which the allowed amount is no longer valid
    error AllowanceExpired(uint256 deadline);

    /// @notice Thrown when an allowance on a token has been depleted.
    /// @param amount The maximum amount allowed
    error InsufficientAllowance(uint256 amount);

    /// @notice Thrown when too many nonces are invalidated.
    error ExcessiveInvalidation();

    /// @notice Emits an event when the owner successfully invalidates an ordered nonce.
    event NonceInvalidation(
        address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
    );

    /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
    event Approval(
        address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
    );

    /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
    event Permit(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint160 amount,
        uint48 expiration,
        uint48 nonce
    );

    /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
    event Lockdown(address indexed owner, address token, address spender);

    /// @notice The permit data for a token
    struct PermitDetails {
        // ERC20 token address
        address token;
        // the maximum amount allowed to spend
        uint160 amount;
        // timestamp at which a spender's token allowances become invalid
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice The permit message signed for a single token allowance
    struct PermitSingle {
        // the permit data for a single token alownce
        PermitDetails details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The permit message signed for multiple token allowances
    struct PermitBatch {
        // the permit data for multiple token allowances
        PermitDetails[] details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The saved permissions
    /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    struct PackedAllowance {
        // amount allowed
        uint160 amount;
        // permission expiry
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice A token spender pair.
    struct TokenSpenderPair {
        // the token the spender is approved
        address token;
        // the spender address
        address spender;
    }

    /// @notice Details for a token transfer.
    struct AllowanceTransferDetails {
        // the owner of the token
        address from;
        // the recipient of the token
        address to;
        // the amount of the token
        uint160 amount;
        // the token to be transferred
        address token;
    }

    /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
    /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
    /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
    function allowance(address user, address token, address spender)
        external
        view
        returns (uint160 amount, uint48 expiration, uint48 nonce);

    /// @notice Approves the spender to use up to amount of the specified token up until the expiration
    /// @param token The token to approve
    /// @param spender The spender address to approve
    /// @param amount The approved amount of the token
    /// @param expiration The timestamp at which the approval is no longer valid
    /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    function approve(address token, address spender, uint160 amount, uint48 expiration) external;

    /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitSingle Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;

    /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitBatch Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;

    /// @notice Transfer approved tokens from one address to another
    /// @param from The address to transfer from
    /// @param to The address of the recipient
    /// @param amount The amount of the token to transfer
    /// @param token The token address to transfer
    /// @dev Requires the from address to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(address from, address to, uint160 amount, address token) external;

    /// @notice Transfer approved tokens in a batch
    /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
    /// @dev Requires the from addresses to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;

    /// @notice Enables performing a "lockdown" of the sender's Permit2 identity
    /// by batch revoking approvals
    /// @param approvals Array of approvals to revoke.
    function lockdown(TokenSpenderPair[] calldata approvals) external;

    /// @notice Invalidate nonces for a given (token, spender) pair
    /// @param token The token to invalidate nonces for
    /// @param spender The spender to invalidate nonces for
    /// @param newNonce The new nonce to set. Invalidates all nonces less than it.
    /// @dev Can't invalidate more than 2**16 nonces per transaction.
    function invalidateNonces(address token, address spender, uint48 newNonce) external;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

address constant AERODROME_ROUTER = address(0xBE6D8f0d05cC4be24d5167a3eF062215bE6D18a5);

interface IAerodrome {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        int24 tickSpacing;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    function exactInputSingle(ExactInputSingleParams calldata params) external returns (uint256 amountOut);
}

File 24 of 81 : ActionConstants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Action Constants
/// @notice Common constants used in actions
/// @dev Constants are gas efficient alternatives to their literal values
library ActionConstants {
    /// @notice used to signal that an action should use the input value of the open delta on the pool manager
    /// or of the balance that the contract holds
    uint128 internal constant OPEN_DELTA = 0;
    /// @notice used to signal that an action should use the contract's entire balance of a currency
    /// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
    uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000;

    /// @notice used to signal that the recipient of an action should be the msgSender
    address internal constant MSG_SENDER = address(1);

    /// @notice used to signal that the recipient of an action should be the address(this)
    address internal constant ADDRESS_THIS = address(2);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {IV4Router} from "../interfaces/IV4Router.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";

/// @title Library for abi decoding in calldata
library CalldataDecoder {
    using CalldataDecoder for bytes;

    error SliceOutOfBounds();

    /// @notice mask used for offsets and lengths to ensure no overflow
    /// @dev no sane abi encoding will pass in an offset or length greater than type(uint32).max
    ///      (note that this does deviate from standard solidity behavior and offsets/lengths will
    ///      be interpreted as mod type(uint32).max which will only impact malicious/buggy callers)
    uint256 constant OFFSET_OR_LENGTH_MASK = 0xffffffff;
    uint256 constant OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN = 0xffffffe0;

    /// @notice equivalent to SliceOutOfBounds.selector, stored in least-significant bits
    uint256 constant SLICE_ERROR_SELECTOR = 0x3b99b53d;

    /// @dev equivalent to: abi.decode(params, (bytes, bytes[])) in calldata (requires strict abi encoding)
    function decodeActionsRouterParams(bytes calldata _bytes)
        internal
        pure
        returns (bytes calldata actions, bytes[] calldata params)
    {
        assembly ("memory-safe") {
            // Strict encoding requires that the data begin with:
            // 0x00: 0x40 (offset to `actions.length`)
            // 0x20: 0x60 + actions.length (offset to `params.length`)
            // 0x40: `actions.length`
            // 0x60: beginning of actions

            // Verify actions offset matches strict encoding
            let invalidData := xor(calldataload(_bytes.offset), 0x40)
            actions.offset := add(_bytes.offset, 0x60)
            actions.length := and(calldataload(add(_bytes.offset, 0x40)), OFFSET_OR_LENGTH_MASK)

            // Round actions length up to be word-aligned, and add 0x60 (for the first 3 words of encoding)
            let paramsLengthOffset := add(and(add(actions.length, 0x1f), OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN), 0x60)
            // Verify params offset matches strict encoding
            invalidData := or(invalidData, xor(calldataload(add(_bytes.offset, 0x20)), paramsLengthOffset))
            let paramsLengthPointer := add(_bytes.offset, paramsLengthOffset)
            params.length := and(calldataload(paramsLengthPointer), OFFSET_OR_LENGTH_MASK)
            params.offset := add(paramsLengthPointer, 0x20)

            // Expected offset for `params[0]` is params.length * 32
            // As the first `params.length` slots are pointers to each of the array element lengths
            let tailOffset := shl(5, params.length)
            let expectedOffset := tailOffset

            for { let offset := 0 } lt(offset, tailOffset) { offset := add(offset, 32) } {
                let itemLengthOffset := calldataload(add(params.offset, offset))
                // Verify that the offset matches the expected offset from strict encoding
                invalidData := or(invalidData, xor(itemLengthOffset, expectedOffset))
                let itemLengthPointer := add(params.offset, itemLengthOffset)
                let length :=
                    add(and(add(calldataload(itemLengthPointer), 0x1f), OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN), 0x20)
                expectedOffset := add(expectedOffset, length)
            }

            // if the data encoding was invalid, or the provided bytes string isnt as long as the encoding says, revert
            if or(invalidData, lt(add(_bytes.length, _bytes.offset), add(params.offset, expectedOffset))) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
        }
    }

    /// @dev equivalent to: abi.decode(params, (uint256, uint256, uint128, uint128, bytes)) in calldata
    function decodeModifyLiquidityParams(bytes calldata params)
        internal
        pure
        returns (uint256 tokenId, uint256 liquidity, uint128 amount0, uint128 amount1, bytes calldata hookData)
    {
        // no length check performed, as there is a length check in `toBytes`
        assembly ("memory-safe") {
            tokenId := calldataload(params.offset)
            liquidity := calldataload(add(params.offset, 0x20))
            amount0 := calldataload(add(params.offset, 0x40))
            amount1 := calldataload(add(params.offset, 0x60))
        }

        hookData = params.toBytes(4);
    }

    /// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata
    function decodeIncreaseLiquidityFromDeltasParams(bytes calldata params)
        internal
        pure
        returns (uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData)
    {
        // no length check performed, as there is a length check in `toBytes`
        assembly ("memory-safe") {
            tokenId := calldataload(params.offset)
            amount0Max := calldataload(add(params.offset, 0x20))
            amount1Max := calldataload(add(params.offset, 0x40))
        }

        hookData = params.toBytes(3);
    }

    /// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint256, uint128, uint128, address, bytes)) in calldata
    function decodeMintParams(bytes calldata params)
        internal
        pure
        returns (
            PoolKey calldata poolKey,
            int24 tickLower,
            int24 tickUpper,
            uint256 liquidity,
            uint128 amount0Max,
            uint128 amount1Max,
            address owner,
            bytes calldata hookData
        )
    {
        // no length check performed, as there is a length check in `toBytes`
        assembly ("memory-safe") {
            poolKey := params.offset
            tickLower := calldataload(add(params.offset, 0xa0))
            tickUpper := calldataload(add(params.offset, 0xc0))
            liquidity := calldataload(add(params.offset, 0xe0))
            amount0Max := calldataload(add(params.offset, 0x100))
            amount1Max := calldataload(add(params.offset, 0x120))
            owner := calldataload(add(params.offset, 0x140))
        }
        hookData = params.toBytes(11);
    }

    /// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint128, uint128, address, bytes)) in calldata
    function decodeMintFromDeltasParams(bytes calldata params)
        internal
        pure
        returns (
            PoolKey calldata poolKey,
            int24 tickLower,
            int24 tickUpper,
            uint128 amount0Max,
            uint128 amount1Max,
            address owner,
            bytes calldata hookData
        )
    {
        // no length check performed, as there is a length check in `toBytes`
        assembly ("memory-safe") {
            poolKey := params.offset
            tickLower := calldataload(add(params.offset, 0xa0))
            tickUpper := calldataload(add(params.offset, 0xc0))
            amount0Max := calldataload(add(params.offset, 0xe0))
            amount1Max := calldataload(add(params.offset, 0x100))
            owner := calldataload(add(params.offset, 0x120))
        }

        hookData = params.toBytes(10);
    }

    /// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata
    function decodeBurnParams(bytes calldata params)
        internal
        pure
        returns (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData)
    {
        // no length check performed, as there is a length check in `toBytes`
        assembly ("memory-safe") {
            tokenId := calldataload(params.offset)
            amount0Min := calldataload(add(params.offset, 0x20))
            amount1Min := calldataload(add(params.offset, 0x40))
        }

        hookData = params.toBytes(3);
    }

    /// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputParams))
    function decodeSwapExactInParams(bytes calldata params)
        internal
        pure
        returns (IV4Router.ExactInputParams calldata swapParams)
    {
        // ExactInputParams is a variable length struct so we just have to look up its location
        assembly ("memory-safe") {
            // only safety checks for the minimum length, where path is empty
            // 0xa0 = 5 * 0x20 -> 3 elements, path offset, and path length 0
            if lt(params.length, 0xa0) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            swapParams := add(params.offset, calldataload(params.offset))
        }
    }

    /// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputSingleParams))
    function decodeSwapExactInSingleParams(bytes calldata params)
        internal
        pure
        returns (IV4Router.ExactInputSingleParams calldata swapParams)
    {
        // ExactInputSingleParams is a variable length struct so we just have to look up its location
        assembly ("memory-safe") {
            // only safety checks for the minimum length, where hookData is empty
            // 0x140 = 10 * 0x20 -> 8 elements, bytes offset, and bytes length 0
            if lt(params.length, 0x140) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            swapParams := add(params.offset, calldataload(params.offset))
        }
    }

    /// @dev equivalent to: abi.decode(params, (IV4Router.ExactOutputParams))
    function decodeSwapExactOutParams(bytes calldata params)
        internal
        pure
        returns (IV4Router.ExactOutputParams calldata swapParams)
    {
        // ExactOutputParams is a variable length struct so we just have to look up its location
        assembly ("memory-safe") {
            // only safety checks for the minimum length, where path is empty
            // 0xa0 = 5 * 0x20 -> 3 elements, path offset, and path length 0
            if lt(params.length, 0xa0) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            swapParams := add(params.offset, calldataload(params.offset))
        }
    }

    /// @dev equivalent to: abi.decode(params, (IV4Router.ExactOutputSingleParams))
    function decodeSwapExactOutSingleParams(bytes calldata params)
        internal
        pure
        returns (IV4Router.ExactOutputSingleParams calldata swapParams)
    {
        // ExactOutputSingleParams is a variable length struct so we just have to look up its location
        assembly ("memory-safe") {
            // only safety checks for the minimum length, where hookData is empty
            // 0x140 = 10 * 0x20 -> 8 elements, bytes offset, and bytes length 0
            if lt(params.length, 0x140) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            swapParams := add(params.offset, calldataload(params.offset))
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency)) in calldata
    function decodeCurrency(bytes calldata params) internal pure returns (Currency currency) {
        assembly ("memory-safe") {
            if lt(params.length, 0x20) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency := calldataload(params.offset)
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency, Currency)) in calldata
    function decodeCurrencyPair(bytes calldata params) internal pure returns (Currency currency0, Currency currency1) {
        assembly ("memory-safe") {
            if lt(params.length, 0x40) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency0 := calldataload(params.offset)
            currency1 := calldataload(add(params.offset, 0x20))
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency, Currency, address)) in calldata
    function decodeCurrencyPairAndAddress(bytes calldata params)
        internal
        pure
        returns (Currency currency0, Currency currency1, address _address)
    {
        assembly ("memory-safe") {
            if lt(params.length, 0x60) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency0 := calldataload(params.offset)
            currency1 := calldataload(add(params.offset, 0x20))
            _address := calldataload(add(params.offset, 0x40))
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency, address)) in calldata
    function decodeCurrencyAndAddress(bytes calldata params)
        internal
        pure
        returns (Currency currency, address _address)
    {
        assembly ("memory-safe") {
            if lt(params.length, 0x40) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency := calldataload(params.offset)
            _address := calldataload(add(params.offset, 0x20))
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency, address, uint256)) in calldata
    function decodeCurrencyAddressAndUint256(bytes calldata params)
        internal
        pure
        returns (Currency currency, address _address, uint256 amount)
    {
        assembly ("memory-safe") {
            if lt(params.length, 0x60) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency := calldataload(params.offset)
            _address := calldataload(add(params.offset, 0x20))
            amount := calldataload(add(params.offset, 0x40))
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency, uint256)) in calldata
    function decodeCurrencyAndUint256(bytes calldata params)
        internal
        pure
        returns (Currency currency, uint256 amount)
    {
        assembly ("memory-safe") {
            if lt(params.length, 0x40) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency := calldataload(params.offset)
            amount := calldataload(add(params.offset, 0x20))
        }
    }

    /// @dev equivalent to: abi.decode(params, (uint256)) in calldata
    function decodeUint256(bytes calldata params) internal pure returns (uint256 amount) {
        assembly ("memory-safe") {
            if lt(params.length, 0x20) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            amount := calldataload(params.offset)
        }
    }

    /// @dev equivalent to: abi.decode(params, (Currency, uint256, bool)) in calldata
    function decodeCurrencyUint256AndBool(bytes calldata params)
        internal
        pure
        returns (Currency currency, uint256 amount, bool boolean)
    {
        assembly ("memory-safe") {
            if lt(params.length, 0x60) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
            currency := calldataload(params.offset)
            amount := calldataload(add(params.offset, 0x20))
            boolean := calldataload(add(params.offset, 0x40))
        }
    }

    /// @notice Decode the `_arg`-th element in `_bytes` as `bytes`
    /// @param _bytes The input bytes string to extract a bytes string from
    /// @param _arg The index of the argument to extract
    function toBytes(bytes calldata _bytes, uint256 _arg) internal pure returns (bytes calldata res) {
        uint256 length;
        assembly ("memory-safe") {
            // The offset of the `_arg`-th element is `32 * arg`, which stores the offset of the length pointer.
            // shl(5, x) is equivalent to mul(32, x)
            let lengthPtr :=
                add(_bytes.offset, and(calldataload(add(_bytes.offset, shl(5, _arg))), OFFSET_OR_LENGTH_MASK))
            // the number of bytes in the bytes string
            length := and(calldataload(lengthPtr), OFFSET_OR_LENGTH_MASK)
            // the offset where the bytes string begins
            let offset := add(lengthPtr, 0x20)
            // assign the return parameters
            res.length := length
            res.offset := offset

            // if the provided bytes string isnt as long as the encoding says, revert
            if lt(add(_bytes.length, _bytes.offset), add(length, offset)) {
                mstore(0, SLICE_ERROR_SELECTOR)
                revert(0x1c, 4)
            }
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

/// @title Library for Bytes Manipulation
pragma solidity ^0.8.0;

import {BytesLib} from '../modules/uniswap/v3/BytesLib.sol';

library BytesLibExt {
    /// @notice Decode the `_arg`-th element in `_bytes` as `uint256[]`
    /// @param _bytes The input bytes string to extract an uint256 array from
    /// @param _arg The index of the argument to extract
    function toUint256Array(bytes calldata _bytes, uint256 _arg) internal pure returns (uint256[] calldata res) {
        (uint256 length, uint256 offset) = BytesLib.toLengthOffset(_bytes, _arg);
        assembly {
            res.length := length
            res.offset := offset
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title IWETH9
interface IWETH9 is IERC20 {
    /// @notice Deposit ether to get wrapped ether
    function deposit() external payable;

    /// @notice Withdraw wrapped ether to get ether
    function withdraw(uint256) external;
}

File 28 of 81 : IPermit2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ISignatureTransfer} from "./ISignatureTransfer.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";

/// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.
/// @dev Users must approve Permit2 before calling any of the transfer functions.
interface IPermit2 is ISignatureTransfer, IAllowanceTransfer {
// IPermit2 unifies the two interfaces so users have maximal flexibility with their approval.
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol';
import {SafeCast160} from 'permit2/src/libraries/SafeCast160.sol';
import {Payments} from './Payments.sol';

/// @title Payments through Permit2
/// @notice Performs interactions with Permit2 to transfer tokens
abstract contract Permit2Payments is Payments {
    using SafeCast160 for uint256;

    error FromAddressIsNotOwner();

    /// @notice Performs a transferFrom on Permit2
    /// @param token The token to transfer
    /// @param from The address to transfer from
    /// @param to The recipient of the transfer
    /// @param amount The amount to transfer
    function permit2TransferFrom(address token, address from, address to, uint160 amount) internal {
        PERMIT2.transferFrom(from, to, amount, token);
    }

    /// @notice Performs a batch transferFrom on Permit2
    /// @param batchDetails An array detailing each of the transfers that should occur
    /// @param owner The address that should be the owner of all transfers
    function permit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] calldata batchDetails, address owner)
        internal
    {
        uint256 batchLength = batchDetails.length;
        for (uint256 i = 0; i < batchLength; ++i) {
            if (batchDetails[i].from != owner) revert FromAddressIsNotOwner();
        }
        PERMIT2.transferFrom(batchDetails);
    }

    /// @notice Either performs a regular payment or transferFrom on Permit2, depending on the payer address
    /// @param token The token to transfer
    /// @param payer The address to pay for the transfer
    /// @param recipient The recipient of the transfer
    /// @param amount The amount to transfer
    function payOrPermit2Transfer(address token, address payer, address recipient, uint256 amount) internal {
        if (payer == address(this)) pay(token, recipient, amount);
        else permit2TransferFrom(token, payer, recipient, amount.toUint160());
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Currency} from "../types/Currency.sol";
import {PoolKey} from "../types/PoolKey.sol";
import {IHooks} from "./IHooks.sol";
import {IERC6909Claims} from "./external/IERC6909Claims.sol";
import {IProtocolFees} from "./IProtocolFees.sol";
import {BalanceDelta} from "../types/BalanceDelta.sol";
import {PoolId} from "../types/PoolId.sol";
import {IExtsload} from "./IExtsload.sol";
import {IExttload} from "./IExttload.sol";

/// @notice Interface for the PoolManager
interface IPoolManager is IProtocolFees, IERC6909Claims, IExtsload, IExttload {
    /// @notice Thrown when a currency is not netted out after the contract is unlocked
    error CurrencyNotSettled();

    /// @notice Thrown when trying to interact with a non-initialized pool
    error PoolNotInitialized();

    /// @notice Thrown when unlock is called, but the contract is already unlocked
    error AlreadyUnlocked();

    /// @notice Thrown when a function is called that requires the contract to be unlocked, but it is not
    error ManagerLocked();

    /// @notice Pools are limited to type(int16).max tickSpacing in #initialize, to prevent overflow
    error TickSpacingTooLarge(int24 tickSpacing);

    /// @notice Pools must have a positive non-zero tickSpacing passed to #initialize
    error TickSpacingTooSmall(int24 tickSpacing);

    /// @notice PoolKey must have currencies where address(currency0) < address(currency1)
    error CurrenciesOutOfOrderOrEqual(address currency0, address currency1);

    /// @notice Thrown when a call to updateDynamicLPFee is made by an address that is not the hook,
    /// or on a pool that does not have a dynamic swap fee.
    error UnauthorizedDynamicLPFeeUpdate();

    /// @notice Thrown when trying to swap amount of 0
    error SwapAmountCannotBeZero();

    ///@notice Thrown when native currency is passed to a non native settlement
    error NonzeroNativeValue();

    /// @notice Thrown when `clear` is called with an amount that is not exactly equal to the open currency delta.
    error MustClearExactPositiveDelta();

    /// @notice Emitted when a new pool is initialized
    /// @param id The abi encoded hash of the pool key struct for the new pool
    /// @param currency0 The first currency of the pool by address sort order
    /// @param currency1 The second currency of the pool by address sort order
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks
    /// @param hooks The hooks contract address for the pool, or address(0) if none
    /// @param sqrtPriceX96 The price of the pool on initialization
    /// @param tick The initial tick of the pool corresponding to the initialized price
    event Initialize(
        PoolId indexed id,
        Currency indexed currency0,
        Currency indexed currency1,
        uint24 fee,
        int24 tickSpacing,
        IHooks hooks,
        uint160 sqrtPriceX96,
        int24 tick
    );

    /// @notice Emitted when a liquidity position is modified
    /// @param id The abi encoded hash of the pool key struct for the pool that was modified
    /// @param sender The address that modified the pool
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param liquidityDelta The amount of liquidity that was added or removed
    /// @param salt The extra data to make positions unique
    event ModifyLiquidity(
        PoolId indexed id, address indexed sender, int24 tickLower, int24 tickUpper, int256 liquidityDelta, bytes32 salt
    );

    /// @notice Emitted for swaps between currency0 and currency1
    /// @param id The abi encoded hash of the pool key struct for the pool that was modified
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param amount0 The delta of the currency0 balance of the pool
    /// @param amount1 The delta of the currency1 balance of the pool
    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
    /// @param liquidity The liquidity of the pool after the swap
    /// @param tick The log base 1.0001 of the price of the pool after the swap
    /// @param fee The swap fee in hundredths of a bip
    event Swap(
        PoolId indexed id,
        address indexed sender,
        int128 amount0,
        int128 amount1,
        uint160 sqrtPriceX96,
        uint128 liquidity,
        int24 tick,
        uint24 fee
    );

    /// @notice Emitted for donations
    /// @param id The abi encoded hash of the pool key struct for the pool that was donated to
    /// @param sender The address that initiated the donate call
    /// @param amount0 The amount donated in currency0
    /// @param amount1 The amount donated in currency1
    event Donate(PoolId indexed id, address indexed sender, uint256 amount0, uint256 amount1);

    /// @notice All interactions on the contract that account deltas require unlocking. A caller that calls `unlock` must implement
    /// `IUnlockCallback(msg.sender).unlockCallback(data)`, where they interact with the remaining functions on this contract.
    /// @dev The only functions callable without an unlocking are `initialize` and `updateDynamicLPFee`
    /// @param data Any data to pass to the callback, via `IUnlockCallback(msg.sender).unlockCallback(data)`
    /// @return The data returned by the call to `IUnlockCallback(msg.sender).unlockCallback(data)`
    function unlock(bytes calldata data) external returns (bytes memory);

    /// @notice Initialize the state for a given pool ID
    /// @dev A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee
    /// @param key The pool key for the pool to initialize
    /// @param sqrtPriceX96 The initial square root price
    /// @return tick The initial tick of the pool
    function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24 tick);

    struct ModifyLiquidityParams {
        // the lower and upper tick of the position
        int24 tickLower;
        int24 tickUpper;
        // how to modify the liquidity
        int256 liquidityDelta;
        // a value to set if you want unique liquidity positions at the same range
        bytes32 salt;
    }

    /// @notice Modify the liquidity for the given pool
    /// @dev Poke by calling with a zero liquidityDelta
    /// @param key The pool to modify liquidity in
    /// @param params The parameters for modifying the liquidity
    /// @param hookData The data to pass through to the add/removeLiquidity hooks
    /// @return callerDelta The balance delta of the caller of modifyLiquidity. This is the total of both principal, fee deltas, and hook deltas if applicable
    /// @return feesAccrued The balance delta of the fees generated in the liquidity range. Returned for informational purposes
    function modifyLiquidity(PoolKey memory key, ModifyLiquidityParams memory params, bytes calldata hookData)
        external
        returns (BalanceDelta callerDelta, BalanceDelta feesAccrued);

    struct SwapParams {
        /// Whether to swap token0 for token1 or vice versa
        bool zeroForOne;
        /// The desired input amount if negative (exactIn), or the desired output amount if positive (exactOut)
        int256 amountSpecified;
        /// The sqrt price at which, if reached, the swap will stop executing
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swap against the given pool
    /// @param key The pool to swap in
    /// @param params The parameters for swapping
    /// @param hookData The data to pass through to the swap hooks
    /// @return swapDelta The balance delta of the address swapping
    /// @dev Swapping on low liquidity pools may cause unexpected swap amounts when liquidity available is less than amountSpecified.
    /// Additionally note that if interacting with hooks that have the BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG
    /// the hook may alter the swap input/output. Integrators should perform checks on the returned swapDelta.
    function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData)
        external
        returns (BalanceDelta swapDelta);

    /// @notice Donate the given currency amounts to the in-range liquidity providers of a pool
    /// @dev Calls to donate can be frontrun adding just-in-time liquidity, with the aim of receiving a portion donated funds.
    /// Donors should keep this in mind when designing donation mechanisms.
    /// @dev This function donates to in-range LPs at slot0.tick. In certain edge-cases of the swap algorithm, the `sqrtPrice` of
    /// a pool can be at the lower boundary of tick `n`, but the `slot0.tick` of the pool is already `n - 1`. In this case a call to
    /// `donate` would donate to tick `n - 1` (slot0.tick) not tick `n` (getTickAtSqrtPrice(slot0.sqrtPriceX96)).
    /// Read the comments in `Pool.swap()` for more information about this.
    /// @param key The key of the pool to donate to
    /// @param amount0 The amount of currency0 to donate
    /// @param amount1 The amount of currency1 to donate
    /// @param hookData The data to pass through to the donate hooks
    /// @return BalanceDelta The delta of the caller after the donate
    function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData)
        external
        returns (BalanceDelta);

    /// @notice Writes the current ERC20 balance of the specified currency to transient storage
    /// This is used to checkpoint balances for the manager and derive deltas for the caller.
    /// @dev This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped
    /// for native tokens because the amount to settle is determined by the sent value.
    /// However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle
    /// native funds, this function can be called with the native currency to then be able to settle the native currency
    function sync(Currency currency) external;

    /// @notice Called by the user to net out some value owed to the user
    /// @dev Will revert if the requested amount is not available, consider using `mint` instead
    /// @dev Can also be used as a mechanism for free flash loans
    /// @param currency The currency to withdraw from the pool manager
    /// @param to The address to withdraw to
    /// @param amount The amount of currency to withdraw
    function take(Currency currency, address to, uint256 amount) external;

    /// @notice Called by the user to pay what is owed
    /// @return paid The amount of currency settled
    function settle() external payable returns (uint256 paid);

    /// @notice Called by the user to pay on behalf of another address
    /// @param recipient The address to credit for the payment
    /// @return paid The amount of currency settled
    function settleFor(address recipient) external payable returns (uint256 paid);

    /// @notice WARNING - Any currency that is cleared, will be non-retrievable, and locked in the contract permanently.
    /// A call to clear will zero out a positive balance WITHOUT a corresponding transfer.
    /// @dev This could be used to clear a balance that is considered dust.
    /// Additionally, the amount must be the exact positive balance. This is to enforce that the caller is aware of the amount being cleared.
    function clear(Currency currency, uint256 amount) external;

    /// @notice Called by the user to move value into ERC6909 balance
    /// @param to The address to mint the tokens to
    /// @param id The currency address to mint to ERC6909s, as a uint256
    /// @param amount The amount of currency to mint
    /// @dev The id is converted to a uint160 to correspond to a currency address
    /// If the upper 12 bytes are not 0, they will be 0-ed out
    function mint(address to, uint256 id, uint256 amount) external;

    /// @notice Called by the user to move value from ERC6909 balance
    /// @param from The address to burn the tokens from
    /// @param id The currency address to burn from ERC6909s, as a uint256
    /// @param amount The amount of currency to burn
    /// @dev The id is converted to a uint160 to correspond to a currency address
    /// If the upper 12 bytes are not 0, they will be 0-ed out
    function burn(address from, uint256 id, uint256 amount) external;

    /// @notice Updates the pools lp fees for the a pool that has enabled dynamic lp fees.
    /// @dev A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee
    /// @param key The key of the pool to update dynamic LP fees for
    /// @param newDynamicLPFee The new dynamic pool LP fee
    function updateDynamicLPFee(PoolKey memory key, uint24 newDynamicLPFee) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol";
import {CustomRevert} from "../libraries/CustomRevert.sol";

type Currency is address;

using {greaterThan as >, lessThan as <, greaterThanOrEqualTo as >=, equals as ==} for Currency global;
using CurrencyLibrary for Currency global;

function equals(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) == Currency.unwrap(other);
}

function greaterThan(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) > Currency.unwrap(other);
}

function lessThan(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) < Currency.unwrap(other);
}

function greaterThanOrEqualTo(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) >= Currency.unwrap(other);
}

/// @title CurrencyLibrary
/// @dev This library allows for transferring and holding native tokens and ERC20 tokens
library CurrencyLibrary {
    /// @notice Additional context for ERC-7751 wrapped error when a native transfer fails
    error NativeTransferFailed();

    /// @notice Additional context for ERC-7751 wrapped error when an ERC20 transfer fails
    error ERC20TransferFailed();

    /// @notice A constant to represent the native currency
    Currency public constant ADDRESS_ZERO = Currency.wrap(address(0));

    function transfer(Currency currency, address to, uint256 amount) internal {
        // altered from https://github.com/transmissions11/solmate/blob/44a9963d4c78111f77caa0e65d677b8b46d6f2e6/src/utils/SafeTransferLib.sol
        // modified custom error selectors

        bool success;
        if (currency.isAddressZero()) {
            assembly ("memory-safe") {
                // Transfer the ETH and revert if it fails.
                success := call(gas(), to, amount, 0, 0, 0, 0)
            }
            // revert with NativeTransferFailed, containing the bubbled up error as an argument
            if (!success) {
                CustomRevert.bubbleUpAndRevertWith(to, bytes4(0), NativeTransferFailed.selector);
            }
        } else {
            assembly ("memory-safe") {
                // Get a pointer to some free memory.
                let fmp := mload(0x40)

                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(fmp, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                mstore(add(fmp, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(fmp, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

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

                // Now clean the memory we used
                mstore(fmp, 0) // 4 byte `selector` and 28 bytes of `to` were stored here
                mstore(add(fmp, 0x20), 0) // 4 bytes of `to` and 28 bytes of `amount` were stored here
                mstore(add(fmp, 0x40), 0) // 4 bytes of `amount` were stored here
            }
            // revert with ERC20TransferFailed, containing the bubbled up error as an argument
            if (!success) {
                CustomRevert.bubbleUpAndRevertWith(
                    Currency.unwrap(currency), IERC20Minimal.transfer.selector, ERC20TransferFailed.selector
                );
            }
        }
    }

    function balanceOfSelf(Currency currency) internal view returns (uint256) {
        if (currency.isAddressZero()) {
            return address(this).balance;
        } else {
            return IERC20Minimal(Currency.unwrap(currency)).balanceOf(address(this));
        }
    }

    function balanceOf(Currency currency, address owner) internal view returns (uint256) {
        if (currency.isAddressZero()) {
            return owner.balance;
        } else {
            return IERC20Minimal(Currency.unwrap(currency)).balanceOf(owner);
        }
    }

    function isAddressZero(Currency currency) internal pure returns (bool) {
        return Currency.unwrap(currency) == Currency.unwrap(ADDRESS_ZERO);
    }

    function toId(Currency currency) internal pure returns (uint256) {
        return uint160(Currency.unwrap(currency));
    }

    // If the upper 12 bytes are non-zero, they will be zero-ed out
    // Therefore, fromId() and toId() are not inverses of each other
    function fromId(uint256 id) internal pure returns (Currency) {
        return Currency.wrap(address(uint160(id)));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';
import {BalanceDelta} from '@uniswap/v4-core/src/types/BalanceDelta.sol';
import {PoolKey} from '@uniswap/v4-core/src/types/PoolKey.sol';
import {Currency} from '@uniswap/v4-core/src/types/Currency.sol';
import {TickMath} from '@uniswap/v4-core/src/libraries/TickMath.sol';
import {SafeCast} from '@uniswap/v4-core/src/libraries/SafeCast.sol';

import {PathKey} from '@uniswap/v4-periphery/src/libraries/PathKey.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
import {IV4Router} from '@uniswap/v4-periphery/src/interfaces/IV4Router.sol';
import {BaseActionsRouter} from '@uniswap/v4-periphery/src/base/BaseActionsRouter.sol';
import {DeltaResolver} from '@uniswap/v4-periphery/src/base/DeltaResolver.sol';
import {Actions} from '@uniswap/v4-periphery/src/libraries/Actions.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {BipsLibrary} from '@uniswap/v4-periphery/src/libraries/BipsLibrary.sol';

/// @title V4RouterOnlySwap
/// @notice Based on UniswapV4Router but only swap actions are handled.
abstract contract V4RouterOnlySwap is IV4Router, BaseActionsRouter, DeltaResolver {
    using SafeCast for *;
    using CalldataDecoder for bytes;
    using BipsLibrary for uint256;

    constructor(IPoolManager _poolManager) BaseActionsRouter(_poolManager) {}

    function _handleAction(uint256 action, bytes calldata params) internal override {
        if (action == Actions.SWAP_EXACT_IN) {
            IV4Router.ExactInputParams calldata swapParams = params.decodeSwapExactInParams();
            _swapExactInput(swapParams);
            return;
        } else if (action == Actions.SWAP_EXACT_IN_SINGLE) {
            IV4Router.ExactInputSingleParams calldata swapParams = params.decodeSwapExactInSingleParams();
            _swapExactInputSingle(swapParams);
            return;
        } else {
            if (action == Actions.SETTLE) {
                (Currency currency, uint256 amount, bool payerIsUser) = params.decodeCurrencyUint256AndBool();
                _settle(currency, _mapPayer(payerIsUser), _mapSettleAmount(amount, currency));
                return;
            } else if (action == Actions.SETTLE_ALL) {
                (Currency currency, uint256 maxAmount) = params.decodeCurrencyAndUint256();
                uint256 amount = _getFullDebt(currency);
                if (amount > maxAmount) revert V4TooMuchRequested(maxAmount, amount);
                _settle(currency, msgSender(), amount);
                return;
            } else if (action == Actions.TAKE_ALL) {
                (Currency currency, uint256 minAmount) = params.decodeCurrencyAndUint256();
                uint256 amount = _getFullCredit(currency);
                if (amount < minAmount) revert V4TooLittleReceived(minAmount, amount);
                _take(currency, msgSender(), amount);
                return;
            } else if (action == Actions.TAKE) {
                (Currency currency, address recipient, uint256 amount) = params.decodeCurrencyAddressAndUint256();
                _take(currency, _mapRecipient(recipient), _mapTakeAmount(amount, currency));
                return;
            }
        }
        revert UnsupportedAction(action);
    }

    function _swapExactInputSingle(IV4Router.ExactInputSingleParams calldata params) private {
        uint128 amountIn = params.amountIn;
        if (amountIn == ActionConstants.OPEN_DELTA) {
            amountIn =
                _getFullCredit(params.zeroForOne ? params.poolKey.currency0 : params.poolKey.currency1).toUint128();
        }
        uint128 amountOut =
            _swap(params.poolKey, params.zeroForOne, -int256(uint256(amountIn)), params.hookData).toUint128();
        if (amountOut < params.amountOutMinimum) revert V4TooLittleReceived(params.amountOutMinimum, amountOut);
    }

    function _swapExactInput(IV4Router.ExactInputParams calldata params) private {
        unchecked {
            // Caching for gas savings
            uint256 pathLength = params.path.length;
            uint128 amountOut;
            Currency currencyIn = params.currencyIn;
            uint128 amountIn = params.amountIn;
            if (amountIn == ActionConstants.OPEN_DELTA) amountIn = _getFullCredit(currencyIn).toUint128();
            PathKey calldata pathKey;

            for (uint256 i = 0; i < pathLength; i++) {
                pathKey = params.path[i];
                (PoolKey memory poolKey, bool zeroForOne) = pathKey.getPoolAndSwapDirection(currencyIn);
                // The output delta will always be positive, except for when interacting with certain hook pools
                amountOut = _swap(poolKey, zeroForOne, -int256(uint256(amountIn)), pathKey.hookData).toUint128();

                amountIn = amountOut;
                currencyIn = pathKey.intermediateCurrency;
            }

            if (amountOut < params.amountOutMinimum) revert V4TooLittleReceived(params.amountOutMinimum, amountOut);
        }
    }

    function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams calldata params) private {
        uint128 amountOut = params.amountOut;
        if (amountOut == ActionConstants.OPEN_DELTA) {
            amountOut =
                _getFullDebt(params.zeroForOne ? params.poolKey.currency1 : params.poolKey.currency0).toUint128();
        }
        uint128 amountIn = (
            uint256(-int256(_swap(params.poolKey, params.zeroForOne, int256(uint256(amountOut)), params.hookData)))
        ).toUint128();
        if (amountIn > params.amountInMaximum) revert V4TooMuchRequested(params.amountInMaximum, amountIn);
    }

    function _swapExactOutput(IV4Router.ExactOutputParams calldata params) private {
        unchecked {
            // Caching for gas savings
            uint256 pathLength = params.path.length;
            uint128 amountIn;
            uint128 amountOut = params.amountOut;
            Currency currencyOut = params.currencyOut;
            PathKey calldata pathKey;

            if (amountOut == ActionConstants.OPEN_DELTA) {
                amountOut = _getFullDebt(currencyOut).toUint128();
            }

            for (uint256 i = pathLength; i > 0; i--) {
                pathKey = params.path[i - 1];
                (PoolKey memory poolKey, bool oneForZero) = pathKey.getPoolAndSwapDirection(currencyOut);
                // The output delta will always be negative, except for when interacting with certain hook pools
                amountIn = (uint256(-int256(_swap(poolKey, !oneForZero, int256(uint256(amountOut)), pathKey.hookData))))
                    .toUint128();

                amountOut = amountIn;
                currencyOut = pathKey.intermediateCurrency;
            }
            if (amountIn > params.amountInMaximum) revert V4TooMuchRequested(params.amountInMaximum, amountIn);
        }
    }

    function _swap(PoolKey memory poolKey, bool zeroForOne, int256 amountSpecified, bytes calldata hookData)
        private
        returns (int128 reciprocalAmount)
    {
        // for protection of exactOut swaps, sqrtPriceLimit is not exposed as a feature in this contract
        unchecked {
            BalanceDelta delta = poolManager.swap(
                poolKey,
                IPoolManager.SwapParams(
                    zeroForOne, amountSpecified, zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1
                ),
                hookData
            );

            reciprocalAmount = (zeroForOne == amountSpecified < 0) ? delta.amount1() : delta.amount0();
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

interface IBalancerVault {
    enum SwapKind {
        GIVEN_IN,
        GIVEN_OUT
    }

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

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

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

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

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

pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;

import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import {V2BytesLib, Constants} from './V2BytesLib.sol';

/// @title Uniswap v2 Helper Library
/// @notice Calculates the recipient address for a command
library UniswapV2Library {
    using V2BytesLib for bytes;

    error InvalidReserves();
    error InvalidPath();

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

    /// @notice Decodes the first pair in path
    /// @param path The bytes encoded swap path
    /// @return input The first token of the given pair
    /// @return feeNumerator The fee numerator
    /// @return pair The pair address
    /// @return output The second token of the given pair
    function toPair(bytes calldata path) internal pure returns (address, uint16, address, address) {
        return path.toPair();
    }

    /// @notice Decodes the first pair output
    /// @param path The bytes encoded swap path
    /// @return output The second token of the first pair
    function decodeFirstOutput(bytes calldata path) internal pure returns (address) {
        return skipToken(path).toAddress();
    }

    /// @notice Decodes the last pair output
    /// @param path The bytes encoded swap path
    /// @return output The second token of the last pair
    function decodeLastOutput(bytes calldata path) internal pure returns (address) {
        return path.toLastAddress();
    }

    /// @notice Skips a token + fee + pair element
    /// @param path The swap path
    function skipToken(bytes calldata path) internal pure returns (bytes calldata) {
        return path[Constants.NEXT_V2_PAIR_OFFSET:];
    }

    /// @notice Calculates the v2 address for a pair without making any external calls
    /// @param factory The address of the v2 factory
    /// @param initCodeHash The hash of the pair initcode
    /// @param tokenA One of the tokens in the pair
    /// @param tokenB The other token in the pair
    /// @return pair The resultant v2 pair address
    function pairFor(address factory, bytes32 initCodeHash, address tokenA, address tokenB)
        internal
        pure
        returns (address pair)
    {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = pairForPreSorted(factory, initCodeHash, token0, token1);
    }

    /// @notice Calculates the v2 address for a pair and the pair's token0
    /// @param factory The address of the v2 factory
    /// @param initCodeHash The hash of the pair initcode
    /// @param tokenA One of the tokens in the pair
    /// @param tokenB The other token in the pair
    /// @return pair The resultant v2 pair address
    /// @return token0 The token considered token0 in this pair
    function pairAndToken0For(address factory, bytes32 initCodeHash, address tokenA, address tokenB)
        internal
        pure
        returns (address pair, address token0)
    {
        address token1;
        (token0, token1) = sortTokens(tokenA, tokenB);
        pair = pairForPreSorted(factory, initCodeHash, token0, token1);
    }

    /// @notice Calculates the v2 address for a pair assuming the input tokens are pre-sorted
    /// @param factory The address of the v2 factory
    /// @param initCodeHash The hash of the pair initcode
    /// @param token0 The pair's token0
    /// @param token1 The pair's token1
    /// @return pair The resultant v2 pair address
    function pairForPreSorted(address factory, bytes32 initCodeHash, address token0, address token1)
        private
        pure
        returns (address pair)
    {
        pair = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(hex'ff', factory, keccak256(abi.encodePacked(token0, token1)), initCodeHash)
                    )
                )
            )
        );
    }

    /// @notice Calculates the v2 address for a pair and fetches the reserves for each token
    /// @param factory The address of the v2 factory
    /// @param initCodeHash The hash of the pair initcode
    /// @param tokenA One of the tokens in the pair
    /// @param tokenB The other token in the pair
    /// @return pair The resultant v2 pair address
    /// @return reserveA The reserves for tokenA
    /// @return reserveB The reserves for tokenB
    function pairAndReservesFor(address factory, bytes32 initCodeHash, address tokenA, address tokenB)
        private
        view
        returns (address pair, uint256 reserveA, uint256 reserveB)
    {
        address token0;
        (pair, token0) = pairAndToken0For(factory, initCodeHash, tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    /// @notice Given an input asset amount returns the maximum output amount of the other asset
    /// @param amountIn The token input amount
    /// @param reserveIn The reserves available of the input token
    /// @param reserveOut The reserves available of the output token
    /// @return amountOut The output amount of the output token
    function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint256 amountOut)
    {
        if (reserveIn == 0 || reserveOut == 0) revert InvalidReserves();
        uint256 amountInWithFee = amountIn * 997;
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * 1000 + amountInWithFee;
        amountOut = numerator / denominator;
    }

    /// @notice Returns the input amount needed for a desired output amount in a single-hop trade
    /// @param amountOut The desired output amount
    /// @param reserveIn The reserves available of the input token
    /// @param reserveOut The reserves available of the output token
    /// @return amountIn The input amount of the input token
    function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint256 amountIn)
    {
        if (reserveIn == 0 || reserveOut == 0) revert InvalidReserves();
        uint256 numerator = reserveIn * amountOut * 1000;
        uint256 denominator = (reserveOut - amountOut) * 997;
        amountIn = (numerator / denominator) + 1;
    }

    /// @notice Returns the input amount needed for a desired output amount in a multi-hop trade
    /// @param factory The address of the v2 factory
    /// @param initCodeHash The hash of the pair initcode
    /// @param amountOut The desired output amount
    /// @param path The path of the multi-hop trade
    /// @return amount The input amount of the input token
    /// @return pair The first pair in the trade
    function getAmountInMultihop(address factory, bytes32 initCodeHash, uint256 amountOut, address[] calldata path)
        internal
        view
        returns (uint256 amount, address pair)
    {
        if (path.length < 2) revert InvalidPath();
        amount = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            uint256 reserveIn;
            uint256 reserveOut;

            (pair, reserveIn, reserveOut) = pairAndReservesFor(factory, initCodeHash, path[i - 1], path[i]);
            amount = getAmountIn(amount, reserveIn, reserveOut);
        }
    }

    /// @notice Sorts two tokens to return token0 and token1
    /// @param tokenA The first token to sort
    /// @param tokenB The other token to sort
    /// @return token0 The smaller token by address value
    /// @return token1 The larger token by address value
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    }

    /// @notice Given an input asset amount and a fee numerator (to the denominator of 10_000),
    /// returns the maximum output amount of the other asset
    /// @param amountIn The token input amount
    /// @param feeNumerator The fee numerator
    /// @param reserveIn The reserves available of the input token
    /// @param reserveOut The reserves available of the output token
    /// @return amountOut The output amount of the output token
    function getAmountOut(uint256 amountIn, uint16 feeNumerator, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint256 amountOut)
    {
        if (reserveIn == 0 || reserveOut == 0) revert InvalidReserves();
        uint256 amountInWithFee = amountIn * feeNumerator;
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * 10_000 + amountInWithFee;
        amountOut = numerator / denominator;
    }
}

File 36 of 81 : Constants.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

/// @title Constant state
/// @notice Constant state used by the Universal Router
library Constants {
    /// @dev Used for identifying cases when a v2 pair has already received input tokens
    uint256 internal constant ALREADY_PAID = 0;

    /// @dev Used as a flag for identifying the transfer of ETH instead of a token
    address internal constant ETH = address(0);

    /// @dev The length of the bytes encoded address
    uint256 internal constant ADDR_SIZE = 20;

    /// @dev The length of the bytes encoded fee numerator
    uint256 internal constant FEE_NUMERATOR_SIZE = 2;

    /// @dev The offset of a single token address (20), fee numerator (2) and pool address (20)
    uint256 internal constant NEXT_V2_PAIR_OFFSET = ADDR_SIZE + FEE_NUMERATOR_SIZE + ADDR_SIZE;

    /// @dev The offset of an encoded pair
    /// Token (20) + Fee Numerator (2) + Pool (20) + Token (20) = 62
    uint256 internal constant V2_POP_OFFSET = NEXT_V2_PAIR_OFFSET + ADDR_SIZE;

    /// @dev The minimum length of an encoding that contains 2 or more pairs
    uint256 internal constant MULTIPLE_V2_PAIRS_MIN_LENGTH = V2_POP_OFFSET + NEXT_V3_POOL_OFFSET;

    /// @dev The offset of a single token address (20) and pool address (20)
    uint256 internal constant NEXT_V3_POOL_OFFSET = ADDR_SIZE + ADDR_SIZE;

    /// @dev The offset of an encoded pool key
    /// Token (20) + Pool (20) + Token (20) = 60
    uint256 internal constant V3_POP_OFFSET = NEXT_V3_POOL_OFFSET + ADDR_SIZE;

    /// @dev The minimum length of an encoding that contains 2 or more pools
    uint256 internal constant MULTIPLE_V3_POOLS_MIN_LENGTH = V3_POP_OFFSET + NEXT_V3_POOL_OFFSET;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0;

import {BytesLib} from './BytesLib.sol';
import {Constants} from '../../../libraries/Constants.sol';

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

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

    /// @notice Decodes the first pool in path
    /// @param path The bytes encoded swap path
    /// @return tokenA The first token of the given pool
    /// @return pool The pool address
    /// @return tokenB The second token of the given pool
    function decodeFirstPool(bytes calldata path) internal pure returns (address, address, address) {
        return path.toPool();
    }

    /// @notice Gets the segment corresponding to the first pool in the path
    /// @param path The bytes encoded swap path
    /// @return The segment containing all data necessary to target the first pool in the path
    function getFirstPool(bytes calldata path) internal pure returns (bytes calldata) {
        return path[:Constants.V3_POP_OFFSET];
    }

    function decodeFirstToken(bytes calldata path) internal pure returns (address tokenA) {
        tokenA = path.toAddress();
    }

    /// @notice Skips a token + pool element
    /// @param path The swap path
    function skipToken(bytes calldata path) internal pure returns (bytes calldata) {
        return path[Constants.NEXT_V3_POOL_OFFSET:];
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
    /// @notice Cast a uint256 to a uint160, revert on overflow
    /// @param y The uint256 to be downcasted
    /// @return z The downcasted integer, now type uint160
    function toUint160(uint256 y) internal pure returns (uint160 z) {
        require((z = uint160(y)) == y);
    }

    /// @notice Cast a int256 to a int128, revert on overflow or underflow
    /// @param y The int256 to be downcasted
    /// @return z The downcasted integer, now type int128
    function toInt128(int256 y) internal pure returns (int128 z) {
        require((z = int128(y)) == y);
    }

    /// @notice Cast a uint256 to a int256, revert on overflow
    /// @param y The uint256 to be casted
    /// @return z The casted integer, now type int256
    function toInt256(uint256 y) internal pure returns (int256 z) {
        require(y < 2**255);
        z = int256(y);
    }
}

File 39 of 81 : IUniswapV3Pool.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
    IUniswapV3PoolImmutables,
    IUniswapV3PoolState,
    IUniswapV3PoolDerivedState,
    IUniswapV3PoolActions,
    IUniswapV3PoolOwnerActions,
    IUniswapV3PoolEvents
{

}

File 40 of 81 : IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

/// @notice A library used to store the maximum desired amount of input tokens for exact output swaps; used for checking slippage
library MaxInputAmount {
    // The slot holding the the maximum desired amount of input tokens, transiently. bytes32(uint256(keccak256("MaxAmountIn")) - 1)
    bytes32 constant MAX_AMOUNT_IN_SLOT = 0xaf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f692;

    function set(uint256 maxAmountIn) internal {
        assembly ("memory-safe") {
            tstore(MAX_AMOUNT_IN_SLOT, maxAmountIn)
        }
    }

    function get() internal view returns (uint256 maxAmountIn) {
        assembly ("memory-safe") {
            maxAmountIn := tload(MAX_AMOUNT_IN_SLOT)
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

/// @notice A library to implement a reentrancy lock in transient storage.
/// @dev Instead of storing a boolean, the locker's address is stored to allow the contract to know who locked the contract
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library Locker {
    // The slot holding the locker state, transiently. bytes32(uint256(keccak256("Locker")) - 1)
    bytes32 constant LOCKER_SLOT = 0x0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a708;

    function set(address locker) internal {
        // The locker is always msg.sender or address(0) so does not need to be cleaned
        assembly ("memory-safe") {
            tstore(LOCKER_SLOT, locker)
        }
    }

    function get() internal view returns (address locker) {
        assembly ("memory-safe") {
            locker := tload(LOCKER_SLOT)
        }
    }

    function isLocked() internal view returns (bool) {
        return Locker.get() != address(0);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title For calculating a percentage of an amount, using bips
library BipsLibrary {
    uint256 internal constant BPS_DENOMINATOR = 10_000;

    /// @notice emitted when an invalid percentage is provided
    error InvalidBips();

    /// @param amount The total amount to calculate a percentage of
    /// @param bips The percentage to calculate, in bips
    function calculatePortion(uint256 amount, uint256 bips) internal pure returns (uint256) {
        if (bips > BPS_DENOMINATOR) revert InvalidBips();
        return (amount * bips) / BPS_DENOMINATOR;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

File 45 of 81 : IV4Router.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {PathKey} from "../libraries/PathKey.sol";
import {IImmutableState} from "./IImmutableState.sol";

/// @title IV4Router
/// @notice Interface for the V4Router contract
interface IV4Router is IImmutableState {
    /// @notice Emitted when an exactInput swap does not receive its minAmountOut
    error V4TooLittleReceived(uint256 minAmountOutReceived, uint256 amountReceived);
    /// @notice Emitted when an exactOutput is asked for more than its maxAmountIn
    error V4TooMuchRequested(uint256 maxAmountInRequested, uint256 amountRequested);

    /// @notice Parameters for a single-hop exact-input swap
    struct ExactInputSingleParams {
        PoolKey poolKey;
        bool zeroForOne;
        uint128 amountIn;
        uint128 amountOutMinimum;
        bytes hookData;
    }

    /// @notice Parameters for a multi-hop exact-input swap
    struct ExactInputParams {
        Currency currencyIn;
        PathKey[] path;
        uint128 amountIn;
        uint128 amountOutMinimum;
    }

    /// @notice Parameters for a single-hop exact-output swap
    struct ExactOutputSingleParams {
        PoolKey poolKey;
        bool zeroForOne;
        uint128 amountOut;
        uint128 amountInMaximum;
        bytes hookData;
    }

    /// @notice Parameters for a multi-hop exact-output swap
    struct ExactOutputParams {
        Currency currencyOut;
        PathKey[] path;
        uint128 amountOut;
        uint128 amountInMaximum;
    }
}

File 46 of 81 : PoolKey.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Currency} from "./Currency.sol";
import {IHooks} from "../interfaces/IHooks.sol";
import {PoolIdLibrary} from "./PoolId.sol";

using PoolIdLibrary for PoolKey global;

/// @notice Returns the key for identifying a pool
struct PoolKey {
    /// @notice The lower currency of the pool, sorted numerically
    Currency currency0;
    /// @notice The higher currency of the pool, sorted numerically
    Currency currency1;
    /// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000
    uint24 fee;
    /// @notice Ticks that involve positions must be a multiple of tick spacing
    int24 tickSpacing;
    /// @notice The hooks of the pool
    IHooks hooks;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IEIP712} from "./IEIP712.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;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

library SafeCast160 {
    /// @notice Thrown when a valude greater than type(uint160).max is cast to uint160
    error UnsafeCast();

    /// @notice Safely casts uint256 to uint160
    /// @param value The uint256 to be cast
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) revert UnsafeCast();
        return uint160(value);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {PoolKey} from "../types/PoolKey.sol";
import {BalanceDelta} from "../types/BalanceDelta.sol";
import {IPoolManager} from "./IPoolManager.sol";
import {BeforeSwapDelta} from "../types/BeforeSwapDelta.sol";

/// @notice V4 decides whether to invoke specific hooks by inspecting the least significant bits
/// of the address that the hooks contract is deployed to.
/// For example, a hooks contract deployed to address: 0x0000000000000000000000000000000000002400
/// has the lowest bits '10 0100 0000 0000' which would cause the 'before initialize' and 'after add liquidity' hooks to be used.
/// See the Hooks library for the full spec.
/// @dev Should only be callable by the v4 PoolManager.
interface IHooks {
    /// @notice The hook called before the state of a pool is initialized
    /// @param sender The initial msg.sender for the initialize call
    /// @param key The key for the pool being initialized
    /// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
    /// @return bytes4 The function selector for the hook
    function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96) external returns (bytes4);

    /// @notice The hook called after the state of a pool is initialized
    /// @param sender The initial msg.sender for the initialize call
    /// @param key The key for the pool being initialized
    /// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
    /// @param tick The current tick after the state of a pool is initialized
    /// @return bytes4 The function selector for the hook
    function afterInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, int24 tick)
        external
        returns (bytes4);

    /// @notice The hook called before liquidity is added
    /// @param sender The initial msg.sender for the add liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for adding liquidity
    /// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function beforeAddLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4);

    /// @notice The hook called after liquidity is added
    /// @param sender The initial msg.sender for the add liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for adding liquidity
    /// @param delta The caller's balance delta after adding liquidity; the sum of principal delta, fees accrued, and hook delta
    /// @param feesAccrued The fees accrued since the last time fees were collected from this position
    /// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
    function afterAddLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        BalanceDelta delta,
        BalanceDelta feesAccrued,
        bytes calldata hookData
    ) external returns (bytes4, BalanceDelta);

    /// @notice The hook called before liquidity is removed
    /// @param sender The initial msg.sender for the remove liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for removing liquidity
    /// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function beforeRemoveLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4);

    /// @notice The hook called after liquidity is removed
    /// @param sender The initial msg.sender for the remove liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for removing liquidity
    /// @param delta The caller's balance delta after removing liquidity; the sum of principal delta, fees accrued, and hook delta
    /// @param feesAccrued The fees accrued since the last time fees were collected from this position
    /// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
    function afterRemoveLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        BalanceDelta delta,
        BalanceDelta feesAccrued,
        bytes calldata hookData
    ) external returns (bytes4, BalanceDelta);

    /// @notice The hook called before a swap
    /// @param sender The initial msg.sender for the swap call
    /// @param key The key for the pool
    /// @param params The parameters for the swap
    /// @param hookData Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return BeforeSwapDelta The hook's delta in specified and unspecified currencies. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
    /// @return uint24 Optionally override the lp fee, only used if three conditions are met: 1. the Pool has a dynamic fee, 2. the value's 2nd highest bit is set (23rd bit, 0x400000), and 3. the value is less than or equal to the maximum fee (1 million)
    function beforeSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4, BeforeSwapDelta, uint24);

    /// @notice The hook called after a swap
    /// @param sender The initial msg.sender for the swap call
    /// @param key The key for the pool
    /// @param params The parameters for the swap
    /// @param delta The amount owed to the caller (positive) or owed to the pool (negative)
    /// @param hookData Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return int128 The hook's delta in unspecified currency. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
    function afterSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        BalanceDelta delta,
        bytes calldata hookData
    ) external returns (bytes4, int128);

    /// @notice The hook called before donate
    /// @param sender The initial msg.sender for the donate call
    /// @param key The key for the pool
    /// @param amount0 The amount of token0 being donated
    /// @param amount1 The amount of token1 being donated
    /// @param hookData Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function beforeDonate(
        address sender,
        PoolKey calldata key,
        uint256 amount0,
        uint256 amount1,
        bytes calldata hookData
    ) external returns (bytes4);

    /// @notice The hook called after donate
    /// @param sender The initial msg.sender for the donate call
    /// @param key The key for the pool
    /// @param amount0 The amount of token0 being donated
    /// @param amount1 The amount of token1 being donated
    /// @param hookData Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function afterDonate(
        address sender,
        PoolKey calldata key,
        uint256 amount0,
        uint256 amount1,
        bytes calldata hookData
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Interface for claims over a contract balance, wrapped as a ERC6909
interface IERC6909Claims {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

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

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

    /*//////////////////////////////////////////////////////////////
                                 FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Owner balance of an id.
    /// @param owner The address of the owner.
    /// @param id The id of the token.
    /// @return amount The balance of the token.
    function balanceOf(address owner, uint256 id) external view returns (uint256 amount);

    /// @notice Spender allowance of an id.
    /// @param owner The address of the owner.
    /// @param spender The address of the spender.
    /// @param id The id of the token.
    /// @return amount The allowance of the token.
    function allowance(address owner, address spender, uint256 id) external view returns (uint256 amount);

    /// @notice Checks if a spender is approved by an owner as an operator
    /// @param owner The address of the owner.
    /// @param spender The address of the spender.
    /// @return approved The approval status.
    function isOperator(address owner, address spender) external view returns (bool approved);

    /// @notice Transfers an amount of an id from the caller to a receiver.
    /// @param receiver The address of the receiver.
    /// @param id The id of the token.
    /// @param amount The amount of the token.
    /// @return bool True, always, unless the function reverts
    function transfer(address receiver, uint256 id, uint256 amount) external returns (bool);

    /// @notice Transfers an amount of an id from a sender to a receiver.
    /// @param sender The address of the sender.
    /// @param receiver The address of the receiver.
    /// @param id The id of the token.
    /// @param amount The amount of the token.
    /// @return bool True, always, unless the function reverts
    function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool);

    /// @notice Approves an amount of an id to a spender.
    /// @param spender The address of the spender.
    /// @param id The id of the token.
    /// @param amount The amount of the token.
    /// @return bool True, always
    function approve(address spender, uint256 id, uint256 amount) external returns (bool);

    /// @notice Sets or removes an operator for the caller.
    /// @param operator The address of the operator.
    /// @param approved The approval status.
    /// @return bool True, always
    function setOperator(address operator, bool approved) external returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Currency} from "../types/Currency.sol";
import {PoolId} from "../types/PoolId.sol";
import {PoolKey} from "../types/PoolKey.sol";

/// @notice Interface for all protocol-fee related functions in the pool manager
interface IProtocolFees {
    /// @notice Thrown when protocol fee is set too high
    error ProtocolFeeTooLarge(uint24 fee);

    /// @notice Thrown when collectProtocolFees or setProtocolFee is not called by the controller.
    error InvalidCaller();

    /// @notice Thrown when collectProtocolFees is attempted on a token that is synced.
    error ProtocolFeeCurrencySynced();

    /// @notice Emitted when the protocol fee controller address is updated in setProtocolFeeController.
    event ProtocolFeeControllerUpdated(address indexed protocolFeeController);

    /// @notice Emitted when the protocol fee is updated for a pool.
    event ProtocolFeeUpdated(PoolId indexed id, uint24 protocolFee);

    /// @notice Given a currency address, returns the protocol fees accrued in that currency
    /// @param currency The currency to check
    /// @return amount The amount of protocol fees accrued in the currency
    function protocolFeesAccrued(Currency currency) external view returns (uint256 amount);

    /// @notice Sets the protocol fee for the given pool
    /// @param key The key of the pool to set a protocol fee for
    /// @param newProtocolFee The fee to set
    function setProtocolFee(PoolKey memory key, uint24 newProtocolFee) external;

    /// @notice Sets the protocol fee controller
    /// @param controller The new protocol fee controller
    function setProtocolFeeController(address controller) external;

    /// @notice Collects the protocol fees for a given recipient and currency, returning the amount collected
    /// @dev This will revert if the contract is unlocked
    /// @param recipient The address to receive the protocol fees
    /// @param currency The currency to withdraw
    /// @param amount The amount of currency to withdraw
    /// @return amountCollected The amount of currency successfully withdrawn
    function collectProtocolFees(address recipient, Currency currency, uint256 amount)
        external
        returns (uint256 amountCollected);

    /// @notice Returns the current protocol fee controller address
    /// @return address The current protocol fee controller address
    function protocolFeeController() external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SafeCast} from "../libraries/SafeCast.sol";

/// @dev Two `int128` values packed into a single `int256` where the upper 128 bits represent the amount0
/// and the lower 128 bits represent the amount1.
type BalanceDelta is int256;

using {add as +, sub as -, eq as ==, neq as !=} for BalanceDelta global;
using BalanceDeltaLibrary for BalanceDelta global;
using SafeCast for int256;

function toBalanceDelta(int128 _amount0, int128 _amount1) pure returns (BalanceDelta balanceDelta) {
    assembly ("memory-safe") {
        balanceDelta := or(shl(128, _amount0), and(sub(shl(128, 1), 1), _amount1))
    }
}

function add(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta) {
    int256 res0;
    int256 res1;
    assembly ("memory-safe") {
        let a0 := sar(128, a)
        let a1 := signextend(15, a)
        let b0 := sar(128, b)
        let b1 := signextend(15, b)
        res0 := add(a0, b0)
        res1 := add(a1, b1)
    }
    return toBalanceDelta(res0.toInt128(), res1.toInt128());
}

function sub(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta) {
    int256 res0;
    int256 res1;
    assembly ("memory-safe") {
        let a0 := sar(128, a)
        let a1 := signextend(15, a)
        let b0 := sar(128, b)
        let b1 := signextend(15, b)
        res0 := sub(a0, b0)
        res1 := sub(a1, b1)
    }
    return toBalanceDelta(res0.toInt128(), res1.toInt128());
}

function eq(BalanceDelta a, BalanceDelta b) pure returns (bool) {
    return BalanceDelta.unwrap(a) == BalanceDelta.unwrap(b);
}

function neq(BalanceDelta a, BalanceDelta b) pure returns (bool) {
    return BalanceDelta.unwrap(a) != BalanceDelta.unwrap(b);
}

/// @notice Library for getting the amount0 and amount1 deltas from the BalanceDelta type
library BalanceDeltaLibrary {
    /// @notice A BalanceDelta of 0
    BalanceDelta public constant ZERO_DELTA = BalanceDelta.wrap(0);

    function amount0(BalanceDelta balanceDelta) internal pure returns (int128 _amount0) {
        assembly ("memory-safe") {
            _amount0 := sar(128, balanceDelta)
        }
    }

    function amount1(BalanceDelta balanceDelta) internal pure returns (int128 _amount1) {
        assembly ("memory-safe") {
            _amount1 := signextend(15, balanceDelta)
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

type PoolId is bytes32;

/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
    /// @notice Returns value equal to keccak256(abi.encode(poolKey))
    function toId(PoolKey memory poolKey) internal pure returns (PoolId poolId) {
        assembly ("memory-safe") {
            // 0xa0 represents the total size of the poolKey struct (5 slots of 32 bytes)
            poolId := keccak256(poolKey, 0xa0)
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Interface for functions to access any storage slot in a contract
interface IExtsload {
    /// @notice Called by external contracts to access granular pool state
    /// @param slot Key of slot to sload
    /// @return value The value of the slot as bytes32
    function extsload(bytes32 slot) external view returns (bytes32 value);

    /// @notice Called by external contracts to access granular pool state
    /// @param startSlot Key of slot to start sloading from
    /// @param nSlots Number of slots to load into return value
    /// @return values List of loaded values.
    function extsload(bytes32 startSlot, uint256 nSlots) external view returns (bytes32[] memory values);

    /// @notice Called by external contracts to access sparse pool state
    /// @param slots List of slots to SLOAD from.
    /// @return values List of loaded values.
    function extsload(bytes32[] calldata slots) external view returns (bytes32[] memory values);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Interface for functions to access any transient storage slot in a contract
interface IExttload {
    /// @notice Called by external contracts to access transient storage of the contract
    /// @param slot Key of slot to tload
    /// @return value The value of the slot as bytes32
    function exttload(bytes32 slot) external view returns (bytes32 value);

    /// @notice Called by external contracts to access sparse transient pool state
    /// @param slots List of slots to tload
    /// @return values List of loaded values
    function exttload(bytes32[] calldata slots) external view returns (bytes32[] memory values);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Minimal ERC20 interface for Uniswap
/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3
interface IERC20Minimal {
    /// @notice Returns an account's balance in the token
    /// @param account The account for which to look up the number of tokens it has, i.e. its balance
    /// @return The number of tokens held by the account
    function balanceOf(address account) external view returns (uint256);

    /// @notice Transfers the amount of token from the `msg.sender` to the recipient
    /// @param recipient The account that will receive the amount transferred
    /// @param amount The number of tokens to send from the sender to the recipient
    /// @return Returns true for a successful transfer, false for an unsuccessful transfer
    function transfer(address recipient, uint256 amount) external returns (bool);

    /// @notice Returns the current allowance given to a spender by an owner
    /// @param owner The account of the token owner
    /// @param spender The account of the token spender
    /// @return The current allowance granted by `owner` to `spender`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
    /// @param spender The account which will be allowed to spend a given amount of the owners tokens
    /// @param amount The amount of tokens allowed to be used by `spender`
    /// @return Returns true for a successful approval, false for unsuccessful
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
    /// @param sender The account from which the transfer will be initiated
    /// @param recipient The recipient of the transfer
    /// @param amount The amount of the transfer
    /// @return Returns true for a successful transfer, false for unsuccessful
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
    /// @param from The account from which the tokens were sent, i.e. the balance decreased
    /// @param to The account to which the tokens were sent, i.e. the balance increased
    /// @param value The amount of tokens that were transferred
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
    /// @param owner The account that approved spending of its tokens
    /// @param spender The account for which the spending allowance was modified
    /// @param value The new allowance from the owner to the spender
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Library for reverting with custom errors efficiently
/// @notice Contains functions for reverting with custom errors with different argument types efficiently
/// @dev To use this library, declare `using CustomRevert for bytes4;` and replace `revert CustomError()` with
/// `CustomError.selector.revertWith()`
/// @dev The functions may tamper with the free memory pointer but it is fine since the call context is exited immediately
library CustomRevert {
    /// @dev ERC-7751 error for wrapping bubbled up reverts
    error WrappedError(address target, bytes4 selector, bytes reason, bytes details);

    /// @dev Reverts with the selector of a custom error in the scratch space
    function revertWith(bytes4 selector) internal pure {
        assembly ("memory-safe") {
            mstore(0, selector)
            revert(0, 0x04)
        }
    }

    /// @dev Reverts with a custom error with an address argument in the scratch space
    function revertWith(bytes4 selector, address addr) internal pure {
        assembly ("memory-safe") {
            mstore(0, selector)
            mstore(0x04, and(addr, 0xffffffffffffffffffffffffffffffffffffffff))
            revert(0, 0x24)
        }
    }

    /// @dev Reverts with a custom error with an int24 argument in the scratch space
    function revertWith(bytes4 selector, int24 value) internal pure {
        assembly ("memory-safe") {
            mstore(0, selector)
            mstore(0x04, signextend(2, value))
            revert(0, 0x24)
        }
    }

    /// @dev Reverts with a custom error with a uint160 argument in the scratch space
    function revertWith(bytes4 selector, uint160 value) internal pure {
        assembly ("memory-safe") {
            mstore(0, selector)
            mstore(0x04, and(value, 0xffffffffffffffffffffffffffffffffffffffff))
            revert(0, 0x24)
        }
    }

    /// @dev Reverts with a custom error with two int24 arguments
    function revertWith(bytes4 selector, int24 value1, int24 value2) internal pure {
        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(fmp, selector)
            mstore(add(fmp, 0x04), signextend(2, value1))
            mstore(add(fmp, 0x24), signextend(2, value2))
            revert(fmp, 0x44)
        }
    }

    /// @dev Reverts with a custom error with two uint160 arguments
    function revertWith(bytes4 selector, uint160 value1, uint160 value2) internal pure {
        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(fmp, selector)
            mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
            revert(fmp, 0x44)
        }
    }

    /// @dev Reverts with a custom error with two address arguments
    function revertWith(bytes4 selector, address value1, address value2) internal pure {
        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(fmp, selector)
            mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
            revert(fmp, 0x44)
        }
    }

    /// @notice bubble up the revert message returned by a call and revert with a wrapped ERC-7751 error
    /// @dev this method can be vulnerable to revert data bombs
    function bubbleUpAndRevertWith(
        address revertingContract,
        bytes4 revertingFunctionSelector,
        bytes4 additionalContext
    ) internal pure {
        bytes4 wrappedErrorSelector = WrappedError.selector;
        assembly ("memory-safe") {
            // Ensure the size of the revert data is a multiple of 32 bytes
            let encodedDataSize := mul(div(add(returndatasize(), 31), 32), 32)

            let fmp := mload(0x40)

            // Encode wrapped error selector, address, function selector, offset, additional context, size, revert reason
            mstore(fmp, wrappedErrorSelector)
            mstore(add(fmp, 0x04), and(revertingContract, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(
                add(fmp, 0x24),
                and(revertingFunctionSelector, 0xffffffff00000000000000000000000000000000000000000000000000000000)
            )
            // offset revert reason
            mstore(add(fmp, 0x44), 0x80)
            // offset additional context
            mstore(add(fmp, 0x64), add(0xa0, encodedDataSize))
            // size revert reason
            mstore(add(fmp, 0x84), returndatasize())
            // revert reason
            returndatacopy(add(fmp, 0xa4), 0, returndatasize())
            // size additional context
            mstore(add(fmp, add(0xa4, encodedDataSize)), 0x04)
            // additional context
            mstore(
                add(fmp, add(0xc4, encodedDataSize)),
                and(additionalContext, 0xffffffff00000000000000000000000000000000000000000000000000000000)
            )
            revert(fmp, add(0xe4, encodedDataSize))
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {BitMath} from "./BitMath.sol";
import {CustomRevert} from "./CustomRevert.sol";

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    using CustomRevert for bytes4;

    /// @notice Thrown when the tick passed to #getSqrtPriceAtTick is not between MIN_TICK and MAX_TICK
    error InvalidTick(int24 tick);
    /// @notice Thrown when the price passed to #getTickAtSqrtPrice does not correspond to a price between MIN_TICK and MAX_TICK
    error InvalidSqrtPrice(uint160 sqrtPriceX96);

    /// @dev The minimum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**-128
    /// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**128
    /// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
    int24 internal constant MAX_TICK = 887272;

    /// @dev The minimum tick spacing value drawn from the range of type int16 that is greater than 0, i.e. min from the range [1, 32767]
    int24 internal constant MIN_TICK_SPACING = 1;
    /// @dev The maximum tick spacing value drawn from the range of type int16, i.e. max from the range [1, 32767]
    int24 internal constant MAX_TICK_SPACING = type(int16).max;

    /// @dev The minimum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_PRICE = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_PRICE = 1461446703485210103287273052203988822378723970342;
    /// @dev A threshold used for optimized bounds check, equals `MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1`
    uint160 internal constant MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE =
        1461446703485210103287273052203988822378723970342 - 4295128739 - 1;

    /// @notice Given a tickSpacing, compute the maximum usable tick
    function maxUsableTick(int24 tickSpacing) internal pure returns (int24) {
        unchecked {
            return (MAX_TICK / tickSpacing) * tickSpacing;
        }
    }

    /// @notice Given a tickSpacing, compute the minimum usable tick
    function minUsableTick(int24 tickSpacing) internal pure returns (int24) {
        unchecked {
            return (MIN_TICK / tickSpacing) * tickSpacing;
        }
    }

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the price of the two assets (currency1/currency0)
    /// at the given tick
    function getSqrtPriceAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        unchecked {
            uint256 absTick;
            assembly ("memory-safe") {
                tick := signextend(2, tick)
                // mask = 0 if tick >= 0 else -1 (all 1s)
                let mask := sar(255, tick)
                // if tick >= 0, |tick| = tick = 0 ^ tick
                // if tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = (-1) ^ (tick - 1)
                // either way, |tick| = mask ^ (tick + mask)
                absTick := xor(mask, add(mask, tick))
            }

            if (absTick > uint256(int256(MAX_TICK))) InvalidTick.selector.revertWith(tick);

            // The tick is decomposed into bits, and for each bit with index i that is set, the product of 1/sqrt(1.0001^(2^i))
            // is calculated (using Q128.128). The constants used for this calculation are rounded to the nearest integer

            // Equivalent to:
            //     price = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
            //     or price = int(2**128 / sqrt(1.0001)) if (absTick & 0x1) else 1 << 128
            uint256 price;
            assembly ("memory-safe") {
                price := xor(shl(128, 1), mul(xor(shl(128, 1), 0xfffcb933bd6fad37aa2d162d1a594001), and(absTick, 0x1)))
            }
            if (absTick & 0x2 != 0) price = (price * 0xfff97272373d413259a46990580e213a) >> 128;
            if (absTick & 0x4 != 0) price = (price * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
            if (absTick & 0x8 != 0) price = (price * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
            if (absTick & 0x10 != 0) price = (price * 0xffcb9843d60f6159c9db58835c926644) >> 128;
            if (absTick & 0x20 != 0) price = (price * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
            if (absTick & 0x40 != 0) price = (price * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
            if (absTick & 0x80 != 0) price = (price * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
            if (absTick & 0x100 != 0) price = (price * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
            if (absTick & 0x200 != 0) price = (price * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
            if (absTick & 0x400 != 0) price = (price * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
            if (absTick & 0x800 != 0) price = (price * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
            if (absTick & 0x1000 != 0) price = (price * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
            if (absTick & 0x2000 != 0) price = (price * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
            if (absTick & 0x4000 != 0) price = (price * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
            if (absTick & 0x8000 != 0) price = (price * 0x31be135f97d08fd981231505542fcfa6) >> 128;
            if (absTick & 0x10000 != 0) price = (price * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
            if (absTick & 0x20000 != 0) price = (price * 0x5d6af8dedb81196699c329225ee604) >> 128;
            if (absTick & 0x40000 != 0) price = (price * 0x2216e584f5fa1ea926041bedfe98) >> 128;
            if (absTick & 0x80000 != 0) price = (price * 0x48a170391f7dc42444e8fa2) >> 128;

            assembly ("memory-safe") {
                // if (tick > 0) price = type(uint256).max / price;
                if sgt(tick, 0) { price := div(not(0), price) }

                // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
                // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
                // we round up in the division so getTickAtSqrtPrice of the output price is always consistent
                // `sub(shl(32, 1), 1)` is `type(uint32).max`
                // `price + type(uint32).max` will not overflow because `price` fits in 192 bits
                sqrtPriceX96 := shr(32, add(price, sub(shl(32, 1), 1)))
            }
        }
    }

    /// @notice Calculates the greatest tick value such that getSqrtPriceAtTick(tick) <= sqrtPriceX96
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_PRICE, as MIN_SQRT_PRICE is the lowest value getSqrtPriceAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt price for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the getSqrtPriceAtTick(tick) is less than or equal to the input sqrtPriceX96
    function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        unchecked {
            // Equivalent: if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 >= MAX_SQRT_PRICE) revert InvalidSqrtPrice();
            // second inequality must be >= because the price can never reach the price at the max tick
            // if sqrtPriceX96 < MIN_SQRT_PRICE, the `sub` underflows and `gt` is true
            // if sqrtPriceX96 >= MAX_SQRT_PRICE, sqrtPriceX96 - MIN_SQRT_PRICE > MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1
            if ((sqrtPriceX96 - MIN_SQRT_PRICE) > MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE) {
                InvalidSqrtPrice.selector.revertWith(sqrtPriceX96);
            }

            uint256 price = uint256(sqrtPriceX96) << 32;

            uint256 r = price;
            uint256 msb = BitMath.mostSignificantBit(r);

            if (msb >= 128) r = price >> (msb - 127);
            else r = price << (127 - msb);

            int256 log_2 = (int256(msb) - 128) << 64;

            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(63, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(62, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(61, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(60, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(59, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(58, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(57, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(56, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(55, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(54, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(53, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(52, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(51, f))
                r := shr(f, r)
            }
            assembly ("memory-safe") {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(50, f))
            }

            int256 log_sqrt10001 = log_2 * 255738958999603826347141; // Q22.128 number

            // Magic number represents the ceiling of the maximum value of the error when approximating log_sqrt10001(x)
            int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);

            // Magic number represents the minimum value of the error when approximating log_sqrt10001(x), when
            // sqrtPrice is from the range (2^-64, 2^64). This is safe as MIN_SQRT_PRICE is more than 2^-64. If MIN_SQRT_PRICE
            // is changed, this may need to be changed too
            int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

            tick = tickLow == tickHi ? tickLow : getSqrtPriceAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
    using CustomRevert for bytes4;

    error SafeCastOverflow();

    /// @notice Cast a uint256 to a uint160, revert on overflow
    /// @param x The uint256 to be downcasted
    /// @return y The downcasted integer, now type uint160
    function toUint160(uint256 x) internal pure returns (uint160 y) {
        y = uint160(x);
        if (y != x) SafeCastOverflow.selector.revertWith();
    }

    /// @notice Cast a uint256 to a uint128, revert on overflow
    /// @param x The uint256 to be downcasted
    /// @return y The downcasted integer, now type uint128
    function toUint128(uint256 x) internal pure returns (uint128 y) {
        y = uint128(x);
        if (x != y) SafeCastOverflow.selector.revertWith();
    }

    /// @notice Cast a int128 to a uint128, revert on overflow or underflow
    /// @param x The int128 to be casted
    /// @return y The casted integer, now type uint128
    function toUint128(int128 x) internal pure returns (uint128 y) {
        if (x < 0) SafeCastOverflow.selector.revertWith();
        y = uint128(x);
    }

    /// @notice Cast a int256 to a int128, revert on overflow or underflow
    /// @param x The int256 to be downcasted
    /// @return y The downcasted integer, now type int128
    function toInt128(int256 x) internal pure returns (int128 y) {
        y = int128(x);
        if (y != x) SafeCastOverflow.selector.revertWith();
    }

    /// @notice Cast a uint256 to a int256, revert on overflow
    /// @param x The uint256 to be casted
    /// @return y The casted integer, now type int256
    function toInt256(uint256 x) internal pure returns (int256 y) {
        y = int256(x);
        if (y < 0) SafeCastOverflow.selector.revertWith();
    }

    /// @notice Cast a uint256 to a int128, revert on overflow
    /// @param x The uint256 to be downcasted
    /// @return The downcasted integer, now type int128
    function toInt128(uint256 x) internal pure returns (int128) {
        if (x >= 1 << 127) SafeCastOverflow.selector.revertWith();
        return int128(int256(x));
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";

struct PathKey {
    Currency intermediateCurrency;
    uint24 fee;
    int24 tickSpacing;
    IHooks hooks;
    bytes hookData;
}

using PathKeyLibrary for PathKey global;

/// @title PathKey Library
/// @notice Functions for working with PathKeys
library PathKeyLibrary {
    /// @notice Get the pool and swap direction for a given PathKey
    /// @param params the given PathKey
    /// @param currencyIn the input currency
    /// @return poolKey the pool key of the swap
    /// @return zeroForOne the direction of the swap, true if currency0 is being swapped for currency1
    function getPoolAndSwapDirection(PathKey calldata params, Currency currencyIn)
        internal
        pure
        returns (PoolKey memory poolKey, bool zeroForOne)
    {
        Currency currencyOut = params.intermediateCurrency;
        (Currency currency0, Currency currency1) =
            currencyIn < currencyOut ? (currencyIn, currencyOut) : (currencyOut, currencyIn);

        zeroForOne = currencyIn == currency0;
        poolKey = PoolKey(currency0, currency1, params.fee, params.tickSpacing, params.hooks);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {SafeCallback} from "./SafeCallback.sol";
import {CalldataDecoder} from "../libraries/CalldataDecoder.sol";
import {ActionConstants} from "../libraries/ActionConstants.sol";

/// @notice Abstract contract for performing a combination of actions on Uniswap v4.
/// @dev Suggested uint256 action values are defined in Actions.sol, however any definition can be used
abstract contract BaseActionsRouter is SafeCallback {
    using CalldataDecoder for bytes;

    /// @notice emitted when different numbers of parameters and actions are provided
    error InputLengthMismatch();

    /// @notice emitted when an inheriting contract does not support an action
    error UnsupportedAction(uint256 action);

    constructor(IPoolManager _poolManager) SafeCallback(_poolManager) {}

    /// @notice internal function that triggers the execution of a set of actions on v4
    /// @dev inheriting contracts should call this function to trigger execution
    function _executeActions(bytes calldata unlockData) internal {
        poolManager.unlock(unlockData);
    }

    /// @notice function that is called by the PoolManager through the SafeCallback.unlockCallback
    /// @param data Abi encoding of (bytes actions, bytes[] params)
    /// where params[i] is the encoded parameters for actions[i]
    function _unlockCallback(bytes calldata data) internal override returns (bytes memory) {
        // abi.decode(data, (bytes, bytes[]));
        (bytes calldata actions, bytes[] calldata params) = data.decodeActionsRouterParams();
        _executeActionsWithoutUnlock(actions, params);
        return "";
    }

    function _executeActionsWithoutUnlock(bytes calldata actions, bytes[] calldata params) internal {
        uint256 numActions = actions.length;
        if (numActions != params.length) revert InputLengthMismatch();

        for (uint256 actionIndex = 0; actionIndex < numActions; actionIndex++) {
            uint256 action = uint8(actions[actionIndex]);

            _handleAction(action, params[actionIndex]);
        }
    }

    /// @notice function to handle the parsing and execution of an action and its parameters
    function _handleAction(uint256 action, bytes calldata params) internal virtual;

    /// @notice function that returns address considered executor of the actions
    /// @dev The other context functions, _msgData and _msgValue, are not supported by this contract
    /// In many contracts this will be the address that calls the initial entry point that calls `_executeActions`
    /// `msg.sender` shouldn't be used, as this will be the v4 pool manager contract that calls `unlockCallback`
    /// If using ReentrancyLock.sol, this function can return _getLocker()
    function msgSender() public view virtual returns (address);

    /// @notice Calculates the address for a action
    function _mapRecipient(address recipient) internal view returns (address) {
        if (recipient == ActionConstants.MSG_SENDER) {
            return msgSender();
        } else if (recipient == ActionConstants.ADDRESS_THIS) {
            return address(this);
        } else {
            return recipient;
        }
    }

    /// @notice Calculates the payer for an action
    function _mapPayer(bool payerIsUser) internal view returns (address) {
        return payerIsUser ? msgSender() : address(this);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {ImmutableState} from "./ImmutableState.sol";
import {ActionConstants} from "../libraries/ActionConstants.sol";

/// @notice Abstract contract used to sync, send, and settle funds to the pool manager
/// @dev Note that sync() is called before any erc-20 transfer in `settle`.
abstract contract DeltaResolver is ImmutableState {
    using TransientStateLibrary for IPoolManager;

    /// @notice Emitted trying to settle a positive delta.
    error DeltaNotPositive(Currency currency);
    /// @notice Emitted trying to take a negative delta.
    error DeltaNotNegative(Currency currency);
    /// @notice Emitted when the contract does not have enough balance to wrap or unwrap.
    error InsufficientBalance();

    /// @notice Take an amount of currency out of the PoolManager
    /// @param currency Currency to take
    /// @param recipient Address to receive the currency
    /// @param amount Amount to take
    /// @dev Returns early if the amount is 0
    function _take(Currency currency, address recipient, uint256 amount) internal {
        if (amount == 0) return;
        poolManager.take(currency, recipient, amount);
    }

    /// @notice Pay and settle a currency to the PoolManager
    /// @dev The implementing contract must ensure that the `payer` is a secure address
    /// @param currency Currency to settle
    /// @param payer Address of the payer
    /// @param amount Amount to send
    /// @dev Returns early if the amount is 0
    function _settle(Currency currency, address payer, uint256 amount) internal {
        if (amount == 0) return;

        poolManager.sync(currency);
        if (currency.isAddressZero()) {
            poolManager.settle{value: amount}();
        } else {
            _pay(currency, payer, amount);
            poolManager.settle();
        }
    }

    /// @notice Abstract function for contracts to implement paying tokens to the poolManager
    /// @dev The recipient of the payment should be the poolManager
    /// @param token The token to settle. This is known not to be the native currency
    /// @param payer The address who should pay tokens
    /// @param amount The number of tokens to send
    function _pay(Currency token, address payer, uint256 amount) internal virtual;

    /// @notice Obtain the full amount owed by this contract (negative delta)
    /// @param currency Currency to get the delta for
    /// @return amount The amount owed by this contract as a uint256
    function _getFullDebt(Currency currency) internal view returns (uint256 amount) {
        int256 _amount = poolManager.currencyDelta(address(this), currency);
        // If the amount is positive, it should be taken not settled.
        if (_amount > 0) revert DeltaNotNegative(currency);
        // Casting is safe due to limits on the total supply of a pool
        amount = uint256(-_amount);
    }

    /// @notice Obtain the full credit owed to this contract (positive delta)
    /// @param currency Currency to get the delta for
    /// @return amount The amount owed to this contract as a uint256
    function _getFullCredit(Currency currency) internal view returns (uint256 amount) {
        int256 _amount = poolManager.currencyDelta(address(this), currency);
        // If the amount is negative, it should be settled not taken.
        if (_amount < 0) revert DeltaNotPositive(currency);
        amount = uint256(_amount);
    }

    /// @notice Calculates the amount for a settle action
    function _mapSettleAmount(uint256 amount, Currency currency) internal view returns (uint256) {
        if (amount == ActionConstants.CONTRACT_BALANCE) {
            return currency.balanceOfSelf();
        } else if (amount == ActionConstants.OPEN_DELTA) {
            return _getFullDebt(currency);
        } else {
            return amount;
        }
    }

    /// @notice Calculates the amount for a take action
    function _mapTakeAmount(uint256 amount, Currency currency) internal view returns (uint256) {
        if (amount == ActionConstants.OPEN_DELTA) {
            return _getFullCredit(currency);
        } else {
            return amount;
        }
    }

    /// @notice Calculates the sanitized amount before wrapping/unwrapping.
    /// @param inputCurrency The currency, either native or wrapped native, that this contract holds
    /// @param amount The amount to wrap or unwrap. Can be CONTRACT_BALANCE, OPEN_DELTA or a specific amount
    /// @param outputCurrency The currency after the wrap/unwrap that the user may owe a balance in on the poolManager
    function _mapWrapUnwrapAmount(Currency inputCurrency, uint256 amount, Currency outputCurrency)
        internal
        view
        returns (uint256)
    {
        // if wrapping, the balance in this contract is in ETH
        // if unwrapping, the balance in this contract is in WETH
        uint256 balance = inputCurrency.balanceOf(address(this));
        if (amount == ActionConstants.CONTRACT_BALANCE) {
            // return early to avoid unnecessary balance check
            return balance;
        }
        if (amount == ActionConstants.OPEN_DELTA) {
            // if wrapping, the open currency on the PoolManager is WETH.
            // if unwrapping, the open currency on the PoolManager is ETH.
            // note that we use the DEBT amount. Positive deltas can be taken and then wrapped.
            amount = _getFullDebt(outputCurrency);
        }
        if (amount > balance) revert InsufficientBalance();
        return amount;
    }
}

File 64 of 81 : Actions.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Library to define different pool actions.
/// @dev These are suggested common commands, however additional commands should be defined as required
/// Some of these actions are not supported in the Router contracts or Position Manager contracts, but are left as they may be helpful commands for other peripheral contracts.
library Actions {
    // pool actions
    // liquidity actions
    uint256 internal constant INCREASE_LIQUIDITY = 0x00;
    uint256 internal constant DECREASE_LIQUIDITY = 0x01;
    uint256 internal constant MINT_POSITION = 0x02;
    uint256 internal constant BURN_POSITION = 0x03;
    uint256 internal constant INCREASE_LIQUIDITY_FROM_DELTAS = 0x04;
    uint256 internal constant MINT_POSITION_FROM_DELTAS = 0x05;

    // swapping
    uint256 internal constant SWAP_EXACT_IN_SINGLE = 0x06;
    uint256 internal constant SWAP_EXACT_IN = 0x07;
    uint256 internal constant SWAP_EXACT_OUT_SINGLE = 0x08;
    uint256 internal constant SWAP_EXACT_OUT = 0x09;

    // donate
    // note this is not supported in the position manager or router
    uint256 internal constant DONATE = 0x0a;

    // closing deltas on the pool manager
    // settling
    uint256 internal constant SETTLE = 0x0b;
    uint256 internal constant SETTLE_ALL = 0x0c;
    uint256 internal constant SETTLE_PAIR = 0x0d;
    // taking
    uint256 internal constant TAKE = 0x0e;
    uint256 internal constant TAKE_ALL = 0x0f;
    uint256 internal constant TAKE_PORTION = 0x10;
    uint256 internal constant TAKE_PAIR = 0x11;

    uint256 internal constant CLOSE_CURRENCY = 0x12;
    uint256 internal constant CLEAR_OR_TAKE = 0x13;
    uint256 internal constant SWEEP = 0x14;

    uint256 internal constant WRAP = 0x15;
    uint256 internal constant UNWRAP = 0x16;

    // minting/burning 6909s to close deltas
    // note this is not supported in the position manager or router
    uint256 internal constant MINT_6909 = 0x17;
    uint256 internal constant BURN_6909 = 0x18;
}

// SPDX-License-Identifier: GPL-3.0-or-later

/// @title Library for Bytes Manipulation
pragma solidity ^0.8.0;

import {Constants} from '../../../libraries/Constants.sol';

library V2BytesLib {
    error SliceOutOfBounds();

    /// @notice Returns the address starting at byte 0
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return _address The address starting at byte 0
    function toAddress(bytes calldata _bytes) internal pure returns (address _address) {
        if (_bytes.length < Constants.ADDR_SIZE) revert SliceOutOfBounds();
        assembly {
            _address := shr(96, calldataload(_bytes.offset))
        }
    }

    /// @notice Returns the pair details starting at byte 0
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return tokenIn The address at byte 0
    /// @return feeNumerator The uint16 starting at byte 20
    /// @return pair The address starting at byte 22
    /// @return tokenOut The address at byte 42
    function toPair(bytes calldata _bytes)
        internal
        pure
        returns (address tokenIn, uint16 feeNumerator, address pair, address tokenOut)
    {
        if (_bytes.length < Constants.V2_POP_OFFSET) revert SliceOutOfBounds();
        assembly {
            tokenIn := shr(96, calldataload(_bytes.offset))
            feeNumerator := shr(240, calldataload(add(_bytes.offset, 20)))
            pair := shr(96, calldataload(add(_bytes.offset, 22)))
            tokenOut := shr(96, calldataload(add(_bytes.offset, 42)))
        }
    }

    /// @notice Returns the last address in the bytes calldata
    /// @dev length and overflow checks must be carried out before calling
    /// @param _bytes The input bytes string to slice
    /// @return _address The last address in the bytes calldata
    function toLastAddress(bytes calldata _bytes) internal pure returns (address _address) {
        if (_bytes.length < Constants.ADDR_SIZE) revert SliceOutOfBounds();
        assembly {
            _address := shr(96, calldataload(add(_bytes.offset, sub(_bytes.length, 20))))
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
    /// @return The contract address
    function factory() external view returns (address);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice The maximum amount of position liquidity that can use any tick in the range
    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
    /// @return The max amount of liquidity per tick
    function maxLiquidityPerTick() external view returns (uint128);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// observationIndex The index of the last oracle observation that was written,
    /// observationCardinality The current maximum number of observations stored in the pool,
    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
    /// feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal0X128() external view returns (uint256);

    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal1X128() external view returns (uint256);

    /// @notice The amounts of token0 and token1 that are owed to the protocol
    /// @dev Protocol fees will never exceed uint128 max in either token
    function protocolFees() external view returns (uint128 token0, uint128 token1);

    /// @notice The currently in range liquidity available to the pool
    /// @dev This value has no relationship to the total liquidity across all ticks
    function liquidity() external view returns (uint128);

    /// @notice Look up information about a specific tick in the pool
    /// @param tick The tick to look up
    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
    /// tick upper,
    /// liquidityNet how much liquidity changes when the pool price crosses the tick,
    /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
    /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
    /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
    /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
    /// secondsOutside the seconds spent on the other side of the tick from the current tick,
    /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
    /// a specific position.
    function ticks(int24 tick)
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
    function tickBitmap(int16 wordPosition) external view returns (uint256);

    /// @notice Returns the information about a position by the position's key
    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
    /// @return _liquidity The amount of liquidity in the position,
    /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
    /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
    /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
    /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
    function positions(bytes32 key)
        external
        view
        returns (
            uint128 _liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Returns data about a specific observation index
    /// @param index The element of the observations array to fetch
    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
    /// ago, rather than at a specific index in the array.
    /// @return blockTimestamp The timestamp of the observation,
    /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
    /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
    /// Returns initialized whether the observation has been initialized and the values are safe to use
    function observations(uint256 index)
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
    /// you must call it with secondsAgos = [3600, 0].
    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
    /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
    /// timestamp
    function observe(uint32[] calldata secondsAgos)
        external
        view
        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
    /// snapshot is taken and the second snapshot is taken.
    /// @param tickLower The lower tick of the range
    /// @param tickUpper The upper tick of the range
    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
    /// @return secondsInside The snapshot of seconds per liquidity for the range
    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
        external
        view
        returns (
            int56 tickCumulativeInside,
            uint160 secondsPerLiquidityInsideX128,
            uint32 secondsInside
        );
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface IUniswapV3PoolActions {
    /// @notice Sets the initial price for the pool
    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
    function initialize(uint160 sqrtPriceX96) external;

    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
    /// on tickLower, tickUpper, the amount of liquidity, and the current price.
    /// @param recipient The address for which the liquidity will be created
    /// @param tickLower The lower tick of the position in which to add liquidity
    /// @param tickUpper The upper tick of the position in which to add liquidity
    /// @param amount The amount of liquidity to mint
    /// @param data Any data that should be passed through to the callback
    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
    function mint(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount,
        bytes calldata data
    ) external returns (uint256 amount0, uint256 amount1);

    /// @notice Collects tokens owed to a position
    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
    /// @param recipient The address which should receive the fees collected
    /// @param tickLower The lower tick of the position for which to collect fees
    /// @param tickUpper The upper tick of the position for which to collect fees
    /// @param amount0Requested How much token0 should be withdrawn from the fees owed
    /// @param amount1Requested How much token1 should be withdrawn from the fees owed
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
    /// @dev Fees must be collected separately via a call to #collect
    /// @param tickLower The lower tick of the position for which to burn liquidity
    /// @param tickUpper The upper tick of the position for which to burn liquidity
    /// @param amount How much liquidity to burn
    /// @return amount0 The amount of token0 sent to the recipient
    /// @return amount1 The amount of token1 sent to the recipient
    function burn(
        int24 tickLower,
        int24 tickUpper,
        uint128 amount
    ) external returns (uint256 amount0, uint256 amount1);

    /// @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);

    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
    /// with 0 amount{0,1} and sending the donation amount(s) from the callback
    /// @param recipient The address which will receive the token0 and token1 amounts
    /// @param amount0 The amount of token0 to send
    /// @param amount1 The amount of token1 to send
    /// @param data Any data to be passed through to the callback
    function flash(
        address recipient,
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external;

    /// @notice Increase the maximum number of price and liquidity observations that this pool will store
    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
    /// the input observationCardinalityNext.
    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by the factory owner
interface IUniswapV3PoolOwnerActions {
    /// @notice Set the denominator of the protocol's % share of the fees
    /// @param feeProtocol0 new protocol fee for token0 of the pool
    /// @param feeProtocol1 new protocol fee for token1 of the pool
    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;

    /// @notice Collect the protocol fee accrued to the pool
    /// @param recipient The address to which collected protocol fees should be sent
    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
    /// @return amount0 The protocol fee collected in token0
    /// @return amount1 The protocol fee collected in token1
    function collectProtocol(
        address recipient,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);
}

File 71 of 81 : IUniswapV3PoolEvents.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Events emitted by a pool
/// @notice Contains all events emitted by the pool
interface IUniswapV3PoolEvents {
    /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
    /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
    /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
    /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
    event Initialize(uint160 sqrtPriceX96, int24 tick);

    /// @notice Emitted when liquidity is minted for a given position
    /// @param sender The address that minted the liquidity
    /// @param owner The owner of the position and recipient of any minted liquidity
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount The amount of liquidity minted to the position range
    /// @param amount0 How much token0 was required for the minted liquidity
    /// @param amount1 How much token1 was required for the minted liquidity
    event Mint(
        address sender,
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );

    /// @notice Emitted when fees are collected by the owner of a position
    /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
    /// @param owner The owner of the position for which fees are collected
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount0 The amount of token0 fees collected
    /// @param amount1 The amount of token1 fees collected
    event Collect(
        address indexed owner,
        address recipient,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount0,
        uint128 amount1
    );

    /// @notice Emitted when a position's liquidity is removed
    /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
    /// @param owner The owner of the position for which liquidity is removed
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount The amount of liquidity to remove
    /// @param amount0 The amount of token0 withdrawn
    /// @param amount1 The amount of token1 withdrawn
    event Burn(
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );

    /// @notice Emitted by the pool for any swaps between token0 and token1
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param recipient The address that received the output of the swap
    /// @param amount0 The delta of the token0 balance of the pool
    /// @param amount1 The delta of the token1 balance of the pool
    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
    /// @param liquidity The liquidity of the pool after the swap
    /// @param tick The log base 1.0001 of price of the pool after the swap
    event Swap(
        address indexed sender,
        address indexed recipient,
        int256 amount0,
        int256 amount1,
        uint160 sqrtPriceX96,
        uint128 liquidity,
        int24 tick
    );

    /// @notice Emitted by the pool for any flashes of token0/token1
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param recipient The address that received the tokens from flash
    /// @param amount0 The amount of token0 that was flashed
    /// @param amount1 The amount of token1 that was flashed
    /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
    /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
    event Flash(
        address indexed sender,
        address indexed recipient,
        uint256 amount0,
        uint256 amount1,
        uint256 paid0,
        uint256 paid1
    );

    /// @notice Emitted by the pool for increases to the number of observations that can be stored
    /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
    /// just before a mint/swap/burn.
    /// @param observationCardinalityNextOld The previous value of the next observation cardinality
    /// @param observationCardinalityNextNew The updated value of the next observation cardinality
    event IncreaseObservationCardinalityNext(
        uint16 observationCardinalityNextOld,
        uint16 observationCardinalityNextNew
    );

    /// @notice Emitted when the protocol fee is changed by the pool
    /// @param feeProtocol0Old The previous value of the token0 protocol fee
    /// @param feeProtocol1Old The previous value of the token1 protocol fee
    /// @param feeProtocol0New The updated value of the token0 protocol fee
    /// @param feeProtocol1New The updated value of the token1 protocol fee
    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);

    /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
    /// @param sender The address that collects the protocol fees
    /// @param recipient The address that receives the collected protocol fees
    /// @param amount0 The amount of token0 protocol fees that is withdrawn
    /// @param amount0 The amount of token1 protocol fees that is withdrawn
    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}

File 72 of 81 : IImmutableState.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";

/// @title IImmutableState
/// @notice Interface for the ImmutableState contract
interface IImmutableState {
    /// @notice The Uniswap v4 PoolManager contract
    function poolManager() external view returns (IPoolManager);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Return type of the beforeSwap hook.
// Upper 128 bits is the delta in specified tokens. Lower 128 bits is delta in unspecified tokens (to match the afterSwap hook)
type BeforeSwapDelta is int256;

// Creates a BeforeSwapDelta from specified and unspecified
function toBeforeSwapDelta(int128 deltaSpecified, int128 deltaUnspecified)
    pure
    returns (BeforeSwapDelta beforeSwapDelta)
{
    assembly ("memory-safe") {
        beforeSwapDelta := or(shl(128, deltaSpecified), and(sub(shl(128, 1), 1), deltaUnspecified))
    }
}

/// @notice Library for getting the specified and unspecified deltas from the BeforeSwapDelta type
library BeforeSwapDeltaLibrary {
    /// @notice A BeforeSwapDelta of 0
    BeforeSwapDelta public constant ZERO_DELTA = BeforeSwapDelta.wrap(0);

    /// extracts int128 from the upper 128 bits of the BeforeSwapDelta
    /// returned by beforeSwap
    function getSpecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaSpecified) {
        assembly ("memory-safe") {
            deltaSpecified := sar(128, delta)
        }
    }

    /// extracts int128 from the lower 128 bits of the BeforeSwapDelta
    /// returned by beforeSwap and afterSwap
    function getUnspecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaUnspecified) {
        assembly ("memory-safe") {
            deltaUnspecified := signextend(15, delta)
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
/// @author Solady (https://github.com/Vectorized/solady/blob/8200a70e8dc2a77ecb074fc2e99a2a0d36547522/src/utils/LibBit.sol)
library BitMath {
    /// @notice Returns the index of the most significant bit of the number,
    ///     where the least significant bit is at index 0 and the most significant bit is at index 255
    /// @param x the value for which to compute the most significant bit, must be greater than 0
    /// @return r the index of the most significant bit
    function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
        require(x > 0);

        assembly ("memory-safe") {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020500060203020504000106050205030304010505030400000000))
        }
    }

    /// @notice Returns the index of the least significant bit of the number,
    ///     where the least significant bit is at index 0 and the most significant bit is at index 255
    /// @param x the value for which to compute the least significant bit, must be greater than 0
    /// @return r the index of the least significant bit
    function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
        require(x > 0);

        assembly ("memory-safe") {
            // Isolate the least significant bit.
            x := and(x, sub(0, x))
            // For the upper 3 bits of the result, use a De Bruijn-like lookup.
            // Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
            // forgefmt: disable-next-item
            r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
                0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
                0x8040405543005266443200005020610674053026020000107506200176117077)))
            // For the lower 5 bits of the result, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {ImmutableState} from "./ImmutableState.sol";

/// @title Safe Callback
/// @notice A contract that only allows the Uniswap v4 PoolManager to call the unlockCallback
abstract contract SafeCallback is ImmutableState, IUnlockCallback {
    /// @notice Thrown when calling unlockCallback where the caller is not PoolManager
    error NotPoolManager();

    constructor(IPoolManager _poolManager) ImmutableState(_poolManager) {}

    /// @notice Only allow calls from the PoolManager contract
    modifier onlyPoolManager() {
        if (msg.sender != address(poolManager)) revert NotPoolManager();
        _;
    }

    /// @inheritdoc IUnlockCallback
    /// @dev We force the onlyPoolManager modifier by exposing a virtual function after the onlyPoolManager check.
    function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
        return _unlockCallback(data);
    }

    /// @dev to be implemented by the child contract, to safely guarantee the logic is only executed by the PoolManager
    function _unlockCallback(bytes calldata data) internal virtual returns (bytes memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IPoolManager} from "../interfaces/IPoolManager.sol";
import {Currency} from "../types/Currency.sol";
import {CurrencyReserves} from "./CurrencyReserves.sol";
import {NonzeroDeltaCount} from "./NonzeroDeltaCount.sol";
import {Lock} from "./Lock.sol";

/// @notice A helper library to provide state getters that use exttload
library TransientStateLibrary {
    /// @notice returns the reserves for the synced currency
    /// @param manager The pool manager contract.

    /// @return uint256 The reserves of the currency.
    /// @dev returns 0 if the reserves are not synced or value is 0.
    /// Checks the synced currency to only return valid reserve values (after a sync and before a settle).
    function getSyncedReserves(IPoolManager manager) internal view returns (uint256) {
        if (getSyncedCurrency(manager).isAddressZero()) return 0;
        return uint256(manager.exttload(CurrencyReserves.RESERVES_OF_SLOT));
    }

    function getSyncedCurrency(IPoolManager manager) internal view returns (Currency) {
        return Currency.wrap(address(uint160(uint256(manager.exttload(CurrencyReserves.CURRENCY_SLOT)))));
    }

    /// @notice Returns the number of nonzero deltas open on the PoolManager that must be zeroed out before the contract is locked
    function getNonzeroDeltaCount(IPoolManager manager) internal view returns (uint256) {
        return uint256(manager.exttload(NonzeroDeltaCount.NONZERO_DELTA_COUNT_SLOT));
    }

    /// @notice Get the current delta for a caller in the given currency
    /// @param target The credited account address
    /// @param currency The currency for which to lookup the delta
    function currencyDelta(IPoolManager manager, address target, Currency currency) internal view returns (int256) {
        bytes32 key;
        assembly ("memory-safe") {
            mstore(0, and(target, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(32, and(currency, 0xffffffffffffffffffffffffffffffffffffffff))
            key := keccak256(0, 64)
        }
        return int256(uint256(manager.exttload(key)));
    }

    /// @notice Returns whether the contract is unlocked or not
    function isUnlocked(IPoolManager manager) internal view returns (bool) {
        return manager.exttload(Lock.IS_UNLOCKED_SLOT) != 0x0;
    }
}

File 77 of 81 : ImmutableState.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {IImmutableState} from "../interfaces/IImmutableState.sol";

/// @title Immutable State
/// @notice A collection of immutable state variables, commonly used across multiple contracts
contract ImmutableState is IImmutableState {
    /// @inheritdoc IImmutableState
    IPoolManager public immutable poolManager;

    constructor(IPoolManager _poolManager) {
        poolManager = _poolManager;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Interface for the callback executed when an address unlocks the pool manager
interface IUnlockCallback {
    /// @notice Called by the pool manager on `msg.sender` when the manager is unlocked
    /// @param data The data that was passed to the call to unlock
    /// @return Any data that you want to be returned from the unlock call
    function unlockCallback(bytes calldata data) external returns (bytes memory);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {Currency} from "../types/Currency.sol";
import {CustomRevert} from "./CustomRevert.sol";

library CurrencyReserves {
    using CustomRevert for bytes4;

    /// bytes32(uint256(keccak256("ReservesOf")) - 1)
    bytes32 constant RESERVES_OF_SLOT = 0x1e0745a7db1623981f0b2a5d4232364c00787266eb75ad546f190e6cebe9bd95;
    /// bytes32(uint256(keccak256("Currency")) - 1)
    bytes32 constant CURRENCY_SLOT = 0x27e098c505d44ec3574004bca052aabf76bd35004c182099d8c575fb238593b9;

    function getSyncedCurrency() internal view returns (Currency currency) {
        assembly ("memory-safe") {
            currency := tload(CURRENCY_SLOT)
        }
    }

    function resetCurrency() internal {
        assembly ("memory-safe") {
            tstore(CURRENCY_SLOT, 0)
        }
    }

    function syncCurrencyAndReserves(Currency currency, uint256 value) internal {
        assembly ("memory-safe") {
            tstore(CURRENCY_SLOT, and(currency, 0xffffffffffffffffffffffffffffffffffffffff))
            tstore(RESERVES_OF_SLOT, value)
        }
    }

    function getSyncedReserves() internal view returns (uint256 value) {
        assembly ("memory-safe") {
            value := tload(RESERVES_OF_SLOT)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

/// @notice This is a temporary library that allows us to use transient storage (tstore/tload)
/// for the nonzero delta count.
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library NonzeroDeltaCount {
    // The slot holding the number of nonzero deltas. bytes32(uint256(keccak256("NonzeroDeltaCount")) - 1)
    bytes32 internal constant NONZERO_DELTA_COUNT_SLOT =
        0x7d4b3164c6e45b97e7d87b7125a44c5828d005af88f9d751cfd78729c5d99a0b;

    function read() internal view returns (uint256 count) {
        assembly ("memory-safe") {
            count := tload(NONZERO_DELTA_COUNT_SLOT)
        }
    }

    function increment() internal {
        assembly ("memory-safe") {
            let count := tload(NONZERO_DELTA_COUNT_SLOT)
            count := add(count, 1)
            tstore(NONZERO_DELTA_COUNT_SLOT, count)
        }
    }

    /// @notice Potential to underflow. Ensure checks are performed by integrating contracts to ensure this does not happen.
    /// Current usage ensures this will not happen because we call decrement with known boundaries (only up to the number of times we call increment).
    function decrement() internal {
        assembly ("memory-safe") {
            let count := tload(NONZERO_DELTA_COUNT_SLOT)
            count := sub(count, 1)
            tstore(NONZERO_DELTA_COUNT_SLOT, count)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

/// @notice This is a temporary library that allows us to use transient storage (tstore/tload)
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library Lock {
    // The slot holding the unlocked state, transiently. bytes32(uint256(keccak256("Unlocked")) - 1)
    bytes32 internal constant IS_UNLOCKED_SLOT = 0xc090fc4683624cfc3884e9d8de5eca132f2d0ec062aff75d43c0465d5ceeab23;

    function unlock() internal {
        assembly ("memory-safe") {
            // unlock
            tstore(IS_UNLOCKED_SLOT, true)
        }
    }

    function lock() internal {
        assembly ("memory-safe") {
            tstore(IS_UNLOCKED_SLOT, false)
        }
    }

    function isUnlocked() internal view returns (bool unlocked) {
        assembly ("memory-safe") {
            unlocked := tload(IS_UNLOCKED_SLOT)
        }
    }
}

Settings
{
  "remappings": [
    "solmate/=lib/solmate/",
    "permit2/=lib/permit2/",
    "forge-std/=lib/forge-std/src/",
    "@uniswap/v3-core/=node_modules/@uniswap/v3-core/",
    "@uniswap/v2-core/=node_modules/@uniswap/v2-core/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswap/v4-periphery/=lib/v4-periphery/",
    "@ensdomains/=lib/v4-periphery/lib/v4-core/node_modules/@ensdomains/",
    "@openzeppelin/=lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "@uniswap/v4-core/=lib/v4-periphery/lib/v4-core/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src/",
    "hardhat/=lib/v4-periphery/lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts/=lib/permit2/lib/openzeppelin-contracts/",
    "v3-periphery/=lib/v3-periphery/contracts/",
    "v4-core/=lib/v4-periphery/lib/v4-core/src/",
    "v4-periphery/=lib/v4-periphery/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 44444444
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"v4PoolManager","type":"address"},{"internalType":"address","name":"balancerVault","type":"address"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"CannotTransferFromETH","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotNegative","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotPositive","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidEthSender","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"MaverickV1InvalidPayer","type":"error"},{"inputs":[],"name":"MaverickV1InvalidSwap","type":"error"},{"inputs":[],"name":"MaverickV1TooLittleReceived","type":"error"},{"inputs":[],"name":"MaverickV2TooLittleReceived","type":"error"},{"inputs":[],"name":"NotPoolManager","type":"error"},{"inputs":[],"name":"SkyTooMuchRequested","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"SwapFailed","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[{"internalType":"uint256","name":"action","type":"uint256"}],"name":"UnsupportedAction","type":"error"},{"inputs":[{"internalType":"uint8","name":"poolType","type":"uint8"}],"name":"UnsupportedPoolType","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidPayer","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountOutReceived","type":"uint256"},{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"V4TooLittleReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxAmountInRequested","type":"uint256"},{"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"V4TooMuchRequested","type":"error"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"pancakeV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"unlockCallback","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

610100604052346101a957604051601f615de438819003918201601f19168301916001600160401b03831184841017610195578084926080946040528339810103126101a9576040516080810191906001600160401b038311818410176101955760609260405261006f826101ad565b9182825261007f602082016101ad565b918260208201526100a385610096604085016101ad565b93846040850152016101ad565b940184905260408051939084016001600160401b038111858210176101955760409081526001600160a01b039182168552928116602085019081529181166080529051811660a0529151821660c052911660e052670de0b6b3a76400005f5551615c2290816101c28239608051818181610478015281816107bd0152818161164901528181612e9e01528181615034015281816150dd01528181615156015281816151ec015281816157f00152615a2c015260a0518181816115da015281816131a10152613331015260c051818181610afa015281816130960152613886015260e051818181612b5801526147ee0152f35b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036101a95756fe60806040526004361015610022575b3615610018575f80fd5b6100206115c3565b005b5f3560e01c806323a69e751461008a57806324856bc3146100a85780633593564c146100a357806391dd73461461009e578063923b8a2a14610099578063d737d0c714610094578063dc4c90d31461008f578063fa461e331461008a5763fa483e720361000e575b610134565b610773565b610703565b61058e565b610411565b6102af565b61017f565b9181601f840112156100db5782359167ffffffffffffffff83116100db57602083818601950101116100db57565b5f80fd5b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126100db5760043591602435916044359067ffffffffffffffff82116100db57610130916004016100ad565b9091565b346100db57610020610145366100df565b92919091611368565b9181601f840112156100db5782359167ffffffffffffffff83116100db576020808501948460051b0101116100db57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760043567ffffffffffffffff81116100db576101c99036906004016100ad565b60243567ffffffffffffffff81116100db576101e990369060040161014e565b913330146102a65773ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1661027e5761025993337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d61089e565b5f7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d005b7f6f5ffb7e000000000000000000000000000000000000000000000000000000005f5260045ffd5b6100209361089e565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760043567ffffffffffffffff81116100db576102f99036906004016100ad565b60243567ffffffffffffffff81116100db5761031990369060040161014e565b916044354211610392573330146102a65773ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1661027e5761025993337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d61089e565b7f5bf6f916000000000000000000000000000000000000000000000000000000005f5260045ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b90602061040e9281815201906103ba565b90565b346100db5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760043567ffffffffffffffff81116100db576104609036906004016100ad565b9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036105665760408135189063ffffffff60408201351663ffffffe0601f8201169260608401602084013518179282019260608401359483641fffffffe08760051b16805f905b88818310610537579050608092915001019101101761052a576060608063ffffffff61050f961694019201613a98565b61052661051a611876565b604051918291826103fd565b0390f35b633b99b53d5f526004601cfd5b8294509263ffffffe0601f60808060209687969801013599848b1817998d0101350116010192018692916104df565b7fae18210a000000000000000000000000000000000000000000000000000000005f5260045ffd5b346100db5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760243560043560443567ffffffffffffffff81116100db576105e39036906004016100ad565b909282159081156106f0575b506106c85782604091810103126100db5773ffffffffffffffffffffffffffffffffffffffff602083359361062385611176565b013561062e81611176565b16913083141580610687575b61065f576100209273ffffffffffffffffffffffffffffffffffffffff3392166119b6565b7ffcb574ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b5073ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1683141561063a565b7f1fe4d9d1000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050155f6105ef565b5f9103126100db57565b346100db575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760207f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346100db575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9082101561081a570190565b6107e1565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156100db570180359067ffffffffffffffff82116100db576020019181360383136100db57565b9082101561081a576101309160051b81019061081f565b60409061040e9392815281602082015201906103ba565b90939284810361114e575f5b8581106108b957505050509050565b6108c481878561080e565b356108d0828487610870565b6060929160019160f884901c603f166022811015610ede576010811015610e33576008811015610b9457806109d957509061090e61095792826119f3565b906080830135156109d2577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b60408101359061095260208201359135611cdc565b613934565b1590816109a7575b5061096d57506001016108aa565b906109a36040519283927f2c4029e900000000000000000000000000000000000000000000000000000000845260048401610887565b0390fd5b7f8000000000000000000000000000000000000000000000000000000000000000161590505f61095f565b309261093d565b60028103610a2f575050610a2a90610a0f7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b90604081013591610a236020830135611cdc565b913561386d565b610957565b9192909160038103610b2b5750505f92935090610ae0610a50849383611a49565b909190610ab47f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9160405194859360208501977f2a2d80d100000000000000000000000000000000000000000000000000000000895280350190602486016118b9565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826111e2565b51908273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af190610b2561188a565b91610957565b90929150600503610b5457806040610a2a92013590610b4d6020820135611cdc565b90356137a0565b610a2a906040810135907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c610b8d6020830135611cdc565b91356135e9565b60088103610bfb575090610bab610a2a92826119f3565b90608083013515610bf4577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b604081013590610bef60208201359135611cdc565b613488565b3092610bda565b91929091600a8103610c7c5750505f92935081610c1c610ae0928594611a1e565b929091610ab47f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9460405194859360208501977f2b67b5700000000000000000000000000000000000000000000000000000000089526024860161180a565b909290600b8103610ca15750610a2a9150610c9c60208201359135611cdc565b6132e5565b600c8103610cc35750610a2a9150610cbe60208201359135611cdc565b61315c565b600d8103610d025750610a2a91610cd991612f3f565b907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c91613072565b9092509050600e8103610e0557506040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8235166004820152906020828060248101038173ffffffffffffffffffffffffffffffffffffffff84860135165afa918215610e00575f92610dcd575b5060400135111580610a2a576040517fa3281672000000000000000000000000000000000000000000000000000000006020820152909250610b258160248101610ab4565b6040919250610df29060203d8111610df9575b610dea81836111e2565b81019061171b565b9190610d88565b503d610de0565b61172a565b7fd76a1e9e000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b5ffd5b60108103610e455750610a2a91612e42565b905060118103610e055750610a2a90610ed9610e5f6116cd565b91803583526020810135602084015260408101356040840152610ebf610eba8883013560a08a870194828652608081013582890152013560c087015242608087015273ffffffffffffffffffffffffffffffffffffffff1690565b611cdc565b73ffffffffffffffffffffffffffffffffffffffff169052565b612ce6565b60228103610f175750508060a0610a2a920135906080810135908681013590604081013590610f1260208201359135611cdc565b612a1b565b60238103610fb7575050610a2a90610fb2610f30611671565b9180358352602081013560208401526040810135604084015286810135878401526080810135608084015260a081013560a084015260c081013560c084015260e081013560e0840152610ebf610eba610100830135610120610100870194828652013561012087015273ffffffffffffffffffffffffffffffffffffffff1690565b6125b9565b6024810361101957505080610fd26020610a2a930135611cdc565b90608081013515611012577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c915b8682013592604083013592356122ec565b3091611001565b60258103611080575090611030610a2a92826119f3565b90608083013515611079577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b60408101359061107460208201359135611cdc565b612160565b309261105f565b602681036110e7575090611097610a2a92826119f3565b906080830135156110e0577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b6040810135906110db60208201359135611cdc565b611f7e565b30926110c6565b60278103610e055750906110fe610a2a92826119f3565b90608083013515611147577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b60408101359061114260208201359135611cdc565b611d3b565b309261112d565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff8116036100db57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60a0810190811067ffffffffffffffff8211176111dd57604052565b611194565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176111dd57604052565b6040519061123260c0836111e2565b565b604051906112326080836111e2565b604051906112326060836111e2565b6040519061123260a0836111e2565b67ffffffffffffffff81116111dd57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b91906040838203126100db57823567ffffffffffffffff81116100db5783019080601f830112156100db5781356112d181611261565b916112df60405193846111e2565b818352602082850101116100db576020815f9282809601838601378301015292013561040e81611176565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f80000000000000000000000000000000000000000000000000000000000000008114611363575f0390565b61130a565b91905f8313918215806115b9575b6115915773ffffffffffffffffffffffffffffffffffffffff61139b8683018361129b565b905016943086141580611550575b611528576113b691611a74565b916113c18383613b61565b9291959073ffffffffffffffffffffffffffffffffffffffff3391160361150057156114ca575073ffffffffffffffffffffffffffffffffffffffff811673ffffffffffffffffffffffffffffffffffffffff8516105b1561142b575050506112329233916119b6565b9091925061143a836064111590565b1561146c575061146793929161144f91611a9c565b9161146261145d3392611ad0565b611337565b611b89565b505050565b9150507faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925c82116114a2576112329233916119b6565b7f739dbe52000000000000000000000000000000000000000000000000000000005f5260045ffd5b945073ffffffffffffffffffffffffffffffffffffffff841673ffffffffffffffffffffffffffffffffffffffff821610611418565b7f32b13d91000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f063c2022000000000000000000000000000000000000000000000000000000005f5260045ffd5b5073ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c168614156113a9565b7f316cf0eb000000000000000000000000000000000000000000000000000000005f5260045ffd5b505f821315611376565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633141580611631575b61160957565b7f38bbd576000000000000000000000000000000000000000000000000000000005f5260045ffd5b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016331415611603565b60405190610140820182811067ffffffffffffffff8211176111dd576040525f610120838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201520152565b60405190610100820182811067ffffffffffffffff8211176111dd576040525f60e0838281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b908160209103126100db575190565b6040513d5f823e3d90fd5b359061123282611176565b359065ffffffffffff821682036100db57565b65ffffffffffff6117c66060809373ffffffffffffffffffffffffffffffffffffffff813561178181611176565b16865273ffffffffffffffffffffffffffffffffffffffff60208201356117a781611176565b166020870152836117ba60408301611740565b16604087015201611740565b16910152565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b60a061040e959373ffffffffffffffffffffffffffffffffffffffff6101009416835261183a6020840182611753565b73ffffffffffffffffffffffffffffffffffffffff608082013561185d81611176565b1682840152013560c08201528160e082015201916117cc565b604051906118856020836111e2565b5f8252565b3d156118b4573d9061189b82611261565b916118a960405193846111e2565b82523d5f602084013e565b606090565b929073ffffffffffffffffffffffffffffffffffffffff90959492951683526060602084015260c083019285357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1873603018112156100db5786016020813591019467ffffffffffffffff82116100db578160071b360386136100db5781906060808501525260e0820194905f5b81811061199857505050604086611984611967602061040e999a01611735565b73ffffffffffffffffffffffffffffffffffffffff166080850152565b013560a082015260408185039101526117cc565b909195608080826119ab6001948b611753565b019701929101611947565b92919073ffffffffffffffffffffffffffffffffffffffff811630036119e05750611232926137a0565b6119ed6112329493613b05565b9261386d565b909163ffffffff60608301351682019263ffffffff8435169260208086019585010191011061052a57565b909163ffffffff60c08301351682019263ffffffff8435169260208086019585010191011061052a57565b909163ffffffff60208301351682019263ffffffff8435169260208086019585010191011061052a57565b909163ffffffff82351682019263ffffffff8435169260208086019585010191011061052a57565b919091826028116100db57602801917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd80190565b7f80000000000000000000000000000000000000000000000000000000000000008110156100db5790565b91611b2960209273ffffffffffffffffffffffffffffffffffffffff929695966040865260408601916117cc565b9416910152565b91908260409103126100db576020825192015190565b919360a09361040e969573ffffffffffffffffffffffffffffffffffffffff809416855215156020850152604084015216606082015281608082015201906103ba565b90604092935f73ffffffffffffffffffffffffffffffffffffffff8093611c1a82611bb4868b613b61565b929187999199501691161094611be5869a878714611c6357610ab46401000276a49d5b8c5194859360208501611afb565b88519a8b98899788957f128acb0800000000000000000000000000000000000000000000000000000000875260048701611b46565b0393165af18015610e00575f925f91611c3257509192565b9050611c5791925060403d604011611c5c575b611c4f81836111e2565b810190611b30565b919092565b503d611c45565b610ab473fffd8963efd1fc6a506488495d951d5263988d259d611bd7565b90604092935f73ffffffffffffffffffffffffffffffffffffffff8093611c1a82611cac868b613b61565b879992939199501691161094611be5869a878714611c6357610ab46401000276a49d8c5194859360208501611afb565b73ffffffffffffffffffffffffffffffffffffffff811660018103611d225750507f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b60020361040e57503090565b9190820391821161136357565b9392919490611d4a8484613b61565b509082611eba575b5050505083155f14611d68576112329350613bd6565b90611d92611d79611d798385614df7565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201529390602085602481845afa928315610e0057611e51955f94611e92575b5082611e009160209596613bd6565b60405180809681947f70a082310000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa8015610e0057611e6c925f91611e73575b50611d2e565b106100db57565b611e8c915060203d602011610df957610dea81836111e2565b5f611e66565b60209450611e0091611eb18592873d8911610df957610dea81836111e2565b95509150611df1565b7f80000000000000000000000000000000000000000000000000000000000000008314611ef5575b92611eec936119b6565b5f808080611d52565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529390925060208460248173ffffffffffffffffffffffffffffffffffffffff87165afa8015610e0057611eec945f91611f5f575b5092909350611ee2565b611f78915060203d602011610df957610dea81836111e2565b5f611f55565b90939592611f8c8782613b61565b9891969087819a993073ffffffffffffffffffffffffffffffffffffffff8216145f1461214757507f800000000000000000000000000000000000000000000000000000000000000083146120c0575b611fe79183916137a0565b9087989796915b905b611ffb816064111590565b935f851561209757509173ffffffffffffffffffffffffffffffffffffffff989961202a61204b938b95611a9c565b909b9095612038878e613b61565b909d91509c9687935b1691161092613edd565b921561205f57979596918798979691611ff0565b5050935050925092501061206f57565b7f224d1e62000000000000000000000000000000000000000000000000000000005f5260045ffd5b989973ffffffffffffffffffffffffffffffffffffffff93919a61204b93958589968793612041565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291925060208260248173ffffffffffffffffffffffffffffffffffffffff85165afa8015610e0057611fe7925f91612128575b50929150611fdc565b612141915060203d602011610df957610dea81836111e2565b5f61211f565b91612155926119ed85613b05565b908798979691611fee565b9192949593956121708787613b61565b91938491907f8000000000000000000000000000000000000000000000000000000000000000811461222a575b9994506121c1939291905b6121b3866064111590565b9a8b1561222357309061405f565b95156121ef576121d56121c1913096611a9c565b969095906121e38888613b61565b929994939291906121a8565b5050915091106121fb57565b7fd347d70a000000000000000000000000000000000000000000000000000000005f5260045ffd5b879061405f565b506040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152946020908690602490829073ffffffffffffffffffffffffffffffffffffffff165afa8015610e00576121c1955f91612291575b5061219d565b6122aa915060203d602011610df957610dea81836111e2565b5f61228b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600411156122e757565b6122b0565b929091936001936122fc816122dd565b806124db575063959912769373a188eec8f81263234da3622a406892f3d630f98c93838573a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485b73ffffffffffffffffffffffffffffffffffffffff841630146123fd576123826123879461236385613b05565b90309073ffffffffffffffffffffffffffffffffffffffff851661386d565b6141df565b156123f55750905b803b156100db5760405160e09390931b835273ffffffffffffffffffffffffffffffffffffffff90931660048301526024820152905f908290818381604481015b03925af18015610e00576123e15750565b806123ef5f611232936111e2565b806106f9565b90509061238f565b917f800000000000000000000000000000000000000000000000000000000000000091935014612433575b6123879185916141df565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015294509291905060208460248173ffffffffffffffffffffffffffffffffffffffff87165afa8015610e005785945f916124bc575b508094859183156124aa575b509192939050612428565b64e8d4a510009004935061238761249f565b6124d5915060203d602011610df957610dea81836111e2565b5f612493565b6124e4816122dd565b60018103612525575063959912769373f6e72db5454dd049d0788e411b06cfaf1685304293838573a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48612336565b600291939450612534816122dd565b036125745773dc035d45d973e3ec169d2276ddab16f1e407384f9173a188eec8f81263234da3622a406892f3d630f98c92638d7ef9bb9483855f93612336565b736b175474e89094c44da98b954eedeac495271d0f9173f6e72db5454dd049d0788e411b06cfaf1685304292638d7ef9bb9483855f93612336565b600511156122e757565b60408101805173ffffffffffffffffffffffffffffffffffffffff16806127ef575050608081017f80000000000000000000000000000000000000000000000000000000000000008151146127e6575b505b8051612616816125af565b61261f816125af565b61270c5761262c816147ab565b610100810173ffffffffffffffffffffffffffffffffffffffff612664825173ffffffffffffffffffffffffffffffffffffffff1690565b168015159081612701575b50612678575050565b8151612683816125af565b61268c816125af565b1580156126e4575b61269c575050565b6126de6126c3606061123294015173ffffffffffffffffffffffffffffffffffffffff1690565b915173ffffffffffffffffffffffffffffffffffffffff1690565b906136e7565b50600382516126f2816125af565b6126fb816125af565b14612694565b90503014155f61266f565b60018151612719816125af565b612722816125af565b0361273557612730816145a8565b61262c565b60028151612742816125af565b61274b816125af565b0361275957612730816144aa565b60038151612766816125af565b61276f816125af565b0361277d5761273081614483565b6004815161278a816125af565b612793816125af565b036127a15761273081614230565b610e3090516127af816125af565b6127b8816125af565b7f90001ffd000000000000000000000000000000000000000000000000000000005f5260ff16600452602490565b4790525f612609565b60808301907f8000000000000000000000000000000000000000000000000000000000000000825114612871575b50612842611d7961286c935173ffffffffffffffffffffffffffffffffffffffff1690565b90612864602085015173ffffffffffffffffffffffffffffffffffffffff1690565b9051916141df565b61260b565b6128909073ffffffffffffffffffffffffffffffffffffffff16611d79565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610e005761286c9361284292611d79925f916128ee575b50845293505061281d565b612907915060203d602011610df957610dea81836111e2565b5f6128e3565b6040519061291c6020836111e2565b5f808352366020840137565b909194939260e08252825160e0830152602083015160028110156122e7576129b960a060c095612a129361010087015273ffffffffffffffffffffffffffffffffffffffff60408201511661012087015273ffffffffffffffffffffffffffffffffffffffff60608201511661014087015260808101516101608701520151856101808601526101a08501906103ba565b9660208401906060809173ffffffffffffffffffffffffffffffffffffffff815116845260208101511515602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015201511515910152565b60a08201520152565b91929493907f80000000000000000000000000000000000000000000000000000000000000008114612ba2575b612aae602095612a5883856147d2565b612a91612a6361290d565b94612a6c611223565b998a525f898b015273ffffffffffffffffffffffffffffffffffffffff1660408a0152565b73ffffffffffffffffffffffffffffffffffffffff166060880152565b608086015260a0850152612aff73ffffffffffffffffffffffffffffffffffffffff612ad8611234565b308152925f868501521673ffffffffffffffffffffffffffffffffffffffff166040830152565b5f6060820152612b3e60405194859384937f52bbbe29000000000000000000000000000000000000000000000000000000008552429260048601612928565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610e0057612b875750565b612b9f9060203d602011610df957610dea81836111e2565b50565b506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529360208560248173ffffffffffffffffffffffffffffffffffffffff86165afa948515610e0057602095612aae915f91612c0e575b5091955050612a48565b612c259150873d8911610df957610dea81836111e2565b5f612c04565b6112329092919260e08061010083019573ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff6020820151166020850152604081015160020b6040850152612ca96060820151606086019073ffffffffffffffffffffffffffffffffffffffff169052565b6080810151608085015260a081015160a085015260c081015160c0850152015191019073ffffffffffffffffffffffffffffffffffffffff169052565b60a08101907f8000000000000000000000000000000000000000000000000000000000000000825114612da1575b80612d47612d7a93612d3f611d796020955173ffffffffffffffffffffffffffffffffffffffff1690565b90519061411c565b604051809381927fa026383e00000000000000000000000000000000000000000000000000000000835260048301612c2b565b03815f73be6d8f0d05cc4be24d5167a3ef062215be6d18a55af18015610e0057612b875750565b612dc5611d79611d79835173ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529190602090839060249082905afa918215610e0057612d7a93602093612d47925f91612e25575b5081529350509050612d14565b612e3c9150853d8711610df957610dea81836111e2565b5f612e18565b5f90612e849260405193849283927f48c894910000000000000000000000000000000000000000000000000000000084526020600485015260248401916117cc565b03818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610e0057612ecd5750565b3d805f833e612edc81836111e2565b8101906020818303126100db5780519067ffffffffffffffff82116100db570181601f820112156100db578051612f1281611261565b92612f2060405194856111e2565b818452602082840101116100db575f928160208094018483015e010152565b91823583019160208335948185019403850101908185116113635710612f6157565b7f3b99b53d000000000000000000000000000000000000000000000000000000005f5260045ffd5b919081101561081a5760071b0190565b3561040e81611176565b60208082528101839052604001915f5b818110612fc05750505090565b90919260808060019273ffffffffffffffffffffffffffffffffffffffff8735612fe981611176565b16815273ffffffffffffffffffffffffffffffffffffffff602088013561300f81611176565b16602082015273ffffffffffffffffffffffffffffffffffffffff604088013561303881611176565b16604082015273ffffffffffffffffffffffffffffffffffffffff606088013561306181611176565b166060820152019401929101612fb3565b90915f5b8381106130fa57505073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156100db576123d0925f92836040518096819582947f0d58b1db00000000000000000000000000000000000000000000000000000000845260048401612fa3565b61310d613108828686612f89565b612f99565b73ffffffffffffffffffffffffffffffffffffffff80841691160361313457600101613076565b7fe7002877000000000000000000000000000000000000000000000000000000005f5260045ffd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152909173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690602083602481855afa928315610e00575f936132a5575b50821061327d57816131ea57505050565b803b156100db575f60405180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161322f88600483019190602083019252565b03925af18015610e0057613269575b503073ffffffffffffffffffffffffffffffffffffffff831603613260575050565b61123291614814565b806123ef5f613277936111e2565b5f61323e565b7f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b6132bf91935060203d602011610df957610dea81836111e2565b915f6131d9565b801515036100db57565b908160209103126100db575161040e816132c6565b907f80000000000000000000000000000000000000000000000000000000000000008103613457575047905b8161331a575050565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156100db576040517fd0e30db00000000000000000000000000000000000000000000000000000000081525f8160048185885af18015610e0057613443575b503073ffffffffffffffffffffffffffffffffffffffff8316036133b657505050565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff929092166004830152602482015290602090829060449082905f905af18015610e005761341b5750565b612b9f9060203d60201161343c575b61343481836111e2565b8101906132d0565b503d61342a565b806123ef5f613451936111e2565b5f613393565b9047821115613311577f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b9392919490613495603e90565b84106135c157806135a2575b5050836134b25761123293506148d2565b906134c3611d79611d798385614df7565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201529390602085602481845afa928315610e0057613531955f9461357a575b5082611e0091602095966148d2565b03915afa8015610e005761354b925f91611e735750611d2e565b1061355257565b7f849eaf98000000000000000000000000000000000000000000000000000000005f5260045ffd5b60209450611e00916135998592873d8911610df957610dea81836111e2565b95509150613522565b6135ba916135b08585614e81565b50929190506119b6565b5f806134a1565b7fae52ad0c000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff9093919293169182156136bf575f8093606493602096604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d1160015f51141617161561366157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b7f78fca676000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f80000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff168061372e575061123291614814565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292909150602083602481855afa918215610e0057611232935f9361377f575b50614b68565b61379991935060203d602011610df957610dea81836111e2565b915f613779565b90919073ffffffffffffffffffffffffffffffffffffffff16806137c8575061123291614814565b7f800000000000000000000000000000000000000000000000000000000000000082146137fa575b9161123292614b68565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292909150602083602481855afa8015610e0057611232935f9161384e575b50919092506137f0565b613867915060203d602011610df957610dea81836111e2565b5f613844565b919273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156100db575f73ffffffffffffffffffffffffffffffffffffffff9384829681608496816040519b8c9a8b997f36c78516000000000000000000000000000000000000000000000000000000008b521660048a01521660248801521660448601521660648401525af18015610e005761391f5750565b8061392b5f80936111e2565b8003126100db57565b9490919293947f80000000000000000000000000000000000000000000000000000000000000008314613a0d575b949061399b6139a9915b61398061397a856064111590565b95611ad0565b8515613a065730905b613993868a614c17565b929091611c81565b909190156139ff5750611337565b91156139cb5761399b6139c06139a9923096611a9c565b95909592919061396c565b5091509150106139d757565b7f39d35496000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050611337565b8890613989565b9150613a1f611d79611d798787614fda565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa918215610e00576139a99261399b925f91613a79575b509391509150613962565b613a92915060203d602011610df957610dea81836111e2565b5f613a6e565b9290818103613add575f5b818110613ab1575050505050565b80613ad7613ac2600193858961080e565b3560f81c613ad1838789610870565b91614c25565b01613aa3565b7faaad13f7000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff8111613b395773ffffffffffffffffffffffffffffffffffffffff1690565b7fc4bd89a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b90603c11612f6157803560601c916028601483013560601c92013560601c90565b908160609103126100db578051916040602083015192015190565b9061040e949360809373ffffffffffffffffffffffffffffffffffffffff928452602084015216604082015281606082015201906103ba565b909192613be38383613b61565b95919390955b73ffffffffffffffffffffffffffffffffffffffff8716966040517f0902f1ac0000000000000000000000000000000000000000000000000000000081526060816004818c5afa908115610e00575f905f92613ea4575b506020613cf3929373ffffffffffffffffffffffffffffffffffffffff8a169273ffffffffffffffffffffffffffffffffffffffff871684105f14613e895773ffffffffffffffffffffffffffffffffffffffff8b5b168414958615613e8157509260405180809681947f70a082310000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa8015610e00576020925f91613e64575b506040517ff140a35a000000000000000000000000000000000000000000000000000000008152919003600482015273ffffffffffffffffffffffffffffffffffffffff979097166024880152866044818b5afa958615610e00575f96613e44575b5015613e3d575f94955b613d7e816064111590565b925f808515613e2e57505090613d9391611a9c565b96909290613da18885613b61565b909891509988905b613db161290d565b93813b156100db575f8094613df5604051978896879586947f022c0d9f00000000000000000000000000000000000000000000000000000000865260048601613b9d565b03925af18015610e0057613e1a575b5015613e11579395613be9565b50505092505050565b806123ef5f613e28936111e2565b5f613e04565b97909986999295939990613da9565b5f95613d73565b613e5d91965060203d8111610df957610dea81836111e2565b945f613d69565b613e7b9150833d8111610df957610dea81836111e2565b5f613d07565b905092611e00565b73ffffffffffffffffffffffffffffffffffffffff87613c96565b613cf3925060209150613ecd9060603d8111613ed6575b613ec581836111e2565b810190613b82565b50925090613c40565b503d613ebb565b9091604092613f1d613fba95805f14613fee57613f0e637fffffff915b613f02611234565b96875215156020870152565b5f8587015260030b6060850152565b5f73ffffffffffffffffffffffffffffffffffffffff85518097819682957f3eece7db000000000000000000000000000000000000000000000000000000008452600484019060e09273ffffffffffffffffffffffffffffffffffffffff606092168352805160208401526020810151151560408401526040810151151582840152015160030b608082015260c060a08201525f60c08201520190565b0393165af1908115610e00575f91613fd0575090565b613fe9915060403d604011611c5c57611c4f81836111e2565b905090565b613f0e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000000091613efa565b919260c09373ffffffffffffffffffffffffffffffffffffffff61040e9796931684526020840152151560408301525f606083015260808201528160a082015201906103ba565b9073ffffffffffffffffffffffffffffffffffffffff5f91613fba8260409899979916838a16109687851461410d576140d873fffd8963efd1fc6a506488495d951d5263988d259a5b8a5173ffffffffffffffffffffffffffffffffffffffff9182166020820152921660408301528160608101610ab4565b8851998a98899788957fc51c902900000000000000000000000000000000000000000000000000000000875260048701614018565b6140d86401000276a49a6140a8565b60446020925f8093604051927f095ea7b300000000000000000000000000000000000000000000000000000000845273be6d8f0d05cc4be24d5167a3ef062215be6d18a5600485015260248401525af13d15601f3d1160015f51141617161561418157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152fd5b5f9182604492602095604051937f095ea7b3000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160015f51141617161561418157565b6101008101805173ffffffffffffffffffffffffffffffffffffffff163003614349575061427b611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6142b1604084015173ffffffffffffffffffffffffffffffffffffffff1690565b16614342576080820151915b6142ca60c0820151614e2a565b916142d860e0830151614e2a565b60a0608084015193015194823b156100db575f946123d0604051978896879586947f3df0212400000000000000000000000000000000000000000000000000000000865260048601909493926060926080830196600f0b8352600f0b602083015260408201520152565b5f916142bd565b9080614373611d79611d7960208095015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6143a9604084015173ffffffffffffffffffffffffffffffffffffffff1690565b1661447d5760808201515b6143c160c0840151614e2a565b906143cf60e0850151614e2a565b9161446c6143fb60a06080880151970151985173ffffffffffffffffffffffffffffffffffffffff1690565b604051988997889687957fddc1f59d000000000000000000000000000000000000000000000000000000008752600487019360809373ffffffffffffffffffffffffffffffffffffffff939796929760a0870198600f0b8752600f0b60208701526040860152606085015216910152565b03925af18015610e0057612b875750565b5f6143b4565b61427b611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b6144d1611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b61012082015115159081156145a1576080830151905b60c08401519160e08501519060808601519361452061010060a089015198015173ffffffffffffffffffffffffffffffffffffffff1690565b95843b156100db575f966123d091604051998a98899788967fce7d65030000000000000000000000000000000000000000000000000000000088526004880194909695919373ffffffffffffffffffffffffffffffffffffffff9360a09560c088019988526020880152604087015260608601521515608085015216910152565b5f906144e7565b61010081015173ffffffffffffffffffffffffffffffffffffffff16308190036146ac57506145f4611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff61462a604084015173ffffffffffffffffffffffffffffffffffffffff1690565b166146a5576080820151915b60c08101519160e082015160a0608084015193015194823b156100db575f946123d0604051978896879586947f5b41b908000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b5f91614636565b6146d3611d79611d79602085015173ffffffffffffffffffffffffffffffffffffffff1690565b9173ffffffffffffffffffffffffffffffffffffffff61470a604083015173ffffffffffffffffffffffffffffffffffffffff1690565b166147a4576080810151925b60c08201519360e08301519060a0608085015194015195833b156100db576123d05f96604051988997889687957fa64833a0000000000000000000000000000000000000000000000000000000008752600487019360809373ffffffffffffffffffffffffffffffffffffffff939796929760a0870198875260208701526040860152606085015216910152565b5f92614716565b6145f4611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b906112329173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000091166141df565b5f80809381935af11561482357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b51906dffffffffffffffffffffffffffff821682036100db57565b908160609103126100db576148b081614881565b9160406148bf60208401614881565b92015163ffffffff811681036100db5790565b929190926148e08482614e81565b92969092935b6148f08589614eac565b506040517f0902f1ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff861696916060826004818b5afa8015610e00578b6149dc935f905f93614b0b575b5073ffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff80829316941692169316831498895f14614b0157602091935b60405180809781947f70a082310000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa8015610e00578188916149fb955f91614ae3575b5003614ef7565b9415614adc575f94935b614a10846066111590565b998a15614aca5750505090614a2491614fa6565b90929091614a328385614e81565b9a92979193909397945b614a4461290d565b92803b156100db575f92838793614a8a604051978896879586947f022c0d9f00000000000000000000000000000000000000000000000000000000865260048601613b9d565b03925af18015610e0057614ab6575b5015614aac5790969390929091906148e6565b5050505050509050565b806123ef5f614ac4936111e2565b5f614a99565b9394889a929a97919796939693614a3c565b5f93614a05565b614afb915060203d8111610df957610dea81836111e2565b5f6149f4565b602091929361498b565b6dffffffffffffffffffffffffffff80945073ffffffffffffffffffffffffffffffffffffffff9250614b55839260603d8111614b61575b614b4d81836111e2565b81019061489c565b5095909350505061494d565b503d614b43565b5f9182604492602095604051937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160015f511416171615614bb957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b90603c116100db5790603c90565b90919060078103614c435750614c3e90611232926155b4565b615656565b60068103614c5e5750614c599061123292615393565b615454565b600b8103614c925750614c8c614c7a614c869261123294614fe9565b93829493929193615324565b92615352565b916151ce565b91600c8303614d1557614ca592506150c3565b90614caf8161514f565b91808311614ce5575061123291907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c906151ce565b7f12bacdd3000000000000000000000000000000000000000000000000000000005f52600452602482905260445ffd5b600f8303614d9757614d2792506150c3565b90614d31816150d6565b91808310614d67575061123291907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90615015565b7f8b063d73000000000000000000000000000000000000000000000000000000005f52600452602482905260445ffd5b600e8314614dcc577f5cda29d7000000000000000000000000000000000000000000000000000000005f52600483905260245ffd5b6112329250614de1614df192614deb92614fe9565b9282949291611cdc565b92615001565b91615015565b9060148110612f6157017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec013560601c90565b6f7fffffffffffffffffffffffffffffff8111614e59576fffffffffffffffffffffffffffffffff16600f0b90565b7fb4fa3fb3000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190603e11612f6157813560601c91601481013560f01c91602a601683013560601c92013560601c90565b73ffffffffffffffffffffffffffffffffffffffff821673ffffffffffffffffffffffffffffffffffffffff8216105f146101305791565b8181029291811591840414171561136357565b92909192831592838015614f9e575b614f7657614f239161ffff614f1c921690614ee4565b9182614ee4565b916127108402938404612710141715611363578201809211611363578115614f49570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f7b9c8916000000000000000000000000000000000000000000000000000000005f5260045ffd5b508215614f06565b91909182602a116100db57602a01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60190565b90601411612f61573560601c90565b9060601161052a578035916040602083013592013590565b90816150115761040e91506150d6565b5090565b909180156114675773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b156100db575f928360649273ffffffffffffffffffffffffffffffffffffffff948560405198899788967f0b0d9c0900000000000000000000000000000000000000000000000000000000885216600487015216602485015260448401525af18015610e005761391f5750565b919060401161052a576020823592013590565b61510181307f0000000000000000000000000000000000000000000000000000000000000000615739565b905f821261510d575090565b73ffffffffffffffffffffffffffffffffffffffff907f4c085bf1000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b61517a81307f0000000000000000000000000000000000000000000000000000000000000000615739565b905f821361518c575061040e90611337565b73ffffffffffffffffffffffffffffffffffffffff907f3351b260000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b9082156114675773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156100db576040517fa584119400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201525f8160248183885af18015610e0057615310575b5073ffffffffffffffffffffffffffffffffffffffff81166152c65750506020906004604051809481937f11da60b40000000000000000000000000000000000000000000000000000000083525af18015610e0057612b875750565b5f93602093926152d5926157d3565b6004604051809481937f11da60b40000000000000000000000000000000000000000000000000000000083525af18015610e0057612b875750565b806123ef5f61531e936111e2565b5f61526a565b1561534e577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b3090565b907f800000000000000000000000000000000000000000000000000000000000000082036153845761040e9150615817565b816150115761040e915061514f565b906101401161052a5780350190565b356fffffffffffffffffffffffffffffffff811681036100db5790565b3561040e816132c6565b62ffffff8116036100db57565b8060020b036100db57565b91908260a09103126100db576040516153f9816111c1565b6080808294803561540981611176565b8452602081013561541981611176565b6020850152604081013561542c816153c9565b6040850152606081013561543f816153d6565b606085015201359161545083611176565b0152565b61546060c082016153a2565b6fffffffffffffffffffffffffffffffff81161561555e575b6154cf6154ca60e0926154a96fffffffffffffffffffffffffffffffff6154a260a088016153bf565b9216611337565b906154b861010087018761081f565b9290916154c536896153e1565b615992565b615ab6565b91016154f26154dd826153a2565b6fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff83161061550e575050565b9061551b610e30926153a2565b7f8b063d73000000000000000000000000000000000000000000000000000000005f526fffffffffffffffffffffffffffffffff90811660045216602452604490565b5061556b60a082016153bf565b1561559a5760e06154cf6154ca61559161558c61558786612f99565b6150d6565b6158b0565b92505050615479565b60e06154cf6154ca61559161558c61558760208701612f99565b9060a01161052a5780350190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156100db570180359067ffffffffffffffff82116100db57602001918160051b360383136100db57565b919081101561081a5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156100db570190565b602081019061566582826155c2565b90505f9261567283612f99565b9061567f604085016153a2565b906fffffffffffffffffffffffffffffffff821615615725575b92915f915b8383106156b95750505050506060016154f26154dd826153a2565b8496506fffffffffffffffffffffffffffffffff6154ca916156f56156ee866156e861570e9798999a8c6155c2565b90615616565b9586615ae9565b615702608088018861081f565b949093165f0391615992565b94600161571b8793612f99565b919493019161569e565b905061573361558c836150d6565b90615699565b73ffffffffffffffffffffffffffffffffffffffff809381602094165f52168252602460405f2060405194859384927ff135baaa0000000000000000000000000000000000000000000000000000000084526004840152165afa908115610e00575f916157a4575090565b90506020813d6020116157cb575b816157bf602093836111e2565b810103126100db575190565b3d91506157b2565b611232929173ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001692166119b6565b73ffffffffffffffffffffffffffffffffffffffff811661583757504790565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152906020908290602490829073ffffffffffffffffffffffffffffffffffffffff165afa908115610e00575f91615897575090565b61040e915060203d602011610df957610dea81836111e2565b906fffffffffffffffffffffffffffffffff82168092036158cd57565b7f93dafdf1000000000000000000000000000000000000000000000000000000005f5260045ffd5b61598461040e959373ffffffffffffffffffffffffffffffffffffffff60806101209582815116865282602082015116602087015262ffffff6040820151166040870152606081015160020b6060870152015116608084015260a083019073ffffffffffffffffffffffffffffffffffffffff6040809280511515855260208101516020860152015116910152565b8161010082015201916117cc565b936020919394845f14615a9857615a126401000276a4925b6159de6159b5611243565b88151581529485870188905273ffffffffffffffffffffffffffffffffffffffff166040860152565b60405197889485947ff3cd914c000000000000000000000000000000000000000000000000000000008652600486016158f5565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610e00575f93615a75575b505f13901515145f14615a6f57600f0b90565b60801d90565b5f919350615a919060203d602011610df957610dea81836111e2565b9290615a5c565b615a1273fffd8963efd1fc6a506488495d951d5263988d25926159aa565b5f81600f0b126158cd576fffffffffffffffffffffffffffffffff1690565b3561040e816153c9565b3561040e816153d6565b905f6080604051615af9816111c1565b8281528260208201528260408201528260608201520152615b1982612f99565b9073ffffffffffffffffffffffffffffffffffffffff8083169082161015615bec5790615be990615bcc60015b94615bc2615b5660208301615ad5565b615bb6615b716060615b6a60408701615adf565b9501612f99565b95615b99615b7d611252565b73ffffffffffffffffffffffffffffffffffffffff909a168a52565b73ffffffffffffffffffffffffffffffffffffffff166020890152565b62ffffff166040870152565b60020b6060850152565b73ffffffffffffffffffffffffffffffffffffffff166080830152565b91565b73ffffffffffffffffffffffffffffffffffffffff91821691615be99116615bcc818414615b4656fea164736f6c634300081a000a000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8

Deployed Bytecode

0x60806040526004361015610022575b3615610018575f80fd5b6100206115c3565b005b5f3560e01c806323a69e751461008a57806324856bc3146100a85780633593564c146100a357806391dd73461461009e578063923b8a2a14610099578063d737d0c714610094578063dc4c90d31461008f578063fa461e331461008a5763fa483e720361000e575b610134565b610773565b610703565b61058e565b610411565b6102af565b61017f565b9181601f840112156100db5782359167ffffffffffffffff83116100db57602083818601950101116100db57565b5f80fd5b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126100db5760043591602435916044359067ffffffffffffffff82116100db57610130916004016100ad565b9091565b346100db57610020610145366100df565b92919091611368565b9181601f840112156100db5782359167ffffffffffffffff83116100db576020808501948460051b0101116100db57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760043567ffffffffffffffff81116100db576101c99036906004016100ad565b60243567ffffffffffffffff81116100db576101e990369060040161014e565b913330146102a65773ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1661027e5761025993337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d61089e565b5f7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d005b7f6f5ffb7e000000000000000000000000000000000000000000000000000000005f5260045ffd5b6100209361089e565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760043567ffffffffffffffff81116100db576102f99036906004016100ad565b60243567ffffffffffffffff81116100db5761031990369060040161014e565b916044354211610392573330146102a65773ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1661027e5761025993337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d61089e565b7f5bf6f916000000000000000000000000000000000000000000000000000000005f5260045ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b90602061040e9281815201906103ba565b90565b346100db5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760043567ffffffffffffffff81116100db576104609036906004016100ad565b9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a901633036105665760408135189063ffffffff60408201351663ffffffe0601f8201169260608401602084013518179282019260608401359483641fffffffe08760051b16805f905b88818310610537579050608092915001019101101761052a576060608063ffffffff61050f961694019201613a98565b61052661051a611876565b604051918291826103fd565b0390f35b633b99b53d5f526004601cfd5b8294509263ffffffe0601f60808060209687969801013599848b1817998d0101350116010192018692916104df565b7fae18210a000000000000000000000000000000000000000000000000000000005f5260045ffd5b346100db5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760243560043560443567ffffffffffffffff81116100db576105e39036906004016100ad565b909282159081156106f0575b506106c85782604091810103126100db5773ffffffffffffffffffffffffffffffffffffffff602083359361062385611176565b013561062e81611176565b16913083141580610687575b61065f576100209273ffffffffffffffffffffffffffffffffffffffff3392166119b6565b7ffcb574ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b5073ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1683141561063a565b7f1fe4d9d1000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050155f6105ef565b5f9103126100db57565b346100db575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db5760207f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346100db575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100db57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90168152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9082101561081a570190565b6107e1565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156100db570180359067ffffffffffffffff82116100db576020019181360383136100db57565b9082101561081a576101309160051b81019061081f565b60409061040e9392815281602082015201906103ba565b90939284810361114e575f5b8581106108b957505050509050565b6108c481878561080e565b356108d0828487610870565b6060929160019160f884901c603f166022811015610ede576010811015610e33576008811015610b9457806109d957509061090e61095792826119f3565b906080830135156109d2577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b60408101359061095260208201359135611cdc565b613934565b1590816109a7575b5061096d57506001016108aa565b906109a36040519283927f2c4029e900000000000000000000000000000000000000000000000000000000845260048401610887565b0390fd5b7f8000000000000000000000000000000000000000000000000000000000000000161590505f61095f565b309261093d565b60028103610a2f575050610a2a90610a0f7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b90604081013591610a236020830135611cdc565b913561386d565b610957565b9192909160038103610b2b5750505f92935090610ae0610a50849383611a49565b909190610ab47f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9160405194859360208501977f2a2d80d100000000000000000000000000000000000000000000000000000000895280350190602486016118b9565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826111e2565b51908273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3165af190610b2561188a565b91610957565b90929150600503610b5457806040610a2a92013590610b4d6020820135611cdc565b90356137a0565b610a2a906040810135907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c610b8d6020830135611cdc565b91356135e9565b60088103610bfb575090610bab610a2a92826119f3565b90608083013515610bf4577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b604081013590610bef60208201359135611cdc565b613488565b3092610bda565b91929091600a8103610c7c5750505f92935081610c1c610ae0928594611a1e565b929091610ab47f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9460405194859360208501977f2b67b5700000000000000000000000000000000000000000000000000000000089526024860161180a565b909290600b8103610ca15750610a2a9150610c9c60208201359135611cdc565b6132e5565b600c8103610cc35750610a2a9150610cbe60208201359135611cdc565b61315c565b600d8103610d025750610a2a91610cd991612f3f565b907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c91613072565b9092509050600e8103610e0557506040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8235166004820152906020828060248101038173ffffffffffffffffffffffffffffffffffffffff84860135165afa918215610e00575f92610dcd575b5060400135111580610a2a576040517fa3281672000000000000000000000000000000000000000000000000000000006020820152909250610b258160248101610ab4565b6040919250610df29060203d8111610df9575b610dea81836111e2565b81019061171b565b9190610d88565b503d610de0565b61172a565b7fd76a1e9e000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b5ffd5b60108103610e455750610a2a91612e42565b905060118103610e055750610a2a90610ed9610e5f6116cd565b91803583526020810135602084015260408101356040840152610ebf610eba8883013560a08a870194828652608081013582890152013560c087015242608087015273ffffffffffffffffffffffffffffffffffffffff1690565b611cdc565b73ffffffffffffffffffffffffffffffffffffffff169052565b612ce6565b60228103610f175750508060a0610a2a920135906080810135908681013590604081013590610f1260208201359135611cdc565b612a1b565b60238103610fb7575050610a2a90610fb2610f30611671565b9180358352602081013560208401526040810135604084015286810135878401526080810135608084015260a081013560a084015260c081013560c084015260e081013560e0840152610ebf610eba610100830135610120610100870194828652013561012087015273ffffffffffffffffffffffffffffffffffffffff1690565b6125b9565b6024810361101957505080610fd26020610a2a930135611cdc565b90608081013515611012577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c915b8682013592604083013592356122ec565b3091611001565b60258103611080575090611030610a2a92826119f3565b90608083013515611079577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b60408101359061107460208201359135611cdc565b612160565b309261105f565b602681036110e7575090611097610a2a92826119f3565b906080830135156110e0577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b6040810135906110db60208201359135611cdc565b611f7e565b30926110c6565b60278103610e055750906110fe610a2a92826119f3565b90608083013515611147577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b60408101359061114260208201359135611cdc565b611d3b565b309261112d565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff8116036100db57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60a0810190811067ffffffffffffffff8211176111dd57604052565b611194565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176111dd57604052565b6040519061123260c0836111e2565b565b604051906112326080836111e2565b604051906112326060836111e2565b6040519061123260a0836111e2565b67ffffffffffffffff81116111dd57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b91906040838203126100db57823567ffffffffffffffff81116100db5783019080601f830112156100db5781356112d181611261565b916112df60405193846111e2565b818352602082850101116100db576020815f9282809601838601378301015292013561040e81611176565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f80000000000000000000000000000000000000000000000000000000000000008114611363575f0390565b61130a565b91905f8313918215806115b9575b6115915773ffffffffffffffffffffffffffffffffffffffff61139b8683018361129b565b905016943086141580611550575b611528576113b691611a74565b916113c18383613b61565b9291959073ffffffffffffffffffffffffffffffffffffffff3391160361150057156114ca575073ffffffffffffffffffffffffffffffffffffffff811673ffffffffffffffffffffffffffffffffffffffff8516105b1561142b575050506112329233916119b6565b9091925061143a836064111590565b1561146c575061146793929161144f91611a9c565b9161146261145d3392611ad0565b611337565b611b89565b505050565b9150507faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925c82116114a2576112329233916119b6565b7f739dbe52000000000000000000000000000000000000000000000000000000005f5260045ffd5b945073ffffffffffffffffffffffffffffffffffffffff841673ffffffffffffffffffffffffffffffffffffffff821610611418565b7f32b13d91000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f063c2022000000000000000000000000000000000000000000000000000000005f5260045ffd5b5073ffffffffffffffffffffffffffffffffffffffff7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c168614156113a9565b7f316cf0eb000000000000000000000000000000000000000000000000000000005f5260045ffd5b505f821315611376565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633141580611631575b61160957565b7f38bbd576000000000000000000000000000000000000000000000000000000005f5260045ffd5b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a9016331415611603565b60405190610140820182811067ffffffffffffffff8211176111dd576040525f610120838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201520152565b60405190610100820182811067ffffffffffffffff8211176111dd576040525f60e0838281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b908160209103126100db575190565b6040513d5f823e3d90fd5b359061123282611176565b359065ffffffffffff821682036100db57565b65ffffffffffff6117c66060809373ffffffffffffffffffffffffffffffffffffffff813561178181611176565b16865273ffffffffffffffffffffffffffffffffffffffff60208201356117a781611176565b166020870152836117ba60408301611740565b16604087015201611740565b16910152565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b60a061040e959373ffffffffffffffffffffffffffffffffffffffff6101009416835261183a6020840182611753565b73ffffffffffffffffffffffffffffffffffffffff608082013561185d81611176565b1682840152013560c08201528160e082015201916117cc565b604051906118856020836111e2565b5f8252565b3d156118b4573d9061189b82611261565b916118a960405193846111e2565b82523d5f602084013e565b606090565b929073ffffffffffffffffffffffffffffffffffffffff90959492951683526060602084015260c083019285357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1873603018112156100db5786016020813591019467ffffffffffffffff82116100db578160071b360386136100db5781906060808501525260e0820194905f5b81811061199857505050604086611984611967602061040e999a01611735565b73ffffffffffffffffffffffffffffffffffffffff166080850152565b013560a082015260408185039101526117cc565b909195608080826119ab6001948b611753565b019701929101611947565b92919073ffffffffffffffffffffffffffffffffffffffff811630036119e05750611232926137a0565b6119ed6112329493613b05565b9261386d565b909163ffffffff60608301351682019263ffffffff8435169260208086019585010191011061052a57565b909163ffffffff60c08301351682019263ffffffff8435169260208086019585010191011061052a57565b909163ffffffff60208301351682019263ffffffff8435169260208086019585010191011061052a57565b909163ffffffff82351682019263ffffffff8435169260208086019585010191011061052a57565b919091826028116100db57602801917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd80190565b7f80000000000000000000000000000000000000000000000000000000000000008110156100db5790565b91611b2960209273ffffffffffffffffffffffffffffffffffffffff929695966040865260408601916117cc565b9416910152565b91908260409103126100db576020825192015190565b919360a09361040e969573ffffffffffffffffffffffffffffffffffffffff809416855215156020850152604084015216606082015281608082015201906103ba565b90604092935f73ffffffffffffffffffffffffffffffffffffffff8093611c1a82611bb4868b613b61565b929187999199501691161094611be5869a878714611c6357610ab46401000276a49d5b8c5194859360208501611afb565b88519a8b98899788957f128acb0800000000000000000000000000000000000000000000000000000000875260048701611b46565b0393165af18015610e00575f925f91611c3257509192565b9050611c5791925060403d604011611c5c575b611c4f81836111e2565b810190611b30565b919092565b503d611c45565b610ab473fffd8963efd1fc6a506488495d951d5263988d259d611bd7565b90604092935f73ffffffffffffffffffffffffffffffffffffffff8093611c1a82611cac868b613b61565b879992939199501691161094611be5869a878714611c6357610ab46401000276a49d8c5194859360208501611afb565b73ffffffffffffffffffffffffffffffffffffffff811660018103611d225750507f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b60020361040e57503090565b9190820391821161136357565b9392919490611d4a8484613b61565b509082611eba575b5050505083155f14611d68576112329350613bd6565b90611d92611d79611d798385614df7565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201529390602085602481845afa928315610e0057611e51955f94611e92575b5082611e009160209596613bd6565b60405180809681947f70a082310000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa8015610e0057611e6c925f91611e73575b50611d2e565b106100db57565b611e8c915060203d602011610df957610dea81836111e2565b5f611e66565b60209450611e0091611eb18592873d8911610df957610dea81836111e2565b95509150611df1565b7f80000000000000000000000000000000000000000000000000000000000000008314611ef5575b92611eec936119b6565b5f808080611d52565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529390925060208460248173ffffffffffffffffffffffffffffffffffffffff87165afa8015610e0057611eec945f91611f5f575b5092909350611ee2565b611f78915060203d602011610df957610dea81836111e2565b5f611f55565b90939592611f8c8782613b61565b9891969087819a993073ffffffffffffffffffffffffffffffffffffffff8216145f1461214757507f800000000000000000000000000000000000000000000000000000000000000083146120c0575b611fe79183916137a0565b9087989796915b905b611ffb816064111590565b935f851561209757509173ffffffffffffffffffffffffffffffffffffffff989961202a61204b938b95611a9c565b909b9095612038878e613b61565b909d91509c9687935b1691161092613edd565b921561205f57979596918798979691611ff0565b5050935050925092501061206f57565b7f224d1e62000000000000000000000000000000000000000000000000000000005f5260045ffd5b989973ffffffffffffffffffffffffffffffffffffffff93919a61204b93958589968793612041565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291925060208260248173ffffffffffffffffffffffffffffffffffffffff85165afa8015610e0057611fe7925f91612128575b50929150611fdc565b612141915060203d602011610df957610dea81836111e2565b5f61211f565b91612155926119ed85613b05565b908798979691611fee565b9192949593956121708787613b61565b91938491907f8000000000000000000000000000000000000000000000000000000000000000811461222a575b9994506121c1939291905b6121b3866064111590565b9a8b1561222357309061405f565b95156121ef576121d56121c1913096611a9c565b969095906121e38888613b61565b929994939291906121a8565b5050915091106121fb57565b7fd347d70a000000000000000000000000000000000000000000000000000000005f5260045ffd5b879061405f565b506040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152946020908690602490829073ffffffffffffffffffffffffffffffffffffffff165afa8015610e00576121c1955f91612291575b5061219d565b6122aa915060203d602011610df957610dea81836111e2565b5f61228b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600411156122e757565b6122b0565b929091936001936122fc816122dd565b806124db575063959912769373a188eec8f81263234da3622a406892f3d630f98c93838573a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485b73ffffffffffffffffffffffffffffffffffffffff841630146123fd576123826123879461236385613b05565b90309073ffffffffffffffffffffffffffffffffffffffff851661386d565b6141df565b156123f55750905b803b156100db5760405160e09390931b835273ffffffffffffffffffffffffffffffffffffffff90931660048301526024820152905f908290818381604481015b03925af18015610e00576123e15750565b806123ef5f611232936111e2565b806106f9565b90509061238f565b917f800000000000000000000000000000000000000000000000000000000000000091935014612433575b6123879185916141df565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015294509291905060208460248173ffffffffffffffffffffffffffffffffffffffff87165afa8015610e005785945f916124bc575b508094859183156124aa575b509192939050612428565b64e8d4a510009004935061238761249f565b6124d5915060203d602011610df957610dea81836111e2565b5f612493565b6124e4816122dd565b60018103612525575063959912769373f6e72db5454dd049d0788e411b06cfaf1685304293838573a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48612336565b600291939450612534816122dd565b036125745773dc035d45d973e3ec169d2276ddab16f1e407384f9173a188eec8f81263234da3622a406892f3d630f98c92638d7ef9bb9483855f93612336565b736b175474e89094c44da98b954eedeac495271d0f9173f6e72db5454dd049d0788e411b06cfaf1685304292638d7ef9bb9483855f93612336565b600511156122e757565b60408101805173ffffffffffffffffffffffffffffffffffffffff16806127ef575050608081017f80000000000000000000000000000000000000000000000000000000000000008151146127e6575b505b8051612616816125af565b61261f816125af565b61270c5761262c816147ab565b610100810173ffffffffffffffffffffffffffffffffffffffff612664825173ffffffffffffffffffffffffffffffffffffffff1690565b168015159081612701575b50612678575050565b8151612683816125af565b61268c816125af565b1580156126e4575b61269c575050565b6126de6126c3606061123294015173ffffffffffffffffffffffffffffffffffffffff1690565b915173ffffffffffffffffffffffffffffffffffffffff1690565b906136e7565b50600382516126f2816125af565b6126fb816125af565b14612694565b90503014155f61266f565b60018151612719816125af565b612722816125af565b0361273557612730816145a8565b61262c565b60028151612742816125af565b61274b816125af565b0361275957612730816144aa565b60038151612766816125af565b61276f816125af565b0361277d5761273081614483565b6004815161278a816125af565b612793816125af565b036127a15761273081614230565b610e3090516127af816125af565b6127b8816125af565b7f90001ffd000000000000000000000000000000000000000000000000000000005f5260ff16600452602490565b4790525f612609565b60808301907f8000000000000000000000000000000000000000000000000000000000000000825114612871575b50612842611d7961286c935173ffffffffffffffffffffffffffffffffffffffff1690565b90612864602085015173ffffffffffffffffffffffffffffffffffffffff1690565b9051916141df565b61260b565b6128909073ffffffffffffffffffffffffffffffffffffffff16611d79565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610e005761286c9361284292611d79925f916128ee575b50845293505061281d565b612907915060203d602011610df957610dea81836111e2565b5f6128e3565b6040519061291c6020836111e2565b5f808352366020840137565b909194939260e08252825160e0830152602083015160028110156122e7576129b960a060c095612a129361010087015273ffffffffffffffffffffffffffffffffffffffff60408201511661012087015273ffffffffffffffffffffffffffffffffffffffff60608201511661014087015260808101516101608701520151856101808601526101a08501906103ba565b9660208401906060809173ffffffffffffffffffffffffffffffffffffffff815116845260208101511515602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015201511515910152565b60a08201520152565b91929493907f80000000000000000000000000000000000000000000000000000000000000008114612ba2575b612aae602095612a5883856147d2565b612a91612a6361290d565b94612a6c611223565b998a525f898b015273ffffffffffffffffffffffffffffffffffffffff1660408a0152565b73ffffffffffffffffffffffffffffffffffffffff166060880152565b608086015260a0850152612aff73ffffffffffffffffffffffffffffffffffffffff612ad8611234565b308152925f868501521673ffffffffffffffffffffffffffffffffffffffff166040830152565b5f6060820152612b3e60405194859384937f52bbbe29000000000000000000000000000000000000000000000000000000008552429260048601612928565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8165af18015610e0057612b875750565b612b9f9060203d602011610df957610dea81836111e2565b50565b506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529360208560248173ffffffffffffffffffffffffffffffffffffffff86165afa948515610e0057602095612aae915f91612c0e575b5091955050612a48565b612c259150873d8911610df957610dea81836111e2565b5f612c04565b6112329092919260e08061010083019573ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff6020820151166020850152604081015160020b6040850152612ca96060820151606086019073ffffffffffffffffffffffffffffffffffffffff169052565b6080810151608085015260a081015160a085015260c081015160c0850152015191019073ffffffffffffffffffffffffffffffffffffffff169052565b60a08101907f8000000000000000000000000000000000000000000000000000000000000000825114612da1575b80612d47612d7a93612d3f611d796020955173ffffffffffffffffffffffffffffffffffffffff1690565b90519061411c565b604051809381927fa026383e00000000000000000000000000000000000000000000000000000000835260048301612c2b565b03815f73be6d8f0d05cc4be24d5167a3ef062215be6d18a55af18015610e0057612b875750565b612dc5611d79611d79835173ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529190602090839060249082905afa918215610e0057612d7a93602093612d47925f91612e25575b5081529350509050612d14565b612e3c9150853d8711610df957610dea81836111e2565b5f612e18565b5f90612e849260405193849283927f48c894910000000000000000000000000000000000000000000000000000000084526020600485015260248401916117cc565b03818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90165af18015610e0057612ecd5750565b3d805f833e612edc81836111e2565b8101906020818303126100db5780519067ffffffffffffffff82116100db570181601f820112156100db578051612f1281611261565b92612f2060405194856111e2565b818452602082840101116100db575f928160208094018483015e010152565b91823583019160208335948185019403850101908185116113635710612f6157565b7f3b99b53d000000000000000000000000000000000000000000000000000000005f5260045ffd5b919081101561081a5760071b0190565b3561040e81611176565b60208082528101839052604001915f5b818110612fc05750505090565b90919260808060019273ffffffffffffffffffffffffffffffffffffffff8735612fe981611176565b16815273ffffffffffffffffffffffffffffffffffffffff602088013561300f81611176565b16602082015273ffffffffffffffffffffffffffffffffffffffff604088013561303881611176565b16604082015273ffffffffffffffffffffffffffffffffffffffff606088013561306181611176565b166060820152019401929101612fb3565b90915f5b8381106130fa57505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31691823b156100db576123d0925f92836040518096819582947f0d58b1db00000000000000000000000000000000000000000000000000000000845260048401612fa3565b61310d613108828686612f89565b612f99565b73ffffffffffffffffffffffffffffffffffffffff80841691160361313457600101613076565b7fe7002877000000000000000000000000000000000000000000000000000000005f5260045ffd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152909173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690602083602481855afa928315610e00575f936132a5575b50821061327d57816131ea57505050565b803b156100db575f60405180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161322f88600483019190602083019252565b03925af18015610e0057613269575b503073ffffffffffffffffffffffffffffffffffffffff831603613260575050565b61123291614814565b806123ef5f613277936111e2565b5f61323e565b7f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b6132bf91935060203d602011610df957610dea81836111e2565b915f6131d9565b801515036100db57565b908160209103126100db575161040e816132c6565b907f80000000000000000000000000000000000000000000000000000000000000008103613457575047905b8161331a575050565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21691823b156100db576040517fd0e30db00000000000000000000000000000000000000000000000000000000081525f8160048185885af18015610e0057613443575b503073ffffffffffffffffffffffffffffffffffffffff8316036133b657505050565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff929092166004830152602482015290602090829060449082905f905af18015610e005761341b5750565b612b9f9060203d60201161343c575b61343481836111e2565b8101906132d0565b503d61342a565b806123ef5f613451936111e2565b5f613393565b9047821115613311577f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b9392919490613495603e90565b84106135c157806135a2575b5050836134b25761123293506148d2565b906134c3611d79611d798385614df7565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201529390602085602481845afa928315610e0057613531955f9461357a575b5082611e0091602095966148d2565b03915afa8015610e005761354b925f91611e735750611d2e565b1061355257565b7f849eaf98000000000000000000000000000000000000000000000000000000005f5260045ffd5b60209450611e00916135998592873d8911610df957610dea81836111e2565b95509150613522565b6135ba916135b08585614e81565b50929190506119b6565b5f806134a1565b7fae52ad0c000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff9093919293169182156136bf575f8093606493602096604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d1160015f51141617161561366157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b7f78fca676000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f80000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff168061372e575061123291614814565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292909150602083602481855afa918215610e0057611232935f9361377f575b50614b68565b61379991935060203d602011610df957610dea81836111e2565b915f613779565b90919073ffffffffffffffffffffffffffffffffffffffff16806137c8575061123291614814565b7f800000000000000000000000000000000000000000000000000000000000000082146137fa575b9161123292614b68565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292909150602083602481855afa8015610e0057611232935f9161384e575b50919092506137f0565b613867915060203d602011610df957610dea81836111e2565b5f613844565b919273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31691823b156100db575f73ffffffffffffffffffffffffffffffffffffffff9384829681608496816040519b8c9a8b997f36c78516000000000000000000000000000000000000000000000000000000008b521660048a01521660248801521660448601521660648401525af18015610e005761391f5750565b8061392b5f80936111e2565b8003126100db57565b9490919293947f80000000000000000000000000000000000000000000000000000000000000008314613a0d575b949061399b6139a9915b61398061397a856064111590565b95611ad0565b8515613a065730905b613993868a614c17565b929091611c81565b909190156139ff5750611337565b91156139cb5761399b6139c06139a9923096611a9c565b95909592919061396c565b5091509150106139d757565b7f39d35496000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050611337565b8890613989565b9150613a1f611d79611d798787614fda565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa918215610e00576139a99261399b925f91613a79575b509391509150613962565b613a92915060203d602011610df957610dea81836111e2565b5f613a6e565b9290818103613add575f5b818110613ab1575050505050565b80613ad7613ac2600193858961080e565b3560f81c613ad1838789610870565b91614c25565b01613aa3565b7faaad13f7000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff8111613b395773ffffffffffffffffffffffffffffffffffffffff1690565b7fc4bd89a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b90603c11612f6157803560601c916028601483013560601c92013560601c90565b908160609103126100db578051916040602083015192015190565b9061040e949360809373ffffffffffffffffffffffffffffffffffffffff928452602084015216604082015281606082015201906103ba565b909192613be38383613b61565b95919390955b73ffffffffffffffffffffffffffffffffffffffff8716966040517f0902f1ac0000000000000000000000000000000000000000000000000000000081526060816004818c5afa908115610e00575f905f92613ea4575b506020613cf3929373ffffffffffffffffffffffffffffffffffffffff8a169273ffffffffffffffffffffffffffffffffffffffff871684105f14613e895773ffffffffffffffffffffffffffffffffffffffff8b5b168414958615613e8157509260405180809681947f70a082310000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa8015610e00576020925f91613e64575b506040517ff140a35a000000000000000000000000000000000000000000000000000000008152919003600482015273ffffffffffffffffffffffffffffffffffffffff979097166024880152866044818b5afa958615610e00575f96613e44575b5015613e3d575f94955b613d7e816064111590565b925f808515613e2e57505090613d9391611a9c565b96909290613da18885613b61565b909891509988905b613db161290d565b93813b156100db575f8094613df5604051978896879586947f022c0d9f00000000000000000000000000000000000000000000000000000000865260048601613b9d565b03925af18015610e0057613e1a575b5015613e11579395613be9565b50505092505050565b806123ef5f613e28936111e2565b5f613e04565b97909986999295939990613da9565b5f95613d73565b613e5d91965060203d8111610df957610dea81836111e2565b945f613d69565b613e7b9150833d8111610df957610dea81836111e2565b5f613d07565b905092611e00565b73ffffffffffffffffffffffffffffffffffffffff87613c96565b613cf3925060209150613ecd9060603d8111613ed6575b613ec581836111e2565b810190613b82565b50925090613c40565b503d613ebb565b9091604092613f1d613fba95805f14613fee57613f0e637fffffff915b613f02611234565b96875215156020870152565b5f8587015260030b6060850152565b5f73ffffffffffffffffffffffffffffffffffffffff85518097819682957f3eece7db000000000000000000000000000000000000000000000000000000008452600484019060e09273ffffffffffffffffffffffffffffffffffffffff606092168352805160208401526020810151151560408401526040810151151582840152015160030b608082015260c060a08201525f60c08201520190565b0393165af1908115610e00575f91613fd0575090565b613fe9915060403d604011611c5c57611c4f81836111e2565b905090565b613f0e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000000091613efa565b919260c09373ffffffffffffffffffffffffffffffffffffffff61040e9796931684526020840152151560408301525f606083015260808201528160a082015201906103ba565b9073ffffffffffffffffffffffffffffffffffffffff5f91613fba8260409899979916838a16109687851461410d576140d873fffd8963efd1fc6a506488495d951d5263988d259a5b8a5173ffffffffffffffffffffffffffffffffffffffff9182166020820152921660408301528160608101610ab4565b8851998a98899788957fc51c902900000000000000000000000000000000000000000000000000000000875260048701614018565b6140d86401000276a49a6140a8565b60446020925f8093604051927f095ea7b300000000000000000000000000000000000000000000000000000000845273be6d8f0d05cc4be24d5167a3ef062215be6d18a5600485015260248401525af13d15601f3d1160015f51141617161561418157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152fd5b5f9182604492602095604051937f095ea7b3000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160015f51141617161561418157565b6101008101805173ffffffffffffffffffffffffffffffffffffffff163003614349575061427b611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6142b1604084015173ffffffffffffffffffffffffffffffffffffffff1690565b16614342576080820151915b6142ca60c0820151614e2a565b916142d860e0830151614e2a565b60a0608084015193015194823b156100db575f946123d0604051978896879586947f3df0212400000000000000000000000000000000000000000000000000000000865260048601909493926060926080830196600f0b8352600f0b602083015260408201520152565b5f916142bd565b9080614373611d79611d7960208095015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6143a9604084015173ffffffffffffffffffffffffffffffffffffffff1690565b1661447d5760808201515b6143c160c0840151614e2a565b906143cf60e0850151614e2a565b9161446c6143fb60a06080880151970151985173ffffffffffffffffffffffffffffffffffffffff1690565b604051988997889687957fddc1f59d000000000000000000000000000000000000000000000000000000008752600487019360809373ffffffffffffffffffffffffffffffffffffffff939796929760a0870198600f0b8752600f0b60208701526040860152606085015216910152565b03925af18015610e0057612b875750565b5f6143b4565b61427b611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b6144d1611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b61012082015115159081156145a1576080830151905b60c08401519160e08501519060808601519361452061010060a089015198015173ffffffffffffffffffffffffffffffffffffffff1690565b95843b156100db575f966123d091604051998a98899788967fce7d65030000000000000000000000000000000000000000000000000000000088526004880194909695919373ffffffffffffffffffffffffffffffffffffffff9360a09560c088019988526020880152604087015260608601521515608085015216910152565b5f906144e7565b61010081015173ffffffffffffffffffffffffffffffffffffffff16308190036146ac57506145f4611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff61462a604084015173ffffffffffffffffffffffffffffffffffffffff1690565b166146a5576080820151915b60c08101519160e082015160a0608084015193015194823b156100db575f946123d0604051978896879586947f5b41b908000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b5f91614636565b6146d3611d79611d79602085015173ffffffffffffffffffffffffffffffffffffffff1690565b9173ffffffffffffffffffffffffffffffffffffffff61470a604083015173ffffffffffffffffffffffffffffffffffffffff1690565b166147a4576080810151925b60c08201519360e08301519060a0608085015194015195833b156100db576123d05f96604051988997889687957fa64833a0000000000000000000000000000000000000000000000000000000008752600487019360809373ffffffffffffffffffffffffffffffffffffffff939796929760a0870198875260208701526040860152606085015216910152565b5f92614716565b6145f4611d79611d79602084015173ffffffffffffffffffffffffffffffffffffffff1690565b906112329173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c891166141df565b5f80809381935af11561482357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b51906dffffffffffffffffffffffffffff821682036100db57565b908160609103126100db576148b081614881565b9160406148bf60208401614881565b92015163ffffffff811681036100db5790565b929190926148e08482614e81565b92969092935b6148f08589614eac565b506040517f0902f1ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff861696916060826004818b5afa8015610e00578b6149dc935f905f93614b0b575b5073ffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff80829316941692169316831498895f14614b0157602091935b60405180809781947f70a082310000000000000000000000000000000000000000000000000000000083526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa8015610e00578188916149fb955f91614ae3575b5003614ef7565b9415614adc575f94935b614a10846066111590565b998a15614aca5750505090614a2491614fa6565b90929091614a328385614e81565b9a92979193909397945b614a4461290d565b92803b156100db575f92838793614a8a604051978896879586947f022c0d9f00000000000000000000000000000000000000000000000000000000865260048601613b9d565b03925af18015610e0057614ab6575b5015614aac5790969390929091906148e6565b5050505050509050565b806123ef5f614ac4936111e2565b5f614a99565b9394889a929a97919796939693614a3c565b5f93614a05565b614afb915060203d8111610df957610dea81836111e2565b5f6149f4565b602091929361498b565b6dffffffffffffffffffffffffffff80945073ffffffffffffffffffffffffffffffffffffffff9250614b55839260603d8111614b61575b614b4d81836111e2565b81019061489c565b5095909350505061494d565b503d614b43565b5f9182604492602095604051937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160015f511416171615614bb957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b90603c116100db5790603c90565b90919060078103614c435750614c3e90611232926155b4565b615656565b60068103614c5e5750614c599061123292615393565b615454565b600b8103614c925750614c8c614c7a614c869261123294614fe9565b93829493929193615324565b92615352565b916151ce565b91600c8303614d1557614ca592506150c3565b90614caf8161514f565b91808311614ce5575061123291907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c906151ce565b7f12bacdd3000000000000000000000000000000000000000000000000000000005f52600452602482905260445ffd5b600f8303614d9757614d2792506150c3565b90614d31816150d6565b91808310614d67575061123291907f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90615015565b7f8b063d73000000000000000000000000000000000000000000000000000000005f52600452602482905260445ffd5b600e8314614dcc577f5cda29d7000000000000000000000000000000000000000000000000000000005f52600483905260245ffd5b6112329250614de1614df192614deb92614fe9565b9282949291611cdc565b92615001565b91615015565b9060148110612f6157017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec013560601c90565b6f7fffffffffffffffffffffffffffffff8111614e59576fffffffffffffffffffffffffffffffff16600f0b90565b7fb4fa3fb3000000000000000000000000000000000000000000000000000000005f5260045ffd5b9190603e11612f6157813560601c91601481013560f01c91602a601683013560601c92013560601c90565b73ffffffffffffffffffffffffffffffffffffffff821673ffffffffffffffffffffffffffffffffffffffff8216105f146101305791565b8181029291811591840414171561136357565b92909192831592838015614f9e575b614f7657614f239161ffff614f1c921690614ee4565b9182614ee4565b916127108402938404612710141715611363578201809211611363578115614f49570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7f7b9c8916000000000000000000000000000000000000000000000000000000005f5260045ffd5b508215614f06565b91909182602a116100db57602a01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60190565b90601411612f61573560601c90565b9060601161052a578035916040602083013592013590565b90816150115761040e91506150d6565b5090565b909180156114675773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a9016803b156100db575f928360649273ffffffffffffffffffffffffffffffffffffffff948560405198899788967f0b0d9c0900000000000000000000000000000000000000000000000000000000885216600487015216602485015260448401525af18015610e005761391f5750565b919060401161052a576020823592013590565b61510181307f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90615739565b905f821261510d575090565b73ffffffffffffffffffffffffffffffffffffffff907f4c085bf1000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b61517a81307f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90615739565b905f821361518c575061040e90611337565b73ffffffffffffffffffffffffffffffffffffffff907f3351b260000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b9082156114675773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a901691823b156100db576040517fa584119400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201525f8160248183885af18015610e0057615310575b5073ffffffffffffffffffffffffffffffffffffffff81166152c65750506020906004604051809481937f11da60b40000000000000000000000000000000000000000000000000000000083525af18015610e0057612b875750565b5f93602093926152d5926157d3565b6004604051809481937f11da60b40000000000000000000000000000000000000000000000000000000083525af18015610e0057612b875750565b806123ef5f61531e936111e2565b5f61526a565b1561534e577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b3090565b907f800000000000000000000000000000000000000000000000000000000000000082036153845761040e9150615817565b816150115761040e915061514f565b906101401161052a5780350190565b356fffffffffffffffffffffffffffffffff811681036100db5790565b3561040e816132c6565b62ffffff8116036100db57565b8060020b036100db57565b91908260a09103126100db576040516153f9816111c1565b6080808294803561540981611176565b8452602081013561541981611176565b6020850152604081013561542c816153c9565b6040850152606081013561543f816153d6565b606085015201359161545083611176565b0152565b61546060c082016153a2565b6fffffffffffffffffffffffffffffffff81161561555e575b6154cf6154ca60e0926154a96fffffffffffffffffffffffffffffffff6154a260a088016153bf565b9216611337565b906154b861010087018761081f565b9290916154c536896153e1565b615992565b615ab6565b91016154f26154dd826153a2565b6fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff83161061550e575050565b9061551b610e30926153a2565b7f8b063d73000000000000000000000000000000000000000000000000000000005f526fffffffffffffffffffffffffffffffff90811660045216602452604490565b5061556b60a082016153bf565b1561559a5760e06154cf6154ca61559161558c61558786612f99565b6150d6565b6158b0565b92505050615479565b60e06154cf6154ca61559161558c61558760208701612f99565b9060a01161052a5780350190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156100db570180359067ffffffffffffffff82116100db57602001918160051b360383136100db57565b919081101561081a5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156100db570190565b602081019061566582826155c2565b90505f9261567283612f99565b9061567f604085016153a2565b906fffffffffffffffffffffffffffffffff821615615725575b92915f915b8383106156b95750505050506060016154f26154dd826153a2565b8496506fffffffffffffffffffffffffffffffff6154ca916156f56156ee866156e861570e9798999a8c6155c2565b90615616565b9586615ae9565b615702608088018861081f565b949093165f0391615992565b94600161571b8793612f99565b919493019161569e565b905061573361558c836150d6565b90615699565b73ffffffffffffffffffffffffffffffffffffffff809381602094165f52168252602460405f2060405194859384927ff135baaa0000000000000000000000000000000000000000000000000000000084526004840152165afa908115610e00575f916157a4575090565b90506020813d6020116157cb575b816157bf602093836111e2565b810103126100db575190565b3d91506157b2565b611232929173ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a901692166119b6565b73ffffffffffffffffffffffffffffffffffffffff811661583757504790565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152906020908290602490829073ffffffffffffffffffffffffffffffffffffffff165afa908115610e00575f91615897575090565b61040e915060203d602011610df957610dea81836111e2565b906fffffffffffffffffffffffffffffffff82168092036158cd57565b7f93dafdf1000000000000000000000000000000000000000000000000000000005f5260045ffd5b61598461040e959373ffffffffffffffffffffffffffffffffffffffff60806101209582815116865282602082015116602087015262ffffff6040820151166040870152606081015160020b6060870152015116608084015260a083019073ffffffffffffffffffffffffffffffffffffffff6040809280511515855260208101516020860152015116910152565b8161010082015201916117cc565b936020919394845f14615a9857615a126401000276a4925b6159de6159b5611243565b88151581529485870188905273ffffffffffffffffffffffffffffffffffffffff166040860152565b60405197889485947ff3cd914c000000000000000000000000000000000000000000000000000000008652600486016158f5565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90165af1928315610e00575f93615a75575b505f13901515145f14615a6f57600f0b90565b60801d90565b5f919350615a919060203d602011610df957610dea81836111e2565b9290615a5c565b615a1273fffd8963efd1fc6a506488495d951d5263988d25926159aa565b5f81600f0b126158cd576fffffffffffffffffffffffffffffffff1690565b3561040e816153c9565b3561040e816153d6565b905f6080604051615af9816111c1565b8281528260208201528260408201528260608201520152615b1982612f99565b9073ffffffffffffffffffffffffffffffffffffffff8083169082161015615bec5790615be990615bcc60015b94615bc2615b5660208301615ad5565b615bb6615b716060615b6a60408701615adf565b9501612f99565b95615b99615b7d611252565b73ffffffffffffffffffffffffffffffffffffffff909a168a52565b73ffffffffffffffffffffffffffffffffffffffff166020890152565b62ffffff166040870152565b60020b6060850152565b73ffffffffffffffffffffffffffffffffffffffff166080830152565b91565b73ffffffffffffffffffffffffffffffffffffffff91821691615be99116615bcc818414615b4656fea164736f6c634300081a000a

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

000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8

-----Decoded View---------------
Arg [0] : params (tuple):
Arg [1] : permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
Arg [2] : weth9 (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : v4PoolManager (address): 0x000000000004444c5dc75cB358380D2e3dE08A90
Arg [4] : balancerVault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8


-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [2] : 000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90
Arg [3] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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