ETH Price: $3,585.77 (+5.20%)

Contract

0x816EBC5cb8A5651C902Cb06659907A93E574Db0B

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
145046702025-10-23 19:24:4117 days ago1761247481
0x816EBC5c...3E574Db0B
0 ETH
145046702025-10-23 19:24:4117 days ago1761247481
0x816EBC5c...3E574Db0B
0 ETH
143279192025-10-21 18:18:5019 days ago1761070730
0x816EBC5c...3E574Db0B
0.002 ETH
143278252025-10-21 18:17:1619 days ago1761070636
0x816EBC5c...3E574Db0B
0 ETH
143216032025-10-21 16:33:3419 days ago1761064414
0x816EBC5c...3E574Db0B
0 ETH
143216032025-10-21 16:33:3419 days ago1761064414
0x816EBC5c...3E574Db0B
0 ETH
143166292025-10-21 15:10:4019 days ago1761059440
0x816EBC5c...3E574Db0B
0 ETH
142304902025-10-20 15:15:0120 days ago1760973301
0x816EBC5c...3E574Db0B
0.00029273 ETH
142136612025-10-20 10:34:3220 days ago1760956472
0x816EBC5c...3E574Db0B
0 ETH
138868282025-10-16 15:47:1924 days ago1760629639
0x816EBC5c...3E574Db0B
0.0125 ETH
138154822025-10-15 19:58:1325 days ago1760558293
0x816EBC5c...3E574Db0B
0 ETH
138154312025-10-15 19:57:2225 days ago1760558242
0x816EBC5c...3E574Db0B
0 ETH
137269972025-10-14 19:23:2826 days ago1760469808
0x816EBC5c...3E574Db0B
0 ETH
137269972025-10-14 19:23:2826 days ago1760469808
0x816EBC5c...3E574Db0B
0 ETH
136528522025-10-13 22:47:4327 days ago1760395663
0x816EBC5c...3E574Db0B
0 ETH
136527662025-10-13 22:46:1727 days ago1760395577
0x816EBC5c...3E574Db0B
0 ETH
136526602025-10-13 22:44:3127 days ago1760395471
0x816EBC5c...3E574Db0B
0.00148965 ETH
136096682025-10-13 10:47:5927 days ago1760352479
0x816EBC5c...3E574Db0B
0 ETH
128466942025-10-04 14:51:4536 days ago1759589505
0x816EBC5c...3E574Db0B
0 ETH
128465512025-10-04 14:49:2236 days ago1759589362
0x816EBC5c...3E574Db0B
0.003 ETH
128309742025-10-04 10:29:4536 days ago1759573785
0x816EBC5c...3E574Db0B
0 ETH
127795702025-10-03 20:13:0137 days ago1759522381
0x816EBC5c...3E574Db0B
0 ETH
127793932025-10-03 20:10:0437 days ago1759522204
0x816EBC5c...3E574Db0B
0 ETH
127793392025-10-03 20:09:1037 days ago1759522150
0x816EBC5c...3E574Db0B
0 ETH
127576322025-10-03 14:07:2337 days ago1759500443
0x816EBC5c...3E574Db0B
0 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OneDeltaComposerKatana

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {BaseComposer} from "../../BaseComposer.sol";
import {SwapCallbacks} from "./flashSwap/SwapCallbacks.sol";
import {FlashLoanCallbacks} from "./flashLoan/FlashLoanCallbacks.sol";
import {UniversalFlashLoan} from "./flashLoan/UniversalFlashLoan.sol";

/**
 * @title Chain-dependent Universal aggregator contract.
 * @author 1delta Labs AG
 */
contract OneDeltaComposerKatana is BaseComposer, UniversalFlashLoan, SwapCallbacks {
    /**
     * Execute a set of packed operations
     */
    function _deltaComposeInternal(
        address callerAddress,
        uint256 currentOffset,
        uint256 calldataLength //
    )
        internal
        override(BaseComposer, FlashLoanCallbacks, SwapCallbacks)
    {
        return BaseComposer._deltaComposeInternal(
            callerAddress,
            currentOffset,
            calldataLength //
        );
    }

    function _universalFlashLoan(
        uint256 currentOffset,
        address callerAddress
    )
        internal
        override(UniversalFlashLoan, BaseComposer)
        returns (uint256)
    {
        return UniversalFlashLoan._universalFlashLoan(
            currentOffset,
            callerAddress //
        );
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {ComposerCommands} from "./enums/DeltaEnums.sol";
import {ExternalCall} from "./generic/ExternalCall.sol";
import {Transfers} from "./transfers/Transfers.sol";
import {ERC4626Operations} from "./ERC4626/ERC4626Operations.sol";
import {UniversalLending} from "./lending/UniversalLending.sol";
import {Permits} from "./permit/Permits.sol";
import {Swaps} from "./swappers/Swaps.sol";
import {Gen2025DexActions} from "./singletons/Gen2025DexActions.sol";
import {DeadLogger} from "../shared/logs/DeadLogger.sol";

/**
 * @title Base aggregator contract that needs overrides for explicit chains.
 *        Allows spot and margin swap aggregation
 *        Efficient batching through compact calldata usage.
 *        Needs to inherit callback implementations
 * @author 1delta Labs AG
 */
abstract contract BaseComposer is
    DeadLogger,
    Swaps,
    Gen2025DexActions,
    UniversalLending,
    ERC4626Operations,
    Transfers,
    Permits,
    ExternalCall //
{
    constructor() {}

    receive() external payable {}

    /**
     * Batch-executes a series of operations
     * The calldata is loaded in assembly and therefore not referred to here
     */
    function deltaCompose(bytes calldata) external payable {
        uint256 length;
        assembly {
            // the length of the calldata is stored per abi encoding standards
            length := calldataload(0x24)
        }
        _deltaComposeInternal(
            msg.sender,
            0x44, // the offset is constant
            length
        );

        // log for tracing
        _deadLog();
    }

    /**
     * Execute a set of packed operations
     * @param callerAddress the address of the EOA/contract that
     *                      initially triggered the `deltaCompose`
     *                      - this is called within flash & swap callbacks
     *                      - strict validations need to be made in these cases to
     *                        prevent an entity to call this with a non-matching callerAddress
     * @param currentOffset offset packed ops array
     * @param calldataLength length of packed ops array
     * | op0 | data0 | op1 | ...
     * | 1   | ...   |  1  | ...
     */
    function _deltaComposeInternal(
        address callerAddress,
        uint256 currentOffset,
        uint256 calldataLength //
    )
        internal
        virtual
    {
        // data loop paramters
        uint256 maxIndex;
        assembly {
            maxIndex := add(currentOffset, calldataLength)
        }
        ////////////////////////////////////////////////////
        // Progressively loop through the calldata
        // The first byte defines the operation
        // From there on, we read the data based on the
        // what the operation expects, e.g. read the next 32 bytes as uint256.
        //
        // `currentOffset` represents the current byte at which we
        //            are in the calldata
        // `maxIndex` is used as break criteria, this means that if
        //            currentOffset >= maxIndex, we iterated through
        //            the entire calldata.
        ////////////////////////////////////////////////////
        while (true) {
            uint256 operation;
            // fetch op metadata
            assembly {
                operation := shr(248, calldataload(currentOffset)) // last byte
                // we increment the current offset to skip the operation
                currentOffset := add(1, currentOffset)
            }
            if (operation < ComposerCommands.PERMIT) {
                if (operation == ComposerCommands.SWAPS) {
                    currentOffset = _swap(currentOffset, callerAddress);
                } else if (operation == ComposerCommands.EXT_CALL) {
                    currentOffset = _callExternal(currentOffset);
                } else if (operation == ComposerCommands.LENDING) {
                    currentOffset = _lendingOperations(callerAddress, currentOffset);
                } else if (operation == ComposerCommands.TRANSFERS) {
                    currentOffset = _transfers(currentOffset, callerAddress);
                } else {
                    _invalidOperation();
                }
            } else {
                if (operation == ComposerCommands.PERMIT) {
                    currentOffset = _permit(currentOffset, callerAddress);
                } else if (operation == ComposerCommands.FLASH_LOAN) {
                    currentOffset = _universalFlashLoan(currentOffset, callerAddress);
                } else if (operation == ComposerCommands.ERC4626) {
                    currentOffset = _ERC4626Operations(currentOffset, callerAddress);
                } else if (operation == ComposerCommands.GEN_2025_SINGELTONS) {
                    currentOffset = _gen2025DexActions(currentOffset, callerAddress);
                } else {
                    _invalidOperation();
                }
            }
            // break if we skipped over the calldata
            if (currentOffset >= maxIndex) break;
        }
        // revert if some excess is left
        if (currentOffset > maxIndex) revert InvalidCalldata();
    }

    function _universalFlashLoan(uint256 currentOffset, address callerAddress) internal virtual returns (uint256) {}
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {Masks} from "../../../../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../../../../shared/errors/Errors.sol";

/**
 * @title All Morpho Blue flash callbacks
 */
contract MorphoFlashLoanCallback is Masks, DeltaErrors {
    /// @dev Constant MorphoB address
    address private constant MORPHO_BLUE = 0xD50F2DffFd62f94Ee4AEd9ca05C61d0753268aBc;

    /**
     * Morpho blue callbacks
     */

    /// @dev Morpho Blue flash loan
    function onMorphoFlashLoan(uint256, bytes calldata) external {
        _onMorphoCallback();
    }

    /// @dev Morpho Blue supply callback
    function onMorphoSupply(uint256, bytes calldata) external {
        _onMorphoCallback();
    }

    /// @dev Morpho Blue repay callback
    function onMorphoRepay(uint256, bytes calldata) external {
        _onMorphoCallback();
    }

    /// @dev Morpho Blue supply collateral callback
    function onMorphoSupplyCollateral(uint256, bytes calldata) external {
        _onMorphoCallback();
    }

    /// @dev Morpho Blue is immutable and their flash loans are callbacks to msg.sender,
    /// Since it is universal batching and the same validation for all
    /// Morpho callbacks, we can use the same logic everywhere
    function _onMorphoCallback() internal {
        address origCaller;
        uint256 calldataLength;
        assembly {
            // validate caller
            // - extract id from params
            let firstWord := calldataload(100)

            switch and(UINT8_MASK, shr(88, firstWord))
            case 0 {
                if xor(caller(), MORPHO_BLUE) {
                    mstore(0, INVALID_CALLER)
                    revert(0, 0x4)
                }
            }
            default {
                mstore(0, INVALID_FLASH_LOAN)
                revert(0, 0x4)
            }
            // Slice the original caller off the beginnig of the calldata
            // From here on we have validated that the origCaller
            // was attached in the deltaCompose function
            // Otherwise, this would be a vulnerability
            origCaller := shr(96, firstWord)
            // shift / slice params
            calldataLength := sub(calldataload(68), 21)
        }
        // within the flash loan, any compose operation
        // can be executed
        _deltaComposeInternal(
            origCaller,
            121, // offset is constant (100 native + 21)
            calldataLength
        );
    }

    function _deltaComposeInternal(address callerAddress, uint256 offset, uint256 length) internal virtual {}
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {MorphoFlashLoanCallback} from "./callbacks/MorphoCallback.sol";

/**
 * @title Flash loan callbacks - these are chain-specific
 * @author 1delta Labs AG
 */
contract FlashLoanCallbacks is
    MorphoFlashLoanCallback //
{
    // override the compose
    function _deltaComposeInternal(
        address callerAddress,
        uint256 offset,
        uint256 length
    )
        internal
        virtual
        override(
            MorphoFlashLoanCallback //
        )
    {}
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {MorphoFlashLoans} from "../../../flashLoan/Morpho.sol";

import {FlashLoanCallbacks} from "./FlashLoanCallbacks.sol";
import {FlashLoanIds} from "../../../enums/DeltaEnums.sol";
import {DeltaErrors} from "../../../../shared/errors/Errors.sol";

/**
 * @title Flash loan aggregator
 * @author 1delta Labs AG
 */
contract UniversalFlashLoan is
    MorphoFlashLoans,
    FlashLoanCallbacks //
{
    /**
     * All flash ones in one function -what do you need more?
     */
    function _universalFlashLoan(uint256 currentOffset, address callerAddress) internal virtual returns (uint256) {
        uint256 flashLoanType; // architecture type
        assembly {
            flashLoanType := shr(248, calldataload(currentOffset)) // already masks uint8 as last byte
            currentOffset := add(currentOffset, 1)
        }

        if (flashLoanType == FlashLoanIds.MORPHO) {
            return morphoFlashLoan(currentOffset, callerAddress);
        } else {
            _invalidOperation();
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

/**
 * Author: Achthar | 1delta
 * /*****************************************************************************
 */
import {ValidatorLib} from "../../../../swappers/callbacks/ValidatorLib.sol";
import {Masks} from "../../../../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../../../../shared/errors/Errors.sol";

/**
 * @title Contract Module for general Margin Trading on an borrow delegation compatible Lender
 * @notice Contains main logic for uniswap-type callbacks and initiator functions
 */
abstract contract UniV2Callbacks is Masks, DeltaErrors {
    // factories

    bytes32 private constant SUSHISWAP_V2_FF_FACTORY = 0xff72D111b4d6f31B38919ae39779f570b747d6Acd90000000000000000000000;
    bytes32 private constant SUSHISWAP_V2_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303;

    /**
     * Generic Uniswap v2 style callbck executor
     */
    function _executeUniV2IfSelector(bytes32 selector) internal {
        bytes32 codeHash;
        bytes32 ffFactoryAddress;
        // this is a data strip that contains [tokenOut(20)|forkId(1)|calldataLength(2)|xxx...xxx(9)]
        bytes32 outData;
        uint256 forkId;
        assembly {
            outData := calldataload(204)
            switch selector
            case 0x10d1e85c00000000000000000000000000000000000000000000000000000000 {
                forkId := and(UINT8_MASK, shr(88, outData))

                ffFactoryAddress := SUSHISWAP_V2_FF_FACTORY
                codeHash := SUSHISWAP_V2_CODE_HASH
            }
        }

        if (ValidatorLib._hasData(ffFactoryAddress)) {
            uint256 calldataLength;
            address callerAddress;
            assembly {
                // revert if sender param is not this address
                if xor(calldataload(4), address()) {
                    mstore(0, INVALID_CALLER)
                    revert(0, 0x4)
                }

                // get tokens
                let tokenIn := shr(96, calldataload(184))
                let tokenOut := shr(96, outData)

                let ptr := mload(0x40)
                switch lt(tokenIn, tokenOut)
                case 0 {
                    mstore(add(ptr, 0x14), tokenIn)
                    mstore(ptr, tokenOut)
                }
                default {
                    mstore(add(ptr, 0x14), tokenOut)
                    mstore(ptr, tokenIn)
                }
                let salt
                // 128 and higher is solidly
                // 128-130 are reserved for the ones that have no isStable flag
                switch gt(forkId, 130)
                case 1 {
                    mstore8(
                        add(ptr, 0x34),
                        gt(forkId, 191) // store isStable (id>=192)
                    )
                    salt := keccak256(add(ptr, 0x0C), 0x29)
                }
                default { salt := keccak256(add(ptr, 0x0C), 0x28) }
                // calculate pool address in next 4 lines
                mstore(ptr, ffFactoryAddress)
                mstore(add(ptr, 0x15), salt)
                mstore(add(ptr, 0x35), codeHash)

                // verify that the caller is a v2 type pool
                if xor(and(ADDRESS_MASK, keccak256(ptr, 0x55)), caller()) {
                    mstore(0x0, BAD_POOL)
                    revert(0x0, 0x4)
                }

                calldataLength := and(UINT16_MASK, shr(72, outData))
                // get caller address as provided in the call setup
                callerAddress := shr(96, calldataload(164))
            }
            _deltaComposeInternal(
                callerAddress,
                // the naive offset is 164
                // we skip the entire callback validation data
                // that is tokens (+40), caller (+20), dexId (+1) datalength (+2)
                // = 227
                227,
                calldataLength
            );
            // force return
            assembly {
                return(0, 0)
            }
        }
    }

    function _deltaComposeInternal(address callerAddress, uint256 offset, uint256 length) internal virtual {}
}

File 7 of 49 : UniV3Callback.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

/**
 * Author: Achthar | 1delta
 * /*****************************************************************************
 */
import {ValidatorLib} from "../../../../swappers/callbacks/ValidatorLib.sol";
import {Masks} from "../../../../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../../../../shared/errors/Errors.sol";
import {V3Callbacker} from "../../../../swappers/callbacks/V3Callbacker.sol";

/**
 * @title Uniswap V3 type callback implementations
 */
abstract contract UniV3Callbacks is V3Callbacker, Masks, DeltaErrors {
    // factory ff addresses

    bytes32 private constant SUSHISWAP_V3_FF_FACTORY = 0xff203e8740894c8955cB8950759876d7E7E45E04c10000000000000000000000;
    bytes32 private constant SUSHISWAP_V3_CODE_HASH = 0xe040f12c7cee3904b78f24f8fc395629c2e69525c2815da7a659f7483e378ecb;

    /**
     * Generic UniswapV3 callback executor
     * The call looks like
     * function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata) external {...}
     *
     * Izumi deviates from this, we handle these below if it is deployed on this chain
     */
    function _executeUniV3IfSelector(bytes32 selector) internal {
        bytes32 codeHash;
        bytes32 ffFactoryAddress;
        // we use the amount to pay as shorthand here to
        // allow paying without added calldata
        uint256 amountToPay;
        assembly {
            switch selector
            case 0xfa461e3300000000000000000000000000000000000000000000000000000000 {
                ffFactoryAddress := SUSHISWAP_V3_FF_FACTORY
                codeHash := SUSHISWAP_V3_CODE_HASH

                let _amount1 := calldataload(36)
                switch sgt(_amount1, 0)
                case 1 { amountToPay := _amount1 }
                default { amountToPay := calldataload(4) }
            }
        }

        if (ValidatorLib._hasData(ffFactoryAddress)) {
            uint256 calldataLength;
            address callerAddress;
            address tokenIn;
            assembly {
                tokenIn := shr(96, calldataload(152))
                let tokenOutAndFee := calldataload(172)
                let tokenOut := shr(96, tokenOutAndFee)
                let s := mload(0x40)
                mstore(s, ffFactoryAddress)
                let p := add(s, 21)
                // Compute the inner hash in-place
                switch lt(tokenIn, tokenOut)
                case 0 {
                    mstore(p, tokenOut)
                    mstore(add(p, 32), tokenIn)
                }
                default {
                    mstore(p, tokenIn)
                    mstore(add(p, 32), tokenOut)
                }

                switch and(FF_ADDRESS_COMPLEMENT, ffFactoryAddress)
                case 0 {
                    // cases with fee
                    mstore(add(p, 64), and(UINT16_MASK, shr(72, tokenOutAndFee)))
                    mstore(p, keccak256(p, 96))
                }
                default {
                    // cases without fee, e.g. algebra case
                    mstore(p, keccak256(p, 64))
                }
                p := add(p, 32)
                mstore(p, codeHash)

                ////////////////////////////////////////////////////
                // If the caller is not the calculated pool, we revert
                ////////////////////////////////////////////////////
                if xor(caller(), and(ADDRESS_MASK, keccak256(s, 85))) {
                    mstore(0x0, BAD_POOL)
                    revert(0x0, 0x4)
                }

                calldataLength := and(UINT16_MASK, shr(56, tokenOutAndFee))

                // get original caller address
                callerAddress := shr(96, calldataload(132))
            }
            clSwapCallback(amountToPay, tokenIn, callerAddress, calldataLength);
            // force return
            assembly {
                return(0, 0)
            }
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {UniV3Callbacks, V3Callbacker} from "./callbacks/UniV3Callback.sol";
import {UniV2Callbacks} from "./callbacks/UniV2Callback.sol";

/**
 * @title Swap Callback executor
 * @author 1delta Labs AG
 */
contract SwapCallbacks is
    UniV3Callbacks,
    UniV2Callbacks //
{
    // override the compose
    function _deltaComposeInternal(
        address callerAddress,
        uint256 offset,
        uint256 length
    )
        internal
        virtual
        override(
            V3Callbacker,
            UniV2Callbacks //
        )
    {}

    /**
     * Swap callbacks are taken in the fallback
     * We do this to have an easier time in validating similar callbacks
     * with separate selectors
     *
     * We identify the selector in the fallback and then map it to the DEX
     *
     * Note that each "_execute..." function returns (exits) when a callback is run.
     *
     * If it falls through all variations, it reverts at the end.
     */
    fallback() external {
        bytes32 selector;
        assembly {
            selector :=
                and(
                    0xffffffff00000000000000000000000000000000000000000000000000000000, // masks upper 4 bytes
                    calldataload(0)
                )
        }
        _executeUniV3IfSelector(selector);
        _executeUniV2IfSelector(selector);

        // we do not allow a fallthrough
        assembly {
            revert(0, 0)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

/**
 * Permit classifier enums
 */
library TransferIds {
    uint256 internal constant TRANSFER_FROM = 0;
    uint256 internal constant SWEEP = 1;
    uint256 internal constant WRAP_NATIVE = 2;
    uint256 internal constant UNWRAP_WNATIVE = 3;
    uint256 internal constant PERMIT2_TRANSFER_FROM = 4;
    uint256 internal constant APPROVE = 5;
}

/**
 * Permit classifier enums
 */
library PermitIds {
    uint256 internal constant TOKEN_PERMIT = 0;
    uint256 internal constant AAVE_V3_CREDIT_PERMIT = 1;
    uint256 internal constant ALLOW_CREDIT_PERMIT = 2;
}

/**
 * Lender classifier enums, expected to be encoded as uint16
 */
library LenderIds {
    uint256 internal constant UP_TO_AAVE_V3 = 1000;
    uint256 internal constant UP_TO_AAVE_V2 = 2000;
    uint256 internal constant UP_TO_COMPOUND_V3 = 3000;
    uint256 internal constant UP_TO_COMPOUND_V2 = 4000;
    uint256 internal constant UP_TO_MORPHO = 5000;
}

/**
 * Operations enums, encoded as uint8
 */
library LenderOps {
    uint256 internal constant DEPOSIT = 0;
    uint256 internal constant BORROW = 1;
    uint256 internal constant REPAY = 2;
    uint256 internal constant WITHDRAW = 3;
    uint256 internal constant DEPOSIT_LENDING_TOKEN = 4;
    uint256 internal constant WITHDRAW_LENDING_TOKEN = 5;
}

/**
 * Lender classifier enums, expected to be encoded as uint16
 */
library FlashLoanIds {
    uint256 internal constant MORPHO = 0;
    uint256 internal constant BALANCER_V2 = 1;
    uint256 internal constant AAVE_V3 = 2;
    uint256 internal constant AAVE_V2 = 3;
}

/**
 * ERC4626 classifier enums
 */
library ERC4626Ids {
    uint256 internal constant DEPOSIT = 0;
    uint256 internal constant WITHDRAW = 1;
}

/**
 * Uniswp V4 operations outside of swaps
 */
library Gen2025ActionIds {
    uint256 internal constant UNLOCK = 0;
    uint256 internal constant UNI_V4_TAKE = 1;
    uint256 internal constant UNI_V4_SETTLE = 2;
    uint256 internal constant UNI_V4_SYNC = 3;
    uint256 internal constant BAL_V3_TAKE = 4;
    uint256 internal constant BAL_V3_SETTLE = 5;
}

/// @title Commands for OneDeltaComposer
library ComposerCommands {
    uint256 internal constant SWAPS = 0x10;
    uint256 internal constant EXT_CALL = 0x20;
    uint256 internal constant EXT_TRY_CALL = 0x21;
    uint256 internal constant LENDING = 0x30;
    uint256 internal constant TRANSFERS = 0x40;
    uint256 internal constant PERMIT = 0x50;
    uint256 internal constant FLASH_LOAN = 0x60;
    uint256 internal constant ERC4626 = 0x70;
    uint256 internal constant GEN_2025_SINGELTONS = 0x80;
    uint256 internal constant BRIDGING = 0x90;
}

/// @title Commands for Bridge
library BridgeIds {
    uint256 internal constant STARGATE_V2 = 0x00;
    uint256 internal constant ACROSS = 0x0A;
}

File 10 of 49 : ERC4626Operations.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {ERC4626Transfers} from "./ERC4626Transfers.sol";
import {ERC4626Ids} from "../enums/DeltaEnums.sol";

/**
 * @notice ERC4626 deposit and withdraw actions
 */
abstract contract ERC4626Operations is ERC4626Transfers {
    /// @notice withdraw from (e.g. morpho) vault
    function _ERC4626Operations(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        uint256 erc4626Operation;
        assembly {
            erc4626Operation := shr(248, calldataload(currentOffset))
            currentOffset := add(currentOffset, 1)
        }
        /**
         * ERC6464 deposit
         */
        if (erc4626Operation == ERC4626Ids.DEPOSIT) {
            currentOffset = _encodeErc4646Deposit(currentOffset);
        }
        /**
         * ERC6464 withdraw
         */
        else if (erc4626Operation == ERC4626Ids.WITHDRAW) {
            currentOffset = _encodeErc4646Withdraw(currentOffset, callerAddress);
        } else {
            _invalidOperation();
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";

// solhint-disable max-line-length

/**
 * @notice ERC4626 deposit and withdraw actions
 */
abstract contract ERC4626Transfers is ERC20Selectors, Masks, DeltaErrors {
    /// @dev  mint(...)
    bytes32 private constant ERC4626_MINT = 0x94bf804d00000000000000000000000000000000000000000000000000000000;

    /// @dev  deposit(...)
    bytes32 private constant ERC4626_DEPOSIT = 0x6e553f6500000000000000000000000000000000000000000000000000000000;

    /// @dev  withdraw(...)
    bytes32 private constant ERC4626_WITHDRAW = 0xb460af9400000000000000000000000000000000000000000000000000000000;

    /// @dev  redeem(...)
    bytes32 private constant ERC4626_REDEEM = 0xba08765200000000000000000000000000000000000000000000000000000000;

    /// @notice Deposit to (e.g. morpho) vault
    function _encodeErc4646Deposit(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)

            // loan token
            let asset := shr(96, calldataload(currentOffset))

            currentOffset := add(currentOffset, 20)

            let vaultContract := shr(96, calldataload(currentOffset))

            currentOffset := add(currentOffset, 20)

            let amount := shr(128, calldataload(currentOffset))
            let amountToDeposit := and(UINT120_MASK, amount)

            currentOffset := add(currentOffset, 16)

            /**
             * check if it is by shares or assets
             */
            switch and(USE_SHARES_FLAG, amount)
            case 0 {
                mstore(ptr, ERC4626_DEPOSIT)
                /**
                 * if the amount is zero, we assume that the contract balance is deposited
                 */
                if iszero(amountToDeposit) {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to asset
                    pop(staticcall(gas(), asset, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    amountToDeposit := mload(0x0)
                }
            }
            default { mstore(ptr, ERC4626_MINT) }

            mstore(add(ptr, 0x4), amountToDeposit) // shares or assets
            mstore(add(ptr, 0x24), shr(96, calldataload(currentOffset))) // receiver

            if iszero(call(gas(), vaultContract, 0x0, ptr, 0x44, 0x0, 0x0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0x0, returndatasize())
            }
            currentOffset := add(currentOffset, 20)
        }
        return currentOffset;
    }

    /// @notice withdraw from (e.g. morpho) vault
    function _encodeErc4646Withdraw(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)

            let vaultContract := shr(96, calldataload(currentOffset))

            currentOffset := add(currentOffset, 20)

            let amount := shr(128, calldataload(currentOffset))
            let amountToWithdrawOrRedeem := and(UINT120_MASK, amount)

            currentOffset := add(currentOffset, 16)

            /**
             * check if it is by shares or assets
             */
            switch and(USE_SHARES_FLAG, amount)
            case 0 {
                // plain withdraw amount
                mstore(ptr, ERC4626_WITHDRAW)
            }
            default {
                // note that this covers max withdraw already as the user can apply the
                // static shares amount hey own
                mstore(ptr, ERC4626_REDEEM)
            }

            mstore(add(ptr, 0x4), amountToWithdrawOrRedeem) // shares or assets
            mstore(add(ptr, 0x24), shr(96, calldataload(currentOffset))) // receiver
            currentOffset := add(currentOffset, 20)
            mstore(add(ptr, 0x44), callerAddress) // owner

            if iszero(call(gas(), vaultContract, 0x0, ptr, 0x64, 0x0, 0x0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {Masks} from "../../shared/masks/Masks.sol";

/**
 * @title Morpho flash loans
 * @author 1delta Labs AG
 */
contract MorphoFlashLoans is Masks {
    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | asset                           |
     * | 20     | 20             | pool                            | <-- we allow ANY morpho style pool here
     * | 40     | 16             | amount                          |
     * | 56     | 2              | paramsLength                    |
     * | 58     | paramsLength   | params                          |
     */
    function morphoFlashLoan(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            // get token to loan
            let token := shr(96, calldataload(currentOffset))
            // morpho-like pool as target
            let pool := shr(96, calldataload(add(currentOffset, 20)))
            // second calldata slice including amount annd params length
            let slice := calldataload(add(currentOffset, 40))
            let amount := shr(128, slice) // shr will already mask uint128 here
            // length of params
            let calldataLength := and(UINT16_MASK, shr(112, slice))
            // skip uint128 and uint16
            currentOffset := add(currentOffset, 58)

            // morpho should be the primary choice
            let ptr := mload(0x40)

            /**
             * Prepare call
             */

            // flashLoan(...)
            mstore(ptr, 0xe0232b4200000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 4), token)
            mstore(add(ptr, 36), amount)
            mstore(add(ptr, 68), 0x60) // offset
            mstore(add(ptr, 100), add(20, calldataLength)) // data length
            mstore(add(ptr, 132), shl(96, callerAddress)) // caller
            calldatacopy(add(ptr, 152), currentOffset, calldataLength) // calldata
            if iszero(
                call(
                    gas(),
                    pool,
                    0x0,
                    ptr,
                    add(calldataLength, 152),
                    0x0,
                    0x0 //
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0x0, returndatasize())
            }
            // increment offset
            currentOffset := add(currentOffset, calldataLength)
        }
        return currentOffset;
    }
}

File 13 of 49 : BaseUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ERC20Selectors} from "contracts/1delta/shared/selectors/ERC20Selectors.sol";
import {Masks} from "contracts/1delta/shared/masks/Masks.sol";
import {DeltaErrors} from "contracts/1delta/shared/errors/Errors.sol";

contract BaseUtils is ERC20Selectors, Masks, DeltaErrors {
    error InvalidAssetId(uint16 assetId);
    error InsufficientValue();
    error SlippageTooHigh(uint256 expected, uint256 actual);
    error ZeroBalance();
    error BridgeFailed();

    uint256 internal constant FEE_DENOMINATOR = 1e9;
    uint256 internal constant INSUFFICIENT_VALUE = 0x1101129400000000000000000000000000000000000000000000000000000000;
    uint256 internal constant ZERO_BALANCE = 0x669567ea00000000000000000000000000000000000000000000000000000000;
    uint256 internal constant BRIDGE_FAILED = 0xc3b9eede00000000000000000000000000000000000000000000000000000000;
}

File 14 of 49 : ExternalCall.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {BaseUtils} from "contracts/1delta/composer/generic/BaseUtils.sol";

// solhint-disable max-line-length

/**
 * @notice External call on call forwarder which can safely execute any calls for a specific selector
 * without comprimising this contract
 */
abstract contract ExternalCall is BaseUtils {
    /// @notice selector for deltaForwardCompose(bytes)
    bytes32 private constant DELTA_FORWARD_COMPOSE = 0x6a0c90ff00000000000000000000000000000000000000000000000000000000;

    /**
     * This is not a real external call, this one has a pre-determined selector
     * that prevents collision with any calls that can be made in this contract
     * This prevents unauthorized calls that would pull funds from other users
     *
     * On top of that, this makes the contract arbitrarily extensible.
     */
    function _callExternal(uint256 currentOffset) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description          |
         * |--------|----------------|----------------------|
         * | 0      | 20             | target               |
         * | 20     | 16             | nativeValue          |
         * | 36     | 2              | calldataLength       |
         * | 38     | calldataLength | calldata             |
         */
        assembly {
            let target := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)

            let callValue := shr(128, calldataload(currentOffset))
            currentOffset := add(16, currentOffset)

            let dataLength := shr(240, calldataload(currentOffset))
            currentOffset := add(2, currentOffset)

            // this is a slightly different behavior as, unlike for ERC20, the
            // 0-value is a commonly used one, as such, a flag is used for this
            switch and(NATIVE_FLAG, callValue)
            case 0 { callValue := and(callValue, UINT120_MASK) }
            default { callValue := selfbalance() }

            // free memo ptr for populating the tx
            let ptr := mload(0x40)

            mstore(ptr, DELTA_FORWARD_COMPOSE)
            mstore(add(ptr, 0x4), 0x20) // offset
            mstore(add(ptr, 0x24), dataLength) // length

            // copy calldata
            calldatacopy(add(ptr, 0x44), currentOffset, dataLength)
            if iszero(
                call(
                    gas(),
                    target,
                    callValue,
                    ptr, //
                    add(0x44, dataLength),
                    //selector plus 0x44 (selector, offset, length)
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            // increment offset by data length
            currentOffset := add(currentOffset, dataLength)
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";

// solhint-disable max-line-length

/**
 * @notice Lending base contract that wraps multiple Aave lender types (V2, V3, non-ir mode based).
 */
abstract contract AaveLending is ERC20Selectors, Masks {
    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | aToken                          |
     * | 96     | 20             | pool                            |
     */
    /// @notice Withdraw from lender lastgiven user address and lender Id
    function _withdrawFromAave(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)
            // Aave types need to trasfer collateral tokens
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))

            let amount := and(UINT120_MASK, amountData)
            // get aToken
            let collateralToken := shr(96, calldataload(add(currentOffset, 56)))
            // skip  to end
            currentOffset := add(currentOffset, 76)

            // apply max if needed
            switch amount
            case 0xffffffffffffffffffffffffffff {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add caller address as parameter
                mstore(0x04, callerAddress)
                // call to collateral token
                pop(staticcall(gas(), collateralToken, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amount := mload(0x0)
            }

            /**
             * PREPARE TRANSFER_FROM USER
             */

            // selector for transferFrom(address,address,uint256)
            mstore(ptr, ERC20_TRANSFER_FROM)
            mstore(add(ptr, 0x04), callerAddress)
            mstore(add(ptr, 0x24), address())
            mstore(add(ptr, 0x44), amount)

            let success := call(gas(), collateralToken, 0x0, ptr, 0x64, 0x0, 0x20)

            let rdsize := returndatasize()

            success :=
                and(
                    success, // call itself succeeded
                    or(
                        iszero(rdsize), // no return data, or
                        and(
                            gt(rdsize, 31), // at least 32 bytes
                            eq(mload(0x0), 1) // starts with uint256(1)
                        )
                    )
                )

            if iszero(success) {
                returndatacopy(0x0, 0x0, rdsize)
                revert(0x0, rdsize)
            }

            // selector withdraw(address,uint256,address)
            mstore(ptr, 0x69328dec00000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), underlying)
            mstore(add(ptr, 0x24), amount)
            mstore(add(ptr, 0x44), receiver)

            let pool := shr(96, calldataload(currentOffset))

            // skip token (end of data)
            currentOffset := add(currentOffset, 20)
            // call pool
            if iszero(call(gas(), pool, 0x0, ptr, 0x64, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 1              | mode                            |
     * | 77     | 20             | pool                            |
     */
    function _borrowFromAave(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            let receiverAndMode := calldataload(add(currentOffset, 36))
            // receiver
            let receiver := shr(96, receiverAndMode)
            let mode := and(UINT8_MASK, shr(88, receiverAndMode))
            // get pool
            let pool := shr(96, calldataload(add(currentOffset, 57)))
            // skip pool (end of data)
            currentOffset := add(currentOffset, 77)

            let amount := and(UINT120_MASK, amountData)

            let ptr := mload(0x40)
            switch mode
            case 0 {
                // borrowing with no irMode (special aave forks)
                // selector borrow(address,uint256,uint16,address)
                mstore(ptr, 0x1d5d723700000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x04), underlying)
                mstore(add(ptr, 0x24), amount)
                mstore(add(ptr, 0x44), 0x0)
                mstore(add(ptr, 0x64), callerAddress)
                // call pool
                if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                    returndatacopy(0x0, 0x0, returndatasize())
                    revert(0x0, returndatasize())
                }
            }
            default {
                // selector borrow(address,uint256,uint256,uint16,address)
                mstore(ptr, 0xa415bcad00000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x04), underlying)
                mstore(add(ptr, 0x24), amount)
                mstore(add(ptr, 0x44), mode)
                mstore(add(ptr, 0x64), 0x0)
                mstore(add(ptr, 0x84), callerAddress)
                // call pool
                if iszero(call(gas(), pool, 0x0, ptr, 0xA4, 0x0, 0x0)) {
                    returndatacopy(0x0, 0x0, returndatasize())
                    revert(0x0, returndatasize())
                }
            }

            //  transfer underlying if needed
            if xor(receiver, address()) {
                // selector for transfer(address,uint256)
                mstore(ptr, ERC20_TRANSFER)
                mstore(add(ptr, 0x04), receiver)
                // mstore(add(ptr, 0x24), amount) <-- this one is still in this memo location

                let success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)

                let rdsize := returndatasize()

                // Check for ERC20 success. ERC20 tokens should return a boolean,
                // but some don't. We accept 0-length return data as success, or at
                // least 32 bytes that starts with a 32-byte boolean true.
                success :=
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(ptr), 1) // starts with uint256(1)
                            )
                        )
                    )

                if iszero(success) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | pool                            |
     */
    /// @notice Withdraw from lender lastgiven user address and lender Id
    function _depositToAaveV3(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))
            // get pool
            let pool := shr(96, calldataload(add(currentOffset, 56)))
            // skip pool (end of data)
            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)
            // zero is this balance
            if iszero(amount) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amount := mload(0x0)
            }

            let ptr := mload(0x40)

            // selector supply(address,uint256,address,uint16)
            mstore(ptr, 0x617ba03700000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), underlying)
            mstore(add(ptr, 0x24), amount)
            mstore(add(ptr, 0x44), receiver)
            mstore(add(ptr, 0x64), 0x0)
            // call pool
            if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | pool                            |
     */
    /// @notice Withdraw from lender lastgiven user address and lender Id
    function _depositToAaveV2(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))
            // get pool
            let pool := shr(96, calldataload(add(currentOffset, 56)))
            // skip pool (end of data)
            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)
            // zero is this balance
            if iszero(amount) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(
                    staticcall(
                        gas(),
                        underlying, // token
                        0x0,
                        0x24,
                        0x0,
                        0x20
                    )
                )
                // load the retrieved balance
                amount := mload(0x0)
            }

            let ptr := mload(0x40)
            // selector deposit(address,uint256,address,uint16)
            mstore(ptr, 0xe8eda9df00000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), underlying)
            mstore(add(ptr, 0x24), amount)
            mstore(add(ptr, 0x44), receiver)
            mstore(add(ptr, 0x64), 0x0)
            // call pool
            if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 1              | mode                            |
     * | 77     | 20             | debtToken                       |
     * | 97     | 20             | pool                            |
     */
    function _repayToAave(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            let receiverAndMode := calldataload(add(currentOffset, 36))
            // receiver
            let receiver := shr(96, receiverAndMode)
            let mode := and(UINT8_MASK, shr(88, receiverAndMode))

            let amount := and(UINT120_MASK, amountData)
            switch amount
            case 0 {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amount := mload(0x0)
            }
            // safe repay maximum: fetch contract balance and user debt and take minimum
            case 0xffffffffffffffffffffffffffff {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)

                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), underlying, 0x0, 0x24, 0x4, 0x20))
                // load the retrieved balance
                amount := mload(0x4)

                // add caller address as parameter
                mstore(0x04, callerAddress)
                // call to debt token
                pop(staticcall(gas(), shr(96, calldataload(add(currentOffset, 57))), 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                let borrowBalance := mload(0x0)
                // if borrow balance is less than the amount, select borrow balance
                if lt(borrowBalance, amount) { amount := borrowBalance }
            }

            // get pool
            let pool := shr(96, calldataload(add(currentOffset, 77)))
            // skip pool (end of data)
            currentOffset := add(currentOffset, 97)

            let ptr := mload(0x40)

            // some Aaves dropped the IR mode, mode=0 is using their selector
            switch mode
            case 0 {
                // selector repay(address,uint256,address)
                mstore(ptr, 0x5ceae9c400000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x04), underlying)
                mstore(add(ptr, 0x24), amount)
                mstore(add(ptr, 0x44), receiver)
                // call pool
                if iszero(call(gas(), pool, 0x0, ptr, 0x64, 0x0, 0x0)) {
                    returndatacopy(0x0, 0x0, returndatasize())
                    revert(0x0, returndatasize())
                }
            }
            default {
                // selector repay(address,uint256,uint256,address)
                mstore(ptr, 0x573ade8100000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x04), underlying)
                mstore(add(ptr, 0x24), amount)
                mstore(add(ptr, 0x44), mode)
                mstore(add(ptr, 0x64), receiver)
                // call pool
                if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                    returndatacopy(0x0, 0x0, returndatasize())
                    revert(0x0, returndatasize())
                }
            }
        }

        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";

// solhint-disable max-line-length

/**
 * @notice Lending base contract that wraps multiple Compound V2 lender types.
 * Most effective for Venus
 */
abstract contract CompoundV2Lending is ERC20Selectors, Masks {
    /*
     * Note this is for Venus Finance only as other Compound forks
     * do not have this feature.
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | cToken                          |
     */
    function _borrowFromCompoundV2(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)
            // Compound V3 types need to trasfer collateral tokens
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))

            let cToken := shr(96, calldataload(add(currentOffset, 56)))

            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)

            // selector for borrowBehlaf(address,uint256)
            mstore(ptr, 0x856e5bb300000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x4), callerAddress) // user
            mstore(add(ptr, 0x24), amount) // to this address
            if iszero(call(gas(), cToken, 0x0, ptr, 0x44, ptr, 0x0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            if xor(address(), receiver) {
                switch underlying
                // native case
                case 0 { if iszero(call(gas(), receiver, amount, 0, 0, 0, 0)) { revert(0, 0) } }
                // erc20 case
                default {
                    // 4) TRANSFER TO RECIPIENT
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), receiver)
                    mstore(add(ptr, 0x24), amount)

                    let success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)

                    let rdsize := returndatasize()

                    // Check for ERC20 success. ERC20 tokens should return a boolean,
                    // but some don't. We accept 0-length return data as success, or at
                    // least 32 bytes that starts with a 32-byte boolean true.
                    success :=
                        and(
                            success, // call itself succeeded
                            or(
                                iszero(rdsize), // no return data, or
                                and(
                                    gt(rdsize, 31), // at least 32 bytes
                                    eq(mload(ptr), 1) // starts with uint256(1)
                                )
                            )
                        )

                    if iszero(success) {
                        returndatacopy(0, 0, rdsize)
                        revert(0, rdsize)
                    }
                }
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | cToken                          |
     */
    function _withdrawFromCompoundV2(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)
            // Compound V2 types need to trasfer collateral tokens
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))

            let cToken := shr(96, calldataload(add(currentOffset, 56)))

            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)
            if eq(amount, 0xffffffffffffffffffffffffffff) {
                // selector for balanceOfUnderlying(address)
                mstore(0, 0x3af9e66900000000000000000000000000000000000000000000000000000000)
                // add caller address as parameter
                mstore(0x04, callerAddress)
                // call to token
                pop(
                    call(
                        gas(),
                        cToken, // collateral token
                        0x0,
                        0x0,
                        0x24,
                        0x0,
                        0x20
                    )
                )
                // load the retrieved balance
                amount := mload(0x0)
            }

            // 1) CALCULTAE TRANSFER AMOUNT
            // Store fnSig (=bytes4(abi.encodeWithSignature("exchangeRateCurrent()"))) at params
            // - here we store 32 bytes : 4 bytes of fnSig and 28 bytes of RIGHT padding
            mstore(
                0x0,
                0xbd6d894d00000000000000000000000000000000000000000000000000000000 // with padding
            )
            // call to collateralToken
            // accrues interest. No real risk of failure.
            pop(
                call(
                    gas(),
                    cToken,
                    0x0,
                    0x0,
                    0x24,
                    0x0, // store back to ptr
                    0x20
                )
            )

            // load the retrieved protocol share
            let refAmount := mload(0x0)

            // calculate collateral token amount, rounding up
            let cTokenTransferAmount :=
                add(
                    div(
                        mul(amount, 1000000000000000000), // multiply with 1e18
                        refAmount // divide by rate
                    ),
                    1
                )
            // FETCH BALANCE
            // selector for balanceOf(address)
            mstore(0x0, ERC20_BALANCE_OF)
            // add _from address as parameter
            mstore(0x4, callerAddress)

            // call to collateralToken
            pop(staticcall(gas(), cToken, 0x0, 0x24, 0x0, 0x20))

            // load the retrieved balance
            refAmount := mload(0x0)

            // floor to the balance
            if gt(cTokenTransferAmount, refAmount) { cTokenTransferAmount := refAmount }

            // 2) TRANSFER VTOKENS

            // selector for transferFrom(address,address,uint256)
            mstore(ptr, ERC20_TRANSFER_FROM)
            mstore(add(ptr, 0x04), callerAddress) // from user
            mstore(add(ptr, 0x24), address()) // to this address
            mstore(add(ptr, 0x44), cTokenTransferAmount)

            let success := call(gas(), cToken, 0, ptr, 0x64, ptr, 32)

            if iszero(success) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            // 3) REDEEM
            // selector for redeem(uint256)
            mstore(0, 0xdb006a7500000000000000000000000000000000000000000000000000000000)
            mstore(0x4, cTokenTransferAmount)

            if iszero(
                call(
                    gas(),
                    cToken,
                    0x0,
                    0x0, // input = selector
                    0x24, // input selector + uint256
                    0x0, // output
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            // transfer tokens only if the receiver is not this address
            if xor(address(), receiver) {
                // 4) TRANSFER TO RECIPIENT
                // selector for transfer(address,uint256)
                mstore(ptr, ERC20_TRANSFER)
                mstore(add(ptr, 0x04), receiver)
                mstore(add(ptr, 0x24), amount)

                success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)

                let rdsize := returndatasize()

                // Check for ERC20 success. ERC20 tokens should return a boolean,
                // but some don't. We accept 0-length return data as success, or at
                // least 32 bytes that starts with a 32-byte boolean true.
                success :=
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(ptr), 1) // starts with uint256(1)
                            )
                        )
                    )

                if iszero(success) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
        }
        return currentOffset;
    }

    /*
     * Note: Some Compound V2 forks might not have this feature and need a separate
     * function.
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | cToken                          |
     */
    /// @notice Withdraw from lender lastgiven user address and lender Id
    function _depositToCompoundV2(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))
            // get cToken
            let cToken := shr(96, calldataload(add(currentOffset, 56)))
            currentOffset := add(currentOffset, 76)

            switch underlying
            // case native
            case 0 {
                let amount

                amount := and(UINT120_MASK, amountData)
                // zero is this balance
                if iszero(amount) { amount := selfbalance() }

                // selector for mint()
                mstore(0, 0x1249c58b00000000000000000000000000000000000000000000000000000000)

                if iszero(call(gas(), cToken, amount, 0x0, 0x4, 0x0, 0x0)) {
                    returndatacopy(0x0, 0, returndatasize())
                    revert(0x0, returndatasize())
                }

                // need to transfer collateral to receiver
                if xor(receiver, address()) {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to token
                    pop(staticcall(gas(), cToken, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    let cBalance := mload(0x0)

                    let ptr := mload(0x40)
                    // TRANSFER COLLATERAL
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), receiver)
                    mstore(add(ptr, 0x24), cBalance)

                    let success := call(gas(), cToken, 0, ptr, 0x44, ptr, 32)

                    let rdsize := returndatasize()

                    if iszero(
                        and(
                            success, // call itself succeeded
                            or(
                                iszero(rdsize), // no return data, or
                                and(
                                    gt(rdsize, 31), // at least 32 bytes
                                    eq(mload(ptr), 1) // starts with uint256(1)
                                )
                            )
                        )
                    ) {
                        returndatacopy(0, 0, rdsize)
                        revert(0, rdsize)
                    }
                }
            }
            // erc20 case
            default {
                let amount

                amount := and(UINT120_MASK, amountData)
                // zero is this balance
                if iszero(amount) {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to token
                    pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    amount := mload(0x0)
                }

                let ptr := mload(0x40)

                // selector for mintBehalf(address,uint256)
                mstore(ptr, 0x23323e0300000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x04), receiver)
                mstore(add(ptr, 0x24), amount)

                if iszero(call(gas(), cToken, 0x0, ptr, 0x44, 0x0, 0x0)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | cToken                          |
     */
    function _repayToCompoundV2(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))
            // get comet
            let cToken := shr(96, calldataload(add(currentOffset, 56)))
            currentOffset := add(currentOffset, 76)

            let ptr := mload(0x40)

            switch underlying
            // case native
            case 0 {
                let amount := and(UINT120_MASK, amountData)
                switch amount
                case 0 {
                    // load the retrieved balance
                    amount := selfbalance()
                }
                // safe repay the maximum
                case 0xffffffffffffffffffffffffffff {
                    // contract balance
                    amount := selfbalance()

                    // selector for borrowBalanceCurrent(address)
                    mstore(0, 0x17bfdfbc00000000000000000000000000000000000000000000000000000000)
                    // add this address as parameter
                    mstore(0x04, receiver)
                    // call to token
                    pop(call(gas(), cToken, 0x0, 0x0, 0x24, 0x0, 0x20))

                    // borrow balance smaller than amount available - use max
                    // otherwise, repay whatever is in the contract
                    if lt(mload(0x0), amount) { amount := MAX_UINT256 }
                }

                // selector for repayBorrowBehalf(address)
                mstore(0, 0xe597461900000000000000000000000000000000000000000000000000000000)
                mstore(4, receiver) // user

                if iszero(
                    call(
                        gas(),
                        cToken,
                        amount,
                        0, // input = empty for fallback
                        0x24, // input size = selector + address + uint256
                        0, // output
                        0x0 // output size = zero
                    )
                ) {
                    returndatacopy(0x0, 0, returndatasize())
                    revert(0x0, returndatasize())
                }
            }
            // case ERC20
            default {
                let amount := and(UINT120_MASK, amountData)
                switch amount
                case 0 {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to token
                    pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    amount := mload(0x0)
                }
                // safe repay the maximum
                case 0xffffffffffffffffffffffffffff {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to token
                    pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    amount := mload(0x0)

                    // selector for borrowBalanceCurrent(address)
                    mstore(0, 0x17bfdfbc00000000000000000000000000000000000000000000000000000000)
                    // add this address as parameter
                    mstore(0x04, receiver)
                    // call to collateral token
                    pop(call(gas(), cToken, 0x0, 0x0, 0x24, 0x0, 0x20))

                    // borrow balance smaller than amount available - use max
                    // otherwise, repay whatever is in the contract
                    if lt(mload(0x0), amount) { amount := MAX_UINT256 }
                }

                // selector for repayBorrowBehalf(address,uint256)
                mstore(ptr, 0x2608f81800000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x4), receiver) // user
                mstore(add(ptr, 0x24), amount) // to this address

                if iszero(
                    call(
                        gas(),
                        cToken,
                        0x0,
                        ptr, // input = empty for fallback
                        0x44, // input size = selector + address + uint256
                        ptr, // output
                        0x0 // output size = zero
                    )
                ) {
                    returndatacopy(0x0, 0, returndatasize())
                    revert(0x0, returndatasize())
                }
            }
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";

// solhint-disable max-line-length

/**
 * @notice Lending base contract that wraps Cmpound V3 markets
 */
abstract contract CompoundV3Lending is ERC20Selectors, Masks {
    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 1              | isBase                          |
     * | 77     | 20             | pool                            |
     */
    function _withdrawFromCompoundV3(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)
            // Compound V3 types need to trasfer collateral tokens

            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))

            let isBase := calldataload(add(currentOffset, 36))
            // receiver
            let receiver := shr(96, isBase)

            // adjust isBase flag

            let cometPool := shr(96, calldataload(add(currentOffset, 57)))

            currentOffset := add(currentOffset, 77)

            let amount := and(UINT120_MASK, amountData)
            if eq(amount, 0xffffffffffffffffffffffffffff) {
                switch and(UINT8_MASK, shr(88, isBase))
                case 0 {
                    // selector for userCollateral(address,address)
                    mstore(ptr, 0x2b92a07d00000000000000000000000000000000000000000000000000000000)
                    // add caller address as parameter
                    mstore(add(ptr, 0x04), callerAddress)
                    // add underlying address
                    mstore(add(ptr, 0x24), underlying)
                    // call to comet
                    pop(staticcall(gas(), cometPool, ptr, 0x44, ptr, 0x20))
                    // load the retrieved balance (lower 128 bits)
                    amount := and(UINT128_MASK, mload(ptr))
                }
                // comet.balanceOf(...) is lending token balance
                default {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add caller address as parameter
                    mstore(0x04, callerAddress)
                    // call to comet
                    pop(staticcall(gas(), cometPool, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    amount := mload(0x0)
                }
            }

            // selector withdrawFrom(address,address,address,uint256)
            mstore(ptr, 0x2644131800000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), callerAddress)
            mstore(add(ptr, 0x24), receiver)
            mstore(add(ptr, 0x44), underlying)
            mstore(add(ptr, 0x64), amount)
            // call pool
            if iszero(call(gas(), cometPool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | comet                           |
     */
    function _borrowFromCompoundV3(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptr := mload(0x40)
            // Compound V3 types need to trasfer collateral tokens
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))

            let cometPool := shr(96, calldataload(add(currentOffset, 56)))

            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)

            // selector withdrawFrom(address,address,address,uint256)
            mstore(ptr, 0x2644131800000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), callerAddress)
            mstore(add(ptr, 0x24), receiver)
            mstore(add(ptr, 0x44), underlying)
            mstore(add(ptr, 0x64), amount)
            // call pool
            if iszero(call(gas(), cometPool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | comet                           |
     */
    /// @notice Withdraw from lender lastgiven user address and lender Id
    function _depositToCompoundV3(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))
            // get comet
            let comet := shr(96, calldataload(add(currentOffset, 56)))
            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)
            // zero is this balance
            if iszero(amount) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amount := mload(0x0)
            }

            let ptr := mload(0x40)

            // selector supplyTo(address,address,uint256)
            mstore(ptr, 0x4232cd6300000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), receiver)
            mstore(add(ptr, 0x24), underlying)
            mstore(add(ptr, 0x44), amount)
            // call pool
            if iszero(call(gas(), comet, 0x0, ptr, 0x64, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | underlying                      |
     * | 20     | 16             | amount                          |
     * | 36     | 20             | receiver                        |
     * | 76     | 20             | comet                           |
     */
    function _repayToCompoundV3(uint256 currentOffset) internal returns (uint256) {
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // offset for amount at lower bytes
            let amountData := shr(128, calldataload(add(currentOffset, 20)))
            // receiver
            let receiver := shr(96, calldataload(add(currentOffset, 36)))
            // get comet
            let comet := shr(96, calldataload(add(currentOffset, 56)))
            currentOffset := add(currentOffset, 76)

            let amount := and(UINT120_MASK, amountData)
            switch amount
            case 0 {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amount := mload(0x0)
            }
            // repay maximum safely
            // comet will fail when using blind maxima if the contract has not
            // enough balance
            // to prevent this, we read the contract balance and user borrow balance and take the minimum
            case 0xffffffffffffffffffffffffffff {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amount := mload(0x0)

                // selector for borrowBalanceOf(address)
                mstore(0, 0x374c49b400000000000000000000000000000000000000000000000000000000)
                // add receiver as parameter
                mstore(0x04, receiver)
                // call to comet
                pop(staticcall(gas(), comet, 0x0, 0x24, 0x0, 0x20))
                let userBorrowBalance := mload(0x0)

                // amount greater than borrow balance -> use borrow balance
                // otherwise repay less than the borrow balance safely
                if gt(amount, userBorrowBalance) { amount := userBorrowBalance }
            }

            let ptr := mload(0x40)
            // selector supplyTo(address,address,uint256)
            mstore(ptr, 0x4232cd6300000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x04), receiver)
            mstore(add(ptr, 0x24), underlying)
            mstore(add(ptr, 0x44), amount)
            // call pool
            if iszero(call(gas(), comet, 0x0, ptr, 0x64, 0x0, 0x0)) {
                returndatacopy(0x0, 0x0, returndatasize())
                revert(0x0, returndatasize())
            }
        }

        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";

/**
 * @notice Lending base contract that wraps Morpho Blue
 */
abstract contract MorphoLending is ERC20Selectors, Masks {
    /// @dev Constant MorphoB address
    // address internal constant MORPHO_BLUE = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;

    /// @dev  position(...)
    bytes32 private constant MORPHO_POSITION = 0x93c5206200000000000000000000000000000000000000000000000000000000;

    /// @dev  market(...)
    bytes32 private constant MORPHO_MARKET = 0x5c60e39a00000000000000000000000000000000000000000000000000000000;

    /// @dev  repay(...)
    bytes32 private constant MORPHO_REPAY = 0x20b76e8100000000000000000000000000000000000000000000000000000000;

    /// @dev  supplyCollateral(...)
    bytes32 private constant MORPHO_SUPPLY_COLLATERAL = 0x238d657900000000000000000000000000000000000000000000000000000000;

    /// @dev  supply(...)
    bytes32 private constant MORPHO_SUPPLY = 0xa99aad8900000000000000000000000000000000000000000000000000000000;

    /// @dev  borrow(...)
    bytes32 private constant MORPHO_BORROW = 0x50d8cd4b00000000000000000000000000000000000000000000000000000000;

    /// @dev  withdrawCollateral(...)
    bytes32 private constant MORPHO_WITHDRAW_COLLATERAL = 0x8720316d00000000000000000000000000000000000000000000000000000000;

    /**
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | MarketParams.loanToken          |
     * | 20     | 20             | MarketParams.collateralToken    |
     * | 40     | 20             | MarketParams.oracle             |
     * | 60     | 20             | MarketParams.irm                |
     * | 80     | 16             | MarketParams.lltv               |
     * | 96     |  1             | Assets or Shares                |
     * | 97     | 15             | Amount (borrowAm)               |
     * | 112    | 20             | receiver                        |
     * | 132    | 20             | morpho                          | <-- we allow all morphos (incl forks)
     */
    function _morphoBorrow(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            // morpho should be the primary choice
            let ptr := mload(0x40)

            // borrow(...)
            mstore(ptr, MORPHO_BORROW)

            // market data
            mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
            mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
            mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
            mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm

            let lltvAndAmount := calldataload(add(currentOffset, 80))
            mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv

            let borrowAm := and(UINT112_MASK, lltvAndAmount)

            /**
             * check if it is by shares or assets
             */
            switch and(USE_SHARES_FLAG, lltvAndAmount)
            case 0 {
                mstore(add(ptr, 164), borrowAm) // assets
                mstore(add(ptr, 196), 0) // shares
            }
            default {
                mstore(add(ptr, 164), 0) // assets
                mstore(add(ptr, 196), borrowAm) // shares
            }

            // onbehalf
            mstore(add(ptr, 228), callerAddress) // onBehalfOf
            let lastBit := calldataload(add(currentOffset, 112))
            mstore(add(ptr, 260), shr(96, lastBit)) // receiver
            let morpho := shr(96, calldataload(add(currentOffset, 132)))

            currentOffset := add(currentOffset, 152)
            if iszero(
                call(
                    gas(),
                    morpho,
                    0x0,
                    ptr,
                    292, // = 9 * 32 + 4
                    0x0,
                    0x0 //
                )
            ) {
                let rdlen := returndatasize()
                returndatacopy(0, 0, rdlen)
                revert(0x0, rdlen)
            }
        }

        return currentOffset;
    }

    /**
     * This deposits LENDING TOKEN
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | MarketParams.loanToken          |
     * | 20     | 20             | MarketParams.collateralToken    |
     * | 40     | 20             | MarketParams.oracle             |
     * | 60     | 20             | MarketParams.irm                |
     * | 80     | 16             | MarketParams.lltv               |
     * | 96     |  1             | Assets or Shares                |
     * | 97     | 15             | Amount (depositAm)              |
     * | 112    | 20             | receiver                        |
     * | 132    | 20             | morpho                          | <-- we allow all morphos (incl forks)
     * | 152    | 2              | calldataLength                  |
     * | 154    | calldataLength | calldata                        |
     */
    function _encodeMorphoDeposit(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            let ptrBase := mload(0x40)
            let ptr := add(128, ptrBase)

            // loan token
            let token := shr(96, calldataload(currentOffset))

            // supply(...)
            mstore(ptr, MORPHO_SUPPLY)
            // market data
            mstore(add(ptr, 4), token) // MarketParams.loanToken
            mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
            mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
            mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm

            let lltvAndAmount := calldataload(add(currentOffset, 80))
            mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv
            let amountToDeposit := and(UINT112_MASK, lltvAndAmount)
            // increment for the amounts

            /**
             * check if it is by shares or assets
             */
            switch and(USE_SHARES_FLAG, lltvAndAmount)
            case 0 {
                /**
                 * if the amount is zero, we assume that the contract balance is deposited
                 */
                if iszero(amountToDeposit) {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to token
                    pop(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20))
                    // load the retrieved balance
                    amountToDeposit := mload(0x0)
                }

                mstore(add(ptr, 164), amountToDeposit) // assets
                mstore(add(ptr, 196), 0) // shares
            }
            default {
                mstore(add(ptr, 164), 0) // assets
                mstore(add(ptr, 196), amountToDeposit) // shares
            }

            // receiver address
            let receiver := shr(96, calldataload(add(currentOffset, 112)))

            let morpho := shr(96, calldataload(add(currentOffset, 132)))

            // get calldatalength
            let inputCalldataLength := and(UINT16_MASK, shr(240, calldataload(add(currentOffset, 152))))
            let calldataLength := inputCalldataLength

            currentOffset := add(currentOffset, 154)

            // leftover params
            mstore(add(ptr, 228), receiver) // onBehalfOf is the receiver here
            mstore(add(ptr, 260), 0x120) // offset
            // add calldata if needed
            if xor(0, calldataLength) {
                calldataLength := add(calldataLength, 20)
                mstore(add(ptr, 324), shl(96, callerAddress)) // caller
                calldatacopy(add(ptr, 344), currentOffset, inputCalldataLength) // calldata
                currentOffset := add(currentOffset, inputCalldataLength)
            }

            mstore(add(ptr, 292), calldataLength) // calldatalength

            if iszero(
                call(
                    gas(),
                    morpho,
                    0x0,
                    ptr,
                    add(calldataLength, 324), // = 10 * 32 + 4
                    0x0,
                    0x0 //
                )
            ) {
                let rdlen := returndatasize()
                returndatacopy(0, 0, rdlen)
                revert(0x0, rdlen)
            }
        }
        return currentOffset;
    }

    /**
     * This deposits COLLATERAL - never uses shares
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 20             | MarketParams.loanToken          |
     * | 20     | 20             | MarketParams.collateralToken    |
     * | 40     | 20             | MarketParams.oracle             |
     * | 60     | 20             | MarketParams.irm                |
     * | 80     | 16             | MarketParams.lltv               |
     * | 96     | 16             | Amount (depositAm)              |
     * | 112    | 20             | receiver                        |
     * | 132    | 20             | morpho                          | <-- we allow all morphos (incl forks)
     * | 152    | 2              | calldataLength                  |
     * | 154    | calldataLength | calldata                        |
     */
    function _encodeMorphoDepositCollateral(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            // use two memory ranges
            let ptrBase := mload(0x40)
            let ptr := add(256, ptrBase)

            // supplyCollateral(...)
            mstore(ptr, MORPHO_SUPPLY_COLLATERAL)
            mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken

            // get the collateral token and approve if needed
            let token := shr(96, calldataload(add(currentOffset, 20)))
            mstore(add(ptr, 36), token) // MarketParams.collateralToken
            mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
            mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
            let lltvAndAmount := calldataload(add(currentOffset, 80))
            mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv

            // we ignore flags as this only allows assets
            let amountToDeposit := and(UINT120_MASK, lltvAndAmount)

            /**
             * if the amount is zero, we assume that the contract balance is deposited
             */
            if iszero(amountToDeposit) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amountToDeposit := mload(0x0)
            }

            // receiver address
            let receiver := shr(96, calldataload(add(currentOffset, 112)))

            mstore(add(ptr, 164), amountToDeposit) // assets
            mstore(add(ptr, 196), receiver) // onBehalfOf
            mstore(add(ptr, 228), 0x100) // offset

            // get morpho
            let morpho := shr(96, calldataload(add(currentOffset, 132)))

            // get calldatalength
            let inputCalldataLength := and(UINT16_MASK, shr(240, calldataload(add(currentOffset, 152))))
            let calldataLength := inputCalldataLength
            currentOffset := add(currentOffset, 154)

            // add calldata if needed
            if xor(0, calldataLength) {
                calldataLength := add(calldataLength, 20)
                mstore(add(ptr, 292), shl(96, callerAddress)) // caller
                calldatacopy(add(ptr, 312), currentOffset, inputCalldataLength) // calldata
                currentOffset := add(currentOffset, inputCalldataLength)
            }

            mstore(add(ptr, 260), calldataLength) // calldatalength
            if iszero(
                call(
                    gas(),
                    morpho,
                    0x0,
                    ptr,
                    add(calldataLength, 292), // = 10 * 32 + 4
                    0x0,
                    0x0 //
                )
            ) {
                let rdlen := returndatasize()
                returndatacopy(0, 0, rdlen)
                revert(0x0, rdlen)
            }
        }
        return currentOffset;
    }

    /// @notice Withdraw collateral from Morpho Blue
    function _encodeMorphoWithdrawCollateral(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            // morpho should be the primary choice
            let ptr := mload(0x40)

            // withdrawCollateral(...)
            mstore(ptr, MORPHO_WITHDRAW_COLLATERAL)

            // market stuff

            mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
            mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
            mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
            mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm

            let lltvAndAmount := calldataload(add(currentOffset, 80))

            mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv

            mstore(add(ptr, 196), callerAddress) // onBehalfOf

            // store receiver
            mstore(add(ptr, 228), shr(96, calldataload(add(currentOffset, 112)))) // receiver
            // skip receiver in offset

            let morpho := shr(96, calldataload(add(currentOffset, 132)))

            // get amount, ignore flags
            lltvAndAmount := and(UINT120_MASK, lltvAndAmount)

            // technically not needed, hwoever, we keep it consistent
            // to withdraw all like this - maxUnit112 means read collateral balance
            if eq(lltvAndAmount, 0xffffffffffffffffffffffffffff) {
                let ptrBase := add(ptr, 280)
                let marketId := keccak256(add(ptr, 4), 160)
                // position datas (1st slot of return data is the user shares)
                mstore(ptrBase, MORPHO_POSITION)
                mstore(add(ptrBase, 0x4), marketId)
                mstore(add(ptrBase, 0x24), callerAddress)
                if iszero(staticcall(gas(), morpho, ptrBase, 0x44, ptrBase, 0x60)) { revert(0x0, 0x0) }
                lltvAndAmount := mload(add(ptrBase, 0x40))
            }

            // amount is stored last
            mstore(add(ptr, 164), lltvAndAmount) // assets

            currentOffset := add(currentOffset, 152)

            if iszero(
                call(
                    gas(),
                    morpho,
                    0x0,
                    ptr,
                    260, // = 8 * 32 + 4
                    0x0,
                    0x0 //
                )
            ) {
                let rdlen := returndatasize()
                returndatacopy(0, 0, rdlen)
                revert(0x0, rdlen)
            }
        }
        return currentOffset;
    }

    /// @notice Withdraw borrowAsset from Morpho
    function _encodeMorphoWithdraw(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        assembly {
            // morpho should be the primary choice
            let ptrBase := mload(0x40)
            let ptr := add(ptrBase, 256)

            // market data
            mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
            mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
            mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
            mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
            let lltvAndAmount := calldataload(add(currentOffset, 80))
            mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv

            let withdrawAm := and(UINT120_MASK, lltvAndAmount)

            mstore(add(ptr, 228), callerAddress) // onBehalfOf
            mstore(add(ptr, 260), shr(96, calldataload(add(currentOffset, 112)))) // receiver
            // get morpho
            let morpho := shr(96, calldataload(add(currentOffset, 132)))
            currentOffset := add(currentOffset, 152)

            /**
             * check if it is by shares or assets
             * 0 => by assets
             * 1 => by shares
             */
            switch and(USE_SHARES_FLAG, lltvAndAmount)
            case 0 {
                /**
                 * Withdraw amount variations
                 * type(uint120).max:    user supply balance
                 * other:                amount provided
                 */
                switch withdrawAm
                // maximum uint112 means withdraw everything
                case 0xffffffffffffffffffffffffffff {
                    // we need to fetch user shares and just withdraw all shares
                    // https://docs.morpho.org/morpho/tutorials/manage-positions/#repayAll

                    let marketId := keccak256(add(ptr, 4), 160)
                    // position datas (1st slot of return data is the user shares)
                    mstore(ptrBase, MORPHO_POSITION)
                    mstore(add(ptrBase, 0x4), marketId)
                    mstore(add(ptrBase, 0x24), callerAddress)
                    if iszero(staticcall(gas(), morpho, ptrBase, 0x44, ptrBase, 0x20)) { revert(0x0, 0x0) }
                    mstore(add(ptr, 164), 0) // assets
                    mstore(add(ptr, 196), mload(ptrBase)) // shares
                }
                // explicit amount
                default {
                    mstore(add(ptr, 164), withdrawAm) // assets
                    mstore(add(ptr, 196), 0) // shares
                }
            }
            default {
                mstore(add(ptr, 164), 0) // assets
                mstore(add(ptr, 196), withdrawAm) // shares
            }

            // withdraw(...)
            // we have to do it like this to override the selector only in this memory position
            mstore(sub(ptr, 28), 0x5c2bea49)
            if iszero(
                call(
                    gas(),
                    morpho,
                    0x0,
                    ptr,
                    292, // = 9 * 32 + 4
                    0x0,
                    0x0 //
                )
            ) {
                let rdlen := returndatasize()
                returndatacopy(0, 0, rdlen)
                revert(0x0, rdlen)
            }
        }
        return currentOffset;
    }

    /// @notice Repay to morpho blue
    function _morphoRepay(
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (
            // this will be returned as the offset, but initialized as lltvAndAmount
            // we use it here to avoid stack-too deep
            uint256 tempData
        )
    {
        assembly {
            // morpho should be the primary choice
            let ptrBase := mload(0x40)
            let ptr := add(ptrBase, 256)

            let token := shr(96, calldataload(currentOffset))
            // market data
            mstore(add(ptr, 4), token) // MarketParams.loanToken
            mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
            mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
            mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
            tempData := calldataload(add(currentOffset, 80))
            mstore(add(ptr, 132), shr(128, tempData)) // MarketParams.lltv

            let repayAm := and(UINT120_MASK, tempData)
            // skip amounts

            // receiver address
            let receiver := shr(96, calldataload(add(currentOffset, 112)))

            let morpho := shr(96, calldataload(add(currentOffset, 132)))

            /**
             *  if repayAmount is Max -> repay safe maximum (to prevent too low contract balance to revert)
             *  else if repayAmount is 0 -> repay contract balance as assets
             *  else repay amount as shares or assets, based on flag set
             */
            switch repayAm
            case 0xffffffffffffffffffffffffffff {
                // get the contract balance
                mstore(0x0, ERC20_BALANCE_OF)
                mstore(0x04, address())
                if iszero(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20)) { revert(0x0, 0x0) }
                // this is the maximum we can repay
                repayAm := mload(0x0)

                // by assets safe - will not revert if too much is repaid
                // we need to fetch everything and acrure interest
                // https://docs.morpho.org/morpho/tutorials/manage-positions/#repayAll

                // accrue interest
                // add accrueInterest (0x151c1ade)
                mstore(sub(ptr, 28), 0x151c1ade)
                if iszero(call(gas(), morpho, 0x0, ptr, 0xA4, 0x0, 0x0)) { revert(0x0, 0x0) }

                // get market params for conversion
                let marketId := keccak256(add(ptr, 4), 160)
                mstore(0x0, MORPHO_MARKET)
                mstore(0x4, marketId)
                if iszero(staticcall(gas(), morpho, 0x0, 0x24, ptrBase, 0x80)) { revert(0x0, 0x0) }
                let totalBorrowAssets := mload(add(ptrBase, 0x40))
                let totalBorrowShares := mload(add(ptrBase, 0x60))

                // position datas
                mstore(ptrBase, MORPHO_POSITION)
                mstore(add(ptrBase, 0x4), marketId)
                mstore(add(ptrBase, 0x24), receiver)
                if iszero(staticcall(gas(), morpho, ptrBase, 0x44, ptrBase, 0x40)) { revert(0x0, 0x0) }
                let userBorrowShares := mload(add(ptrBase, 0x20))

                // mulDivUp(shares, totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
                let maxAssets := add(totalBorrowShares, 1000000) // VIRTUAL_SHARES=1e6
                maxAssets :=
                    div(
                        add(
                            mul(userBorrowShares, add(totalBorrowAssets, 1)), // VIRTUAL_ASSETS=1
                            sub(maxAssets, 1) //
                        ),
                        maxAssets //
                    )

                // if maxAssets is greater than repay amount
                // we repay whatever is possible
                switch gt(maxAssets, repayAm)
                case 1 {
                    mstore(add(ptr, 164), repayAm) // assets
                    mstore(add(ptr, 196), 0) // shares
                }
                // otherwise, repay all shares, leaving no dust
                default {
                    mstore(add(ptr, 164), 0) // assets
                    mstore(add(ptr, 196), userBorrowShares) // shares
                }
            }
            // by balance (using assets)
            case 0 {
                // get balance
                mstore(0x0, ERC20_BALANCE_OF)
                mstore(0x04, address())
                if iszero(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20)) { revert(0x0, 0x0) }

                // use balance by assets
                mstore(add(ptr, 164), mload(0x0)) // assets
                mstore(add(ptr, 196), 0) // shares
            }
            // plain amount (assets or shares)
            default {
                switch and(USE_SHARES_FLAG, tempData)
                case 0 {
                    // by assets
                    mstore(add(ptr, 164), repayAm) // assets
                    mstore(add(ptr, 196), 0) // shares
                }
                default {
                    // by shares
                    mstore(add(ptr, 164), 0) // assets
                    mstore(add(ptr, 196), repayAm) // shares
                }
            }

            mstore(add(ptr, 228), receiver) // onBehalfOf is the receiver here
            mstore(add(ptr, 260), 0x120) // offset

            // get calldatalength
            let inputCalldataLength := and(UINT16_MASK, shr(240, calldataload(add(currentOffset, 152))))
            let calldataLength := inputCalldataLength
            currentOffset := add(currentOffset, 154)

            // add calldata if needed
            if xor(0, calldataLength) {
                calldataLength := add(calldataLength, 20)
                mstore(add(ptr, 324), shl(96, callerAddress)) // caller
                calldatacopy(add(ptr, 344), currentOffset, inputCalldataLength) // calldata
                currentOffset := add(currentOffset, inputCalldataLength)
            }

            // repay(...)
            // we have to do it like this to override the selector only in this memory position
            mstore(sub(ptr, 28), 0x20b76e81)
            mstore(add(ptr, 292), calldataLength) // calldatalength
            if iszero(
                call(
                    gas(),
                    morpho,
                    0x0,
                    ptr,
                    add(calldataLength, 324), // = 10 * 32 + 4
                    0x0,
                    0x0 //
                )
            ) {
                let rdlen := returndatasize()
                returndatacopy(0, 0, rdlen)
                revert(0x0, rdlen)
            }
        }
        return currentOffset;
    }
}

File 19 of 49 : UniversalLending.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {AaveLending} from "./AaveLending.sol";
import {CompoundV3Lending} from "./CompoundV3Lending.sol";
import {CompoundV2Lending} from "./CompoundV2Lending.sol";
import {MorphoLending} from "./MorphoLending.sol";
import {LenderIds, LenderOps} from "../enums/DeltaEnums.sol";
import {DeltaErrors} from "contracts/1delta/shared/errors/Errors.sol";

// solhint-disable max-line-length

/**
 * @notice Merge all lending ops in one operation
 * Can inject parameters
 * - paramPush for receiving funds (e.g. receiving funds from swaps or flash loans)
 * - paramPull for being required to pay an exact amount (e.g. DEX swap payments, flash loan amounts)
 */
abstract contract UniversalLending is AaveLending, CompoundV3Lending, CompoundV2Lending, MorphoLending, DeltaErrors {
    /**
     * execute ANY lending operation across various lenders
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 1              | lendingOperation                |
     * | 1      | 2              | lender                          |
     * | 3      | variable       | rest                            |
     */
    function _lendingOperations(
        address callerAddress,
        uint256 currentOffset // params similar to deltaComposeInternal
    )
        internal
        returns (uint256)
    {
        uint256 lendingOperation;
        uint256 lender;
        assembly {
            let slice := calldataload(currentOffset)
            lendingOperation := shr(248, slice)
            lender := and(UINT16_MASK, shr(232, slice))
            currentOffset := add(currentOffset, 3)
        }
        /**
         * Deposit collateral
         */
        if (lendingOperation == LenderOps.DEPOSIT) {
            if (lender < LenderIds.UP_TO_AAVE_V3) {
                return _depositToAaveV3(currentOffset);
            } else if (lender < LenderIds.UP_TO_AAVE_V2) {
                return _depositToAaveV2(currentOffset);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
                return _depositToCompoundV3(currentOffset);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
                return _depositToCompoundV2(currentOffset);
            } else {
                return _encodeMorphoDepositCollateral(currentOffset, callerAddress);
            }
        }
        /**
         * Borrow
         */
        else if (lendingOperation == LenderOps.BORROW) {
            if (lender < LenderIds.UP_TO_AAVE_V2) {
                return _borrowFromAave(currentOffset, callerAddress);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
                return _borrowFromCompoundV3(currentOffset, callerAddress);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
                return _borrowFromCompoundV2(currentOffset, callerAddress);
            } else {
                return _morphoBorrow(currentOffset, callerAddress);
            }
        }
        /**
         * Repay
         */
        else if (lendingOperation == LenderOps.REPAY) {
            if (lender < LenderIds.UP_TO_AAVE_V2) {
                return _repayToAave(currentOffset, callerAddress);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
                return _repayToCompoundV3(currentOffset);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
                return _repayToCompoundV2(currentOffset);
            } else {
                return _morphoRepay(currentOffset, callerAddress);
            }
        }
        /**
         * Withdraw collateral
         */
        else if (lendingOperation == LenderOps.WITHDRAW) {
            if (lender < LenderIds.UP_TO_AAVE_V2) {
                return _withdrawFromAave(currentOffset, callerAddress);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
                return _withdrawFromCompoundV3(currentOffset, callerAddress);
            } else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
                return _withdrawFromCompoundV2(currentOffset, callerAddress);
            } else {
                return _encodeMorphoWithdrawCollateral(currentOffset, callerAddress);
            }
        }
        /**
         * deposit lendingToken
         */
        else if (lendingOperation == LenderOps.DEPOSIT_LENDING_TOKEN) {
            return _encodeMorphoDeposit(currentOffset, callerAddress);
        }
        /**
         * withdraw lendingToken
         */
        else if (lendingOperation == LenderOps.WITHDRAW_LENDING_TOKEN) {
            return _encodeMorphoWithdraw(currentOffset, callerAddress);
        } else {
            _invalidOperation();
        }
    }
}

File 20 of 49 : Permits.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {PermitUtils} from "../../shared/permit/PermitUtils.sol";
import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {PermitIds} from "../enums/DeltaEnums.sol";

abstract contract Permits is Masks, PermitUtils, DeltaErrors {
    /*
     * | Offset | Length (bytes) | Description                     |
     * |--------|----------------|---------------------------------|
     * | 0      | 1              | permitOperation                 |
     * | 1      | 20             | asset                           |
     * | 21     | 2              | permitLength                    |
     * | 23     | permitLength   | data                            |
     */
    function _permit(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        uint256 permitOperation;
        address permitTarget;
        uint256 permitLength;
        uint256 permitOffset;
        assembly {
            let firstSlice := calldataload(currentOffset)
            permitOperation := shr(248, firstSlice)
            // this can be a token or morpho blue or compound V3 comet
            permitTarget := and(ADDRESS_MASK, shr(88, firstSlice))
            // calldata length
            permitLength := and(UINT16_MASK, shr(72, firstSlice))
            // increment offset
            permitOffset := add(currentOffset, 23)
            // increment offset
            currentOffset := add(permitOffset, permitLength)
        }
        if (permitOperation == PermitIds.TOKEN_PERMIT) {
            _tryPermit(permitTarget, permitOffset, permitLength, callerAddress);
            return currentOffset;
        } else if (permitOperation == PermitIds.AAVE_V3_CREDIT_PERMIT) {
            _tryCreditPermit(permitTarget, permitOffset, permitLength, callerAddress);
            return currentOffset;
        } else if (permitOperation == PermitIds.ALLOW_CREDIT_PERMIT) {
            _tryFlagBasedLendingPermit(permitTarget, permitOffset, permitLength, callerAddress);
            return currentOffset;
        } else {
            _invalidOperation();
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";

// solhint-disable max-line-length

/**
 * @notice Balancer V3 actions
 */
abstract contract BalancerV3VaultActions is Masks, DeltaErrors {
    // Balancer V3 selectors needed for executing a flash loan
    /// @notice UniV4 pendant: take()
    bytes32 private constant SEND_TO = 0xae63932900000000000000000000000000000000000000000000000000000000;
    /// @notice same selector string name as for UniV4, different params for balancer
    bytes32 private constant SETTLE = 0x15afd40900000000000000000000000000000000000000000000000000000000;

    constructor() {}

    function _encodeBalancerV3Take(uint256 currentOffset) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description         |
         * |--------|----------------|---------------------|
         * | 0      | 20             | manager             |
         * | 20     | 20             | asset               |
         * | 40     | 20             | receiver            |
         * | 60     | 16             | amount              |
         */
        assembly {
            let manager := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let asset := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let receiver := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let amount := shr(128, calldataload(currentOffset))

            // free memo ptr for populating the tx
            let ptr := mload(0x40)

            mstore(ptr, SEND_TO)
            mstore(add(ptr, 4), asset) // offset
            mstore(add(ptr, 36), receiver)
            mstore(add(ptr, 68), amount)

            if iszero(
                call(
                    gas(),
                    manager,
                    0x0,
                    ptr, //
                    100,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            // increment offset by amount length
            currentOffset := add(currentOffset, 16)
        }
        return currentOffset;
    }

    function _balancerV3Settle(uint256 currentOffset) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description       |
         * |--------|----------------|-------------------|
         * | 0      | 20             | manager           |
         * | 20     | 20             | asset             | <-- never native
         * | 40     | 16             | amountHint        |
         */
        assembly {
            let manager := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let asset := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let amountHint := shr(128, calldataload(currentOffset))

            // we can settle exactly for the credit as for the B3 logic
            if eq(amountHint, UINT128_MASK) { amountHint := MAX_UINT256 }

            currentOffset := add(16, currentOffset)

            let ptr := mload(0x40)
            // settle amount
            mstore(ptr, SETTLE)
            mstore(add(ptr, 4), asset)
            mstore(add(ptr, 36), amountHint)
            if iszero(
                call(
                    gas(),
                    manager,
                    0x0, // no native
                    ptr, //
                    68,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
        return currentOffset;
    }
}

File 22 of 49 : Gen2025DexActions.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";
import {UniswapV4SingletonActions} from "./UniswapV4Singleton.sol";
import {BalancerV3VaultActions} from "./BalancerV3Vault.sol";
import {SharedSingletonActions} from "./Shared.sol";

// solhint-disable max-line-length

/**
 * @notice Everything Uniswap V4 & Balancer V3, the major upgrades for DEXs in 2025
 */
abstract contract Gen2025DexActions is UniswapV4SingletonActions, BalancerV3VaultActions, SharedSingletonActions {
    function _gen2025DexActions(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        uint256 transferOperation;
        assembly {
            let firstSlice := calldataload(currentOffset)
            transferOperation := shr(248, firstSlice)
            currentOffset := add(currentOffset, 1)
        }
        if (transferOperation < Gen2025ActionIds.BAL_V3_TAKE) {
            if (transferOperation == Gen2025ActionIds.UNLOCK) {
                return _singletonUnlock(currentOffset, callerAddress);
            } else if (transferOperation == Gen2025ActionIds.UNI_V4_TAKE) {
                return _unoV4Take(currentOffset);
            } else if (transferOperation == Gen2025ActionIds.UNI_V4_SYNC) {
                return _unoV4Sync(currentOffset);
            } else if (transferOperation == Gen2025ActionIds.UNI_V4_SETTLE) {
                return _unoV4Settle(currentOffset);
            } else {
                _invalidOperation();
            }
        } else {
            if (transferOperation == Gen2025ActionIds.BAL_V3_TAKE) {
                return _encodeBalancerV3Take(currentOffset);
            } else if (transferOperation == Gen2025ActionIds.BAL_V3_SETTLE) {
                return _balancerV3Settle(currentOffset);
            } else {
                _invalidOperation();
            }
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {Masks} from "../../shared/masks/Masks.sol";
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";

// solhint-disable max-line-length

abstract contract SharedSingletonActions is Masks {
    // Uni V4 & Balancer V3 unlock
    bytes32 private constant UNLOCK = 0x48c8949100000000000000000000000000000000000000000000000000000000;
    // Hard-coded selector used for Balancer V3 callback
    /// @notice selector by bytes4(keccak256("balancerUnlockCallback(bytes)"))
    bytes32 private constant CB_SELECTOR = 0x480cf7ef00000000000000000000000000000000000000000000000000000000;

    /**
     * Here we need to add a selector deterministically as this function is Identical for B3 and U4
     */
    function _singletonUnlock(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description     |
         * |--------|----------------|-----------------|
         * | 0      | 20             | manager         |
         * | 20     | 2              | length          |
         * | 22     | length         | data            |
         */
        assembly {
            let manager := calldataload(currentOffset)
            let dataLength := and(UINT16_MASK, shr(80, manager))
            manager := shr(96, manager)

            // free memo ptr for populating the tx
            let ptr := mload(0x40)

            /**
             * We populate
             * manager.unlock(
             *  abi.encodeWithSelector(
             *      CB_SELECTOR,
             *      poolId, <- this is for validation purposes, we only allow correct uni V4s or balancer V3s
             *      data
             *   )
             * )
             */
            mstore(ptr, UNLOCK)
            mstore(add(ptr, 4), 0x20) // offset
            mstore(add(ptr, 36), add(dataLength, 88)) // selector, address, poolId, offset, length 4+32+32+20+1
            mstore(add(ptr, 68), CB_SELECTOR)
            mstore(add(ptr, 72), 0x20) // offset as for cb selector
            mstore(add(ptr, 104), add(dataLength, 20)) // length for within cb selector
            mstore(add(ptr, 136), shl(96, callerAddress))

            // increment by manager and dataLength
            currentOffset := add(currentOffset, 22)
            // copy calldata
            calldatacopy(add(ptr, 156), currentOffset, dataLength)
            if iszero(
                call(
                    gas(),
                    manager,
                    0x0,
                    ptr, //
                    add(dataLength, 156),
                    // selector, 2x offset, 2x length, data * address + uint8
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            // increment offset by data length
            currentOffset := add(currentOffset, dataLength)
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";

// solhint-disable max-line-length

/**
 * @notice Everything Uniswap V4 & Balancer V3, the major upgrades for DEXs in 2025
 */
abstract contract UniswapV4SingletonActions is Masks, DeltaErrors {
    // Uni V4 selectors needed for executing a flash loan
    bytes32 private constant TAKE = 0x0b0d9c0900000000000000000000000000000000000000000000000000000000;
    bytes32 private constant SETTLE = 0x11da60b400000000000000000000000000000000000000000000000000000000;
    bytes32 private constant SYNC = 0xa584119400000000000000000000000000000000000000000000000000000000;

    function _unoV4Take(uint256 currentOffset) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description         |
         * |--------|----------------|---------------------|
         * | 0      | 20             | manager             |
         * | 20     | 20             | asset               |
         * | 40     | 20             | receiver            |
         * | 60     | 16             | amount              |
         */
        assembly {
            let manager := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let asset := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let receiver := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let amount := shr(128, calldataload(currentOffset))

            // free memo ptr for populating the tx
            let ptr := mload(0x40)

            mstore(ptr, TAKE)
            mstore(add(ptr, 4), asset) // offset
            mstore(add(ptr, 36), receiver)
            mstore(add(ptr, 68), amount)

            if iszero(
                call(
                    gas(),
                    manager,
                    0x0,
                    ptr, //
                    100,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            // increment offset by amount length
            currentOffset := add(currentOffset, 16)
        }
        return currentOffset;
    }

    function _unoV4Sync(uint256 currentOffset) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description   |
         * |--------|----------------|---------------|
         * | 0      | 20             | manager       |
         * | 20     | 20             | asset         |
         */
        assembly {
            let manager := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let asset := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)

            mstore(0, SYNC)
            mstore(4, asset) // offset

            if iszero(
                call(
                    gas(),
                    manager,
                    0x0,
                    0, //
                    36,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
        return currentOffset;
    }

    function _unoV4Settle(uint256 currentOffset) internal returns (uint256) {
        /*
         * | Offset | Length (bytes) | Description       |
         * |--------|----------------|-------------------|
         * | 0      | 20             | manager           |
         * | 20     | 16             | nativeAmount      |
         */
        assembly {
            let manager := shr(96, calldataload(currentOffset))
            currentOffset := add(20, currentOffset)
            let amount := shr(128, calldataload(currentOffset))

            currentOffset := add(16, currentOffset)

            mstore(0, SETTLE)
            if iszero(
                call(
                    gas(),
                    manager,
                    amount,
                    0, //
                    4,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {DexTypeMappings} from "./dex/DexTypeMappings.sol";
import {V4TypeGeneric} from "./dex/V4Type.sol";
import {V3TypeGeneric} from "./dex/V3Type.sol";
import {V2TypeGeneric} from "./dex/V2Type.sol";
import {WooFiSwapper} from "./dex/WooFi.sol";
import {DodoV2Swapper} from "./dex/DodoV2Swapper.sol";
import {LBSwapper} from "./dex/LBSwapper.sol";
import {Wrapper} from "./dex/Wrapper.sol";
import {GMXSwapper} from "./dex/GMXSwapper.sol";
import {SyncSwapper} from "./dex/SyncSwapper.sol";
import {CurveSwapper} from "./dex/CurveSwapper.sol";
import {BalancerV2Swapper} from "./dex/BalancerV2Swapper.sol";
import {BalancerV3Swapper} from "./dex/BalancerV3Swapper.sol";

// solhint-disable max-line-length

/**
 * Core logic: Encode swaps as nested matrices (r: rows - multihops; c:columns - splits)
 * Every element in a matrix can be another matrix
 * The nesting stops at a (0,0) entry
 * E.g. a multihop with each hop having 2 splits is identified as
 * (1,0)  <-- 1 as row implicates a multihop of length 2 (max index is 1)
 *      \
 *      (0,1)  --------------- (0,1)   <- the 1s in the columns indicate that
 *        |                      |        there are 2 splits in each sub step
 *        ├(0,0)**               ├(0,0)
 *        |                      |
 *        ├(1,0)                 ├(0,0)  <- the (0,0) entries indicate an atomic swap (e.g. a uni V2 swap)
 *           \
 *          (0,0)  --- (0,0)  <- this is a branching multihip within a split (indicated by (1,0)
 *                               the output token is expected to be the same as for the
 *                               prior split in **
 *
 * The logic accumulates values per column to enable consistent multihops without additional balance reads
 *
 * Multihops progressively update value (in amount -> out amount) too always ensure that values
 * within sub splits ((x,0) or (0,y)) are correctly accumulated
 *
 * A case like (1,2) is a violation as we always demand a clear gruping of the branch
 * This is intuitive as we cannot have a split and a multihop at the same time.
 *
 * Every node with (x,0) is expected to have consistent multihop connections
 *
 * Every node with (0,y) is expected to have sub nodes and path that have all the same output currency
 *
 * Swap execution is always along rows then columns
 * In the example above, we indicate a multihop
 * Each hop has length 0 (single swap) but 1 split
 * Each split is a single swap (0,0)
 *
 * This allows arbitrary deep nesting of sub-routes and splits
 *
 * Rows are prioritized over columns.
 * /
 *
 * /**
 * @title Base swapper contract
 * @notice Contains basic logic for swap executions with DEXs
 * DEX Id layout:
 * 0 --- 100 : Self swappers (Uni V3, Curve, Clipper)
 * 100 - 255 : Funded swaps (Uni V2, Solidly, Moe,Joe LB, WooFI, GMX)
 *             Uni V2: 100 - 110
 *             Solidly:121 - 130
 */
abstract contract BaseSwapper is
    Wrapper,
    V4TypeGeneric,
    V3TypeGeneric,
    V2TypeGeneric,
    BalancerV3Swapper,
    BalancerV2Swapper,
    LBSwapper,
    DodoV2Swapper,
    WooFiSwapper,
    CurveSwapper,
    GMXSwapper, //
    SyncSwapper,
    DeltaErrors
{
    /*
     * multihop swapper that allows for splits in each hops
     * forward the amountOut received from the last hop
     */

    function _multihopSplitSwap(
        uint256 amountIn,
        uint256 swapMaxIndex,
        address tokenIn,
        address callerAddress,
        uint256 currentOffset //
    )
        internal
        returns (uint256 amount, uint256, address _tokenIn)
    {
        amount = amountIn;
        _tokenIn = tokenIn;
        uint256 i;
        while (true) {
            (amount, currentOffset, _tokenIn) = _singleSwapSplitOrRoute(
                amount,
                _tokenIn,
                callerAddress,
                currentOffset //
            );
            // break criteria
            if (i == swapMaxIndex) {
                break;
            } else {
                // update context
                assembly {
                    i := add(i, 1)
                }
            }
        }

        return (amount, currentOffset, _tokenIn);
    }

    /**
     * Ensure that all paths end with the same CCY
     * parallel swaps a->...->b; a->...->b for different dexs
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 0-16           | splits               |
     * | sC     | Variable       | datas                |
     *
     * `splits` looks like follows
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 1              | count                |
     * | 1      | 2*count - 1    | splits               | <- count = 0 means there is no data, otherwise uint16 splits
     *
     * `datas` looks like follows
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 2              | (r,c)                | <- indicates whether the swap is non-simple (further splits or hops)
     * | 2      | 1              | dexId                |
     * | 3      | variable       | params               | <- depends on dexId (fixed for each one)
     * | 3+v    | 2              | (r,c)                |
     * | 4+v    | 1              | dexId                |
     * | ...    | variable       | params               | <- depends on dexId (fixed for each one)
     * | ...    | ...            | ...                  | <- count + 1 times of repeating this pattern
     *
     * returns cumulative output, updated offset and nextToken address
     */
    function _singleSwapOrSplit(
        uint256 amountIn,
        uint256 splitsMaxIndex,
        address tokenIn,
        address callerAddress, // caller
        uint256 currentOffset
    )
        internal
        returns (uint256, uint256, address nextToken)
    {
        // no splits, single swap
        if (splitsMaxIndex == 0) {
            (amountIn, currentOffset, nextToken) = _singleSwapSplitOrRoute(
                amountIn,
                tokenIn, //
                callerAddress,
                currentOffset
            );
        } else {
            uint256 splits;
            assembly {
                splits := shr(128, calldataload(currentOffset))
                currentOffset := add(mul(2, splitsMaxIndex), currentOffset)
            }
            uint256 amount;
            uint256 i;
            uint256 swapsLeft = amountIn;
            while (true) {
                uint256 split;
                assembly {
                    switch eq(i, splitsMaxIndex)
                    case 1 {
                        // assign remaing amount to split
                        split := swapsLeft
                    }
                    default {
                        // splits are uint16s as share of uint16.max
                        split :=
                            div(
                                mul(
                                    and(
                                        UINT16_MASK,
                                        shr(sub(112, mul(i, 16)), splits) // read the uin16 in the splits sequence
                                    ),
                                    amountIn //
                                ),
                                UINT16_MASK //
                            )
                    }
                    i := add(i, 1)
                }
                uint256 received;
                // reenter-universal swap
                // can be another split or a multi-path
                (received, currentOffset, nextToken) = _singleSwapSplitOrRoute(
                    split,
                    tokenIn, //
                    callerAddress,
                    currentOffset
                );

                // increment and decrement
                assembly {
                    amount := add(amount, received)
                }

                // if nothing is left, break
                if (i > splitsMaxIndex) break;

                // otherwise, we decrement the swaps left amount
                assembly {
                    swapsLeft := sub(swapsLeft, split)
                }
            }
            amountIn = amount;
        }
        return (amountIn, currentOffset, nextToken);
    }

    /*
     * execute swap or split amounts
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 2              | (r,c)                |
     * | 2      | 20             | nextToken            |
     * | 22     | any            | swapData             |
     *
     * if r=0
     *      if c=0 : single swap
     *      else: split swap
     * else: multihop swap
     *
     * always return output amount, updated offset and nextToken address
     */
    function _singleSwapSplitOrRoute(
        uint256 amountIn,
        address tokenIn,
        address callerAddress,
        uint256 currentOffset //
    )
        internal
        returns (uint256 received, uint256, address nextToken)
    {
        uint256 swapMaxIndex;
        uint256 splitsMaxIndex;
        assembly {
            let datas := calldataload(currentOffset)
            swapMaxIndex := shr(248, datas)
            splitsMaxIndex := and(UINT8_MASK, shr(240, datas))
            currentOffset := add(currentOffset, 2)
        }
        // swapMaxIndex = 0 is simple single swap
        // that is where each single step MUST end
        if (swapMaxIndex == 0) {
            // splitsMaxIndex zero is single swap
            if (splitsMaxIndex == 0) {
                // if the swapMaxIndex is single-swap,
                // the next two addresses are nextToken and receiver
                address receiver;
                assembly {
                    nextToken := shr(96, calldataload(currentOffset))
                    currentOffset := add(currentOffset, 20)
                    receiver := shr(96, calldataload(currentOffset))
                    currentOffset := add(currentOffset, 20)
                }
                (received, currentOffset) = _swapExactInSimple(
                    amountIn,
                    tokenIn,
                    nextToken,
                    callerAddress,
                    receiver, //
                    currentOffset
                );
            } else {
                // nonzero is a split swap
                (received, currentOffset, nextToken) = _singleSwapOrSplit(
                    amountIn,
                    splitsMaxIndex,
                    tokenIn,
                    callerAddress,
                    currentOffset //
                );
            }
        } else {
            // otherwise, execute universal swap (path & splits)
            (received, currentOffset, nextToken) = _multihopSplitSwap(
                amountIn, //
                swapMaxIndex,
                tokenIn,
                callerAddress,
                currentOffset
            );
        }
        return (received, currentOffset, nextToken);
    }

    /**
     * Swaps exact in internally using all implemented Dexs
     * Will NOT use a flash swap
     * The dexId is assumed to be fetched before in a prefunding action
     * As such, the parameter can be plugged in here directly
     * @param amountIn sell amount
     * @return (amountOut, new offset) buy amount
     */
    function _swapExactInSimple(
        uint256 amountIn,
        address tokenIn,
        address tokenOut,
        address payer, // first step
        address receiver, // last step
        uint256 currentOffset
    )
        internal
        returns (uint256, uint256)
    {
        uint256 dexTypeId;
        assembly {
            dexTypeId := shr(248, calldataload(currentOffset))
            currentOffset := add(currentOffset, 1)
        }

        // First block: prioritize Uniswap
        if (dexTypeId <= 63) {
            if (dexTypeId == DexTypeMappings.UNISWAP_V3_ID) {
                return _swapUniswapV3PoolExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
            } else if (dexTypeId == DexTypeMappings.UNISWAP_V4_ID) {
                return _swapUniswapV4ExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
            } else if (dexTypeId == DexTypeMappings.UNISWAP_V2_ID) {
                return _swapUniswapV2PoolExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
            } else if (dexTypeId == DexTypeMappings.IZI_ID) {
                return _swapIZIPoolExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
            } else if (dexTypeId == DexTypeMappings.UNISWAP_V2_FOT_ID) {
                return _swapUniV2ExactInFOTGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
            }
        }
        // Second block: prioritize Curve
        else if (dexTypeId <= 127) {
            if (dexTypeId == DexTypeMappings.CURVE_V1_STANDARD_ID) {
                return _swapCurveGeneral(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.CURVE_RECEIVED_ID) {
                return _swapCurveReceived(tokenIn, amountIn, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.CURVE_FORK_ID) {
                return _swapCurveFork(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.WOO_FI_ID) {
                return _swapWooFiExactIn(amountIn, tokenIn, tokenOut, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.GMX_ID) {
                return _swapGMXExactIn(amountIn, tokenIn, tokenOut, receiver, payer, currentOffset);
            }
        }
        // Third block: prioritize Balancer
        else if (dexTypeId <= 191) {
            if (dexTypeId == DexTypeMappings.BALANCER_V2_ID) {
                return _swapBalancerV2ExactIn(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.BALANCER_V3_ID) {
                return _swapBalancerV3ExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
            } else if (dexTypeId == DexTypeMappings.LB_ID) {
                return _swapLBexactIn(amountIn, tokenIn, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.DODO_ID) {
                return _swapDodoV2ExactIn(amountIn, tokenIn, tokenOut, receiver, payer, currentOffset);
            } else if (dexTypeId == DexTypeMappings.SYNC_SWAP_ID) {
                return _swapSyncExactIn(amountIn, tokenIn, receiver, payer, currentOffset);
            }
        }
        // Rest: Rare wrap/unwrap operations
        else if (dexTypeId == DexTypeMappings.ASSET_WRAP_ID) {
            return _wrapperOperation(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
        }

        // If no match was found, revert
        assembly {
            mstore(0, INVALID_DEX)
            revert(0, 0x4)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

/**
 * Author: Achthar | 1delta
 * /*****************************************************************************
 */
import {DeltaErrors} from "../../../shared/errors/Errors.sol";
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";

/**
 * @title Uniswap V3 type callback implementations
 */
abstract contract V3Callbacker is ERC20Selectors {
    /**
     * This functione executes a simple transfer to shortcut the callback if there is no further calldata
     */
    function clSwapCallback(uint256 amountToPay, address tokenIn, address callerAddress, uint256 calldataLength) internal {
        assembly {
            // one can pass no path to continue
            // we then assume the calldataLength as flag to
            // indicate the pay type
            if lt(calldataLength, 2) {
                let ptr := mload(0x40)

                let success
                // transfer from caller
                switch calldataLength
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), caller())
                    mstore(add(ptr, 0x44), amountToPay)

                    success := call(gas(), tokenIn, 0, ptr, 0x64, ptr, 32)
                }
                // transfer plain
                default {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), caller())
                    mstore(add(ptr, 0x24), amountToPay)
                    success :=
                        call(
                            gas(),
                            tokenIn, // tokenIn, pool + 5x uint8 (i,j,s,a)
                            0,
                            ptr,
                            0x44,
                            ptr,
                            32
                        )
                }

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(ptr), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
                return(0, 0)
            }
        }
        _deltaComposeInternal(
            callerAddress,
            // the naive offset is 132
            // we skip the entire callback validation data
            // that is tokens (+40), fee (+2), caller (+20), forkId (+1) datalength (+2)
            // = 197
            197,
            calldataLength
        );
    }

    function _deltaComposeInternal(address callerAddress, uint256 offset, uint256 length) internal virtual {}
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

/**
 * This is to check whether the provided parameter is nonempty
 */
library ValidatorLib {
    function _hasData(bytes32 data) internal pure returns (bool hasData) {
        assembly {
            hasData := xor(0, data)
        }
    }

    function _hasAddress(address data) internal pure returns (bool hasData) {
        assembly {
            hasData := xor(0, data)
        }
    }
}

File 28 of 49 : BalancerV2Swapper.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title Balancer V2 swapper contract that uses Symmetric's vault
 * @notice Balancer V2 is fun (mostly)
 */
abstract contract BalancerV2Swapper is ERC20Selectors, Masks {
    /// @dev Balancer's single swap function
    bytes32 private constant BALANCER_SWAP = 0x52bbbe2900000000000000000000000000000000000000000000000000000000;

    /**
     * Swaps exact input on Balancer V2
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 32             | pool                 |
     * | 32     | 20             | vault                |
     * | 52     | 1              | pay flag             | <- 0: caller pays; 1: contract pays; greater: pre-funded
     */
    function _swapBalancerV2ExactIn(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        address receiver,
        address callerAddress,
        uint256 currentOffset //
    )
        internal
        returns (uint256 amountOut, uint256 balancerData)
    {
        assembly {
            // balancer vault plus pay flag
            balancerData := calldataload(add(32, currentOffset))

            let ptr := mload(0x40)
            // only need to check whether we have to pull from caller
            if iszero(and(UINT8_MASK, shr(88, balancerData))) {
                // selector for transferFrom(address,address,uint256)
                mstore(ptr, ERC20_TRANSFER_FROM)
                mstore(add(ptr, 0x04), callerAddress)
                mstore(add(ptr, 0x24), address())
                mstore(add(ptr, 0x44), amountIn)

                let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }

            let vault := shr(96, balancerData)

            {
                ////////////////////////////////////////////////////
                // Approve vault if needed
                ////////////////////////////////////////////////////
                mstore(0x0, tokenIn)
                mstore(0x20, 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3) // CALL_MANAGEMENT_APPROVALS
                mstore(0x20, keccak256(0x0, 0x40))
                mstore(0x0, vault)
                let key := keccak256(0x0, 0x40)
                // check if already approved
                if iszero(sload(key)) {
                    // selector for approve(address,uint256)
                    mstore(ptr, ERC20_APPROVE)
                    mstore(add(ptr, 0x04), vault)
                    mstore(add(ptr, 0x24), MAX_UINT256)
                    pop(call(gas(), tokenIn, 0, ptr, 0x44, ptr, 32))
                    sstore(key, 1)
                }
            }

            ////////////////////////////////////////////////////
            // Execute swap function on B2 Vault
            ////////////////////////////////////////////////////
            mstore(ptr, BALANCER_SWAP)
            mstore(add(ptr, 0x4), 0xe0) // FundManagement struct
            mstore(add(ptr, 0x24), address()) // sender
            mstore(add(ptr, 0x44), 0) // fromInternalBalance
            mstore(add(ptr, 0x64), receiver) // receiver
            mstore(add(ptr, 0x84), 0) // toInternalBalance
            mstore(add(ptr, 0xA4), 0) // limit
            mstore(add(ptr, 0xC4), MAX_UINT256) // deadline
            mstore(add(ptr, 0xE4), calldataload(currentOffset)) // poolId
            mstore(add(ptr, 0x104), 0) // swapKind = GIVEN_IN
            mstore(add(ptr, 0x124), tokenIn) // assetIn
            mstore(add(ptr, 0x144), tokenOut) // assetOut
            mstore(add(ptr, 0x164), amountIn) // amount
            mstore(add(ptr, 0x184), 0xC0) // offest
            mstore(add(ptr, 0x1A4), 0) // userData length

            if iszero(
                call(
                    gas(),
                    vault,
                    0x0,
                    ptr,
                    0x1C4,
                    0x0,
                    0x20 // we use the return amount
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            amountOut := mload(0x0)
            balancerData := add(53, currentOffset)
        }
        return (amountOut, balancerData);
    }
}

File 29 of 49 : BalancerV3Swapper.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

// solhint-disable max-line-length

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title Balancer V3 type swapper contract
 * @notice Can only be executed within `manager.unlock()`
 * Ergo, if Balancer v3 is in the path (no matter how many times), one has to push
 * the swap data and execution into the BalancerV3.unlock
 * This should be usable together with flash loans from their singleton
 *
 * Can unlock arbitrary times times!
 *
 * The execution of a swap follows the steps:
 *
 * 1) pm.unlock(...) (outside of this contract)
 * 2) call pm.swap(...)
 * 3) call pm.settle to settle the swap (no native accepted)
 *
 */
abstract contract BalancerV3Swapper is ERC20Selectors, Masks {
    /**
     * We need all these selectors for executing a single swap
     */
    bytes32 private constant SWAP = 0x2bfb780c00000000000000000000000000000000000000000000000000000000;
    /// @notice same selector string name as for UniV4, different params for balancer
    bytes32 private constant SETTLE = 0x15afd40900000000000000000000000000000000000000000000000000000000;
    /// @notice pull funds from vault with this
    bytes32 private constant SEND_TO = 0xae63932900000000000000000000000000000000000000000000000000000000;

    constructor() {}

    /*
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 20             | manager              |
     * | 40     | 1              | payFlag              |
     * | 41     | 2              | calldataLength       | <- this here might be pool-dependent, cannot be used as flag
     * | 43     | calldataLength | calldata             |
     */
    function _swapBalancerV3ExactInGeneric(
        uint256 fromAmount,
        address tokenIn,
        address tokenOut,
        address receiver,
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (
            uint256 receivedAmount,
            // similar to other implementations, we use this temp variable
            // to avoid stackToo deep
            uint256 tempVar
        )
    {
        // enum SwapKind {
        //     EXACT_IN,
        //     EXACT_OUT
        // }
        // struct VaultSwapParams {
        //     SwapKind kind; 4
        //     address pool; 36
        //     IERC20 tokenIn; 68
        //     IERC20 tokenOut; 100
        //     uint256 amountGivenRaw; 132
        //     uint256 limitRaw; 164
        //     bytes userData; (196, 228, 260 - X)
        // }
        ////////////////////////////////////////////
        // This is the function selector we need
        ////////////////////////////////////////////
        // function swap(
        //     VaultSwapParams memory vaultSwapParams
        // )
        //     external
        //     returns (uint256 amountCalculated, uint256 amountIn, uint256 amountOut)

        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            // read the hook address and insta store it to keep stack smaller
            mstore(add(ptr, 132), shr(96, calldataload(currentOffset)))
            let pool := shr(96, calldataload(currentOffset))
            // skip hook
            currentOffset := add(currentOffset, 20)
            // read the pool address
            let vault := calldataload(currentOffset)
            // skip vault plus params
            currentOffset := add(currentOffset, 23)

            // pay flag
            tempVar := and(UINT8_MASK, shr(88, vault))
            let clLength := and(UINT16_MASK, shr(72, vault))
            vault := shr(96, vault)
            // Prepare external call data
            // Store swap selector
            mstore(ptr, SWAP)
            mstore(add(ptr, 4), 0)
            mstore(add(ptr, 36), pool)
            mstore(add(ptr, 68), tokenIn)
            mstore(add(ptr, 100), tokenOut)
            mstore(add(ptr, 132), fromAmount)
            mstore(add(ptr, 164), 1)
            mstore(add(ptr, 196), 0xe0)
            mstore(add(ptr, 228), clLength)

            if xor(0, clLength) {
                // Store further calldata for the pool
                calldatacopy(add(ptr, 260), currentOffset, clLength)
                currentOffset := add(currentOffset, clLength)
            }
            // Perform the external 'swap' call
            if iszero(call(gas(), vault, 0, ptr, add(260, clLength), ptr, 0x60)) {
                returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                revert(0, returndatasize()) // Revert with the error message
            }

            // get real amounts
            fromAmount := mload(add(ptr, 0x20))
            receivedAmount := mload(add(ptr, 0x40))

            /**
             * Pull funds to receiver
             */
            mstore(ptr, SEND_TO)
            mstore(add(ptr, 4), tokenOut) //
            mstore(add(ptr, 36), receiver)
            mstore(add(ptr, 68), receivedAmount)

            if iszero(
                call(
                    gas(),
                    vault,
                    0x0,
                    ptr, //
                    100,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            /**
             * If the pay mode is >=3, we assume deferred payment
             * This means that the composer must manually settle
             * for the input amount
             * Warning: This should not be done for pools with
             * arbitrary hooks as these can have cases where
             * `amountIn` selected != actual `amountIn`
             */
            if lt(tempVar, 2) {
                let success
                switch tempVar
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), vault)
                    mstore(add(ptr, 0x44), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                }
                // transfer plain
                case 1 {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), vault)
                    mstore(add(ptr, 0x24), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                }

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }

                /**
                 * Settle funds in vault
                 */

                // settle amount
                mstore(ptr, SETTLE)
                mstore(add(ptr, 4), tokenIn)
                mstore(add(ptr, 36), fromAmount)
                if iszero(
                    call(
                        gas(),
                        vault,
                        0x0, // no native
                        ptr,
                        68,
                        // selector, offset, length, data
                        0x0, // output = empty
                        0x0 // output size = zero
                    )
                ) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
        }
        return (receivedAmount, currentOffset);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

// solhint-disable max-line-length

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title Curve swapper contract
 * @notice We do Curve stuff here
 */
abstract contract CurveSwapper is ERC20Selectors, Masks {
    // approval slot
    bytes32 private constant CALL_MANAGEMENT_APPROVALS = 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3;

    /**
     * Standard curve pool selectors
     */

    ////////////////////////////////////////////////////
    // General info on the selectors for Curve:
    // There are 5 variations
    // 1) indexes as int128
    // 2) indexes as uint256
    // 3) has receiver
    // 4) has no receiver
    // 5) fork with solidity implementation (typically uint8 indexes)
    // The int128 indexes are preferred and have lower indexes
    // The ones with receiver have even indexes
    ////////////////////////////////////////////////////

    /// @notice selector exchange(int128,int128,uint256,uint256)
    bytes32 private constant EXCHANGE_INT = 0x3df0212400000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange(int128,int128,uint256,uint256,address)
    bytes32 private constant EXCHANGE_INT_WITH_RECEIVER = 0xddc1f59d00000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange(uint256,uint256,uint256,uint256)
    bytes32 private constant EXCHANGE = 0x5b41b90800000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange(uint256,uint256,uint256,uint256,address)
    bytes32 private constant EXCHANGE_WITH_RECEIVER = 0xa64833a000000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange_underlying(uint256,uint256,uint256,uint256)
    bytes32 private constant EXCHANGE_UNDERLYING = 0x65b2489b00000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange_underlying(uint256,uint256,uint256,uint256,address)
    bytes32 private constant EXCHANGE_UNDERLYING_WITH_RECEIVER = 0xe2ad025a00000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange_underlying(uint256,uint256,uint256,uint256)
    bytes32 private constant EXCHANGE_UNDERLYING_INT = 0xa6417ed600000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange_underlying(uint256,uint256,uint256,uint256,address)
    bytes32 private constant EXCHANGE_UNDERLYING_INT_WITH_RECEIVER = 0x44ee198600000000000000000000000000000000000000000000000000000000;

    ////////////////////////////////////////////////////
    // General info on the selectors for Curve Received:
    // They are pre-funded
    // 1) indexes as int128 (NG) or uint256 (Some of the TriCryptos) as above
    // 2) has always a receiver (optinally could be called without it, bet there is no utility for it)
    ////////////////////////////////////////////////////

    /// @notice selector exchange_received(uint256,uint256,uint256,uint256)
    bytes32 private constant EXCHANGE_RECEIVED = 0x29b244bb00000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange_received(uint256,uint256,uint256,uint256,address)
    bytes32 private constant EXCHANGE_RECEIVED_WITH_RECEIVER = 0x767691e700000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange(int128,int128,uint256,uint256)
    bytes32 private constant EXCHANGE_RECEIVED_INT = 0x7e3db03000000000000000000000000000000000000000000000000000000000;

    /// @notice selector exchange_received(int128,int128,uint256,uint256,address)
    bytes32 private constant EXCHANGE_RECEIVED_INT_WITH_RECEIVER = 0xafb4301200000000000000000000000000000000000000000000000000000000;

    /// @notice selector for cuve forks using solidity swap(uint8,uint8,uint256,uint256,uint256)
    bytes32 private constant SWAP = 0x9169558600000000000000000000000000000000000000000000000000000000;

    function _fundAndApproveIfNeeded(address callerAddress, address tokenIn, uint256 amount, uint256 data) private returns (address pool) {
        assembly {
            let ptr := mload(0x40)
            pool := shr(96, data)
            mstore(0x0, tokenIn)
            mstore(0x20, CALL_MANAGEMENT_APPROVALS)
            mstore(0x20, keccak256(0x0, 0x40))
            mstore(0x0, pool)
            let key := keccak256(0x0, 0x40)
            // check if already approved
            if iszero(sload(key)) {
                // approveFlag
                // selector for approve(address,uint256)
                mstore(ptr, ERC20_APPROVE)
                mstore(add(ptr, 0x04), pool)
                mstore(add(ptr, 0x24), MAX_UINT256)
                pop(
                    call(
                        gas(),
                        tokenIn, //
                        0,
                        ptr,
                        0x44,
                        ptr,
                        32
                    )
                )
                sstore(key, 1)
            }
            if iszero(and(UINT16_MASK, shr(56, data))) {
                // selector for transferFrom(address,address,uint256)
                mstore(ptr, ERC20_TRANSFER_FROM)
                mstore(add(ptr, 0x04), callerAddress)
                mstore(add(ptr, 0x24), address())
                mstore(add(ptr, 0x44), amount)

                let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
        }
    }

    /**
     * Swaps using a standard curve pool
     * Data is supposed to be packed as follows
     * tokenIn | actionId | dexId | pool | i | j | sm | tokenOut
     * sm is the selector,
     * i,j are the swap indexes for the pool
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | i                    |
     * | 21     | 1              | j                    |
     * | 22     | 1              | sm                   |
     * | 23     | 2              | payMode              | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
     */
    function _swapCurveGeneral(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        address receiver, //
        address callerAddress,
        uint256 currentOffset
    )
        internal
        returns (
            uint256 amountOut,
            // curve data is a transient memory variable to
            // avoid stack too deep errors
            uint256 curveData
        )
    {
        address pool;
        assembly {
            curveData := calldataload(currentOffset)
        }
        // extract pool
        pool = _fundAndApproveIfNeeded(
            callerAddress,
            tokenIn,
            amountIn,
            curveData // use generic data
        );
        assembly {
            let ptr := mload(0x40)

            // consistent params not overlapping with 32 bytes from selector
            mstore(add(ptr, 0x24), and(shr(80, curveData), UINT8_MASK))
            mstore(add(ptr, 0x44), amountIn)
            mstore(add(ptr, 0x64), 0) // min out

            let success
            ////////////////////////////////////////////////////
            // Execute swap function
            ////////////////////////////////////////////////////
            switch and(shr(72, curveData), UINT8_MASK)
            // selectorId
            case 0 {
                // selector for exchange(int128,int128,uint256,uint256,address)
                mstore(ptr, EXCHANGE_INT_WITH_RECEIVER)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                mstore(add(ptr, 0x84), receiver) // receiver, set curveData accordingly
                success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
                curveData := 0
            }
            case 1 {
                // selector for exchange(int128,int128,uint256,uint256)
                mstore(ptr, EXCHANGE_INT)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
                curveData := MAX_UINT256
            }
            case 2 {
                // selector for exchange(uint256,uint256,uint256,uint256,address)
                mstore(ptr, EXCHANGE_WITH_RECEIVER)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                mstore(add(ptr, 0x84), receiver) // receiver, set curveData accordingly
                success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
                curveData := 0
            }
            case 3 {
                // selector for exchange(uint256,uint256,uint256,uint256)
                mstore(ptr, EXCHANGE)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))

                success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
                curveData := MAX_UINT256
            }
            case 4 {
                // selector for exchange_underlying(int128,int128,uint256,uint256,address)
                mstore(ptr, EXCHANGE_UNDERLYING_INT_WITH_RECEIVER)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                mstore(add(ptr, 0x84), receiver)
                success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
                curveData := 0
            }
            case 5 {
                // selector for exchange_underlying(int128,int128,uint256,uint256)
                mstore(ptr, EXCHANGE_UNDERLYING_INT)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
                curveData := MAX_UINT256
            }
            case 6 {
                // selector for exchange_underlying(uint256,uint256,uint256,uint256,address)
                mstore(ptr, EXCHANGE_UNDERLYING_WITH_RECEIVER)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                mstore(add(ptr, 0x84), receiver)
                success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
                curveData := 0
            }
            case 7 {
                // selector for exchange_underlying(uint256,uint256,uint256,uint256)
                mstore(ptr, EXCHANGE_UNDERLYING)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
                curveData := MAX_UINT256
            }
            case 200 {
                // selector for swap(uint8,uint8,uint256,uint256,uint256)
                mstore(ptr, SWAP)
                mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
                mstore(add(ptr, 0x84), MAX_UINT256) // deadline
                success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
                curveData := MAX_UINT256
            }
            default { revert(0, 0) }

            if iszero(success) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            amountOut := mload(ptr)

            ////////////////////////////////////////////////////
            // Send funds to receiver if needed
            // curveData is now the flag for manually
            // transferuing to the receiver
            ////////////////////////////////////////////////////
            if and(curveData, xor(receiver, address())) {
                // selector for transfer(address,uint256)
                mstore(ptr, ERC20_TRANSFER)
                mstore(add(ptr, 0x04), receiver)
                mstore(add(ptr, 0x24), amountOut)
                success := call(gas(), tokenOut, 0, ptr, 0x44, 0, 32)

                let rdsize := returndatasize()

                // Check for ERC20 success. ERC20 tokens should return a boolean,
                // but some don't. We accept 0-length return data as success, or at
                // least 32 bytes that starts with a 32-byte boolean true.
                success :=
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )

                if iszero(success) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
            curveData := add(currentOffset, 25)
        }
        return (amountOut, curveData);
    }

    /**
     * Swaps using a standard curve pool
     * Data is supposed to be packed as follows
     * tokenIn | actionId | dexId | pool | i | j | sm | tokenOut
     * sm is the selector,
     * i,j are the swap indexes for the pool
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | i                    |
     * | 21     | 1              | j                    |
     * | 22     | 1              | sm                   |
     * | 23     | 2              | payMode              | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
     */
    function _swapCurveFork(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        address receiver, //
        address callerAddress,
        uint256 currentOffset
    )
        internal
        returns (
            uint256 amountOut,
            // curve data is a transient memory variable to
            // avoid stack too deep errors
            uint256 curveData
        )
    {
        address pool;
        assembly {
            curveData := calldataload(currentOffset)
        }
        // extract pool
        pool = _fundAndApproveIfNeeded(
            callerAddress,
            tokenIn,
            amountIn,
            curveData // use generic data
        );
        assembly {
            let ptr := mload(0x40)

            mstore(0x0, ERC20_BALANCE_OF)
            mstore(0x4, address())
            // call to token
            pop(
                staticcall(
                    gas(),
                    tokenOut, // token
                    0x0,
                    0x24,
                    ptr, // use ptr here so that we don't override the scrap space
                    0x20
                )
            )

            amountOut := mload(ptr)

            ////////////////////////////////////////////////////
            // Execute swap function
            ////////////////////////////////////////////////////

            // get selector
            switch and(shr(72, curveData), UINT8_MASK)
            // selectorId
            case 3 {
                // selector for exchange(int128,int128,uint256,uint256,address)
                mstore(ptr, EXCHANGE)
            }
            case 5 {
                // selector for exchange(int128,int128,uint256,uint256)
                mstore(ptr, EXCHANGE_UNDERLYING)
            }
            default { revert(0, 0) }

            mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
            mstore(add(ptr, 0x24), and(shr(80, curveData), UINT8_MASK))
            mstore(add(ptr, 0x44), amountIn)
            mstore(add(ptr, 0x64), 0) // min out
            if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            // call to token - note that 0x-0x24 still holds the respective calldata
            pop(
                staticcall(
                    gas(),
                    tokenOut, // token
                    0x0,
                    0x24,
                    0x0, // output to ptr
                    0x20
                )
            )
            // load the retrieved balance
            amountOut := sub(mload(0x0), amountOut)

            ////////////////////////////////////////////////////
            // Send funds to receiver if needed
            ////////////////////////////////////////////////////
            if xor(receiver, address()) {
                // selector for transfer(address,uint256)
                mstore(ptr, ERC20_TRANSFER)
                mstore(add(ptr, 0x04), receiver)
                mstore(add(ptr, 0x24), amountOut)
                let success :=
                    call(
                        gas(),
                        tokenOut, // tokenIn, pool + 5x uint8 (i,j,s,a)
                        0,
                        ptr,
                        0x44,
                        ptr,
                        32
                    )

                let rdsize := returndatasize()

                // Check for ERC20 success. ERC20 tokens should return a boolean,
                // but some don't. We accept 0-length return data as success, or at
                // least 32 bytes that starts with a 32-byte boolean true.
                success :=
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(ptr), 1) // starts with uint256(1)
                            )
                        )
                    )

                if iszero(success) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
            curveData := add(currentOffset, 25)
        }
        return (amountOut, curveData);
    }

    /**
     * Swaps using a NG pool that allows for pre-funded swaps
     * Data is supposed to be packed as follows
     * tokenIn | actionId | dexId | pool | sm | i | j | tokenOut
     * sm is the selector,
     * i,j are the swap indexes for the pool
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | i                    |
     * | 21     | 1              | j                    |
     * | 22     | 1              | sm                   |
     * | 23     | 2              | payMode              | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
     */
    function _swapCurveReceived(
        address tokenIn,
        uint256 amountIn,
        address receiver, //
        address callerAddress,
        uint256 currentOffset
    )
        internal
        returns (
            // assign payFlag then amountOut
            uint256 payFlagAmountOut,
            uint256 curveData
        )
    {
        assembly {
            let ptr := mload(0x40)
            curveData := calldataload(currentOffset)

            let pool := shr(96, curveData)

            payFlagAmountOut := and(UINT16_MASK, shr(56, curveData))
            if lt(payFlagAmountOut, 2) {
                let success
                switch payFlagAmountOut
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), pool)
                    mstore(add(ptr, 0x44), amountIn)

                    success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                }
                // transfer plain
                case 1 {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), pool)
                    mstore(add(ptr, 0x24), amountIn)
                    success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                }

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
            ////////////////////////////////////////////////////
            // Execute swap function
            ////////////////////////////////////////////////////
            switch and(shr(72, curveData), UINT8_MASK)
            case 0 {
                // selector for exchange_received(int128,int128,uint256,uint256,address)
                mstore(ptr, EXCHANGE_RECEIVED_INT_WITH_RECEIVER)
            }
            case 2 {
                // selector for exchange_received(uint256,uint256,uint256,uint256,address)
                mstore(ptr, EXCHANGE_RECEIVED_WITH_RECEIVER)
            }
            default { revert(0, 0) }

            mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK)) // indexIn
            mstore(add(ptr, 0x24), and(shr(80, curveData), UINT8_MASK)) // indexOut
            mstore(add(ptr, 0x44), amountIn)
            mstore(add(ptr, 0x64), 0) // min out
            mstore(add(ptr, 0x84), receiver)
            if iszero(
                call(
                    gas(),
                    pool, //
                    0x0,
                    ptr,
                    0xA4,
                    0,
                    0x20
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            payFlagAmountOut := mload(0)
            curveData := add(currentOffset, 25)
        }
        return (payFlagAmountOut, curveData);
    }
}

File 31 of 49 : DexTypeMappings.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

library DexTypeMappings {
    /**
     * First Block: Blue Chip DEXs (1)
     */
    uint256 internal constant UNISWAP_V3_ID = 0;
    uint256 internal constant UNISWAP_V2_ID = 1;
    uint256 internal constant UNISWAP_V4_ID = 2;
    uint256 internal constant IZI_ID = 5;
    uint256 internal constant UNISWAP_V2_FOT_ID = 3;

    /**
     * Second Block: Blue Chip DEXs (2): all DEX that behave like curve
     */
    // indexs as input
    // returns out amount
    uint256 internal constant CURVE_V1_STANDARD_ID = 64;
    // curve NG
    uint256 internal constant CURVE_RECEIVED_ID = 65;
    // almost like curve, but slight different implementation,
    // e.g. the function returns no output
    uint256 internal constant CURVE_FORK_ID = 66;

    uint256 internal constant WOO_FI_ID = 80;

    // GMXs (rather rare)
    uint256 internal constant GMX_ID = 90;
    uint256 internal constant KTX_ID = 91;

    /**
     * Third Block: Blue Chips (3): Balancers
     */
    uint256 internal constant BALANCER_V2_ID = 128;
    uint256 internal constant BALANCER_V3_ID = 129;

    // LFM/LFJ LB
    uint256 internal constant LB_ID = 140;

    // more exotics
    uint256 internal constant DODO_ID = 150;
    uint256 internal constant SYNC_SWAP_ID = 160;

    /**
     * Fourth Block: Wrappers
     */
    // wrappers
    uint256 internal constant ERC4626_ID = 253;
    uint256 internal constant ASSET_WRAP_ID = 254;
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

// solhint-disable max-line-length

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title DodoV2 swapper contract
 */
abstract contract DodoV2Swapper is ERC20Selectors, Masks {
    /**
     * We need this to avoid stack too deep in the overall context
     * if `clLength<3` no flash loan will be executed,
     * othewise, we skip the funding transfers
     * 0 is pulling from caller
     * 1 is transferring from contract
     * 2 is pre-funded (meaning no transfers here)
     *
     */
    function _dodoPrepare(
        uint256 amountIn,
        address tokenIn,
        address callerAddress,
        uint256 currentOffset
    )
        private
        returns (uint256 dodoData, address pool, uint256 clLength)
    {
        assembly {
            dodoData := calldataload(currentOffset)
            pool := shr(96, dodoData)

            clLength := and(UINT16_MASK, shr(56, dodoData))

            let ptr := mload(0x40)
            // less than 2: funding via token transfers
            if lt(clLength, 2) {
                let success
                switch clLength
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), pool)
                    mstore(add(ptr, 0x44), amountIn)
                    success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                }
                // transfer plain
                case 1 {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), pool)
                    mstore(add(ptr, 0x24), amountIn)
                    success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                }

                let rdsize := returndatasize()
                // Check for ERC20 success. ERC20 tokens should return a boolean,
                // but some don't. We accept 0-length return data as success, or at
                // least 32 bytes that starts with a 32-byte boolean true.
                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
        }
    }

    /**
     * Swaps exact input on Dodo V2
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | sellQuote            |
     * | 21     | 2              | pId                  | pool index for flash validation
     * | 22     | 2              | clLength / pay flag  | <- 0: caller pays; 1: contract pays; greater: pre-funded
     * | 25     | clLength       | calldata             | calldata for fash loan
     */
    function _swapDodoV2ExactIn(
        uint256 amountIn,
        address tokenIn,
        address tokenOut,
        address receiver,
        address callerAddress,
        uint256 currentOffset
    )
        internal
        returns (uint256 amountOut, uint256 clLength)
    {
        address pool;
        (amountOut, pool, clLength) = _dodoPrepare(
            amountIn,
            tokenIn,
            callerAddress, //
            currentOffset
        );
        assembly {
            let ptr := mload(0x40)
            // if it is a spot swap, it is already funded
            switch lt(clLength, 3)
            case 1 {
                // determine selector
                switch and(UINT8_MASK, shr(88, amountOut))
                case 0 {
                    // sellBase
                    mstore(0x0, 0xbd6015b400000000000000000000000000000000000000000000000000000000)
                }
                default {
                    // sellQuote
                    mstore(0x0, 0xdd93f59a00000000000000000000000000000000000000000000000000000000)
                }
                mstore(0x4, receiver)
                // call swap, revert if invalid/undefined pair
                if iszero(call(gas(), pool, 0x0, 0x0, 0x24, 0x0, 0x20)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                // the swap call returns the output amount directly
                amountOut := mload(0x0)
                // increment offset
                currentOffset := add(25, currentOffset)
            }
            // otherwise, execute flash loan
            default {
                let ptrAfter := add(ptr, 256)

                /**
                 * Similar to Uni V2 flash swaps
                 * We request the output amount and the other one is zero
                 */
                // flashLoan(
                //     uint256 baseAmount,
                //     uint256 quoteAmount,
                //     address assetTo,
                //     bytes calldata data
                // )

                // map data to the pool
                mstore(ptrAfter, 0xd0a494e400000000000000000000000000000000000000000000000000000000)
                /*
                 * Store the data for the callback as follows
                 * | Offset | Length (bytes) | Description          |
                 * |--------|----------------|----------------------|
                 * | 0      | 20             | caller               |
                 * | 20     | 20             | base                 |
                 * | 40     | 20             | quote                |
                 * | 60     | 2              | pId                  | <- we use calldatacopy from here
                 * | 62     | 2              | calldataLength       |
                 * | 64     | calldataLength | calldata             |
                 */

                // this is for the next call
                mstore(add(ptr, 0x24), amountIn)
                // determine selector
                switch and(UINT8_MASK, shr(88, amountOut))
                case 0 {
                    // querySellBase(address,uint256)
                    mstore(ptr, 0x79a0487600000000000000000000000000000000000000000000000000000000)
                    mstore(add(ptr, 0x4), 0) // trader is zero
                    // call pool
                    if iszero(
                        staticcall(
                            gas(),
                            pool,
                            ptr,
                            0x44, //
                            0,
                            0x20
                        )
                    ) {
                        returndatacopy(0, 0, returndatasize())
                        revert(0, returndatasize())
                    }
                    amountOut := mload(0)
                    // sell base -> input is base
                    mstore(add(ptrAfter, 4), 0)
                    mstore(add(ptrAfter, 36), amountOut)
                    // gap will be populated outside this
                    mstore(add(ptrAfter, 164), shl(96, callerAddress))
                    mstore(add(ptrAfter, 184), shl(96, tokenIn))
                    mstore(add(ptrAfter, 204), shl(96, tokenOut))
                }
                default {
                    // querySellQuote(address,uint256)
                    mstore(ptr, 0x66410a2100000000000000000000000000000000000000000000000000000000)
                    mstore(add(ptr, 0x4), 0) // trader is zero
                    // call pool
                    if iszero(
                        staticcall(
                            gas(),
                            pool,
                            ptr,
                            0x44, //
                            0,
                            0x20
                        )
                    ) {
                        returndatacopy(0, 0, returndatasize())
                        revert(0, returndatasize())
                    }
                    amountOut := mload(0)
                    // sell quote -> input is quote
                    mstore(add(ptrAfter, 4), amountOut)
                    mstore(add(ptrAfter, 36), 0)
                    // gap will be populated outside this
                    mstore(add(ptrAfter, 164), shl(96, callerAddress))
                    mstore(add(ptrAfter, 184), shl(96, tokenOut))
                    mstore(add(ptrAfter, 204), shl(96, tokenIn))
                }

                mstore(add(ptrAfter, 68), receiver) // (callback-) receiver - should be self
                mstore(add(ptrAfter, 100), 0x80) // bytes offset
                mstore(add(ptrAfter, 132), add(64, clLength)) // 3x address + pId + length

                calldatacopy(add(ptrAfter, 224), add(21, currentOffset), add(clLength, 4))

                // call swap, revert if invalid/undefined pair
                if iszero(
                    call(
                        gas(),
                        pool,
                        0x0,
                        ptrAfter,
                        add(228, clLength), //
                        0x0,
                        0x0
                    )
                ) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                currentOffset := add(add(25, currentOffset), clLength)
            }
        }
        return (amountOut, currentOffset);
    }
}

File 33 of 49 : GMXSwapper.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title GMX V1 swapper, works for most forks, too
 */
abstract contract GMXSwapper is ERC20Selectors, Masks {
    /**
     * Swaps exact input on GMX V1
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | pay flag             | <- 0: caller pays; 1: contract pays; greater: pre-funded
     */
    function _swapGMXExactIn(
        uint256 fromAmount,
        address tokenIn,
        address tokenOut,
        address receiver, //
        address callerAddress,
        uint256 currentOffset
    )
        internal
        returns (uint256 amountOut, uint256)
    {
        assembly {
            let ptr := mload(0x40)

            let gmxData := calldataload(currentOffset)
            let vault := shr(96, gmxData)

            switch and(UINT8_MASK, shr(88, gmxData))
            case 0 {
                // selector for transferFrom(address,address,uint256)
                mstore(ptr, ERC20_TRANSFER_FROM)
                mstore(add(ptr, 0x04), callerAddress)
                mstore(add(ptr, 0x24), vault)
                mstore(add(ptr, 0x44), fromAmount)

                let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
            // transfer plain
            case 1 {
                // selector for transfer(address,uint256)
                mstore(ptr, ERC20_TRANSFER)
                mstore(add(ptr, 0x04), vault)
                mstore(add(ptr, 0x24), fromAmount)
                let success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)

                let rdsize := returndatasize()
                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }

            // selector for swap(address,address,address)
            mstore(
                ptr, //
                0x9331621200000000000000000000000000000000000000000000000000000000
            )
            mstore(add(ptr, 0x04), tokenIn)
            mstore(add(ptr, 0x24), tokenOut)
            mstore(add(ptr, 0x44), receiver)
            if iszero(
                call(
                    gas(),
                    vault,
                    0x0, // no native transfer
                    ptr,
                    0x64, // input length 66 bytes
                    ptr, // store output here
                    0x20 // output is just uint
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            amountOut := mload(ptr)
            currentOffset := add(currentOffset, 21)
        }
        return (amountOut, currentOffset);
    }
}

File 34 of 49 : LBSwapper.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title LB swapper contract
 */
abstract contract LBSwapper is ERC20Selectors, Masks {
    /**
     * Swaps exact input on LB
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | swapForY             |
     * | 21     | 1              | pay flag             | <- 0: caller pays; 1: contract pays; greater: pre-funded
     */
    function _swapLBexactIn(
        uint256 fromAmount,
        address tokenIn,
        address receiver,
        address callerAddress,
        uint256 currentOffset //
    )
        internal
        returns (uint256 amountOut, uint256 payFlag)
    {
        assembly {
            let ptr := mload(0x40)
            let lbData := calldataload(currentOffset)
            let pool := shr(96, lbData)

            // pre-funded is >= 2
            payFlag := and(UINT8_MASK, shr(80, lbData))
            if lt(payFlag, 2) {
                let success
                // payFlag evaluation
                switch payFlag
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), pool)
                    mstore(add(ptr, 0x44), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                }
                // transfer plain
                case 1 {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), pool)
                    mstore(add(ptr, 0x24), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                }

                let rdsize := returndatasize()

                // revert if needed
                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }

            // swap for Y flag
            let swapForY := and(UINT8_MASK, shr(88, lbData))
            ////////////////////////////////////////////////////
            // Execute swap function
            ////////////////////////////////////////////////////

            // swap(bool,address)
            mstore(ptr, 0x53c059a000000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 0x4), swapForY)
            mstore(add(ptr, 0x24), receiver)
            // call swap, revert if invalid/undefined pool
            if iszero(call(gas(), pool, 0x0, ptr, 0x44, ptr, 0x20)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            // the swap call returns both amounts encoded into a single bytes32 as (amountX,amountY)
            switch swapForY
            case 0 { amountOut := and(mload(ptr), UINT128_MASK) }
            default { amountOut := shr(128, mload(ptr)) }
            // skip 22 bytes
            currentOffset := add(currentOffset, 22)
        }
        return (amountOut, currentOffset);
    }
}

File 35 of 49 : SyncSwapper.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title SyncSwap style swapper, pre-funded, all pool variations
 */
abstract contract SyncSwapper is ERC20Selectors, Masks {
    /// @dev selector for swap(bytes,address,address,bytes)
    bytes32 internal constant SYNCSWAP_SELECTOR = 0x7132bb7f00000000000000000000000000000000000000000000000000000000;

    /**
     * Swaps exact input on SyncSwap
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | pay flag             | <- 0: caller pays; 1: contract pays; greater: pre-funded
     */
    function _swapSyncExactIn(
        uint256 fromAmount,
        address tokenIn,
        address receiver,
        address callerAddress,
        uint256 currentOffset //
    )
        internal
        returns (uint256 buyAmount, uint256 payFlag)
    {
        assembly {
            let syncSwapData := calldataload(currentOffset)

            let ptr := mload(0x40)

            // facilitate payment if needed
            payFlag := and(UINT8_MASK, shr(88, syncSwapData))
            let pool := shr(96, syncSwapData)
            if lt(payFlag, 2) {
                let success
                switch payFlag
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), pool)
                    mstore(add(ptr, 0x44), fromAmount)

                    success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                }
                // transfer plain
                case 1 {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), pool)
                    mstore(add(ptr, 0x24), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                }
                let rdsize := returndatasize()
                // Check for ERC20 success. ERC20 tokens should return a boolean,
                // but some don't. We accept 0-length return data as success, or at
                // least 32 bytes that starts with a 32-byte boolean true.
                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }

            // selector for swap(...)
            mstore(ptr, SYNCSWAP_SELECTOR)
            mstore(add(ptr, 4), 0x80) // first param set offset
            mstore(add(ptr, 36), 0x0) // sender address
            ////////////////////////////////////////////////////
            // We store the bytes length to zero (no callback)
            // and directly trigger the swap
            ////////////////////////////////////////////////////
            mstore(add(ptr, 68), 0x0) // callback receiver address
            mstore(add(ptr, 100), 0x100) // calldata offset
            mstore(add(ptr, 132), 0x60) // datalength
            mstore(add(ptr, 164), tokenIn) // tokenIn
            mstore(add(ptr, 196), receiver) // to
            mstore(add(ptr, 228), 0) // withdraw mode
            mstore(add(ptr, 260), 0) // path length is zero

            if iszero(
                call(
                    gas(),
                    pool, // pool
                    0x0,
                    ptr, // input selector
                    292, // input size = 164 (selector (4bytes) plus 5*32bytes)
                    ptr, // output
                    0x40 // output size = 0x40
                )
            ) {
                // Forward the error
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            buyAmount := mload(add(ptr, 0x20))
            currentOffset := add(currentOffset, 21)
        }
        return (buyAmount, currentOffset);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

// solhint-disable max-line-length

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title Uniswap V2 type swapper contract
 * @notice We do everything UniV2 here, incl Solidly, FoT, exactIn and -Out
 */
abstract contract V2TypeGeneric is ERC20Selectors, Masks {
    ////////////////////////////////////////////////////
    // Uni V2 type selctors
    ////////////////////////////////////////////////////

    /// @dev selector for getReserves()
    bytes32 private constant UNI_V2_GET_RESERVES = 0x0902f1ac00000000000000000000000000000000000000000000000000000000;

    /// @dev selector for swap(...)
    bytes32 private constant UNI_V2_SWAP = 0x022c0d9f00000000000000000000000000000000000000000000000000000000;

    /// @notice fixed selector transferFrom(...) on permit2
    bytes32 private constant PERMIT2_TRANSFER_FROM = 0x36c7851600000000000000000000000000000000000000000000000000000000;

    /// @notice deterministically deployed pemrit2 address
    address private constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 2              | feeDenom             |
     * | 22     | 1              | forkId               |
     * | 23     | 2              | calldataLength       | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
     * | 25     | calldataLength | calldata             |
     */
    function _swapUniswapV2PoolExactInGeneric(
        uint256 amountIn,
        address tokenIn,
        address tokenOut,
        address receiver,
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (
            uint256 buyAmount,
            // we need this as transient variable to not overflow the stacksize
            // the clLength is the calldata length for the callback at the beginning
            // and willl be used as the new incremented offset
            // this is to prevent `stack too deep` without using additional `mstore`
            uint256 clLength
        )
    {
        assembly {
            let ptr := mload(0x40) // free memory pointer
            ////////////////////////////////////////////////////
            // We extract all relevant data from the path bytes blob
            ////////////////////////////////////////////////////
            let pool := calldataload(currentOffset)
            clLength := and(UINT16_MASK, shr(56, pool))

            // Compute the buy amount based on the pair reserves.

            let zeroForOne :=
                lt(
                    tokenIn,
                    tokenOut //
                )

            switch lt(
                and(UINT8_MASK, shr(72, pool)), // this is the forkId
                128 // less than 128 indicates that it is classic uni V2, solidly otherwise
            )
            case 1 {
                // Pairs are in the range (0, 2¹¹²) so this shouldn't overflow.
                // buyAmount = (pairSellAmount * feeAm * buyReserve) /
                //     (pairSellAmount * feeAm + sellReserve * 10000);
                // this is expected to be 10000 - x, where x is the poolfee in bps
                let poolFeeDenom := and(shr(80, pool), UINT16_MASK)
                pool := shr(96, pool)

                // Call pair.getReserves(), store the results in scrap space
                mstore(0x0, UNI_V2_GET_RESERVES)
                if iszero(staticcall(gas(), pool, 0x0, 0x4, 0x0, 0x40)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                let reserveIn
                switch zeroForOne
                case 1 {
                    // Transpose if pair order is different.
                    buyAmount := mload(0x20)
                    reserveIn := mload(0x0)
                }
                default {
                    reserveIn := mload(0x20)
                    buyAmount := mload(0x0)
                }

                // compute out amount
                poolFeeDenom := mul(amountIn, poolFeeDenom)
                buyAmount :=
                    div(
                        mul(poolFeeDenom, buyAmount),
                        add(poolFeeDenom, mul(reserveIn, 10000)) //
                    )
            }
            // all solidly-based protocols
            default {
                // we ignore the fee denominator for solidly type DEXs
                pool := shr(96, pool)
                // selector for getAmountOut(uint256,address)
                mstore(ptr, 0xf140a35a00000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 0x4), amountIn)
                mstore(add(ptr, 0x24), tokenIn)
                if iszero(staticcall(gas(), pool, ptr, 0x44, 0, 0x20)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }

                buyAmount := mload(0)
            }

            ////////////////////////////////////////////////////
            // Prepare the swap tx
            ////////////////////////////////////////////////////

            // selector for swap(...)
            mstore(ptr, UNI_V2_SWAP)

            switch zeroForOne
            case 0 {
                mstore(add(ptr, 4), buyAmount)
                mstore(add(ptr, 36), 0)
            }
            default {
                mstore(add(ptr, 4), 0)
                mstore(add(ptr, 36), buyAmount)
            }
            mstore(add(ptr, 68), receiver)
            mstore(add(ptr, 100), 0x80) // bytes offset

            ////////////////////////////////////////////////////
            // This one is tricky:
            // if data length is > 3, we assume a flash swap and defer payment
            // if it is 0: we pull from caller,
            //          1: we pay from contract, and
            //          2: we assume pre-funding
            ////////////////////////////////////////////////////
            switch lt(clLength, 3)
            case 0 {
                /*
                 * Store the data for the callback as follows
                 * | Offset | Length (bytes) | Description          |
                 * |--------|----------------|----------------------|
                 * | 0      | 20             | caller               |
                 * | 20     | 20             | tokenIn              |
                 * | 40     | 20             | tokenOut             |
                 * | 60     | 1              | forkId               |
                 * | 61     | 2              | calldataLength       |
                 * | 63     | calldataLength | calldata             |
                 */
                mstore(add(ptr, 132), add(clLength, 63)) // calldataLength (within bytes)
                mstore(add(ptr, 164), shl(96, callerAddress))
                mstore(add(ptr, 184), shl(96, tokenIn))
                mstore(add(ptr, 204), shl(96, tokenOut))
                // we skip to the forkId offset
                currentOffset := add(22, currentOffset)
                // increment by 3 here (forkId, calldataLength)
                clLength := add(clLength, 3)
                // Store callback  (incl forkId)
                calldatacopy(add(ptr, 224), currentOffset, clLength)
                if iszero(
                    call(
                        gas(),
                        pool,
                        0x0,
                        ptr, // input selector
                        add(224, clLength), // 164 + (63+clLength)
                        0x0, // output = 0
                        0x0 // output size = 0
                    )
                ) {
                    // Forward the error
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                // update clLength as new offset
                // we already added forkId and clLengh and datalength
                clLength := add(currentOffset, clLength)
            }
            ////////////////////////////////////////////////////
            // Otherwise, we have to assume that payment needs to
            // be facilitated outside the callback
            // 0: caller pays
            // 1: pay self
            // 2: the swap is pre-funded
            //    the operator needs to ensure that `amountIn`
            //    was already sent to the pool
            ////////////////////////////////////////////////////
            default {
                switch clLength
                case 0 {
                    // we need an incremented ptr here to not override the swap call below
                    clLength := add(ptr, 0xC4)
                    // selector for transferFrom(address,address,uint256)
                    mstore(clLength, ERC20_TRANSFER_FROM)
                    mstore(add(clLength, 0x04), callerAddress)
                    mstore(add(clLength, 0x24), pool)
                    mstore(add(clLength, 0x44), amountIn)

                    clLength := call(gas(), tokenIn, 0, clLength, 0x64, 0, 32)

                    let rdsize := returndatasize()

                    if iszero(
                        and(
                            clLength, // call itself succeeded
                            or(
                                iszero(rdsize), // no return data, or
                                and(
                                    gt(rdsize, 31), // at least 32 bytes
                                    eq(mload(0), 1) // starts with uint256(1)
                                )
                            )
                        )
                    ) {
                        returndatacopy(0, 0, rdsize)
                        revert(0, rdsize)
                    }
                }
                // transfer plain
                case 1 {
                    // we need an incremented ptr here to not override the swap call below
                    clLength := add(ptr, 0xC4)
                    // selector for transfer(address,uint256)
                    mstore(clLength, ERC20_TRANSFER)
                    mstore(add(clLength, 0x04), pool)
                    mstore(add(clLength, 0x24), amountIn)
                    clLength := call(gas(), tokenIn, 0, clLength, 0x44, 0, 32)

                    let rdsize := returndatasize()

                    if iszero(
                        and(
                            clLength, // call itself succeeded
                            or(
                                iszero(rdsize), // no return data, or
                                and(
                                    gt(rdsize, 31), // at least 32 bytes
                                    eq(mload(0), 1) // starts with uint256(1)
                                )
                            )
                        )
                    ) {
                        returndatacopy(0, 0, rdsize)
                        revert(0, rdsize)
                    }
                }
                ////////////////////////////////////////////////////
                // We store the bytes length to zero (no callback)
                // and directly trigger the swap
                ////////////////////////////////////////////////////
                mstore(add(ptr, 0x84), 0) // bytes length
                if iszero(call(gas(), pool, 0x0, ptr, 0xA4, 0, 0)) {
                    // Forward the error
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                // update clLength as new offset
                clLength := add(currentOffset, 25)
            }
        }
        return (buyAmount, clLength);
    }

    /**
     * Executes an exact input swap internally across major UniV2 forks supporting
     * FOT tokens. Will only be used at the begining of a swap path where users sell a FOT token
     * Due to the nature of the V2 impleemntation, the callback is not triggered if no calldata is provided
     * As such, we never enter the callback implementation when using this function
     * @param amountIn sell amount
     * @return buyAmount output amount
     */
    function _swapUniV2ExactInFOTGeneric(
        uint256 amountIn,
        address tokenIn,
        address tokenOut,
        address receiver,
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (uint256 buyAmount, uint256)
    {
        assembly {
            let ptr := mload(0x40) // free memory pointer

            ////////////////////////////////////////////////////
            // We extract all relevant data from the path bytes blob
            ////////////////////////////////////////////////////
            let pair := calldataload(currentOffset)

            // this is expected to be 10000 - x, where x is the poolfee in bps
            let poolFeeDenom := and(shr(80, pair), UINT16_MASK)

            // We only allow the caller to pay as otherwise, the fee is charged twice
            switch and(UINT16_MASK, shr(56, pair))
            case 0 {
                pair := shr(96, pair)
                // selector for transferFrom(address,address,uint256)
                mstore(ptr, ERC20_TRANSFER_FROM)
                mstore(add(ptr, 0x04), callerAddress)
                mstore(add(ptr, 0x24), pair)
                mstore(add(ptr, 0x44), amountIn)

                let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)

                let rdsize := returndatasize()
                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }
            case 1 {
                pair := shr(96, pair)
                // the 1-case here is a permit2 transfer
                // this is needed as FOT usually has no permit, meaning
                // direct permissioning is not possible
                // the "real" 1-case is not mixed up with the "regular" uni V2 case
                // as for FOT the intermediate holding of the asset cannot be facilitated
                mstore(ptr, PERMIT2_TRANSFER_FROM)
                mstore(add(ptr, 0x04), callerAddress)
                mstore(add(ptr, 0x24), pair)
                mstore(add(ptr, 0x44), amountIn)
                mstore(add(ptr, 0x64), tokenIn)
                if iszero(call(gas(), PERMIT2, 0, ptr, 0x84, 0x0, 0x0)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
            default { revert(0, 0) }

            // we define this as token in and later re-assign this to
            // reserve in to prevent stack too deep errors
            // Compute the buy amount based on the pair reserves.
            {
                let zeroForOne :=
                    lt(
                        tokenIn,
                        tokenOut // tokenOut
                    )
                // Pairs are in the range (0, 2¹¹²) so this shouldn't overflow.
                // buyAmount = (pairSellAmount * feeAm * buyReserve) /
                //     (pairSellAmount * feeAm + sellReserve * 1000);
                // Call pair.getReserves(), store the results in scrap space
                mstore(0x0, UNI_V2_GET_RESERVES)
                if iszero(staticcall(gas(), pair, 0x0, 0x4, 0x0, 0x40)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
                // Revert if the pair contract does not return at least two words.
                if lt(returndatasize(), 0x40) { revert(0, 0) }
                let sellReserve
                switch zeroForOne
                case 1 {
                    // Transpose if pair order is different.
                    sellReserve := mload(0x0)
                    buyAmount := mload(0x20)
                }
                default {
                    sellReserve := mload(0x20)
                    buyAmount := mload(0x0)
                }
                // call tokenIn.balanceOf(pair)
                mstore(0x0, ERC20_BALANCE_OF)
                mstore(0x4, pair)
                // we store the result
                pop(staticcall(gas(), tokenIn, 0x0, 0x24, 0x0, 0x20))
                amountIn := sub(mload(0x0), sellReserve)

                // adjustment via denominator
                poolFeeDenom := mul(amountIn, poolFeeDenom)
                buyAmount := div(mul(poolFeeDenom, buyAmount), add(poolFeeDenom, mul(sellReserve, 10000)))

                ////////////////////////////////////////////////////
                // Prepare the swap tx
                ////////////////////////////////////////////////////

                // selector for swap(...)
                mstore(ptr, UNI_V2_SWAP)

                switch zeroForOne
                case 0 {
                    mstore(add(ptr, 0x4), buyAmount)
                    mstore(add(ptr, 0x24), 0)
                }
                default {
                    mstore(add(ptr, 0x4), 0)
                    mstore(add(ptr, 0x24), buyAmount)
                }
                mstore(add(ptr, 0x44), receiver)
                mstore(add(ptr, 0x64), 0x80) // bytes offset

                ////////////////////////////////////////////////////
                // We store the bytes length to zero (no callback)
                // and directly trigger the swap
                ////////////////////////////////////////////////////
                mstore(add(ptr, 0x84), 0) // bytes length
                if iszero(call(gas(), pair, 0x0, ptr, 0xA4, 0, 0)) {
                    // Forward the error
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
            // update clLength as new offset
            currentOffset := add(currentOffset, 25)
        }
        return (buyAmount, currentOffset);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title Uniswap V3 type swapper contract
 * @notice Executes Cl swaps and pushes data to the callbacks
 * the data can be empty, the callback then jsut fills the swap
 */
abstract contract V3TypeGeneric is Masks {
    constructor() {}

    /*
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 20     | 1              | forkId               |
     * | 21     | 2              | fee                  |
     * | 23     | 2              | calldataLength       |
     * | 25     | calldataLength | calldata             |
     */
    function _swapUniswapV3PoolExactInGeneric(
        uint256 fromAmount,
        address tokenIn,
        address tokenOut,
        address receiver,
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (uint256 receivedAmount, uint256)
    {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            // read the pool address
            let pool := calldataload(currentOffset)
            // skip pool
            currentOffset := add(currentOffset, 20)
            let clLength := and(UINT16_MASK, shr(56, pool))
            pool :=
                shr(
                    96,
                    pool // starts as first param
                )
            let zeroForOne :=
                lt(
                    tokenIn,
                    tokenOut //
                )
            // Prepare external call data
            // Store swap selector (0x128acb08)
            mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000)
            // Store toAddress
            mstore(add(ptr, 4), receiver)
            // Store direction
            mstore(add(ptr, 36), zeroForOne)
            // Store fromAmount
            mstore(add(ptr, 68), fromAmount)

            // Store data offset
            mstore(add(ptr, 132), 0xa0)
            let plStored := add(clLength, 65)
            // Store data length
            mstore(add(ptr, 164), plStored)

            /*
             * Store the data for the callback as follows
             * | Offset | Length (bytes) | Description          |
             * |--------|----------------|----------------------|
             * | 0      | 20             | caller               |
             * | 20     | 20             | tokenIn              |
             * | 40     | 20             | tokenOut             |
             * | 60     | 1              | dexId                | <- we use calldatacopy from here
             * | 61     | 2              | fee                  |
             * | 63     | 2              | calldataLength       |
             * | 65     | calldataLength | calldata             |
             */
            mstore(add(ptr, 196), shl(96, callerAddress))
            mstore(add(ptr, 216), shl(96, tokenIn))
            mstore(add(ptr, 236), shl(96, tokenOut))
            // Store furhter calldata (add 4 to length due to fee and clLength)
            calldatacopy(add(ptr, 256), currentOffset, add(clLength, 5))

            switch zeroForOne
            case 0 {
                // Store sqrtPriceLimitX96
                mstore(add(ptr, 100), MAX_SQRT_RATIO)

                // Perform the external 'swap' call
                if iszero(call(gas(), pool, 0, ptr, add(228, plStored), ptr, 32)) {
                    // store return value directly to free memory pointer
                    // The call failed; we retrieve the exact error message and revert with it
                    returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                    revert(0, returndatasize()) // Revert with the error message
                }
                // If direction is 0, return amount0
                receivedAmount := mload(ptr)
            }
            default {
                // Store sqrtPriceLimitX96
                mstore(add(ptr, 100), MIN_SQRT_RATIO)

                // Perform the external 'swap' call
                if iszero(call(gas(), pool, 0, ptr, add(228, plStored), ptr, 64)) {
                    // store return value directly to free memory pointer
                    // The call failed; we retrieve the exact error message and revert with it
                    returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                    revert(0, returndatasize()) // Revert with the error message
                }

                // If direction is 1, return amount1
                receivedAmount := mload(add(ptr, 32))
            }
            // receivedAmount = -receivedAmount
            receivedAmount := sub(0, receivedAmount)

            switch lt(clLength, 2)
            case 1 { currentOffset := add(currentOffset, 5) }
            default { currentOffset := add(currentOffset, add(5, clLength)) }
        }
        return (receivedAmount, currentOffset);
    }

    /// @dev Swap exact input through izumi
    function _swapIZIPoolExactInGeneric(
        uint256 fromAmount,
        address tokenIn,
        address tokenOut,
        address receiver,
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (uint256 receivedAmount, uint256)
    {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            // read the pool address
            let pool := calldataload(currentOffset)
            // skip pool
            currentOffset := add(currentOffset, 20)
            let clLength := and(UINT16_MASK, shr(56, pool))
            pool :=
                shr(
                    96,
                    pool // starts as first param
                )
            switch lt(
                tokenIn,
                tokenOut //
            )
            case 0 {
                // Prepare external call data
                // Store swapY2X selector (0x2c481252)
                mstore(ptr, 0x2c48125200000000000000000000000000000000000000000000000000000000)
                // Store recipient
                mstore(add(ptr, 4), receiver)
                // Store fromAmount
                mstore(add(ptr, 36), fromAmount)
                // Store highPt
                mstore(add(ptr, 68), 799999)
                // Store data offset
                mstore(add(ptr, 100), 0x80)

                let plStored := add(clLength, 65)
                // Store data length
                mstore(add(ptr, 132), plStored)

                /*
                 * Store the data for the callback as follows
                 * | Offset | Length (bytes) | Description          |
                 * |--------|----------------|----------------------|
                 * | 0      | 20             | caller               |
                 * | 20     | 20             | tokenIn              |
                 * | 40     | 20             | tokenOut             |
                 * | 60     | 1              | dexId                |
                 * | 61     | 2              | fee                  | <- we use calldatacopy from here
                 * | 63     | 2              | calldataLength       |
                 * | 65     | calldataLength | calldata             |
                 */
                mstore(add(ptr, 164), shl(96, callerAddress))
                mstore(add(ptr, 184), shl(96, tokenIn))
                mstore(add(ptr, 204), shl(96, tokenOut))

                // Store furhter calldata
                calldatacopy(add(ptr, 224), currentOffset, add(clLength, 5))

                // Perform the external 'swap' call
                if iszero(call(gas(), pool, 0, ptr, add(196, plStored), ptr, 32)) {
                    // store return value directly to free memory pointer
                    // The call failed; we retrieve the exact error message and revert with it
                    returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                    revert(0, returndatasize()) // Revert with the error message
                }
                // If direction is 0, return amount0
                receivedAmount := mload(ptr)
            }
            default {
                // Prepare external call data
                // Store swapX2Y selector (0x857f812f)
                mstore(ptr, 0x857f812f00000000000000000000000000000000000000000000000000000000)
                // Store toAddress
                mstore(add(ptr, 4), receiver)
                // Store fromAmount
                mstore(add(ptr, 36), fromAmount)
                // Store sqrtPriceLimitX96
                mstore(add(ptr, 68), sub(0, 799999))
                // Store data offset
                mstore(add(ptr, 100), 0x80)

                let plStored := add(clLength, 65)
                // Store data length
                mstore(add(ptr, 132), plStored)

                /*
                 * Store the data for the callback as follows
                 * | Offset | Length (bytes) | Description          |
                 * |--------|----------------|----------------------|
                 * | 0      | 20             | caller               |
                 * | 20     | 20             | tokenIn              |
                 * | 40     | 20             | tokenOut             |
                 * | 60     | 1              | dexId                |
                 * | 61     | 2              | fee                  | <- we use calldatacopy from here
                 * | 63     | 2              | calldataLength       |
                 * | 65     | calldataLength | calldata             |
                 */
                mstore(add(ptr, 164), shl(96, callerAddress))
                mstore(add(ptr, 184), shl(96, tokenIn))
                mstore(add(ptr, 204), shl(96, tokenOut))

                // Store furhter calldata
                calldatacopy(add(ptr, 224), currentOffset, add(clLength, 5))

                // Perform the external 'swap' call
                if iszero(call(gas(), pool, 0, ptr, add(196, plStored), ptr, 64)) {
                    // store return value directly to free memory pointer
                    // The call failed; we retrieve the exact error message and revert with it
                    returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                    revert(0, returndatasize()) // Revert with the error message
                }
                // If direction is 1, return amount1
                receivedAmount := mload(add(ptr, 32))
            }

            switch lt(clLength, 2)
            case 1 { currentOffset := add(currentOffset, 5) }
            default { currentOffset := add(currentOffset, add(5, clLength)) }
        }
        return (receivedAmount, currentOffset);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title Uniswap V4 type swapper contract
 * @notice Can only be executed within `manager.unlock()`
 * Ergo, if Uniswap v4 is in the path (no matter how many times), one has to push
 * the swap data and execution into the UniswapV4.unlock
 * This should be usable together with flash loans from their singleton
 *
 * Cannot unlock multiple times!
 *
 * The execution of a swap follows the steps:
 *
 * 1) pm.unlock(...) (outside of this contract)
 * 2) call pm.swap(...)
 * 3) getDeltas via pm.exttload(bytes[]) (we get it for input/output at once)
 *    we get [inputDelta, outputDelta] (pay amount, receive amount)
 *    As for the V4 docs, `swap` does not necessarily return the correct
 *    deltas when using hooks, that is why we remain with fetching the deltas
 * 4) call pm.take to pull the outputDelta from pm
 * 4) if input nonnative call pm.sync() and send inputDelta to pm
 * 5) call pm.settle to settle the swap (if native with input value)
 *
 * Technically it is possible to call multihops within V4 where one would
 * skip the `take` when the next swap is also for V4
 * This is a bit annoying to implement and for this first version we skip it
 */
abstract contract V4TypeGeneric is ERC20Selectors, Masks {
    /**
     * We need all these selectors for executing a single swap
     */
    bytes32 private constant SWAP = 0xf3cd914c00000000000000000000000000000000000000000000000000000000;
    bytes32 private constant TAKE = 0x0b0d9c0900000000000000000000000000000000000000000000000000000000;
    bytes32 private constant SETTLE = 0x11da60b400000000000000000000000000000000000000000000000000000000;
    bytes32 private constant SYNC = 0xa584119400000000000000000000000000000000000000000000000000000000;
    bytes32 private constant EXTTLOAD = 0x9bf6645f00000000000000000000000000000000000000000000000000000000;

    constructor() {}

    /*
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | hooks                |
     * | 20     | 20             | manager              |
     * | 40     | 3              | fee                  |
     * | 43     | 3              | tickSpacing          |
     * | 46     | 1              | payFlag              |
     * | 47     | 2              | calldataLength       |
     * | 49     | calldataLength | calldata             |
     */
    function _swapUniswapV4ExactInGeneric(
        uint256 fromAmount,
        address tokenIn,
        address tokenOut,
        address receiver,
        uint256 currentOffset,
        address callerAddress
    )
        internal
        returns (
            // this var changes from zeroToOne to the received amount
            uint256 receivedAmount,
            // similar to other implementations, we use this temp variable
            // to avoid stackToo deep
            uint256 tempVar
        )
    {
        // struct PoolKey {
        //     address currency0; 4
        //     address currency1; 36
        //     uint24 fee; 68
        //     int24 tickSpacing; 100
        //     address hooks; 132
        // }
        // struct SwapParams {
        //     bool zeroForOne; 164
        //     int256 amountSpecified; 196
        //     uint160 sqrtPriceLimitX96; 228
        // }
        ////////////////////////////////////////////
        // This is the function selector we need
        ////////////////////////////////////////////
        //  swap(
        //        PoolKey memory key,
        //        SwapParams memory params,
        //        bytes calldata hookData //
        //     )

        // solhint-disable-next-line no-inline-assembly
        assembly {
            let ptr := mload(0x40)

            // read the pool address
            let pool := calldataload(add(currentOffset, 20))

            // let tickSpacing := and(UINT24_MASK, shr(48, pool))
            // pay flag
            tempVar := and(UINT8_MASK, shr(40, pool))
            let clLength := and(UINT16_MASK, shr(24, pool))

            // Prepare external call data
            // Store swap selector
            mstore(ptr, SWAP)

            /**
             * PoolKey  (2/2)
             */

            // Store fee
            mstore(add(ptr, 68), and(UINT24_MASK, shr(72, pool)))

            // Store tickSpacing
            mstore(add(ptr, 100), and(UINT24_MASK, shr(48, pool)))

            // get the hook
            let hook := shr(96, calldataload(currentOffset))
            mstore(add(ptr, 132), hook)

            pool := shr(96, pool)

            // Store data offset
            mstore(add(ptr, 260), 0x120)
            // Store data length
            mstore(add(ptr, 292), clLength)

            /**
             * SwapParams
             */

            // Store fromAmount
            mstore(add(ptr, 196), sub(0, fromAmount))

            // skip pool plus params
            currentOffset := add(currentOffset, 49)
            if xor(0, clLength) {
                // Store furhter calldata (add 4 to length due to fee and clLength)
                calldatacopy(add(ptr, 324), currentOffset, clLength)
            }

            // use receivedAmount as zeroForOne
            receivedAmount := lt(tokenIn, tokenOut)
            // Store zeroForOne
            mstore(add(ptr, 164), receivedAmount)
            // store pool key and limits
            switch receivedAmount
            // zeroForOne
            case 1 {
                /**
                 * PoolKey  (1/2)
                 */

                // Store ccy0
                mstore(add(ptr, 4), tokenIn)
                // Store ccy1
                mstore(add(ptr, 36), tokenOut)

                // Store sqrtPriceLimitX96
                mstore(add(ptr, 228), MIN_SQRT_RATIO)
            }
            default {
                /**
                 * PoolKey  (1/2)
                 */

                // Store ccy0
                mstore(add(ptr, 4), tokenOut)
                // Store ccy1
                mstore(add(ptr, 36), tokenIn)

                // Store sqrtPriceLimitX96
                mstore(add(ptr, 228), MAX_SQRT_RATIO)
            }

            // Perform the external 'swap' call
            if iszero(call(gas(), pool, 0, ptr, add(324, clLength), 0, 0x20)) {
                // store return value directly to free memory pointer
                // The call failed; we retrieve the exact error message and revert with it
                returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                revert(0, returndatasize()) // Revert with the error message
            }

            // if no hook provided, read plain swap, otherwise, read deltas
            switch iszero(hook)
            case 1 {
                fromAmount := mload(0)
                switch receivedAmount
                case 1 {
                    receivedAmount := and(UINT128_MASK, fromAmount)
                    fromAmount := sub(0, sar(128, fromAmount))
                }
                default {
                    receivedAmount := shr(128, fromAmount)
                    fromAmount := sub(0, signextend(15, fromAmount))
                }
            }
            default {
                /**
                 * Load actual deltas from pool manager if hook provided
                 * This is recommended in the docs
                 */
                mstore(ptr, EXTTLOAD)
                mstore(add(ptr, 4), 0x20) // offset
                mstore(add(ptr, 36), 2) // array length

                mstore(0, address())
                mstore(0x20, tokenIn)
                // first key
                mstore(add(ptr, 68), keccak256(0, 0x40))
                // output token for 2nd key
                mstore(0x20, tokenOut)
                // second key
                mstore(add(ptr, 100), keccak256(0, 0x40))

                // get the deltas
                pop(
                    staticcall(
                        gas(),
                        pool,
                        ptr,
                        132, // selector + offs + length + key0 + key1
                        ptr,
                        0x80 // output (offset, length, data0, data1)
                    )
                )

                // 1st array output element
                fromAmount := sub(0, mload(add(ptr, 0x40)))
                // 2nd array output element
                receivedAmount := mload(add(ptr, 0x60))
            }

            /**
             * Pull funds to receiver
             */
            mstore(ptr, TAKE)
            mstore(add(ptr, 4), tokenOut) //
            mstore(add(ptr, 36), receiver)
            mstore(add(ptr, 68), receivedAmount)

            if iszero(
                call(
                    gas(),
                    pool,
                    0x0,
                    ptr, //
                    100,
                    // selector, offset, length, data
                    0x0, // output = empty
                    0x0 // output size = zero
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            /**
             * If the pay mode is >=2, we assume deferred payment
             * This means that the composer must manually settle
             * for the input amount
             * Warning: This should not be done for pools with
             * arbitrary hooks as these can have cases where
             * `amountIn` selected != actual `amountIn`
             */
            if lt(tempVar, 2) {
                /**
                 * Pull funds from payer
                 */
                switch iszero(tokenIn)
                // nonnative
                case 0 {
                    /**
                     * Sync pay asset
                     */
                    mstore(0, SYNC)
                    mstore(4, tokenIn) // offset

                    if iszero(
                        call(
                            gas(),
                            pool,
                            0x0,
                            0, //
                            36,
                            // selector, offset, length, data
                            0x0, // output = empty
                            0x0 // output size = zero
                        )
                    ) {
                        returndatacopy(0, 0, returndatasize())
                        revert(0, returndatasize())
                    }

                    {
                        // temp var is the pay mode and then succes flag
                        switch tempVar
                        case 0 {
                            // selector for transferFrom(address,address,uint256)
                            mstore(ptr, ERC20_TRANSFER_FROM)
                            mstore(add(ptr, 0x04), callerAddress)
                            mstore(add(ptr, 0x24), pool)
                            mstore(add(ptr, 0x44), fromAmount)
                            tempVar := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                        }
                        // transfer plain
                        case 1 {
                            // selector for transfer(address,uint256)
                            mstore(ptr, ERC20_TRANSFER)
                            mstore(add(ptr, 0x04), pool)
                            mstore(add(ptr, 0x24), fromAmount)
                            tempVar := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                        }

                        let rdsize := returndatasize()

                        if iszero(
                            and(
                                tempVar, // call itself succeeded
                                or(
                                    iszero(rdsize), // no return data, or
                                    and(
                                        gt(rdsize, 31), // at least 32 bytes
                                        eq(mload(0), 1) // starts with uint256(1)
                                    )
                                )
                            )
                        ) {
                            returndatacopy(0, 0, rdsize)
                            revert(0, rdsize)
                        }
                    }
                    // we continue to use it as native value
                    // zero for erc20 transfer
                    tempVar := 0
                }
                // native (temVar is the fromAmount)
                default { tempVar := fromAmount }
                /**
                 * Settle funds in pool manager
                 */

                // settle amount
                mstore(0, SETTLE)
                if iszero(
                    call(
                        gas(),
                        pool,
                        tempVar,
                        0, //
                        4,
                        // selector, offset, length, data
                        0x0, // output = empty
                        0x0 // output size = zero
                    )
                ) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
        }
        return (receivedAmount, currentOffset);
    }
}

File 39 of 49 : WooFi.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

// solhint-disable max-line-length

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title WooFi swapper contract
 */
abstract contract WooFiSwapper is ERC20Selectors, Masks {
    /// @dev WooFi rebate receiver
    address private constant REBATE_RECIPIENT = 0x0000000000000000000000000000000000000000;

    constructor() {}

    /**
     * Swaps exact input on WOOFi DEX
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | pool                 |
     * | 21     | 1              | pay flag             | <- 0: caller pays; 1: contract pays; greater: pre-funded
     */
    function _swapWooFiExactIn(
        uint256 fromAmount,
        address tokenIn,
        address tokenOut,
        address receiver,
        address callerAddress,
        uint256 currentOffset
    )
        internal
        returns (
            uint256 poolThenAmountOut,
            // first assign payFlag, then return offset
            uint256 payFlagCurrentOffset
        )
    {
        assembly {
            let ptr := mload(0x40)

            // load first 32 bytes
            poolThenAmountOut := calldataload(currentOffset)
            // pay flag extraction
            payFlagCurrentOffset := and(UINT8_MASK, shr(88, poolThenAmountOut))
            // get pool
            poolThenAmountOut := shr(96, poolThenAmountOut)
            switch lt(payFlagCurrentOffset, 2)
            case 1 {
                let success
                // payFlag evaluation
                switch payFlagCurrentOffset
                case 0 {
                    // selector for transferFrom(address,address,uint256)
                    mstore(ptr, ERC20_TRANSFER_FROM)
                    mstore(add(ptr, 0x04), callerAddress)
                    mstore(add(ptr, 0x24), poolThenAmountOut)
                    mstore(add(ptr, 0x44), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
                }
                // transfer plain
                case 1 {
                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), poolThenAmountOut)
                    mstore(add(ptr, 0x24), fromAmount)
                    success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
                }
                let rdsize := returndatasize()
                // revert if needed
                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }

            // selector for swap(address,address,uint256,uint256,address,address)
            mstore(
                ptr, //
                0x7dc2038200000000000000000000000000000000000000000000000000000000
            )
            mstore(add(ptr, 0x04), tokenIn)
            mstore(add(ptr, 0x24), tokenOut)
            mstore(add(ptr, 0x44), fromAmount)
            mstore(add(ptr, 0x64), 0x0) // amountOutMin unused
            mstore(add(ptr, 0x84), receiver) // recipient
            mstore(add(ptr, 0xA4), REBATE_RECIPIENT) // rebateTo
            if iszero(
                call(
                    gas(),
                    poolThenAmountOut,
                    0x0, // no native transfer
                    ptr,
                    0xC4, // input length 196
                    ptr, // store output here
                    0x20 // output is just uint
                )
            ) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            // map amountOut to var
            poolThenAmountOut := mload(ptr)

            // skip 21 bytes
            payFlagCurrentOffset := add(currentOffset, 21)
        }

        return (poolThenAmountOut, payFlagCurrentOffset);
    }
}

File 40 of 49 : Wrapper.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

// solhint-disable max-line-length

import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";

/**
 * @title ERC4626 vault & native wrap "swapper" contract
 * The logic is as follows
 * - the only parameter is an indicator for the operation (0: native wrap | 1: deposit | 2: redeem)
 * - the native case has the direction implied by the asset address (asset=0 mean native),
 *   as such, assetIn=0 means wrap, in this case amountIn=amountOut
 * - ERC4626 only uses deposit and redeem (as these are the exact in operations), these
 *   require the output amount to be read from the returndata
 */
abstract contract Wrapper is ERC20Selectors, Masks {
    /// @dev  deposit(...)
    bytes32 private constant ERC4626_DEPOSIT = 0x6e553f6500000000000000000000000000000000000000000000000000000000;

    /// @dev  redeem(...)
    bytes32 private constant ERC4626_REDEEM = 0xba08765200000000000000000000000000000000000000000000000000000000;

    // NativeTransferFailed()
    bytes4 private constant NATIVE_TRANSFER = 0xf4b3b1bc;

    // WrapFailed()
    bytes4 private constant WRAP = 0xc30d93ce;

    // Note that the for native wrapping, the assetIn/assetOut constellation defines the direction
    // For ERC4626, the direction is defined by the operation parameter
    // 1: deposit: deposit to vault, assetIn is the underlying and assetOut is the vault
    // 2: redeem: redeem shares, assetIn is the vault, assetOut is the underlyinh
    /**
     * This one is for overring the DEX implementation
     * | Offset | Length (bytes) | Description         |
     * |--------|----------------|---------------------|
     * | 0      | 1              | operation           |
     * | 1      | 1              | pay config          | <- 0: caller pays; 1: contract pays; greater: pre-funded
     */
    function _wrapperOperation(
        address assetIn,
        address assetOut,
        uint256 amount,
        address receiver,
        address callerAddress,
        uint256 currentOffset
    )
        internal
        virtual
        returns (uint256 amountOut, uint256 operationThenOffset)
    {
        assembly {
            operationThenOffset := calldataload(currentOffset)
            let ptr := mload(0x40)
            // only need to check whether we have to pull from caller
            // Note: this should be avoided for native in
            if iszero(and(shr(240, operationThenOffset), UINT8_MASK)) {
                // selector for transferFrom(address,address,uint256)
                mstore(ptr, ERC20_TRANSFER_FROM)
                mstore(add(ptr, 0x04), callerAddress)
                mstore(add(ptr, 0x24), address())
                mstore(add(ptr, 0x44), amount)

                let success := call(gas(), assetIn, 0, ptr, 0x64, 0, 32)

                let rdsize := returndatasize()

                if iszero(
                    and(
                        success, // call itself succeeded
                        or(
                            iszero(rdsize), // no return data, or
                            and(
                                gt(rdsize, 31), // at least 32 bytes
                                eq(mload(0), 1) // starts with uint256(1)
                            )
                        )
                    )
                ) {
                    returndatacopy(0, 0, rdsize)
                    revert(0, rdsize)
                }
            }

            // shift operation to lowest byte
            switch shr(248, operationThenOffset)
            case 0 {
                amountOut := amount
                // native is input: wrap
                switch iszero(assetIn)
                case 1 {
                    if iszero(
                        call(
                            gas(),
                            assetOut,
                            amount, // ETH to deposit
                            0x0, // no input
                            0x0, // input size = zero
                            0x0, // output = empty
                            0x0 // output size = zero
                        )
                    ) {
                        // revert when native transfer fails
                        mstore(0, NATIVE_TRANSFER)
                        revert(0, 0x4)
                    }
                    // transfer to destination if needed
                    // this is to make receipts of native consistent with pre-funded
                    // calls
                    if xor(receiver, address()) {
                        // selector for transfer(address,uint256)
                        mstore(ptr, ERC20_TRANSFER)
                        mstore(add(ptr, 0x04), receiver)
                        mstore(add(ptr, 0x24), amount)
                        let success := call(gas(), assetOut, 0, ptr, 0x44, 0, 32)

                        let rdsize := returndatasize()

                        // Check for ERC20 success. ERC20 tokens should return a boolean,
                        // but some don't. We accept 0-length return data as success, or at
                        // least 32 bytes that starts with a 32-byte boolean true.
                        success :=
                            and(
                                success, // call itself succeeded
                                or(
                                    iszero(rdsize), // no return data, or
                                    and(
                                        gt(rdsize, 31), // at least 32 bytes
                                        eq(mload(0), 1) // starts with uint256(1)
                                    )
                                )
                            )

                        if iszero(success) {
                            returndatacopy(0, 0, rdsize)
                            revert(0, rdsize)
                        }
                    }
                }
                // assetIn is expected to be wNative
                // note that we do not do a transfer to a receiver as DEXs that require native require
                // to attach native to the respective swap call
                default {
                    // selector for withdraw(uint256)
                    mstore(0x0, 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000)
                    mstore(0x4, amount)
                    if iszero(
                        call(
                            gas(),
                            assetIn,
                            0x0, // no ETH
                            0x0, // start of data
                            0x24, // input size = selector plus amount
                            0x0, // output = empty
                            0x0 // output size = zero
                        )
                    ) {
                        // revert when native transfer fails
                        mstore(0, WRAP)
                        revert(0, 0x4)
                    }

                    // transfer native to receiver if needed
                    if xor(receiver, address()) {
                        if iszero(call(gas(), receiver, amount, 0x0, 0x0, 0x0, 0x0)) {
                            // revert when native transfer fails
                            mstore(0, NATIVE_TRANSFER)
                            revert(0, 0x4)
                        }
                    }
                }
            }
            // the other 2 cases have receiver as parameter
            case 1 {
                // Approve vault if needed
                mstore(0x0, assetIn)
                mstore(0x20, 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3) // CALL_MANAGEMENT_APPROVALS
                mstore(0x20, keccak256(0x0, 0x40))
                mstore(0x0, assetOut)
                let key := keccak256(0x0, 0x40)
                // check if already approved
                if iszero(sload(key)) {
                    // selector for approve(address,uint256)
                    mstore(ptr, ERC20_APPROVE)
                    mstore(add(ptr, 0x04), assetOut)
                    mstore(add(ptr, 0x24), MAX_UINT256)
                    pop(call(gas(), assetIn, 0, ptr, 0x44, ptr, 32))
                    sstore(key, 1)
                }

                mstore(ptr, ERC4626_DEPOSIT)
                mstore(add(ptr, 0x4), amount) // assets
                mstore(add(ptr, 0x24), receiver) // receiver

                if iszero(call(gas(), assetOut, 0x0, ptr, 0x44, 0x0, 0x20)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0x0, returndatasize())
                }
                amountOut := mload(0)
            }
            default {
                // this one should not need an approve
                mstore(ptr, ERC4626_REDEEM)
                mstore(add(ptr, 0x4), amount) // shares
                mstore(add(ptr, 0x24), receiver) // receiver
                mstore(add(ptr, 0x44), address()) // owner is self as we expect to hold the vault shares

                if iszero(call(gas(), assetIn, 0x0, ptr, 0x64, 0x0, 0x20)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0x0, returndatasize())
                }
                amountOut := mload(0)
            }
            operationThenOffset := add(currentOffset, 2)
        }
        return (amountOut, operationThenOffset);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

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

// solhint-disable max-line-length

/**
 * @notice entrypoint for swaps
 * - supports a broad variety of DEXs
 * - this also is the entrypoint for flash-swaps, one only needs to
 *   add calldata to the respective call to trigger it
 */
abstract contract Swaps is BaseSwapper {
    function _swap(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        uint256 amountIn;
        uint256 minimumAmountReceived;
        address tokenIn;
        /*
         * Store the data for the callback as follows
         * | Offset | Length (bytes) | Description          |
         * |--------|----------------|----------------------|
         * | 0      | 16             | amount               | <-- input amount
         * | 16     | 16             | amountMax            | <-- slippage check
         * | 32     | 20             | tokenIn              |
         * | 52     | any            | data                 |
         *
         * `data` is a path matrix definition (see BaseSwapepr)
         */
        assembly {
            minimumAmountReceived := calldataload(currentOffset)
            amountIn := shr(128, minimumAmountReceived)
            minimumAmountReceived := and(UINT128_MASK, minimumAmountReceived)
            currentOffset := add(currentOffset, 32)
            let dataStart := calldataload(currentOffset)
            tokenIn := shr(96, dataStart)
            currentOffset := add(20, currentOffset)

            /**
             * if the amount is zero, we assume that the contract balance is swapped
             */
            if iszero(amountIn) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, address())
                // call to token
                pop(staticcall(gas(), tokenIn, 0x0, 0x24, 0x0, 0x20))
                // load the retrieved balance
                amountIn := mload(0x0)
            }
        }
        (amountIn, currentOffset,) = _singleSwapSplitOrRoute(
            amountIn,
            tokenIn,
            callerAddress,
            currentOffset //
        );

        assembly {
            if gt(minimumAmountReceived, amountIn) {
                mstore(0x0, SLIPPAGE)
                revert(0x0, 0x4)
            }
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {BaseUtils} from "contracts/1delta/composer/generic/BaseUtils.sol";

/**
 * @title Token transfer contract - should work across all EVMs - use Uniswap style Permit2
 */
contract AssetTransfers is BaseUtils {
    // approval slot
    bytes32 private constant CALL_MANAGEMENT_APPROVALS = 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3;

    /// @notice fixed selector transferFrom(...) on permit2
    bytes32 private constant PERMIT2_TRANSFER_FROM = 0x36c7851600000000000000000000000000000000000000000000000000000000;

    /// @notice deterministically deployed pemrit2 address
    address private constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*
     * | Offset | Length (bytes) | Description         |
     * |--------|----------------|---------------------|
     * | 0      | 20             | asset               |
     * | 20     | 20             | receiver            |
     * | 40     | 16             | amount              |
     */
    function _permit2TransferFrom(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        ////////////////////////////////////////////////////
        // Transfers tokens from caller to this address
        // zero amount flags that the entire balance is sent
        ////////////////////////////////////////////////////
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            let receiver := shr(96, calldataload(add(currentOffset, 20)))
            let amount := shr(128, calldataload(add(currentOffset, 40)))

            // when entering 0 as amount, use the callwe balance
            if iszero(amount) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, callerAddress)
                // call to token
                pop(
                    staticcall(
                        gas(),
                        underlying, // token
                        0x0,
                        0x24,
                        0x0,
                        0x20
                    )
                )
                // load the retrieved balance
                amount := mload(0x0)
            }

            let ptr := mload(0x40)
            mstore(ptr, PERMIT2_TRANSFER_FROM)
            mstore(add(ptr, 0x04), callerAddress)
            mstore(add(ptr, 0x24), receiver)
            mstore(add(ptr, 0x44), amount)
            mstore(add(ptr, 0x64), underlying)
            if iszero(call(gas(), PERMIT2, 0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            currentOffset := add(currentOffset, 56)
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description         |
     * |--------|----------------|---------------------|
     * | 0      | 20             | asset               |
     * | 20     | 20             | receiver            |
     * | 40     | 16             | amount              |
     */
    function _transferFrom(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        ////////////////////////////////////////////////////
        // Transfers tokens from caller to this address
        // zero amount flags that the entire balance is sent
        ////////////////////////////////////////////////////
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            let receiver := shr(96, calldataload(add(currentOffset, 20)))
            let amount := shr(128, calldataload(add(currentOffset, 40)))

            // when entering 0 as amount, use the callwe balance
            if iszero(amount) {
                // selector for balanceOf(address)
                mstore(0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x04, callerAddress)
                // call to token
                pop(
                    staticcall(
                        gas(),
                        underlying, // token
                        0x0,
                        0x24,
                        0x0,
                        0x20
                    )
                )
                // load the retrieved balance
                amount := mload(0x0)
            }

            let ptr := mload(0x40) // free memory pointer

            // selector for transferFrom(address,address,uint256)
            mstore(ptr, ERC20_TRANSFER_FROM)
            mstore(add(ptr, 0x04), callerAddress)
            mstore(add(ptr, 0x24), receiver)
            mstore(add(ptr, 0x44), amount)

            let success := call(gas(), underlying, 0, ptr, 0x64, ptr, 32)

            let rdsize := returndatasize()

            if iszero(
                and(
                    success, // call itself succeeded
                    or(
                        iszero(rdsize), // no return data, or
                        and(
                            gt(rdsize, 31), // at least 32 bytes
                            eq(mload(ptr), 1) // starts with uint256(1)
                        )
                    )
                )
            ) {
                returndatacopy(0, 0, rdsize)
                revert(0, rdsize)
            }
            currentOffset := add(currentOffset, 56)
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description         |
     * |--------|----------------|---------------------|
     * | 0      | 20             | asset               |
     * | 20     | 20             | receiver            | <- use wrapped native here to wrap
     * | 40     | 1              | config              |
     * | 41     | 16             | amount              |
     */
    function _sweep(uint256 currentOffset) internal returns (uint256) {
        ////////////////////////////////////////////////////
        // Transfers either token or native balance from this
        // contract to receiver. Reverts if minAmount is
        // less than the contract balance
        //  config
        //  0: sweep balance and validate against amount
        //     fetches the balance and checks balance >= amount
        //  1: transfer amount to receiver, skip validation
        ////////////////////////////////////////////////////
        assembly {
            let underlying := shr(96, calldataload(currentOffset))
            // we skip shr by loading the address to the lower bytes
            let receiver := shr(96, calldataload(add(currentOffset, 20)))
            // load so that amount is in the lower 14 bytes already
            let providedAmount := calldataload(add(currentOffset, 25))
            // load config
            let config := and(UINT8_MASK, shr(128, providedAmount))
            // mask amount
            providedAmount := and(UINT128_MASK, providedAmount)

            // initialize transferAmount
            let transferAmount

            // zero address is native
            switch iszero(underlying)
            ////////////////////////////////////////////////////
            // Transfer token
            ////////////////////////////////////////////////////
            case 0 {
                // for config = 0, the amount is the balance and we
                // check that the balance is larger tha the amount provided
                switch config
                case 0 {
                    // selector for balanceOf(address)
                    mstore(0, ERC20_BALANCE_OF)
                    // add this address as parameter
                    mstore(0x04, address())
                    // call to token
                    pop(
                        staticcall(
                            gas(),
                            underlying,
                            0x0,
                            0x24,
                            0x0,
                            0x20 //
                        )
                    )
                    // load the retrieved balance
                    transferAmount := mload(0x0)
                    // revert if balance is not enough
                    if lt(transferAmount, providedAmount) {
                        mstore(0, SLIPPAGE)
                        revert(0, 0x4)
                    }
                }
                default { transferAmount := providedAmount }

                if gt(transferAmount, 0) {
                    let ptr := mload(0x40) // free memory pointer

                    // selector for transfer(address,uint256)
                    mstore(ptr, ERC20_TRANSFER)
                    mstore(add(ptr, 0x04), receiver)
                    mstore(add(ptr, 0x24), transferAmount)

                    let success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)

                    let rdsize := returndatasize()

                    if iszero(
                        and(
                            success, // call itself succeeded
                            or(
                                iszero(rdsize), // no return data, or
                                and(
                                    gt(rdsize, 31), // at least 32 bytes
                                    eq(mload(ptr), 1) // starts with uint256(1)
                                )
                            )
                        )
                    ) {
                        returndatacopy(0, 0, rdsize)
                        revert(0, rdsize)
                    }
                }
            }
            ////////////////////////////////////////////////////
            // Transfer native
            ////////////////////////////////////////////////////
            default {
                switch config
                case 0 {
                    transferAmount := selfbalance()
                    // revert if balance is not enough
                    if lt(transferAmount, providedAmount) {
                        mstore(0, SLIPPAGE)
                        revert(0, 0x4)
                    }
                }
                default { transferAmount := providedAmount }

                if gt(transferAmount, 0) {
                    if iszero(
                        call(
                            gas(),
                            receiver,
                            transferAmount,
                            0x0, // input = empty for fallback/receive
                            0x0, // input size = zero
                            0x0, // output = empty
                            0x0 // output size = zero
                        )
                    ) {
                        mstore(0, NATIVE_TRANSFER)
                        revert(0, 0x4) // revert when native transfer fails
                    }
                }
            }
            currentOffset := add(currentOffset, 57)
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description          |
     * |--------|----------------|----------------------|
     * | 0      | 20             | token                |
     * | 20     | 20             | target               |
     */
    function _approve(uint256 currentOffset) internal returns (uint256) {
        assembly {
            // load underlying and target
            let underlying := shr(96, calldataload(currentOffset))
            let target := shr(96, calldataload(add(currentOffset, 20)))
            // check whether the approval alderady was done
            // if so, we can skip this part silently
            mstore(0x0, underlying)
            mstore(0x20, CALL_MANAGEMENT_APPROVALS)
            mstore(0x20, keccak256(0x0, 0x40))
            mstore(0x0, target)
            let key := keccak256(0x0, 0x40)
            // check if already approved
            if iszero(sload(key)) {
                let ptr := mload(0x40)
                // selector for approve(address,uint256)
                mstore(ptr, ERC20_APPROVE)
                mstore(add(ptr, 0x04), target)
                mstore(add(ptr, 0x24), MAX_UINT256)
                pop(
                    call(
                        gas(),
                        underlying, //
                        0,
                        ptr,
                        0x44,
                        ptr,
                        32
                    )
                )
                sstore(key, 1)
            }
            currentOffset := add(currentOffset, 40)
        }
        return currentOffset;
    }

    /*
     * | Offset | Length (bytes) | Description         |
     * |--------|----------------|---------------------|
     * | 0      | 20             | wrappedNativeAddress|
     * | 20     | 20             | receiver            |
     * | 40     | 1              | config              |
     * | 41     | 16             | amount              |
     */
    function _unwrap(uint256 currentOffset) internal virtual returns (uint256) {
        ////////////////////////////////////////////////////
        // Transfers either token or native balance from this
        // contract to receiver. Reverts if minAmount is
        // less than the contract balance
        //  config
        //  0: sweep balance and validate against amount
        //     fetches the balance and checks balance >= amount
        //  1: transfer amount to receiver, skip validation
        ////////////////////////////////////////////////////
        assembly {
            // load receiver
            let wrapperAsset := shr(96, calldataload(currentOffset))
            // load receiver
            let receiver := shr(96, calldataload(add(currentOffset, 20)))
            // load so that amount is in the lower 14 bytes already
            let providedAmount := calldataload(add(currentOffset, 25))
            // load config
            let config := and(UINT8_MASK, shr(128, providedAmount))
            // mask amount
            providedAmount := and(UINT128_MASK, providedAmount)

            let transferAmount

            // mask away the top bitmap
            providedAmount := and(UINT120_MASK, providedAmount)
            // validate if config is zero, otherwise skip
            switch config
            case 0 {
                // selector for balanceOf(address)
                mstore(0x0, ERC20_BALANCE_OF)
                // add this address as parameter
                mstore(0x4, address())

                // call to underlying
                pop(staticcall(gas(), wrapperAsset, 0x0, 0x24, 0x0, 0x20))

                transferAmount := mload(0x0)
                if lt(transferAmount, providedAmount) {
                    mstore(0, SLIPPAGE)
                    revert(0, 0x4)
                }
            }
            default { transferAmount := providedAmount }

            if gt(transferAmount, 0) {
                // selector for withdraw(uint256)
                mstore(0x0, 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000)
                mstore(0x4, transferAmount)
                if iszero(
                    call(
                        gas(),
                        wrapperAsset,
                        0x0, // no ETH
                        0x0, // start of data
                        0x24, // input size = selector plus amount
                        0x0, // output = empty
                        0x0 // output size = zero
                    )
                ) {
                    // should only revert if receiver cannot receive native
                    mstore(0, NATIVE_TRANSFER)
                    revert(0, 0x4)
                }
                // transfer to receiver if different from this address
                if xor(receiver, address()) {
                    // transfer native to receiver
                    if iszero(
                        call(
                            gas(),
                            receiver,
                            transferAmount,
                            0x0, // input = empty for fallback
                            0x0, // input size = zero
                            0x0, // output = empty
                            0x0 // output size = zero
                        )
                    ) {
                        // should only revert if receiver cannot receive native
                        mstore(0, NATIVE_TRANSFER)
                        revert(0, 0x4)
                    }
                }
            }
            currentOffset := add(currentOffset, 57)
        }
        return currentOffset;
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

import {AssetTransfers} from "./AssetTransfers.sol";
import {TransferIds} from "../enums/DeltaEnums.sol";

/**
 * @title Token transfer contract - should work across all EVMs - user Uniswap style Permit2
 */
contract Transfers is AssetTransfers {
    function _transfers(uint256 currentOffset, address callerAddress) internal returns (uint256) {
        uint256 transferOperation;
        assembly {
            let firstSlice := calldataload(currentOffset)
            transferOperation := shr(248, firstSlice)
            currentOffset := add(currentOffset, 1)
        }
        if (transferOperation == TransferIds.TRANSFER_FROM) {
            return _transferFrom(currentOffset, callerAddress);
        } else if (transferOperation == TransferIds.SWEEP) {
            return _sweep(currentOffset);
        } else if (transferOperation == TransferIds.UNWRAP_WNATIVE) {
            return _unwrap(currentOffset);
        } else if (transferOperation == TransferIds.PERMIT2_TRANSFER_FROM) {
            return _permit2TransferFrom(currentOffset, callerAddress);
        } else if (transferOperation == TransferIds.APPROVE) {
            return _approve(currentOffset);
        } else {
            _invalidOperation();
        }
    }
}

File 44 of 49 : Errors.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

/**
 * @title Raw error data holder contract
 */
abstract contract DeltaErrors {
    ////////////////////////////////////////////////////
    // Error data
    ////////////////////////////////////////////////////

    // the compiler should drop these since they are unused
    // but it should still be included in the ABI to parse the
    // errors below
    error Slippage();
    error NativeTransferFailed();
    error WrapFailed();
    error InvalidDex();
    error BadPool();
    error InvalidFlashLoan();
    error InvalidOperation();
    error InvalidCaller();
    error InvalidInitiator();
    error InvalidCalldata();
    error Target();
    error InvalidDexId();

    // Slippage()
    bytes4 internal constant SLIPPAGE = 0x7dd37f70;
    // NativeTransferFailed()
    bytes4 internal constant NATIVE_TRANSFER = 0xf4b3b1bc;
    // WrapFailed()
    bytes4 internal constant WRAP = 0xc30d93ce;
    // InvalidDex()
    bytes4 internal constant INVALID_DEX = 0x7948739e;
    // BadPool()
    bytes4 internal constant BAD_POOL = 0xb2c02722;
    // InvalidFlashLoan()
    bytes4 internal constant INVALID_FLASH_LOAN = 0xbafe1c53;
    // InvalidOperation()
    bytes4 internal constant INVALID_OPERATION = 0x398d4d32;
    // InvalidCaller()
    bytes4 internal constant INVALID_CALLER = 0x48f5c3ed;
    // InvalidInitiator()
    bytes4 internal constant INVALID_INITIATOR = 0xbfda1f28;
    // InvalidCalldata()
    bytes4 internal constant INVALID_CALLDATA = 0x8129bbcd;
    // Target()
    bytes4 internal constant INVALID_TARGET = 0x4fe6f55f;
    // InvalidDexId()
    bytes4 internal constant INVALID_DEX_ID = 0x0bbef348;

    function _invalidOperation() internal pure {
        assembly {
            mstore(0, INVALID_OPERATION)
            revert(0, 0x4)
        }
    }
}

File 45 of 49 : DeadLogger.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.28;

/// @notice logs a dead log without any content
abstract contract DeadLogger {
    function _deadLog() internal {
        assembly {
            log0(0x0, 0x0)
        }
    }
}

File 46 of 49 : Masks.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

abstract contract Masks {
    /// @dev Mask of lower 20 bytes.
    uint256 internal constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff;
    /// @dev Mask of lower 1 byte.
    uint256 internal constant UINT8_MASK = 0xff;
    /// @dev Mask of lower 2 bytes.
    uint256 internal constant UINT16_MASK = 0xffff;
    /// @dev Mask of lower 3 bytes.
    uint256 internal constant UINT24_MASK = 0xffffff;
    /// @dev Mask of lower 4 bytes.
    uint256 internal constant UINT32_MASK = 0xffffffff;
    /// @dev Mask of lower 16 bytes.
    uint256 internal constant UINT128_MASK = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
    /// @dev Mask of lower 15 bytes.
    uint256 internal constant UINT120_MASK = 0x0000000000000000000000000000000000ffffffffffffffffffffffffffffff;
    /// @dev MIN_SQRT_RATIO + 1 from Uniswap's TickMath
    uint160 internal constant MIN_SQRT_RATIO = 4295128740;
    /// @dev MAX_SQRT_RATIO - 1 from Uniswap's TickMath
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970341;
    /// @dev Maximum Uint256 value
    uint256 internal constant MAX_UINT256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    /// @dev Use this to distinguish FF upper bytes addresses and lower bytes addresses
    uint256 internal constant FF_ADDRESS_COMPLEMENT = 0x000000000000000000000000000000000000000000ffffffffffffffffffffff;

    /// @dev We use uint112-encoded amounts to typically fit one bit flag, one path length (uint16)
    ///      add 2 amounts (2xuint112) into 32bytes, as such we use this mask for extracting those
    uint256 internal constant UINT112_MASK = 0x000000000000000000000000000000000000ffffffffffffffffffffffffffff;

    /// @dev Mask for Is Native
    uint256 internal constant NATIVE_FLAG = 1 << 127;
    /// @dev Mask for shares
    uint256 internal constant USE_SHARES_FLAG = 1 << 126;
}

File 47 of 49 : PermitConstants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

/// @title PermitConstants
/// @notice A contract containing constants used for Permit2 & ERC20 type permits
abstract contract PermitConstants {
    /// @dev default Permit2 address
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; // solhint-disable-line var-name-mixedcase

    bytes32 internal constant ERC20_PERMIT = 0xd505accf00000000000000000000000000000000000000000000000000000000;
    bytes32 internal constant DAI_PERMIT = 0x8fcbaf0c00000000000000000000000000000000000000000000000000000000;
    bytes32 internal constant PERMIT2_PERMIT = 0x2b67b57000000000000000000000000000000000000000000000000000000000;
    bytes32 internal constant CREDIT_PERMIT = 0x0b52d55800000000000000000000000000000000000000000000000000000000;
    bytes32 internal constant COMPOUND_V3_CREDIT_PERMIT = 0xbb24d99400000000000000000000000000000000000000000000000000000000;
    bytes32 internal constant MORPHO_CREDIT_PERMIT = 0x8069218f00000000000000000000000000000000000000000000000000000000;
    bytes32 internal constant PERMIT2_TRANSFER_FROM = 0x36c7851600000000000000000000000000000000000000000000000000000000;

    bytes4 internal constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector

    // bitmap padding
    uint256 internal constant HIGH_BIT = 1 << 255;
    uint256 internal constant SECOND_HIGH_BIT = 1 << 254;
    uint256 internal constant LOWER_BITS = 0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
}

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

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

// solhint-disable max-line-length

/// @title PermitUtils
/// @notice A contract containing utilities for Permits
abstract contract PermitUtils is PermitConstants {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error SafePermitBadLength();

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

    constructor() {}

    /**
     * @notice The function attempts to call the permit function on a given ERC20 token.
     * @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2.
     * It accommodates both Compact and Full formats of these permit types.
     * Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
     * for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
     * gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
     * zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
     * before invocation for optimized performance.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
     * @param token The address of the ERC20 token on which to call the permit function.
     * @param permitOffset The off-chain permit data, containing different fields depending on the type of permit function.
     * @param permitLength Length of the permit calldata.
     */
    function _tryPermit(address token, uint256 permitOffset, uint256 permitLength, address callerAddress) internal {
        assembly {
            // solhint-disable-line no-inline-assembly
            let ptr := mload(0x40)
            let success
            // Switch case for different permit lengths, indicating different permit standards
            switch permitLength
            // Compact IERC20Permit
            case 100 {
                mstore(ptr, ERC20_PERMIT) // store selector
                mstore(add(ptr, 0x04), callerAddress) // store owner
                mstore(add(ptr, 0x24), address()) // store spender

                // Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
                {
                    // stack too deep
                    let deadline := shr(224, calldataload(add(permitOffset, 0x20))) // loads permitOffset 0x20..0x23
                    let vs := calldataload(add(permitOffset, 0x44)) // loads permitOffset 0x44..0x63

                    calldatacopy(add(ptr, 0x44), permitOffset, 0x20) // store value     = copy permitOffset 0x00..0x19
                    mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline  = deadline - 1
                    mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v         = most significant bit of vs + 27 (27 or 28)
                    calldatacopy(add(ptr, 0xa4), add(permitOffset, 0x24), 0x20) // store r         = copy permitOffset 0x24..0x43
                    mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s         = vs without most significant bit
                }
                // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
            }
            // Compact IDaiLikePermit
            case 72 {
                mstore(ptr, DAI_PERMIT) // store selector
                mstore(add(ptr, 0x04), callerAddress) // store owner
                mstore(add(ptr, 0x24), address()) // store spender

                // Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
                {
                    // stack too deep
                    let expiry := shr(224, calldataload(add(permitOffset, 0x04))) // loads permitOffset 0x04..0x07
                    let vs := calldataload(add(permitOffset, 0x28)) // loads permitOffset 0x28..0x47

                    mstore(add(ptr, 0x44), shr(224, calldataload(permitOffset))) // store nonce   = copy permitOffset 0x00..0x03
                    mstore(add(ptr, 0x64), sub(expiry, 1)) // store expiry  = expiry - 1
                    mstore(add(ptr, 0x84), true) // store allowed = true
                    mstore(add(ptr, 0xa4), add(27, shr(255, vs))) // store v       = most significant bit of vs + 27 (27 or 28)
                    calldatacopy(add(ptr, 0xc4), add(permitOffset, 0x08), 0x20) // store r       = copy permitOffset 0x08..0x27
                    mstore(add(ptr, 0xe4), shr(1, shl(1, vs))) // store s       = vs without most significant bit
                }
                // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                success := call(gas(), token, 0, ptr, 0x104, 0, 0)
            }
            // Compact IPermit2
            case 96 {
                // Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
                mstore(ptr, PERMIT2_PERMIT) // store selector
                mstore(add(ptr, 0x04), callerAddress) // store owner
                mstore(add(ptr, 0x24), token) // store token

                calldatacopy(add(ptr, 0x50), permitOffset, 0x14) // store amount = copy permitOffset 0x00..0x13
                // and(0xffffffffffff, ...) - conversion to uint48
                mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permitOffset, 0x14))), 1))) // store expiration = ((permitOffset 0x14..0x17 - 1) & 0xffffffffffff)
                mstore(add(ptr, 0x84), shr(224, calldataload(add(permitOffset, 0x18)))) // store nonce = copy permitOffset 0x18..0x1b
                mstore(add(ptr, 0xa4), address()) // store spender
                // and(0xffffffffffff, ...) - conversion to uint48
                mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permitOffset, 0x1c))), 1))) // store sigDeadline = ((permitOffset 0x1c..0x1f - 1) & 0xffffffffffff)
                mstore(add(ptr, 0xe4), 0x100) // store offset = 256
                mstore(add(ptr, 0x104), 65) // store length = 64
                let vs := calldataload(add(permitOffset, 0x40)) // copy permitOffset 0x40..0x5f
                calldatacopy(add(ptr, 0x124), add(permitOffset, 0x20), 0x20) // store r      = copy permitOffset 0x20..0x3f
                mstore(add(ptr, 0x144), shr(1, shl(1, vs))) // store s     = vs without most significant bit
                mstore8(add(ptr, 0x164), add(27, shr(255, vs))) // store v     = copy permitOffset 0x40..0x5f
                // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                success := call(gas(), PERMIT2, 0, ptr, 0x165, 0, 0)
            }
            // Unknown
            default {
                mstore(ptr, _PERMIT_LENGTH_ERROR)
                revert(ptr, 4)
            }

            // revert if not successful
            if iszero(success) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
    }

    /**
     * Executes credit delegation on given tokens / lenders
     * Note that for lenders like Aave V3, the token needs to
     * be the respective debt token and NOT the underlying
     * Others like compound will not use it at all.
     * @param token asset to permit / delegate
     * @param permitOffset calldata
     */
    function _tryCreditPermit(address token, uint256 permitOffset, uint256 permitLength, address callerAddress) internal {
        assembly {
            let ptr := mload(0x40)
            switch permitLength
            // Compact ICreditPermit
            case 100 {
                mstore(ptr, CREDIT_PERMIT) // store selector
                mstore(add(ptr, 0x04), callerAddress) // store owner
                mstore(add(ptr, 0x24), address()) // store spender

                // Compact ICreditPermit.delegationWithSig(uint256 value, uint32 deadline, uint256 r, uint256 vs)
                {
                    // stack too deep
                    let deadline := shr(224, calldataload(add(permitOffset, 0x20))) // loads permitOffset 0x20..0x23
                    let vs := calldataload(add(permitOffset, 0x44)) // loads permitOffset 0x44..0x63

                    calldatacopy(add(ptr, 0x44), permitOffset, 0x20) // store value     = copy permitOffset 0x00..0x19
                    mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline  = deadline - 1
                    mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v         = most significant bit of vs + 27 (27 or 28)
                    calldatacopy(add(ptr, 0xa4), add(permitOffset, 0x24), 0x20) // store r         = copy permitOffset 0x24..0x43
                    mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s         = vs without most significant bit
                }
                // ICreditPermit.delegationWithSig(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                if iszero(call(gas(), token, 0, ptr, 0xe4, 0, 0)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
            // Unknown
            default {
                mstore(ptr, _PERMIT_LENGTH_ERROR)
                revert(ptr, 4)
            }
        }
    }

    /**
     * Executes compound or morpho permit.
     * @param target target address to permit / delegate
     * @param permitOffset calldata
     * @param permitLength calldata
     */
    function _tryFlagBasedLendingPermit(address target, uint256 permitOffset, uint256 permitLength, address callerAddress) internal {
        assembly {
            let ptr := mload(0x40)
            switch permitLength
            // Compact ICreditPermit
            case 100 {
                let allowedAndNonce := calldataload(permitOffset) // load [allowed nonce] 2 single bits and number
                // mopho blue and CompoundV3 are similarly parametrized
                // if the second high bit is set, use Morpho
                switch and(SECOND_HIGH_BIT, allowedAndNonce)
                case 0 { mstore(ptr, COMPOUND_V3_CREDIT_PERMIT) }
                // store selector
                default { mstore(ptr, MORPHO_CREDIT_PERMIT) }

                mstore(add(ptr, 0x04), callerAddress) // store owner
                mstore(add(ptr, 0x24), address()) // store manager

                // Compact ICreditPermit.allowBySig(uint256 isAllowedAndNonce, uint32 expiry, uint256 r, uint256 vs)
                {
                    // stack too deep
                    let expiry := shr(224, calldataload(add(permitOffset, 0x20))) // loads permitOffset 0x20..0x23
                    let vs := calldataload(add(permitOffset, 0x44)) // loads permitOffset 0x44..0x63
                    // check if high bit is pupulated
                    mstore(add(ptr, 0x44), iszero(iszero(and(HIGH_BIT, allowedAndNonce))))
                    mstore(add(ptr, 0x64), and(LOWER_BITS, allowedAndNonce)) // nonce
                    mstore(add(ptr, 0x84), sub(expiry, 1)) // store expiry  = expiry - 1
                    mstore(add(ptr, 0xA4), add(27, shr(255, vs))) // store v         = most significant bit of vs + 27 (27 or 28)
                    calldatacopy(add(ptr, 0xC4), add(permitOffset, 0x24), 0x20) // store r         = copy permitOffset 0x24..0x43
                    mstore(add(ptr, 0xE4), shr(1, shl(1, vs))) // store s         = vs without most significant bit
                }
                // ICreditPermit.allowBySig(address owner, address manager, bool isAllowed, uint256 value, uint256 expiry, uint8 v, bytes32 r, bytes32 s)
                if iszero(call(gas(), target, 0, ptr, 0x104, 0, 0)) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
            }
            // Unknown
            default {
                mstore(ptr, _PERMIT_LENGTH_ERROR)
                revert(ptr, 4)
            }
        }
    }

    /// @notice transferERC20from version using permit2
    function _transferFromPermit2(address token, address to, uint256 amount, address callerAddress) internal {
        assembly {
            let ptr := mload(0x40)
            ////////////////////////////////////////////////////
            // transferFrom through permit2
            ////////////////////////////////////////////////////
            mstore(ptr, PERMIT2_TRANSFER_FROM)
            mstore(add(ptr, 0x04), callerAddress)
            mstore(add(ptr, 0x24), to)
            mstore(add(ptr, 0x44), amount)
            mstore(add(ptr, 0x64), token)
            if iszero(call(gas(), PERMIT2, 0, ptr, 0x84, 0x0, 0x0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
    }
}

File 49 of 49 : ERC20Selectors.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.28;

abstract contract ERC20Selectors {
    ////////////////////////////////////////////////////
    // ERC20 selectors
    ////////////////////////////////////////////////////

    /// @dev selector for approve(address,uint256)
    bytes32 internal constant ERC20_APPROVE = 0x095ea7b300000000000000000000000000000000000000000000000000000000;

    /// @dev selector for transferFrom(address,address,uint256)
    bytes32 internal constant ERC20_TRANSFER_FROM = 0x23b872dd00000000000000000000000000000000000000000000000000000000;

    /// @dev selector for transfer(address,uint256)
    bytes32 internal constant ERC20_TRANSFER = 0xa9059cbb00000000000000000000000000000000000000000000000000000000;

    /// @dev selector for allowance(address,address)
    bytes32 internal constant ERC20_ALLOWANCE = 0xdd62ed3e00000000000000000000000000000000000000000000000000000000;

    /// @dev selector for balanceOf(address)
    bytes32 internal constant ERC20_BALANCE_OF = 0x70a0823100000000000000000000000000000000000000000000000000000000;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "evmVersion": "shanghai",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"BadPool","type":"error"},{"inputs":[],"name":"BridgeFailed","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[{"internalType":"uint16","name":"assetId","type":"uint16"}],"name":"InvalidAssetId","type":"error"},{"inputs":[],"name":"InvalidCalldata","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidDex","type":"error"},{"inputs":[],"name":"InvalidDexId","type":"error"},{"inputs":[],"name":"InvalidFlashLoan","type":"error"},{"inputs":[],"name":"InvalidInitiator","type":"error"},{"inputs":[],"name":"InvalidOperation","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"SafePermitBadLength","type":"error"},{"inputs":[],"name":"Slippage","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"SlippageTooHigh","type":"error"},{"inputs":[],"name":"Target","type":"error"},{"inputs":[],"name":"WrapFailed","type":"error"},{"inputs":[],"name":"ZeroBalance","type":"error"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"deltaCompose","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoSupplyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080604052348015600e575f5ffd5b50615b8e8061001c5f395ff3fe60806040526004361061005d575f3560e01c80632075be03116100425780632075be03146100aa57806331f57072146100aa578063b1022fdf146100aa57610064565b806305b4591c146100aa57806317d73091146100cb57610064565b3661006457005b34801561006f575f5ffd5b507fffffffff000000000000000000000000000000000000000000000000000000005f351661009d816100de565b6100a68161025d565b5f5ffd5b3480156100b5575f5ffd5b506100c96100c4366004615ad1565b6103f6565b005b6100c96100d9366004615b19565b610403565b5f5f5f837ffa461e3300000000000000000000000000000000000000000000000000000000810361016e577fff203e8740894c8955cb8950759876d7e7e45e04c1000000000000000000000092507fe040f12c7cee3904b78f24f8fc395629c2e69525c2815da7a659f7483e378ecb93506024355f81136001811461016757600435935061016b565b8193505b50505b508115610257575f5f5f60983560601c905060ac358060601c604051878152601581018285105f81146101a9578582528360208301526101b3565b8382528560208301525b506affffffffffffffffffffff891680156101d3576040822082526101e7565b8460481c61ffff1660408301526060822082525b506020018990526055812073ffffffffffffffffffffffffffffffffffffffff163314610236577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b50508060381c61ffff1693505060843560601c91506100c98482848661041a565b50505050565b5f8060cc3581847fef2e17a40000000000000000000000000000000000000000000000000000000081016102db578260581c60ff1691507fff72d111b4d6f31b38919ae39779f570b747d6acd9000000000000000000000093507fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c630394505b5082156103ef575f806004353014610315577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b60b83560601c8460601c6040518183105f811461033a57826014830152838252610344565b8360148301528282525b505f92506082861160018114610362576028600c8301209350610375565b60bf871160348301536029600c83012093505b50878152826015820152886035820152336055822073ffffffffffffffffffffffffffffffffffffffff1618156103ce577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050508360481c61ffff16915060a43560601c90506100c98160e3846104d5565b5050505050565b6103fe6104e0565b505050565b602435610412336044836104d5565b6103fe61059a565b60028110156104cd576040515f82801561046e577fa9059cbb0000000000000000000000000000000000000000000000000000000083523360048401528660248401526020836044855f8a5af191506104b0565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528460048401523360248401528660448401526020836064855f8a5af191505b503d6001835114601f82111681151782166100c957805f5f3e805ffd5b6102578260c5835b6103fe83838361059f565b5f5f6064358060581c60ff165f811461051b577fbafe1c53000000000000000000000000000000000000000000000000000000005f5260045ffd5b3373d50f2dfffd62f94ee4aed9ca05c61d0753268abc1461055e577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060601c9150507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb60443501610596826079836104d5565b5050565b5f5fa0565b8181015b60018301923560f81c605081101561061057601081036105ce576105c784866106ad565b9350610660565b602081036105df576105c784610761565b603081036105f1576105c78585610812565b60408103610603576105c784866109a7565b61060b610a1d565b610660565b60508103610622576105c78486610a45565b60608103610634576105c78486610add565b60708103610646576105c78486610aef565b60808103610658576105c78486610b31565b610660610a1d565b81841061066d5750610673565b506105a3565b80831115610257576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60348201915f908035608081901c916fffffffffffffffffffffffffffffffff909116906020013560601c82610713577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f845afa505f5192505b61071f83828789610bb6565b509650925082821115610754577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b8593505050505b92915050565b60268101905f90803560601c90601481013560801c906024013560f01c6f800000000000000000000000000000008216801561079f574792506107b4565b6effffffffffffffffffffffffffffff831692505b506040517f6a0c90ff00000000000000000000000000000000000000000000000000000000815260206004820152816024820152818660448301375f5f836044018386885af1610806573d5f5f3e3d5ffd5b50939093019392505050565b60038101905f903560f881901c9060e81c61ffff168161088b576103e88110156108485761083f84610c4a565b9250505061075b565b6107d081101561085b5761083f84610d1b565b610bb881101561086e5761083f84610de1565b610fa08110156108815761083f84610ea1565b61083f848661105c565b600182036108d9576107d08110156108a75761083f848661118e565b610bb88110156108bb5761083f84866112f1565b610fa08110156108cf5761083f8486611388565b61083f8486611485565b60028203610925576107d08110156108f55761083f8486611579565b610bb88110156109085761083f8461172a565b610fa081101561091b5761083f84611882565b61083f8486611ae3565b60038203610973576107d08110156109415761083f8486611d8f565b610bb88110156109555761083f8486611ecc565b610fa08110156109695761083f8486612007565b61083f84866121fc565b600482036109855761083f8486612329565b600582036109975761083f8486612495565b61099f610a1d565b505092915050565b60018201915f903560f81c806109c9576109c184846125df565b91505061075b565b600181036109da576109c1846126a1565b600381036109eb576109c184612844565b600481036109fd576109c18484612995565b60058103610a0e576109c184612a47565b610a16610a1d565b5092915050565b7f398d4d32000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f5f5f5f5f86358060f81c94508060581c73ffffffffffffffffffffffffffffffffffffffff1693508060481c61ffff1692505060178701905081810196505f8403610aa357610a9783828489612b01565b8694505050505061075b565b60018403610ab757610a9783828489612d8c565b60028403610acb57610a9783828489612e61565b610ad3610a1d565b5050505092915050565b5f610ae88383612fd7565b9392505050565b60018201915f903560f81c80610b0f57610b0884612ff1565b9350610b29565b60018103610b2157610b0884846130f9565b610b29610a1d565b509192915050565b60018201915f903560f81c6004811015610b945780610b54576109c184846131bb565b60018103610b65576109c184613265565b60038103610b76576109c1846132e9565b60028103610b87576109c18461333f565b610b8f610a1d565b610a16565b60048103610ba5576109c18461338b565b60058103610a0e576109c1846133ff565b60028101905f90819081903560f881901c9060f01c60ff16818303610c2557805f03610c0b5760288601958035606090811c9450601490910135901c610c008a8a868b858c6134b0565b97509550610c3b9050565b610c1889828a8a8a613671565b9097509095509250610c3b565b610c3289838a8a8a613717565b90975090955092505b50849250509450945094915050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610cbd577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f617ba0370000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b509495945050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610d8e577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517fe8eda9df0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610e54577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550835f8114610f67576effffffffffffffffffffffffffffff841680610f19577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa50505f515b6040517f23323e030000000000000000000000000000000000000000000000000000000081528460048201528160248201525f5f6044835f885af1610f60573d5f5f3e3d5ffd5b5050610d10565b6effffffffffffffffffffffffffffff841680610f815750475b7f1249c58b000000000000000000000000000000000000000000000000000000005f525f5f60045f84875af1610fb9573d5f5f3e3d5ffd5b50308314610d10577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa505f516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528460048201528160248201526020816044835f885af191503d6001825114601f821116811517831661104e57805f5f3e805ffd5b505050509495945050505050565b5f604051806101000190507f238d6579000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c806024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806effffffffffffffffffffffffffffff16905080611118577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa50505f515b60a4830152506070840135606090811c60c483015261010060e4830152609a850194608481013590911c906098013560f01c80801561116c57606086901b6101248501526014018187610138860137958101955b806101048501525f5f6101248301865f875af1610d10573d9150815f5f3e815ffd5b5f823560601c601484013560801c60248501358060601c8160581c60ff169150603987013560601c604d88019750836effffffffffffffffffffffffffffff169350604051835f8114611236577fa415bcad0000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301525f60648301528860848301525f5f60a4845f875af1611231573d5f5f3e3d5ffd5b611286565b7f1d5d72370000000000000000000000000000000000000000000000000000000082528660048301528560248301525f60448301528860648301525f5f6084845f875af1611286573d5f5f3e3d5ffd5b503083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000081528260048201526020816044835f8a5af194503d93506001815114601f85111684151785169450846112e457835f5f3e835ffd5b5096979650505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f264413180000000000000000000000000000000000000000000000000000000085528660048601528160248601528360448601528260648601525f5f6084875f855af161137c573d5f5f3e3d5ffd5b50959695505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f856e5bb30000000000000000000000000000000000000000000000000000000085528660048601528260248601525f856044875f855af1611407573d5f5f3e3d5ffd5b50308114610d1057828015611475577fa9059cbb0000000000000000000000000000000000000000000000000000000085528160048601528260248601526020856044875f885af13d6001875114601f821116811517821691508161146e57805f5f3e805ffd5b505061137c565b5f5f5f5f86865af161137c575f5ffd5b5f6040517f50d8cd4b000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c6084830152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f8114611527575f60a48501528160c4850152611534565b8160a48501525f60c48501525b50505060e481018390526070840135606090811c610104830152609885019460840135901c5f806101248482855af1611570573d805f5f3e805ffd5b50929392505050565b5f823560601c601484013560801c60248501358060601c8160581c60ff169150826effffffffffffffffffffffffffffff169250825f81146115cf576dffffffffffffffffffffffffffff811461160957611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f52306004526020600460245f885afa5060045193508660045260205f60245f60398c013560601c5afa505f5184811015611663578094505b505b506040516061880197604d013560601c908380156116d2577f573ade810000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301528360648301525f5f6084845f875af16116cd573d5f5f3e3d5ffd5b61171c565b7f5ceae9c40000000000000000000000000000000000000000000000000000000082528660048301528560248301528360448301525f5f6064845f875af161171c573d5f5f3e3d5ffd5b509798975050505050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff169250825f8114611784576dffffffffffffffffffffffffffff81146117be57611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5193507f374c49b4000000000000000000000000000000000000000000000000000000005f528260045260205f60245f855afa505f5180851115611832578094505b505b506040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550604051845f81146119fc576effffffffffffffffffffffffffffff85168080156118e4576dffffffffffffffffffffffffffff811461191e576119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191506119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f5110156119b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507f2608f8180000000000000000000000000000000000000000000000000000000083526004830185905260248301525f8260448183875af16119f7573d5f5f3e3d5ffd5b61137c565b6effffffffffffffffffffffffffffff8516808015611a2f576dffffffffffffffffffffffffffff8114611a3757611a99565b479150611a99565b4791507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f511015611a99577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507fe5974619000000000000000000000000000000000000000000000000000000005f52846004525f5f60245f84885af1611ad6573d5f5f3e3d5ffd5b5050959695505050505050565b5f6040516101008101843560601c806004830152601486013560601c6024830152602886013560601c6044830152603c86013560601c6064830152605086013593508360801c6084830152836effffffffffffffffffffffffffffff16607087013560601c608488013560601c826dffffffffffffffffffffffffffff8114611ba9578015611cd8576f4000000000000000000000000000000088168015611b96575f60a48801528460c4880152611ba3565b8460a48801525f60c48801525b50611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611be1575f5ffd5b5f51935063151c1ade601c8703525f5f60a4885f865af1611c00575f5ffd5b60a060048701207f5c60e39a000000000000000000000000000000000000000000000000000000005f528060045260808860245f865afa611c3f575f5ffd5b604088015160608901517f93c52062000000000000000000000000000000000000000000000000000000008a528260048b01528560248b015260408a60448c885afa611c89575f5ffd5b60208a01519250620f4240810160018381018502909201620f423f010490878211908114611cc2575f60a48b01528360c48b0152611ccf565b8760a48b01525f60c48b01525b50505050611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611d10575f5ffd5b5f5160a48701525f60c48701525b5060e48501829052610120610104860152609a8901986098013560f01c95508593508315611d63576014840193508760601b6101448601528589610158870137978501975b6320b76e81601c860352836101248601525f5f6101448601875f855af16112e4573d9550855f5f3e855ffd5b5f604051833560601c601485013560801c602486013560601c816effffffffffffffffffffffffffffff169150603887013560601c604c88019750826dffffffffffffffffffffffffffff8103611e16577f70a08231000000000000000000000000000000000000000000000000000000005f528760045260205f60245f855afa505f5193505b507f23b872dd00000000000000000000000000000000000000000000000000000000855286600486015230602486015282604486015260205f6064875f855af190503d60015f5114601f8211168115178216915081611e7757805f5f3e805ffd5b50507f69328dec00000000000000000000000000000000000000000000000000000000845260048401929092526024830152604482015260148401933560601c5f8060648482855af1611570573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c60248601358060601c603988013560601c604d89019850836effffffffffffffffffffffffffffff1693506dffffffffffffffffffffffffffff8403611fb7578260581c60ff165f8114611f65577f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f855afa505f519450611fb5565b7f2b92a07d000000000000000000000000000000000000000000000000000000008752886004880152856024880152602087604489855afa5086516fffffffffffffffffffffffffffffffff1694505b505b7f264413180000000000000000000000000000000000000000000000000000000086528760048701528160248701528460448701528360648701525f5f6084885f855af16112e4573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692506dffffffffffffffffffffffffffff830361208e577f3af9e669000000000000000000000000000000000000000000000000000000005f528660045260205f60245f5f855af1505f5192505b7fbd6d894d000000000000000000000000000000000000000000000000000000005f5260205f60245f5f855af1505f51600181670de0b6b3a7640000860204017f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f865afa505f5191508181111561210e5750805b7f23b872dd0000000000000000000000000000000000000000000000000000000087528860048801523060248801528060448801526020876064895f875af191508161215c573d5f5f3e3d5ffd5b7fdb006a75000000000000000000000000000000000000000000000000000000005f5280600452505f5f60245f5f865af1612199573d5f5f3e3d5ffd5b3083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000086528260048701528360248701526020866044885f895af190503d91506001865114601f83111682151781169050806112e457815f5f3e815ffd5b5f6040517f8720316d000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c60848301528360c4830152607085013560601c60e4830152608485013560601c816effffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff82036122fa57610118830160a060048501207f93c5206200000000000000000000000000000000000000000000000000000000825280600483015250856024820152606081604483855afa6122f3575f5ffd5b6040015191505b8160a48401526098860195505f5f610104855f855af161231f573d9150815f5f3e815ffd5b5093949350505050565b5f604051806080019050833560601c7fa99aad89000000000000000000000000000000000000000000000000000000008252806004830152601485013560601c6024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f81146123d2575f60a48601528160c486015261241a565b8161240d577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5191505b8160a48601525f60c48601525b50505050607084013560601c608485013560601c609886013560f01c61ffff1680609a880197508360e4860152610120610104860152805f181561247357606087901b6101448601526014018188610158870137968101965b806101248601525f5f6101448301875f875af161137c573d9350835f5f3e835ffd5b5f6040516101008101843560601c6004820152601485013560601c6024820152602885013560601c6044820152603c85013560601c606482015260508501358060801c6084830152806effffffffffffffffffffffffffffff168560e4840152607087013560601c610104840152608487013560601c609888019750826f40000000000000000000000000000000165f811461253c575f60a48601528260c48601526125bb565b826dffffffffffffffffffffffffffff8114612563578360a48701525f60c48701526125b9565b60a060048701207f93c5206200000000000000000000000000000000000000000000000000000000885280600489015250886024880152602087604489865afa6125ab575f5ffd5b5f60a4870152865160c48701525b505b50635c2bea49601c8503525f5f610124865f855af161137c573d9450845f5f3e845ffd5b5f823560601c601484013560601c602885013560801c8061262f577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201526020816064835f885af193503d92506001815114601f841116831517841661269157825f5f3e825ffd5b5050506038939093019392505050565b5f813560601c601483013560601c60198401358060801c60ff16816fffffffffffffffffffffffffffffffff1691505f84155f811461275c578280156126e95784925061271c565b4792508483101561271c577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612757575f5f5f5f85895af1612757577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b612833565b82801561276b578492506127d0565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8a5afa505f519250848310156127d0577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612833576040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528560048201528260248201526020816044835f8b5af13d6001835114601f821116811517821661282f57805f5f3e805ffd5b5050505b505050603994909401949350505050565b5f8135606090811c906014840135901c60198401356effffffffffffffffffffffffffffff81169060801c60ff1684818015612882578391506128e7565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa505f519150838210156128e7577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015612985577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52806004525f5f60245f5f895af161294a577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308414612985575f5f5f5f84885af1612985577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050506039939093019392505050565b5f823560601c601484013560601c602885013560801c806129e5577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f36c785160000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201528360648201525f5f6084835f6e22d473030f116ddee9f6b43ac78ba35af1612691573d5f5f3e3d5ffd5b5f813560601c601483013560601c815f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054612af3576040517f095ea7b30000000000000000000000000000000000000000000000000000000081528260048201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526020816044835f885af15050600181555b505050602891909101919050565b6040515f8360648114612b465760488114612be25760608114612c88577f68275857000000000000000000000000000000000000000000000000000000008352600483fd5b7fd505accf000000000000000000000000000000000000000000000000000000008352836004840152306024840152602086013560e01c604487013560208860448701376001820360648601528060ff1c601b01608486015260206024890160a48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4850152505f8060e485828b5af19150612d76565b7f8fcbaf0c000000000000000000000000000000000000000000000000000000008352836004840152306024840152600486013560e01c6028870135873560e01c6044860152600182036064860152600160848601528060ff1c601b0160a486015260206008890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f8061010485828b5af19150612d76565b7f2b67b57000000000000000000000000000000000000000000000000000000000835283600484015286602484015260148660508501376001601487013560e01c0365ffffffffffff166064840152601886013560e01c60848401523060a48401526001601c87013560e01c0365ffffffffffff1660c484015261010060e48401526041610104840152604086013560208088016101248601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116610144850152601b60ff82901c01610164850153505f5f610165855f6e22d473030f116ddee9f6b43ac78ba35af191505b5080612d84573d5f5f3e3d5ffd5b505050505050565b6040518260648114612dc0577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b7f0b52d558000000000000000000000000000000000000000000000000000000008252826004830152306024830152602085013560e01c604486013560208760448601376001820360648501528060ff1c601b01608485015260206024880160a48601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4840152505f8060e484828a5af1612d84573d5f5f3e3d5ffd5b6040518260648114612e95577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b84357f400000000000000000000000000000000000000000000000000000000000000081168015612ee8577f8069218f000000000000000000000000000000000000000000000000000000008452612f0c565b7fbb24d9940000000000000000000000000000000000000000000000000000000084525b50836004840152306024840152602086013560e01c6044870135827f80000000000000000000000000000000000000000000000000000000000000001615156044860152827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660648601526001820360848601528060ff1c601b0160a486015260206024890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f90508061010484828a5af1612d84573d5f5f3e3d5ffd5b60018201915f903560f81c80610a0e576109c1848461374e565b60405160388201915f91908035606090811c91601481013590911c906028013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613067577f94bf804d0000000000000000000000000000000000000000000000000000000086526130c5565b7f6e553f65000000000000000000000000000000000000000000000000000000008652816130c5577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5191505b50600485015250843560601c60248401525f8060448582855af16130eb573d5f5f3e3d5ffd5b505050601491909101919050565b60405160248301925f9190803560601c906014013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613164577fba087652000000000000000000000000000000000000000000000000000000008552613188565b7fb460af940000000000000000000000000000000000000000000000000000000085525b50600484015250843560601c6024830152604482018490526014909401935f8060648482855af1611570573d5f5f3e3d5ffd5b5f82358060501c61ffff168160601c91506040517f48c89491000000000000000000000000000000000000000000000000000000008152602060048201526058820160248201527f480cf7ef000000000000000000000000000000000000000000000000000000006044820152602060488201526014820160688201528460601b60888201526016860195508186609c8301375f5f609c8401835f875af1610806573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517f0b0d9c090000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b5050506010939093019392505050565b7fa5841194000000000000000000000000000000000000000000000000000000005f9081526014820135606090811c600452602883019235901c818060248180855af1613338573d5f5f3e3d5ffd5b5090919050565b7f11da60b4000000000000000000000000000000000000000000000000000000005f9081526024820191803560601c906014013560801c828060048184865af1610b29573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517fae6393290000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b60288101905f908035606090811c9160140135901c833560801c7fffffffffffffffffffffffffffffffff00000000000000000000000000000001810161346357507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b8460100194506040517f15afd4090000000000000000000000000000000000000000000000000000000081528260048201528160248201525f5f6044835f885af161231f573d5f5f3e3d5ffd5b60018101905f9081903560f81c603f811161353f57806134e2576134d889898988888b6137ec565b9250925050613666565b600281036134f8576134d889898988888b61391c565b6001810361350e576134d889898988888b613c7f565b60058103613524576134d889898988888b613f3a565b6003810361353a576134d889898988888b614099565b61363e565b607f81116135b4576040810361355d576134d888888b888a896142ce565b60418103613572576134d8888a87898861470f565b60428103613588576134d888888b888a896148a2565b6050810361359e576134d8898989888a89614a0f565b605a810361353a576134d8898989888a89614b5b565b60bf811161362857608081036135d2576134d888888b888a89614c9d565b608181036135e8576134d889898988888b614e7e565b608c81036135fd576134d889898789886150a8565b60968103613613576134d8898989888a8961520a565b60a0810361353a576134d8898987898861541c565b60fe810361363e576134d888888b888a8961557e565b7f7948739e000000000000000000000000000000000000000000000000000000005f5260045ffd5b965096945050505050565b5f5f5f865f036136935761368788878787610bb6565b91995094509050613709565b600287028401933560801c5f808a5b5f8b8314600181146136c85761ffff8e87601087026070031c61ffff16020491506136cc565b8291505b506001830192505f6136e0828d8d8d610bb6565b909b5097509485019490508c8411156136fa575050613702565b5090036136a2565b5090995050505b969792969550919350505050565b845f84815b61372884838888610bb6565b909650909450915080881461373f5760010161371c565b50839150955095509592505050565b5f823560601c601484013560601c60288501358060801c8160701c61ffff169150603a870196506040517fe0232b42000000000000000000000000000000000000000000000000000000008152846004820152816024820152606060448201528260140160648201528660601b6084820152828860988301375f5f60988501835f885af16137de573d5f5f3e3d5ffd5b505094909401949350505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a107f128acb080000000000000000000000000000000000000000000000000000000084528860048501528060248501528b604485015260a06084850152604182018060a48601528760601b60c48601528b60601b60d88601528a60601b60ec86015260058301896101008701378180156138ac576401000276a460648701526040868360e401885f895af16138a0573d5f5f3e3d5ffd5b602086015197506138e3565b73fffd8963efd1fc6a506488495d951d5263988d2560648701526020868360e401885f895af16138de573d5f5f3e3d5ffd5b855197505b505050845f0394506002811060018114613903579681016005019661390a565b6005880197505b50869350505050965096945050505050565b5f5f60405160148501358060281c60ff1692508060181c61ffff167ff3cd914c0000000000000000000000000000000000000000000000000000000083528160481c62ffffff1660448401528160301c62ffffff166064840152863560601c8060848501528260601c9250610120610104850152816101248501528b5f0360c4850152603188019750815f18156139b65781886101448601375b898b1095508560a485015285600181146139f5578a60048601528b602486015273fffd8963efd1fc6a506488495d951d5263988d2560e4860152613a0d565b8b60048601528a60248601526401000276a460e48601525b5060205f8361014401865f875af1613a27573d5f5f3e3d5ffd5b801560018114613a9e577f9bf6645f0000000000000000000000000000000000000000000000000000000085526020600486015260026024860152305f528b60205260405f2060448601528a60205260405f206064860152608085608487875afa5060408501515f039c5060608501519650613adc565b5f519c508660018114613abd57600f8e900b5f039d60801c9750613ada565b60808e901d5f039d6fffffffffffffffffffffffffffffffff1697505b505b5050507f0b0d9c090000000000000000000000000000000000000000000000000000000082528760048301528660248301528360448301525f5f6064845f855af1613b29573d5f5f3e3d5ffd5b6002831015613c6f5788158015613b42578a9350613c36565b7fa5841194000000000000000000000000000000000000000000000000000000005f52896004525f5f60245f5f865af1613b7e573d5f5f3e3d5ffd5b838015613b925760018114613bd857613c14565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af19450613c14565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528260048501528b602485015260205f6044865f8f5af194505b503d60015f5114601f8211168115178516613c3157805f5f3e805ffd5b505f93505b507f11da60b4000000000000000000000000000000000000000000000000000000005f525f5f60045f86855af1613c6f573d5f5f3e3d5ffd5b5084915050965096945050505050565b5f5f60405184358060381c61ffff16925087891060808260481c60ff161060018114613cf8578260601c92507ff140a35a0000000000000000000000000000000000000000000000000000000084528b60048501528a602485015260205f604486865afa613cef573d5f5f3e3d5ffd5b5f519550613d73565b61ffff8360501c168360601c93507f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f875afa613d3e573d5f5f3e3d5ffd5b5f8360018114613d565760205191505f519850613d60565b60205198505f5191505b5061271002908d02908101960295909504945b507f022c0d9f000000000000000000000000000000000000000000000000000000008352808015613daf575f6004850152856024850152613dbc565b8560048501525f60248501525b505086604483015260806064830152600383105f8114613ed957838015613dea5760018114613e5357613eb2565b60c4840194507f23b872dd0000000000000000000000000000000000000000000000000000000085528660048601528260248601528b604486015260205f6064875f8f5af194503d60015f5114601f8211168115178616613e4d57805f5f3e805ffd5b50613eb2565b60c4840194507fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af194503d60015f5114601f8211168115178616613eb057805f5f3e805ffd5b505b505f60848401525f5f60a4855f865af1613ece573d5f5f3e3d5ffd5b601987019350613f2c565b603f840160848401528560601b60a48401528960601b60b88401528860601b60cc840152866016019650600384019350838760e08501375f5f8560e001855f865af1613f27573d5f5f3e3d5ffd5b928601925b505050965096945050505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a105f8114613ff4577f857f812f0000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff5f03604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376040858260c401875f885af1613fe7573d5f5f3e3d5ffd5b5060208401519550614080565b7f2c4812520000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376020858260c401875f885af161407a573d5f5f3e3d5ffd5b50835195505b506002811060018114613903579681016005019661390a565b5f5f604051843561ffff8160501c168160381c61ffff165f81146140c3576001811461412b575f5ffd5b8260601c92507f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af13d60015f5114601f821116811517821661412457805f5f3e805ffd5b5050614190565b8260601c92507f36c785160000000000000000000000000000000000000000000000000000000084528660048501528260248501528b60448501528a60648501525f5f6084865f6e22d473030f116ddee9f6b43ac78ba35af1614190573d5f5f3e3d5ffd5b50888a107f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f865afa6141cc573d5f5f3e3d5ffd5b60403d10156141d9575f5ffd5b5f81600181146141f15760205191505f5197506141fb565b5f51915060205197505b507f70a08231000000000000000000000000000000000000000000000000000000005f528360045260205f60245f8f5afa505f517f022c0d9f0000000000000000000000000000000000000000000000000000000086528190039c5061271002918c029182019582029590950494808015614281575f600486015286602486015261428e565b8660048601525f60248601525b5050876044840152608060648401525f60848401525f5f60a4855f865af16142b8573d5f5f3e3d5ffd5b5092996019959095019850939650505050505050565b5f8135816142de858a8985615893565b905060405160ff8360501c1660248201528760448201525f60648201525f60ff8460481c165f811461434e576001811461439757600281146143fa576003811461444357600481146144a657600581146144ef5760068114614552576007811461459b5760c881146145fe575f5ffd5b7fddc1f59d00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f3df0212400000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fa64833a000000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f5b41b90800000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f44ee198600000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7fa6417ed600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fe2ad025a00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f65b2489b00000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f9169558600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608484015260208360a4855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b5080614691573d5f5f3e3d5ffd5b815194503088188416156146fb577fa9059cbb00000000000000000000000000000000000000000000000000000000825287600483015284602483015260205f6044845f8e5af190503d60015f5114601f82111681151782169150816146f957805f5f3e805ffd5b505b505060198401915050965096945050505050565b5f5f604051833591508160601c8260381c61ffff16935060028410156147e6575f848015614744576001811461478a576147c6565b7f23b872dd00000000000000000000000000000000000000000000000000000000845287600485015282602485015289604485015260205f6064865f8f5af191506147c6565b7fa9059cbb00000000000000000000000000000000000000000000000000000000845282600485015289602485015260205f6044865f8f5af191505b503d60015f5114601f82111681151782166147e357805f5f3e805ffd5b50505b60ff8360481c165f81146148005760028114614828575f5ffd5b7fafb4301200000000000000000000000000000000000000000000000000000000835261484c565b7f767691e70000000000000000000000000000000000000000000000000000000083525b5060ff8360581c16600483015260ff8360501c1660248301528760448301525f606483015286608483015260205f60a4845f855af161488d573d5f5f3e3d5ffd5b50505f51915050601982019550959350505050565b5f8135816148b2858a8985615893565b90506040517f70a08231000000000000000000000000000000000000000000000000000000005f523060045260208160245f8c5afa508051935060ff8360481c1660038114614907576005811461492f575f5ffd5b7f5b41b908000000000000000000000000000000000000000000000000000000008252614953565b7f65b2489b0000000000000000000000000000000000000000000000000000000082525b5060ff8360581c16600482015260ff8360501c1660248201528760448201525f60648201525f5f6084835f865af161498d573d5f5f3e3d5ffd5b60205f60245f8c5afa50835f51039350308718156149fd577fa9059cbb0000000000000000000000000000000000000000000000000000000081528660048201528360248201526020816044835f8d5af13d6001835114601f82111681151782169150816146f957805f5f3e805ffd5b50505060198201965096945050505050565b5f5f604051833592508260581c60ff1691508260601c92506002821060018103614aea575f838015614a485760018114614a8e57614aca565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528560248501528b604485015260205f6064865f8f5af19150614aca565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528560048501528b602485015260205f6044865f8f5af191505b503d60015f5114601f8211168115178216614ae757805f5f3e805ffd5b50505b507f7dc203820000000000000000000000000000000000000000000000000000000081528760048201528660248201528860448201525f60648201528560848201525f60a482015260208160c4835f875af1614b48573d5f5f3e3d5ffd5b5191505060158201965096945050505050565b5f5f60405183358060601c8160581c60ff165f8114614b815760018114614be357614c3b565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528160248501528b604485015260205f6064865f8f5af13d60015f5114601f8211168115178216614bdc57805f5f3e805ffd5b5050614c3b565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af13d60015f5114601f8211168115178216614c3857805f5f3e805ffd5b50505b507f933162120000000000000000000000000000000000000000000000000000000083528960048401528860248401528760448401526020836064855f855af1614c87573d5f5f3e3d5ffd5b5050519860159390930197509195505050505050565b5f5f826020013590506040518160581c60ff16614d12577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f8211168115178216614d0f57805f5f3e805ffd5b50505b8160601c895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054614db0577f095ea7b30000000000000000000000000000000000000000000000000000000083528160048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f52bbbe2900000000000000000000000000000000000000000000000000000000825260e060048301523060248301525f60448301528660648301525f60848301525f60a48301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c4830152843560e48301525f61010483015289610124830152886101448301528761016483015260c06101848301525f6101a483015260205f6101c4845f855af1614e68573d5f5f3e3d5ffd5b50505f5191505060358201965096945050505050565b5f5f604051843560601c6084820152843560601c60148601955085356017870196508060581c60ff1693508060481c61ffff168160601c91507f2bfb780c0000000000000000000000000000000000000000000000000000000084525f60048501528260248501528a60448501528960648501528b6084850152600160a485015260e060c48501528060e4850152805f1815614f21578088610104860137968701965b6060848261010401865f865af1614f3a573d5f5f3e3d5ffd5b5060208301519a50604083015194507fae6393290000000000000000000000000000000000000000000000000000000083528860048401528760248401528460448401525f5f6064855f855af1614f93573d5f5f3e3d5ffd5b6002841015615097575f9150838015614fb35760018114614ff957615035565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528160248501528b604485015260205f6064865f8f5af19250615035565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af192505b503d60015f5114601f821116811517831661505257805f5f3e805ffd5b507f15afd4090000000000000000000000000000000000000000000000000000000083528960048401528a60248401525f5f6044855f855af1615097573d5f5f3e3d5ffd5b508592505050965096945050505050565b5f5f60405183358060601c8160501c60ff169350600284101561517c575f8480156150da57600181146151205761515c565b7f23b872dd0000000000000000000000000000000000000000000000000000000085528860048601528260248601528b604486015260205f6064875f8f5af1915061515c565b7fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af191505b503d60015f5114601f821116811517821661517957805f5f3e805ffd5b50505b8160581c60ff1691507f53c059a00000000000000000000000000000000000000000000000000000000083528160048401528760248401526020836044855f855af16151ca573d5f5f3e3d5ffd5b508080156151de57825160801c94506151f5565b6fffffffffffffffffffffffffffffffff83511694505b50929860169490940197509295505050505050565b5f5f5f615219898987876159ab565b60405192955093509150600383106001811461538b5761010082017fd0a494e40000000000000000000000000000000000000000000000000000000081528b60248401528560581c60ff165f81146152d9577f66410a210000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa6152a9573d5f5f3e3d5ffd5b5f5196508660048301525f60248301528860601b60a48301528a60601b60b88301528b60601b60cc830152615343565b7f79a048760000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa615317573d5f5f3e3d5ffd5b5f5196505f60048301528660248301528860601b60a48301528b60601b60b88301528a60601b60cc8301525b5088604482015260806064820152846040016084820152600485018760150160e08301375f5f8660e401835f885af161537e573d5f5f3e3d5ffd5b509483016019019461540b565b8460581c60ff165f81146153c1577fdd93f59a000000000000000000000000000000000000000000000000000000005f526153e5565b7fbd6015b4000000000000000000000000000000000000000000000000000000005f525b508760045260205f60245f5f875af1615400573d5f5f3e3d5ffd5b5f5194508560190195505b508492505050965096945050505050565b5f5f82356040518160581c60ff1692508160601c915060028310156154f2575f8380156154505760018114615496576154d2565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528760048401528360248401528a604484015260205f6064855f8e5af191506154d2565b7fa9059cbb0000000000000000000000000000000000000000000000000000000083528360048401528a602484015260205f6044855f8e5af191505b503d60015f5114601f82111681151782166154ef57805f5f3e805ffd5b50505b7f7132bb7f000000000000000000000000000000000000000000000000000000008152608060048201525f60248201525f60448201526101006064820152606060848201528760a48201528660c48201525f60e48201525f610104820152604081610124835f865af1615567573d5f5f3e3d5ffd5b602001519860159490940197509295505050505050565b5f5f8235905060405160ff8260f01c166155f0577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f82111681151782166155ed57805f5f3e805ffd5b50505b8160f81c5f8114615657576001811461579c577fba08765200000000000000000000000000000000000000000000000000000000825287600483015286602483015230604483015260205f6064845f8e5af161564e573d5f5f3e3d5ffd5b5f519350615881565b879350891560018114615700577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52886004525f5f60245f5f8f5af16156c0577fc30d93ce000000000000000000000000000000000000000000000000000000005f5260045ffd5b3088146156fb575f5f5f5f8c8c5af16156fb577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b615796565b5f5f5f5f8c8e5af1615734577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308814615796577fa9059cbb00000000000000000000000000000000000000000000000000000000835287600484015288602484015260205f6044855f8e5af13d60015f5114601f821116811517821691508161579357805f5f3e805ffd5b50505b50615881565b895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052885f5260405f208054615836577f095ea7b30000000000000000000000000000000000000000000000000000000083528960048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f6e553f6500000000000000000000000000000000000000000000000000000000825287600483015286602483015260205f6044845f8d5af161587c573d5f5f3e3d5ffd5b5f5193505b50505060028201965096945050505050565b5f6040518260601c9150845f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052815f5260405f208054615937577f095ea7b30000000000000000000000000000000000000000000000000000000082528260048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301526020826044845f8a5af150600181555b508260381c61ffff166159a2577f23b872dd00000000000000000000000000000000000000000000000000000000815285600482015230602482015283604482015260205f6064835f895af13d60015f5114601f821116811517821661599f57805f5f3e805ffd5b50505b50949350505050565b604051813590606082901c9061ffff603884901c16906002821015615a81575f8280156159df5760018114615a2557615a61565b7f23b872dd00000000000000000000000000000000000000000000000000000000835287600484015284602484015289604484015260205f6064855f8d5af19150615a61565b7fa9059cbb00000000000000000000000000000000000000000000000000000000835284600484015289602484015260205f6044855f8d5af191505b503d60015f5114601f8211168115178216615a7e57805f5f3e805ffd5b50505b509450945094915050565b5f5f83601f840112615a9c575f5ffd5b50813567ffffffffffffffff811115615ab3575f5ffd5b602083019150836020828501011115615aca575f5ffd5b9250929050565b5f5f5f60408486031215615ae3575f5ffd5b83359250602084013567ffffffffffffffff811115615b00575f5ffd5b615b0c86828701615a8c565b9497909650939450505050565b5f5f60208385031215615b2a575f5ffd5b823567ffffffffffffffff811115615b40575f5ffd5b615b4c85828601615a8c565b9096909550935050505056fea2646970667358221220be71ab4bfdc67325f0fe02c45071abb89f031a89e6d0733865dd66f4cbd088c264736f6c634300081c0033

Deployed Bytecode

0x60806040526004361061005d575f3560e01c80632075be03116100425780632075be03146100aa57806331f57072146100aa578063b1022fdf146100aa57610064565b806305b4591c146100aa57806317d73091146100cb57610064565b3661006457005b34801561006f575f5ffd5b507fffffffff000000000000000000000000000000000000000000000000000000005f351661009d816100de565b6100a68161025d565b5f5ffd5b3480156100b5575f5ffd5b506100c96100c4366004615ad1565b6103f6565b005b6100c96100d9366004615b19565b610403565b5f5f5f837ffa461e3300000000000000000000000000000000000000000000000000000000810361016e577fff203e8740894c8955cb8950759876d7e7e45e04c1000000000000000000000092507fe040f12c7cee3904b78f24f8fc395629c2e69525c2815da7a659f7483e378ecb93506024355f81136001811461016757600435935061016b565b8193505b50505b508115610257575f5f5f60983560601c905060ac358060601c604051878152601581018285105f81146101a9578582528360208301526101b3565b8382528560208301525b506affffffffffffffffffffff891680156101d3576040822082526101e7565b8460481c61ffff1660408301526060822082525b506020018990526055812073ffffffffffffffffffffffffffffffffffffffff163314610236577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b50508060381c61ffff1693505060843560601c91506100c98482848661041a565b50505050565b5f8060cc3581847fef2e17a40000000000000000000000000000000000000000000000000000000081016102db578260581c60ff1691507fff72d111b4d6f31b38919ae39779f570b747d6acd9000000000000000000000093507fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c630394505b5082156103ef575f806004353014610315577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b60b83560601c8460601c6040518183105f811461033a57826014830152838252610344565b8360148301528282525b505f92506082861160018114610362576028600c8301209350610375565b60bf871160348301536029600c83012093505b50878152826015820152886035820152336055822073ffffffffffffffffffffffffffffffffffffffff1618156103ce577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050508360481c61ffff16915060a43560601c90506100c98160e3846104d5565b5050505050565b6103fe6104e0565b505050565b602435610412336044836104d5565b6103fe61059a565b60028110156104cd576040515f82801561046e577fa9059cbb0000000000000000000000000000000000000000000000000000000083523360048401528660248401526020836044855f8a5af191506104b0565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528460048401523360248401528660448401526020836064855f8a5af191505b503d6001835114601f82111681151782166100c957805f5f3e805ffd5b6102578260c5835b6103fe83838361059f565b5f5f6064358060581c60ff165f811461051b577fbafe1c53000000000000000000000000000000000000000000000000000000005f5260045ffd5b3373d50f2dfffd62f94ee4aed9ca05c61d0753268abc1461055e577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060601c9150507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb60443501610596826079836104d5565b5050565b5f5fa0565b8181015b60018301923560f81c605081101561061057601081036105ce576105c784866106ad565b9350610660565b602081036105df576105c784610761565b603081036105f1576105c78585610812565b60408103610603576105c784866109a7565b61060b610a1d565b610660565b60508103610622576105c78486610a45565b60608103610634576105c78486610add565b60708103610646576105c78486610aef565b60808103610658576105c78486610b31565b610660610a1d565b81841061066d5750610673565b506105a3565b80831115610257576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60348201915f908035608081901c916fffffffffffffffffffffffffffffffff909116906020013560601c82610713577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f845afa505f5192505b61071f83828789610bb6565b509650925082821115610754577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b8593505050505b92915050565b60268101905f90803560601c90601481013560801c906024013560f01c6f800000000000000000000000000000008216801561079f574792506107b4565b6effffffffffffffffffffffffffffff831692505b506040517f6a0c90ff00000000000000000000000000000000000000000000000000000000815260206004820152816024820152818660448301375f5f836044018386885af1610806573d5f5f3e3d5ffd5b50939093019392505050565b60038101905f903560f881901c9060e81c61ffff168161088b576103e88110156108485761083f84610c4a565b9250505061075b565b6107d081101561085b5761083f84610d1b565b610bb881101561086e5761083f84610de1565b610fa08110156108815761083f84610ea1565b61083f848661105c565b600182036108d9576107d08110156108a75761083f848661118e565b610bb88110156108bb5761083f84866112f1565b610fa08110156108cf5761083f8486611388565b61083f8486611485565b60028203610925576107d08110156108f55761083f8486611579565b610bb88110156109085761083f8461172a565b610fa081101561091b5761083f84611882565b61083f8486611ae3565b60038203610973576107d08110156109415761083f8486611d8f565b610bb88110156109555761083f8486611ecc565b610fa08110156109695761083f8486612007565b61083f84866121fc565b600482036109855761083f8486612329565b600582036109975761083f8486612495565b61099f610a1d565b505092915050565b60018201915f903560f81c806109c9576109c184846125df565b91505061075b565b600181036109da576109c1846126a1565b600381036109eb576109c184612844565b600481036109fd576109c18484612995565b60058103610a0e576109c184612a47565b610a16610a1d565b5092915050565b7f398d4d32000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f5f5f5f5f86358060f81c94508060581c73ffffffffffffffffffffffffffffffffffffffff1693508060481c61ffff1692505060178701905081810196505f8403610aa357610a9783828489612b01565b8694505050505061075b565b60018403610ab757610a9783828489612d8c565b60028403610acb57610a9783828489612e61565b610ad3610a1d565b5050505092915050565b5f610ae88383612fd7565b9392505050565b60018201915f903560f81c80610b0f57610b0884612ff1565b9350610b29565b60018103610b2157610b0884846130f9565b610b29610a1d565b509192915050565b60018201915f903560f81c6004811015610b945780610b54576109c184846131bb565b60018103610b65576109c184613265565b60038103610b76576109c1846132e9565b60028103610b87576109c18461333f565b610b8f610a1d565b610a16565b60048103610ba5576109c18461338b565b60058103610a0e576109c1846133ff565b60028101905f90819081903560f881901c9060f01c60ff16818303610c2557805f03610c0b5760288601958035606090811c9450601490910135901c610c008a8a868b858c6134b0565b97509550610c3b9050565b610c1889828a8a8a613671565b9097509095509250610c3b565b610c3289838a8a8a613717565b90975090955092505b50849250509450945094915050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610cbd577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f617ba0370000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b509495945050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610d8e577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517fe8eda9df0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610e54577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550835f8114610f67576effffffffffffffffffffffffffffff841680610f19577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa50505f515b6040517f23323e030000000000000000000000000000000000000000000000000000000081528460048201528160248201525f5f6044835f885af1610f60573d5f5f3e3d5ffd5b5050610d10565b6effffffffffffffffffffffffffffff841680610f815750475b7f1249c58b000000000000000000000000000000000000000000000000000000005f525f5f60045f84875af1610fb9573d5f5f3e3d5ffd5b50308314610d10577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa505f516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528460048201528160248201526020816044835f885af191503d6001825114601f821116811517831661104e57805f5f3e805ffd5b505050509495945050505050565b5f604051806101000190507f238d6579000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c806024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806effffffffffffffffffffffffffffff16905080611118577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa50505f515b60a4830152506070840135606090811c60c483015261010060e4830152609a850194608481013590911c906098013560f01c80801561116c57606086901b6101248501526014018187610138860137958101955b806101048501525f5f6101248301865f875af1610d10573d9150815f5f3e815ffd5b5f823560601c601484013560801c60248501358060601c8160581c60ff169150603987013560601c604d88019750836effffffffffffffffffffffffffffff169350604051835f8114611236577fa415bcad0000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301525f60648301528860848301525f5f60a4845f875af1611231573d5f5f3e3d5ffd5b611286565b7f1d5d72370000000000000000000000000000000000000000000000000000000082528660048301528560248301525f60448301528860648301525f5f6084845f875af1611286573d5f5f3e3d5ffd5b503083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000081528260048201526020816044835f8a5af194503d93506001815114601f85111684151785169450846112e457835f5f3e835ffd5b5096979650505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f264413180000000000000000000000000000000000000000000000000000000085528660048601528160248601528360448601528260648601525f5f6084875f855af161137c573d5f5f3e3d5ffd5b50959695505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f856e5bb30000000000000000000000000000000000000000000000000000000085528660048601528260248601525f856044875f855af1611407573d5f5f3e3d5ffd5b50308114610d1057828015611475577fa9059cbb0000000000000000000000000000000000000000000000000000000085528160048601528260248601526020856044875f885af13d6001875114601f821116811517821691508161146e57805f5f3e805ffd5b505061137c565b5f5f5f5f86865af161137c575f5ffd5b5f6040517f50d8cd4b000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c6084830152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f8114611527575f60a48501528160c4850152611534565b8160a48501525f60c48501525b50505060e481018390526070840135606090811c610104830152609885019460840135901c5f806101248482855af1611570573d805f5f3e805ffd5b50929392505050565b5f823560601c601484013560801c60248501358060601c8160581c60ff169150826effffffffffffffffffffffffffffff169250825f81146115cf576dffffffffffffffffffffffffffff811461160957611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f52306004526020600460245f885afa5060045193508660045260205f60245f60398c013560601c5afa505f5184811015611663578094505b505b506040516061880197604d013560601c908380156116d2577f573ade810000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301528360648301525f5f6084845f875af16116cd573d5f5f3e3d5ffd5b61171c565b7f5ceae9c40000000000000000000000000000000000000000000000000000000082528660048301528560248301528360448301525f5f6064845f875af161171c573d5f5f3e3d5ffd5b509798975050505050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff169250825f8114611784576dffffffffffffffffffffffffffff81146117be57611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5193507f374c49b4000000000000000000000000000000000000000000000000000000005f528260045260205f60245f855afa505f5180851115611832578094505b505b506040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550604051845f81146119fc576effffffffffffffffffffffffffffff85168080156118e4576dffffffffffffffffffffffffffff811461191e576119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191506119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f5110156119b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507f2608f8180000000000000000000000000000000000000000000000000000000083526004830185905260248301525f8260448183875af16119f7573d5f5f3e3d5ffd5b61137c565b6effffffffffffffffffffffffffffff8516808015611a2f576dffffffffffffffffffffffffffff8114611a3757611a99565b479150611a99565b4791507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f511015611a99577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507fe5974619000000000000000000000000000000000000000000000000000000005f52846004525f5f60245f84885af1611ad6573d5f5f3e3d5ffd5b5050959695505050505050565b5f6040516101008101843560601c806004830152601486013560601c6024830152602886013560601c6044830152603c86013560601c6064830152605086013593508360801c6084830152836effffffffffffffffffffffffffffff16607087013560601c608488013560601c826dffffffffffffffffffffffffffff8114611ba9578015611cd8576f4000000000000000000000000000000088168015611b96575f60a48801528460c4880152611ba3565b8460a48801525f60c48801525b50611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611be1575f5ffd5b5f51935063151c1ade601c8703525f5f60a4885f865af1611c00575f5ffd5b60a060048701207f5c60e39a000000000000000000000000000000000000000000000000000000005f528060045260808860245f865afa611c3f575f5ffd5b604088015160608901517f93c52062000000000000000000000000000000000000000000000000000000008a528260048b01528560248b015260408a60448c885afa611c89575f5ffd5b60208a01519250620f4240810160018381018502909201620f423f010490878211908114611cc2575f60a48b01528360c48b0152611ccf565b8760a48b01525f60c48b01525b50505050611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611d10575f5ffd5b5f5160a48701525f60c48701525b5060e48501829052610120610104860152609a8901986098013560f01c95508593508315611d63576014840193508760601b6101448601528589610158870137978501975b6320b76e81601c860352836101248601525f5f6101448601875f855af16112e4573d9550855f5f3e855ffd5b5f604051833560601c601485013560801c602486013560601c816effffffffffffffffffffffffffffff169150603887013560601c604c88019750826dffffffffffffffffffffffffffff8103611e16577f70a08231000000000000000000000000000000000000000000000000000000005f528760045260205f60245f855afa505f5193505b507f23b872dd00000000000000000000000000000000000000000000000000000000855286600486015230602486015282604486015260205f6064875f855af190503d60015f5114601f8211168115178216915081611e7757805f5f3e805ffd5b50507f69328dec00000000000000000000000000000000000000000000000000000000845260048401929092526024830152604482015260148401933560601c5f8060648482855af1611570573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c60248601358060601c603988013560601c604d89019850836effffffffffffffffffffffffffffff1693506dffffffffffffffffffffffffffff8403611fb7578260581c60ff165f8114611f65577f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f855afa505f519450611fb5565b7f2b92a07d000000000000000000000000000000000000000000000000000000008752886004880152856024880152602087604489855afa5086516fffffffffffffffffffffffffffffffff1694505b505b7f264413180000000000000000000000000000000000000000000000000000000086528760048701528160248701528460448701528360648701525f5f6084885f855af16112e4573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692506dffffffffffffffffffffffffffff830361208e577f3af9e669000000000000000000000000000000000000000000000000000000005f528660045260205f60245f5f855af1505f5192505b7fbd6d894d000000000000000000000000000000000000000000000000000000005f5260205f60245f5f855af1505f51600181670de0b6b3a7640000860204017f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f865afa505f5191508181111561210e5750805b7f23b872dd0000000000000000000000000000000000000000000000000000000087528860048801523060248801528060448801526020876064895f875af191508161215c573d5f5f3e3d5ffd5b7fdb006a75000000000000000000000000000000000000000000000000000000005f5280600452505f5f60245f5f865af1612199573d5f5f3e3d5ffd5b3083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000086528260048701528360248701526020866044885f895af190503d91506001865114601f83111682151781169050806112e457815f5f3e815ffd5b5f6040517f8720316d000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c60848301528360c4830152607085013560601c60e4830152608485013560601c816effffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff82036122fa57610118830160a060048501207f93c5206200000000000000000000000000000000000000000000000000000000825280600483015250856024820152606081604483855afa6122f3575f5ffd5b6040015191505b8160a48401526098860195505f5f610104855f855af161231f573d9150815f5f3e815ffd5b5093949350505050565b5f604051806080019050833560601c7fa99aad89000000000000000000000000000000000000000000000000000000008252806004830152601485013560601c6024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f81146123d2575f60a48601528160c486015261241a565b8161240d577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5191505b8160a48601525f60c48601525b50505050607084013560601c608485013560601c609886013560f01c61ffff1680609a880197508360e4860152610120610104860152805f181561247357606087901b6101448601526014018188610158870137968101965b806101248601525f5f6101448301875f875af161137c573d9350835f5f3e835ffd5b5f6040516101008101843560601c6004820152601485013560601c6024820152602885013560601c6044820152603c85013560601c606482015260508501358060801c6084830152806effffffffffffffffffffffffffffff168560e4840152607087013560601c610104840152608487013560601c609888019750826f40000000000000000000000000000000165f811461253c575f60a48601528260c48601526125bb565b826dffffffffffffffffffffffffffff8114612563578360a48701525f60c48701526125b9565b60a060048701207f93c5206200000000000000000000000000000000000000000000000000000000885280600489015250886024880152602087604489865afa6125ab575f5ffd5b5f60a4870152865160c48701525b505b50635c2bea49601c8503525f5f610124865f855af161137c573d9450845f5f3e845ffd5b5f823560601c601484013560601c602885013560801c8061262f577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201526020816064835f885af193503d92506001815114601f841116831517841661269157825f5f3e825ffd5b5050506038939093019392505050565b5f813560601c601483013560601c60198401358060801c60ff16816fffffffffffffffffffffffffffffffff1691505f84155f811461275c578280156126e95784925061271c565b4792508483101561271c577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612757575f5f5f5f85895af1612757577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b612833565b82801561276b578492506127d0565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8a5afa505f519250848310156127d0577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612833576040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528560048201528260248201526020816044835f8b5af13d6001835114601f821116811517821661282f57805f5f3e805ffd5b5050505b505050603994909401949350505050565b5f8135606090811c906014840135901c60198401356effffffffffffffffffffffffffffff81169060801c60ff1684818015612882578391506128e7565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa505f519150838210156128e7577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015612985577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52806004525f5f60245f5f895af161294a577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308414612985575f5f5f5f84885af1612985577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050506039939093019392505050565b5f823560601c601484013560601c602885013560801c806129e5577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f36c785160000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201528360648201525f5f6084835f6e22d473030f116ddee9f6b43ac78ba35af1612691573d5f5f3e3d5ffd5b5f813560601c601483013560601c815f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054612af3576040517f095ea7b30000000000000000000000000000000000000000000000000000000081528260048201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526020816044835f885af15050600181555b505050602891909101919050565b6040515f8360648114612b465760488114612be25760608114612c88577f68275857000000000000000000000000000000000000000000000000000000008352600483fd5b7fd505accf000000000000000000000000000000000000000000000000000000008352836004840152306024840152602086013560e01c604487013560208860448701376001820360648601528060ff1c601b01608486015260206024890160a48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4850152505f8060e485828b5af19150612d76565b7f8fcbaf0c000000000000000000000000000000000000000000000000000000008352836004840152306024840152600486013560e01c6028870135873560e01c6044860152600182036064860152600160848601528060ff1c601b0160a486015260206008890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f8061010485828b5af19150612d76565b7f2b67b57000000000000000000000000000000000000000000000000000000000835283600484015286602484015260148660508501376001601487013560e01c0365ffffffffffff166064840152601886013560e01c60848401523060a48401526001601c87013560e01c0365ffffffffffff1660c484015261010060e48401526041610104840152604086013560208088016101248601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116610144850152601b60ff82901c01610164850153505f5f610165855f6e22d473030f116ddee9f6b43ac78ba35af191505b5080612d84573d5f5f3e3d5ffd5b505050505050565b6040518260648114612dc0577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b7f0b52d558000000000000000000000000000000000000000000000000000000008252826004830152306024830152602085013560e01c604486013560208760448601376001820360648501528060ff1c601b01608485015260206024880160a48601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4840152505f8060e484828a5af1612d84573d5f5f3e3d5ffd5b6040518260648114612e95577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b84357f400000000000000000000000000000000000000000000000000000000000000081168015612ee8577f8069218f000000000000000000000000000000000000000000000000000000008452612f0c565b7fbb24d9940000000000000000000000000000000000000000000000000000000084525b50836004840152306024840152602086013560e01c6044870135827f80000000000000000000000000000000000000000000000000000000000000001615156044860152827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660648601526001820360848601528060ff1c601b0160a486015260206024890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f90508061010484828a5af1612d84573d5f5f3e3d5ffd5b60018201915f903560f81c80610a0e576109c1848461374e565b60405160388201915f91908035606090811c91601481013590911c906028013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613067577f94bf804d0000000000000000000000000000000000000000000000000000000086526130c5565b7f6e553f65000000000000000000000000000000000000000000000000000000008652816130c5577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5191505b50600485015250843560601c60248401525f8060448582855af16130eb573d5f5f3e3d5ffd5b505050601491909101919050565b60405160248301925f9190803560601c906014013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613164577fba087652000000000000000000000000000000000000000000000000000000008552613188565b7fb460af940000000000000000000000000000000000000000000000000000000085525b50600484015250843560601c6024830152604482018490526014909401935f8060648482855af1611570573d5f5f3e3d5ffd5b5f82358060501c61ffff168160601c91506040517f48c89491000000000000000000000000000000000000000000000000000000008152602060048201526058820160248201527f480cf7ef000000000000000000000000000000000000000000000000000000006044820152602060488201526014820160688201528460601b60888201526016860195508186609c8301375f5f609c8401835f875af1610806573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517f0b0d9c090000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b5050506010939093019392505050565b7fa5841194000000000000000000000000000000000000000000000000000000005f9081526014820135606090811c600452602883019235901c818060248180855af1613338573d5f5f3e3d5ffd5b5090919050565b7f11da60b4000000000000000000000000000000000000000000000000000000005f9081526024820191803560601c906014013560801c828060048184865af1610b29573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517fae6393290000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b60288101905f908035606090811c9160140135901c833560801c7fffffffffffffffffffffffffffffffff00000000000000000000000000000001810161346357507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b8460100194506040517f15afd4090000000000000000000000000000000000000000000000000000000081528260048201528160248201525f5f6044835f885af161231f573d5f5f3e3d5ffd5b60018101905f9081903560f81c603f811161353f57806134e2576134d889898988888b6137ec565b9250925050613666565b600281036134f8576134d889898988888b61391c565b6001810361350e576134d889898988888b613c7f565b60058103613524576134d889898988888b613f3a565b6003810361353a576134d889898988888b614099565b61363e565b607f81116135b4576040810361355d576134d888888b888a896142ce565b60418103613572576134d8888a87898861470f565b60428103613588576134d888888b888a896148a2565b6050810361359e576134d8898989888a89614a0f565b605a810361353a576134d8898989888a89614b5b565b60bf811161362857608081036135d2576134d888888b888a89614c9d565b608181036135e8576134d889898988888b614e7e565b608c81036135fd576134d889898789886150a8565b60968103613613576134d8898989888a8961520a565b60a0810361353a576134d8898987898861541c565b60fe810361363e576134d888888b888a8961557e565b7f7948739e000000000000000000000000000000000000000000000000000000005f5260045ffd5b965096945050505050565b5f5f5f865f036136935761368788878787610bb6565b91995094509050613709565b600287028401933560801c5f808a5b5f8b8314600181146136c85761ffff8e87601087026070031c61ffff16020491506136cc565b8291505b506001830192505f6136e0828d8d8d610bb6565b909b5097509485019490508c8411156136fa575050613702565b5090036136a2565b5090995050505b969792969550919350505050565b845f84815b61372884838888610bb6565b909650909450915080881461373f5760010161371c565b50839150955095509592505050565b5f823560601c601484013560601c60288501358060801c8160701c61ffff169150603a870196506040517fe0232b42000000000000000000000000000000000000000000000000000000008152846004820152816024820152606060448201528260140160648201528660601b6084820152828860988301375f5f60988501835f885af16137de573d5f5f3e3d5ffd5b505094909401949350505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a107f128acb080000000000000000000000000000000000000000000000000000000084528860048501528060248501528b604485015260a06084850152604182018060a48601528760601b60c48601528b60601b60d88601528a60601b60ec86015260058301896101008701378180156138ac576401000276a460648701526040868360e401885f895af16138a0573d5f5f3e3d5ffd5b602086015197506138e3565b73fffd8963efd1fc6a506488495d951d5263988d2560648701526020868360e401885f895af16138de573d5f5f3e3d5ffd5b855197505b505050845f0394506002811060018114613903579681016005019661390a565b6005880197505b50869350505050965096945050505050565b5f5f60405160148501358060281c60ff1692508060181c61ffff167ff3cd914c0000000000000000000000000000000000000000000000000000000083528160481c62ffffff1660448401528160301c62ffffff166064840152863560601c8060848501528260601c9250610120610104850152816101248501528b5f0360c4850152603188019750815f18156139b65781886101448601375b898b1095508560a485015285600181146139f5578a60048601528b602486015273fffd8963efd1fc6a506488495d951d5263988d2560e4860152613a0d565b8b60048601528a60248601526401000276a460e48601525b5060205f8361014401865f875af1613a27573d5f5f3e3d5ffd5b801560018114613a9e577f9bf6645f0000000000000000000000000000000000000000000000000000000085526020600486015260026024860152305f528b60205260405f2060448601528a60205260405f206064860152608085608487875afa5060408501515f039c5060608501519650613adc565b5f519c508660018114613abd57600f8e900b5f039d60801c9750613ada565b60808e901d5f039d6fffffffffffffffffffffffffffffffff1697505b505b5050507f0b0d9c090000000000000000000000000000000000000000000000000000000082528760048301528660248301528360448301525f5f6064845f855af1613b29573d5f5f3e3d5ffd5b6002831015613c6f5788158015613b42578a9350613c36565b7fa5841194000000000000000000000000000000000000000000000000000000005f52896004525f5f60245f5f865af1613b7e573d5f5f3e3d5ffd5b838015613b925760018114613bd857613c14565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af19450613c14565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528260048501528b602485015260205f6044865f8f5af194505b503d60015f5114601f8211168115178516613c3157805f5f3e805ffd5b505f93505b507f11da60b4000000000000000000000000000000000000000000000000000000005f525f5f60045f86855af1613c6f573d5f5f3e3d5ffd5b5084915050965096945050505050565b5f5f60405184358060381c61ffff16925087891060808260481c60ff161060018114613cf8578260601c92507ff140a35a0000000000000000000000000000000000000000000000000000000084528b60048501528a602485015260205f604486865afa613cef573d5f5f3e3d5ffd5b5f519550613d73565b61ffff8360501c168360601c93507f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f875afa613d3e573d5f5f3e3d5ffd5b5f8360018114613d565760205191505f519850613d60565b60205198505f5191505b5061271002908d02908101960295909504945b507f022c0d9f000000000000000000000000000000000000000000000000000000008352808015613daf575f6004850152856024850152613dbc565b8560048501525f60248501525b505086604483015260806064830152600383105f8114613ed957838015613dea5760018114613e5357613eb2565b60c4840194507f23b872dd0000000000000000000000000000000000000000000000000000000085528660048601528260248601528b604486015260205f6064875f8f5af194503d60015f5114601f8211168115178616613e4d57805f5f3e805ffd5b50613eb2565b60c4840194507fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af194503d60015f5114601f8211168115178616613eb057805f5f3e805ffd5b505b505f60848401525f5f60a4855f865af1613ece573d5f5f3e3d5ffd5b601987019350613f2c565b603f840160848401528560601b60a48401528960601b60b88401528860601b60cc840152866016019650600384019350838760e08501375f5f8560e001855f865af1613f27573d5f5f3e3d5ffd5b928601925b505050965096945050505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a105f8114613ff4577f857f812f0000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff5f03604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376040858260c401875f885af1613fe7573d5f5f3e3d5ffd5b5060208401519550614080565b7f2c4812520000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376020858260c401875f885af161407a573d5f5f3e3d5ffd5b50835195505b506002811060018114613903579681016005019661390a565b5f5f604051843561ffff8160501c168160381c61ffff165f81146140c3576001811461412b575f5ffd5b8260601c92507f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af13d60015f5114601f821116811517821661412457805f5f3e805ffd5b5050614190565b8260601c92507f36c785160000000000000000000000000000000000000000000000000000000084528660048501528260248501528b60448501528a60648501525f5f6084865f6e22d473030f116ddee9f6b43ac78ba35af1614190573d5f5f3e3d5ffd5b50888a107f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f865afa6141cc573d5f5f3e3d5ffd5b60403d10156141d9575f5ffd5b5f81600181146141f15760205191505f5197506141fb565b5f51915060205197505b507f70a08231000000000000000000000000000000000000000000000000000000005f528360045260205f60245f8f5afa505f517f022c0d9f0000000000000000000000000000000000000000000000000000000086528190039c5061271002918c029182019582029590950494808015614281575f600486015286602486015261428e565b8660048601525f60248601525b5050876044840152608060648401525f60848401525f5f60a4855f865af16142b8573d5f5f3e3d5ffd5b5092996019959095019850939650505050505050565b5f8135816142de858a8985615893565b905060405160ff8360501c1660248201528760448201525f60648201525f60ff8460481c165f811461434e576001811461439757600281146143fa576003811461444357600481146144a657600581146144ef5760068114614552576007811461459b5760c881146145fe575f5ffd5b7fddc1f59d00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f3df0212400000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fa64833a000000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f5b41b90800000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f44ee198600000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7fa6417ed600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fe2ad025a00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f65b2489b00000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f9169558600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608484015260208360a4855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b5080614691573d5f5f3e3d5ffd5b815194503088188416156146fb577fa9059cbb00000000000000000000000000000000000000000000000000000000825287600483015284602483015260205f6044845f8e5af190503d60015f5114601f82111681151782169150816146f957805f5f3e805ffd5b505b505060198401915050965096945050505050565b5f5f604051833591508160601c8260381c61ffff16935060028410156147e6575f848015614744576001811461478a576147c6565b7f23b872dd00000000000000000000000000000000000000000000000000000000845287600485015282602485015289604485015260205f6064865f8f5af191506147c6565b7fa9059cbb00000000000000000000000000000000000000000000000000000000845282600485015289602485015260205f6044865f8f5af191505b503d60015f5114601f82111681151782166147e357805f5f3e805ffd5b50505b60ff8360481c165f81146148005760028114614828575f5ffd5b7fafb4301200000000000000000000000000000000000000000000000000000000835261484c565b7f767691e70000000000000000000000000000000000000000000000000000000083525b5060ff8360581c16600483015260ff8360501c1660248301528760448301525f606483015286608483015260205f60a4845f855af161488d573d5f5f3e3d5ffd5b50505f51915050601982019550959350505050565b5f8135816148b2858a8985615893565b90506040517f70a08231000000000000000000000000000000000000000000000000000000005f523060045260208160245f8c5afa508051935060ff8360481c1660038114614907576005811461492f575f5ffd5b7f5b41b908000000000000000000000000000000000000000000000000000000008252614953565b7f65b2489b0000000000000000000000000000000000000000000000000000000082525b5060ff8360581c16600482015260ff8360501c1660248201528760448201525f60648201525f5f6084835f865af161498d573d5f5f3e3d5ffd5b60205f60245f8c5afa50835f51039350308718156149fd577fa9059cbb0000000000000000000000000000000000000000000000000000000081528660048201528360248201526020816044835f8d5af13d6001835114601f82111681151782169150816146f957805f5f3e805ffd5b50505060198201965096945050505050565b5f5f604051833592508260581c60ff1691508260601c92506002821060018103614aea575f838015614a485760018114614a8e57614aca565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528560248501528b604485015260205f6064865f8f5af19150614aca565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528560048501528b602485015260205f6044865f8f5af191505b503d60015f5114601f8211168115178216614ae757805f5f3e805ffd5b50505b507f7dc203820000000000000000000000000000000000000000000000000000000081528760048201528660248201528860448201525f60648201528560848201525f60a482015260208160c4835f875af1614b48573d5f5f3e3d5ffd5b5191505060158201965096945050505050565b5f5f60405183358060601c8160581c60ff165f8114614b815760018114614be357614c3b565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528160248501528b604485015260205f6064865f8f5af13d60015f5114601f8211168115178216614bdc57805f5f3e805ffd5b5050614c3b565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af13d60015f5114601f8211168115178216614c3857805f5f3e805ffd5b50505b507f933162120000000000000000000000000000000000000000000000000000000083528960048401528860248401528760448401526020836064855f855af1614c87573d5f5f3e3d5ffd5b5050519860159390930197509195505050505050565b5f5f826020013590506040518160581c60ff16614d12577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f8211168115178216614d0f57805f5f3e805ffd5b50505b8160601c895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054614db0577f095ea7b30000000000000000000000000000000000000000000000000000000083528160048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f52bbbe2900000000000000000000000000000000000000000000000000000000825260e060048301523060248301525f60448301528660648301525f60848301525f60a48301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c4830152843560e48301525f61010483015289610124830152886101448301528761016483015260c06101848301525f6101a483015260205f6101c4845f855af1614e68573d5f5f3e3d5ffd5b50505f5191505060358201965096945050505050565b5f5f604051843560601c6084820152843560601c60148601955085356017870196508060581c60ff1693508060481c61ffff168160601c91507f2bfb780c0000000000000000000000000000000000000000000000000000000084525f60048501528260248501528a60448501528960648501528b6084850152600160a485015260e060c48501528060e4850152805f1815614f21578088610104860137968701965b6060848261010401865f865af1614f3a573d5f5f3e3d5ffd5b5060208301519a50604083015194507fae6393290000000000000000000000000000000000000000000000000000000083528860048401528760248401528460448401525f5f6064855f855af1614f93573d5f5f3e3d5ffd5b6002841015615097575f9150838015614fb35760018114614ff957615035565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528160248501528b604485015260205f6064865f8f5af19250615035565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af192505b503d60015f5114601f821116811517831661505257805f5f3e805ffd5b507f15afd4090000000000000000000000000000000000000000000000000000000083528960048401528a60248401525f5f6044855f855af1615097573d5f5f3e3d5ffd5b508592505050965096945050505050565b5f5f60405183358060601c8160501c60ff169350600284101561517c575f8480156150da57600181146151205761515c565b7f23b872dd0000000000000000000000000000000000000000000000000000000085528860048601528260248601528b604486015260205f6064875f8f5af1915061515c565b7fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af191505b503d60015f5114601f821116811517821661517957805f5f3e805ffd5b50505b8160581c60ff1691507f53c059a00000000000000000000000000000000000000000000000000000000083528160048401528760248401526020836044855f855af16151ca573d5f5f3e3d5ffd5b508080156151de57825160801c94506151f5565b6fffffffffffffffffffffffffffffffff83511694505b50929860169490940197509295505050505050565b5f5f5f615219898987876159ab565b60405192955093509150600383106001811461538b5761010082017fd0a494e40000000000000000000000000000000000000000000000000000000081528b60248401528560581c60ff165f81146152d9577f66410a210000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa6152a9573d5f5f3e3d5ffd5b5f5196508660048301525f60248301528860601b60a48301528a60601b60b88301528b60601b60cc830152615343565b7f79a048760000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa615317573d5f5f3e3d5ffd5b5f5196505f60048301528660248301528860601b60a48301528b60601b60b88301528a60601b60cc8301525b5088604482015260806064820152846040016084820152600485018760150160e08301375f5f8660e401835f885af161537e573d5f5f3e3d5ffd5b509483016019019461540b565b8460581c60ff165f81146153c1577fdd93f59a000000000000000000000000000000000000000000000000000000005f526153e5565b7fbd6015b4000000000000000000000000000000000000000000000000000000005f525b508760045260205f60245f5f875af1615400573d5f5f3e3d5ffd5b5f5194508560190195505b508492505050965096945050505050565b5f5f82356040518160581c60ff1692508160601c915060028310156154f2575f8380156154505760018114615496576154d2565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528760048401528360248401528a604484015260205f6064855f8e5af191506154d2565b7fa9059cbb0000000000000000000000000000000000000000000000000000000083528360048401528a602484015260205f6044855f8e5af191505b503d60015f5114601f82111681151782166154ef57805f5f3e805ffd5b50505b7f7132bb7f000000000000000000000000000000000000000000000000000000008152608060048201525f60248201525f60448201526101006064820152606060848201528760a48201528660c48201525f60e48201525f610104820152604081610124835f865af1615567573d5f5f3e3d5ffd5b602001519860159490940197509295505050505050565b5f5f8235905060405160ff8260f01c166155f0577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f82111681151782166155ed57805f5f3e805ffd5b50505b8160f81c5f8114615657576001811461579c577fba08765200000000000000000000000000000000000000000000000000000000825287600483015286602483015230604483015260205f6064845f8e5af161564e573d5f5f3e3d5ffd5b5f519350615881565b879350891560018114615700577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52886004525f5f60245f5f8f5af16156c0577fc30d93ce000000000000000000000000000000000000000000000000000000005f5260045ffd5b3088146156fb575f5f5f5f8c8c5af16156fb577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b615796565b5f5f5f5f8c8e5af1615734577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308814615796577fa9059cbb00000000000000000000000000000000000000000000000000000000835287600484015288602484015260205f6044855f8e5af13d60015f5114601f821116811517821691508161579357805f5f3e805ffd5b50505b50615881565b895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052885f5260405f208054615836577f095ea7b30000000000000000000000000000000000000000000000000000000083528960048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f6e553f6500000000000000000000000000000000000000000000000000000000825287600483015286602483015260205f6044845f8d5af161587c573d5f5f3e3d5ffd5b5f5193505b50505060028201965096945050505050565b5f6040518260601c9150845f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052815f5260405f208054615937577f095ea7b30000000000000000000000000000000000000000000000000000000082528260048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301526020826044845f8a5af150600181555b508260381c61ffff166159a2577f23b872dd00000000000000000000000000000000000000000000000000000000815285600482015230602482015283604482015260205f6064835f895af13d60015f5114601f821116811517821661599f57805f5f3e805ffd5b50505b50949350505050565b604051813590606082901c9061ffff603884901c16906002821015615a81575f8280156159df5760018114615a2557615a61565b7f23b872dd00000000000000000000000000000000000000000000000000000000835287600484015284602484015289604484015260205f6064855f8d5af19150615a61565b7fa9059cbb00000000000000000000000000000000000000000000000000000000835284600484015289602484015260205f6044855f8d5af191505b503d60015f5114601f8211168115178216615a7e57805f5f3e805ffd5b50505b509450945094915050565b5f5f83601f840112615a9c575f5ffd5b50813567ffffffffffffffff811115615ab3575f5ffd5b602083019150836020828501011115615aca575f5ffd5b9250929050565b5f5f5f60408486031215615ae3575f5ffd5b83359250602084013567ffffffffffffffff811115615b00575f5ffd5b615b0c86828701615a8c565b9497909650939450505050565b5f5f60208385031215615b2a575f5ffd5b823567ffffffffffffffff811115615b40575f5ffd5b615b4c85828601615a8c565b9096909550935050505056fea2646970667358221220be71ab4bfdc67325f0fe02c45071abb89f031a89e6d0733865dd66f4cbd088c264736f6c634300081c0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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.