ETH Price: $3,910.63 (+5.18%)

Contract

0x6A000F20005980200259B80c5102003040001068
Transaction Hash
Method
Block
From
To
Swap Exact Amoun...180977412024-12-05 2:02:236 hrs ago1733364143IN
Paraswap: Augustus V6.2
0 ETH0.000094290.28
Swap Exact Amoun...180942572024-12-04 22:50:2210 hrs ago1733352622IN
Paraswap: Augustus V6.2
0 ETH0.000105650.353
Swap Exact Amoun...180930662024-12-04 21:45:2411 hrs ago1733348724IN
Paraswap: Augustus V6.2
0 ETH0.000174990.472
Swap Exact Amoun...180874302024-12-04 16:35:5016 hrs ago1733330150IN
Paraswap: Augustus V6.2
0 ETH0.00012420.342
Swap Exact Amoun...180868412024-12-04 16:03:1316 hrs ago1733328193IN
Paraswap: Augustus V6.2
0 ETH0.000124580.342
Swap Exact Amoun...180868182024-12-04 16:01:5816 hrs ago1733328118IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180867882024-12-04 16:00:2016 hrs ago1733328020IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180861602024-12-04 15:25:4417 hrs ago1733325944IN
Paraswap: Augustus V6.2
0 ETH0.000123250.342
Swap Exact Amoun...180857522024-12-04 15:02:3117 hrs ago1733324551IN
Paraswap: Augustus V6.2
0 ETH0.000209970.342
Swap Exact Amoun...180856982024-12-04 14:59:2417 hrs ago1733324364IN
Paraswap: Augustus V6.2
0 ETH0.000120680.342
Swap Exact Amoun...180856452024-12-04 14:56:2518 hrs ago1733324185IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180829742024-12-04 12:26:1420 hrs ago1733315174IN
Paraswap: Augustus V6.2
0 ETH0.000100040.31
Swap Exact Amoun...180823792024-12-04 11:51:4421 hrs ago1733313104IN
Paraswap: Augustus V6.2
0 ETH0.000018120.3
Swap Exact Amoun...180823512024-12-04 11:50:0721 hrs ago1733313007IN
Paraswap: Augustus V6.2
0 ETH0.000112560.31
Swap Exact Amoun...180820522024-12-04 11:32:1921 hrs ago1733311939IN
Paraswap: Augustus V6.2
0 ETH0.000124540.34
Swap Exact Amoun...180816152024-12-04 10:55:1522 hrs ago1733309715IN
Paraswap: Augustus V6.2
0 ETH0.000117450.32
Swap Exact Amoun...180812342024-12-04 10:33:2122 hrs ago1733308401IN
Paraswap: Augustus V6.2
0 ETH0.000171280.3
Swap Exact Amoun...180783922024-12-04 7:46:5725 hrs ago1733298417IN
Paraswap: Augustus V6.2
0 ETH0.000107210.29
Swap Exact Amoun...180766792024-12-04 6:08:3926 hrs ago1733292519IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180764392024-12-04 5:55:1427 hrs ago1733291714IN
Paraswap: Augustus V6.2
0 ETH0.000092940.27
Swap Exact Amoun...180685792024-12-03 22:27:0934 hrs ago1733264829IN
Paraswap: Augustus V6.2
0 ETH0.000337140.394
Swap Exact Amoun...180675872024-12-03 21:30:4235 hrs ago1733261442IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180675722024-12-03 21:29:5335 hrs ago1733261393IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180675492024-12-03 21:28:3735 hrs ago1733261317IN
Paraswap: Augustus V6.2
0 ETH0.000144660.342
Swap Exact Amoun...180618482024-12-03 16:04:4140 hrs ago1733241881IN
Paraswap: Augustus V6.2
0 ETH0.000021860.342

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
180960972024-12-05 0:31:428 hrs ago1733358702
Paraswap: Augustus V6.2
0.77061533 ETH
180960972024-12-05 0:31:428 hrs ago1733358702
Paraswap: Augustus V6.2
0.77061533 ETH
180930662024-12-04 21:45:2411 hrs ago1733348724
Paraswap: Augustus V6.2
0.01560017 ETH
180930662024-12-04 21:45:2411 hrs ago1733348724
Paraswap: Augustus V6.2
0.00000104 ETH
180930662024-12-04 21:45:2411 hrs ago1733348724
Paraswap: Augustus V6.2
0.01560122 ETH
180874302024-12-04 16:35:5016 hrs ago1733330150
Paraswap: Augustus V6.2
0.00974175 ETH
180874302024-12-04 16:35:5016 hrs ago1733330150
Paraswap: Augustus V6.2
0.00974175 ETH
180868412024-12-04 16:03:1316 hrs ago1733328193
Paraswap: Augustus V6.2
0.01777914 ETH
180868412024-12-04 16:03:1316 hrs ago1733328193
Paraswap: Augustus V6.2
0.01777914 ETH
180868182024-12-04 16:01:5816 hrs ago1733328118
Paraswap: Augustus V6.2
0.01780471 ETH
180867882024-12-04 16:00:2016 hrs ago1733328020
Paraswap: Augustus V6.2
0.01780471 ETH
180861602024-12-04 15:25:4417 hrs ago1733325944
Paraswap: Augustus V6.2
0.01989107 ETH
180861602024-12-04 15:25:4417 hrs ago1733325944
Paraswap: Augustus V6.2
0.01989107 ETH
180857522024-12-04 15:02:3117 hrs ago1733324551
Paraswap: Augustus V6.2
0.01034845 ETH
180857522024-12-04 15:02:3117 hrs ago1733324551
Paraswap: Augustus V6.2
0.01034845 ETH
180856982024-12-04 14:59:2417 hrs ago1733324364
Paraswap: Augustus V6.2
0.01847024 ETH
180856982024-12-04 14:59:2417 hrs ago1733324364
Paraswap: Augustus V6.2
0.01847024 ETH
180856452024-12-04 14:56:2518 hrs ago1733324185
Paraswap: Augustus V6.2
0.01845116 ETH
180829742024-12-04 12:26:1420 hrs ago1733315174
Paraswap: Augustus V6.2
0.01087575 ETH
180829742024-12-04 12:26:1420 hrs ago1733315174
Paraswap: Augustus V6.2
0.01087575 ETH
180823792024-12-04 11:51:4421 hrs ago1733313104
Paraswap: Augustus V6.2
0.00328326 ETH
180823792024-12-04 11:51:4421 hrs ago1733313104
Paraswap: Augustus V6.2
0.00328326 ETH
180823512024-12-04 11:50:0721 hrs ago1733313007
Paraswap: Augustus V6.2
0.01623017 ETH
180823512024-12-04 11:50:0721 hrs ago1733313007
Paraswap: Augustus V6.2
0.01623017 ETH
180820522024-12-04 11:32:1921 hrs ago1733311939
Paraswap: Augustus V6.2
0.01895686 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AugustusV6

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
shanghai EvmVersion
File 1 of 47 : AugustusV6.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Vendor
import { Diamond } from "./vendor/Diamond.sol";

// Routers
import { Routers } from "./routers/Routers.sol";

//                  ______                                   __                     __  __    ____
//                 /\  _  \                                 /\ \__                 /\ \/\ \  /'___\
//                 \ \ \L\ \  __  __     __   __  __    ____\ \ ,_\  __  __    ____\ \ \ \ \/\ \__/
//                  \ \  __ \/\ \/\ \  /'_ `\/\ \/\ \  /',__\\ \ \/ /\ \/\ \  /',__\\ \ \ \ \ \  _``\
//                   \ \ \/\ \ \ \_\ \/\ \L\ \ \ \_\ \/\__, `\\ \ \_\ \ \_\ \/\__, `\\ \ \_/ \ \ \L\ \
//                    \ \_\ \_\ \____/\ \____ \ \____/\/\____/ \ \__\\ \____/\/\____/ \ `\___/\ \____/
//                     \/_/\/_/\/___/  \/___L\ \/___/  \/___/   \/__/ \/___/  \/___/   `\/__/  \/___/
//                                       /\____/
//                                       \_/__/

/// @title AugustusV6
/// @notice The V6 implementation of the ParaSwap onchain aggregation protocol
contract AugustusV6 is Diamond, Routers {
    /*//////////////////////////////////////////////////////////////
                              CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        /// @dev Diamond
        address _owner,
        address _diamondCutFacet,
        /// @dev Direct Routers
        address _weth,
        address payable _balancerVault,
        uint256 _uniV3FactoryAndFF,
        uint256 _uniswapV3PoolInitCodeHash,
        uint256 _uniswapV2FactoryAndFF,
        uint256 _uniswapV2PoolInitCodeHash,
        address _rfq,
        /// @dev Fees
        address payable _feeVault,
        /// @dev Permit2
        address _permit2
    )
        Diamond(_owner, _diamondCutFacet)
        Routers(
            _weth,
            _uniV3FactoryAndFF,
            _uniswapV3PoolInitCodeHash,
            _uniswapV2FactoryAndFF,
            _uniswapV2PoolInitCodeHash,
            _balancerVault,
            _permit2,
            _rfq,
            _feeVault
        )
    { }

    /*//////////////////////////////////////////////////////////////
                                EXTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @notice Reverts if the caller is one of the following:
    //         - an externally-owned account
    //         - a contract in construction
    //         - an address where a contract will be created
    //         - an address where a contract lived, but was destroyed
    receive() external payable override(Diamond) {
        address addr = msg.sender;
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            if iszero(extcodesize(addr)) { revert(0, 0) }
        }
    }
}

File 2 of 47 : Diamond.sol
// SPDX-License-Identifier: MIT
/**
 * Vendored on October 12, 2023 from:
 * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/Diamond.sol
 */
pragma solidity ^0.8.0;

/**
 * \
 * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
 * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
 *
 * Implementation of a diamond.
 * /*****************************************************************************
 */
import { LibDiamond } from "./libraries/LibDiamond.sol";
import { IDiamondCut } from "./interfaces/IDiamondCut.sol";

contract Diamond {
    error DiamondFunctionDoesNotExist();

    constructor(address _contractOwner, address _diamondCutFacet) payable {
        LibDiamond.setContractOwner(_contractOwner);

        // Add the diamondCut external function from the diamondCutFacet
        IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
        bytes4[] memory functionSelectors = new bytes4[](1);
        functionSelectors[0] = IDiamondCut.diamondCut.selector;
        cut[0] = IDiamondCut.FacetCut({
            facetAddress: _diamondCutFacet,
            action: IDiamondCut.FacetCutAction.Add,
            functionSelectors: functionSelectors
        });
        LibDiamond.diamondCut(cut, address(0), "");
    }

    // Find facet for function that is called and execute the
    // function if a facet is found and return any value.
    fallback() external payable {
        LibDiamond.DiamondStorage storage ds;
        bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
        // get diamond storage
        assembly {
            ds.slot := position
        }
        // get facet from function selector
        address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
        // revert if function does not exist
        if (facet == address(0)) {
            revert DiamondFunctionDoesNotExist();
        }
        // Execute external function from facet using delegatecall and return any value.
        assembly {
            // copy function selector and any arguments
            calldatacopy(0, 0, calldatasize())
            // execute function call using the facet
            let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
            // get any return value
            returndatacopy(0, 0, returndatasize())
            // return any return value or error back to the caller
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    receive() external payable virtual { }
}

File 3 of 47 : Routers.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// DirectSwapExactAmountIn
import { BalancerV2SwapExactAmountIn } from "./swapExactAmountIn/direct/BalancerV2SwapExactAmountIn.sol";
import { CurveV1SwapExactAmountIn } from "./swapExactAmountIn/direct/CurveV1SwapExactAmountIn.sol";
import { CurveV2SwapExactAmountIn } from "./swapExactAmountIn/direct/CurveV2SwapExactAmountIn.sol";
import { UniswapV2SwapExactAmountIn } from "./swapExactAmountIn/direct/UniswapV2SwapExactAmountIn.sol";
import { UniswapV3SwapExactAmountIn } from "./swapExactAmountIn/direct/UniswapV3SwapExactAmountIn.sol";

// DirectSwapExactAmountOut
import { BalancerV2SwapExactAmountOut } from "./swapExactAmountOut/direct/BalancerV2SwapExactAmountOut.sol";
import { UniswapV2SwapExactAmountOut } from "./swapExactAmountOut/direct/UniswapV2SwapExactAmountOut.sol";
import { UniswapV3SwapExactAmountOut } from "./swapExactAmountOut/direct/UniswapV3SwapExactAmountOut.sol";

// Fees
import { AugustusFees } from "../fees/AugustusFees.sol";

// GenericSwapExactAmountIn
import { GenericSwapExactAmountIn } from "./swapExactAmountIn/GenericSwapExactAmountIn.sol";

// GenericSwapExactAmountOut
import { GenericSwapExactAmountOut } from "./swapExactAmountOut/GenericSwapExactAmountOut.sol";

// General
import { AugustusRFQRouter } from "./general/AugustusRFQRouter.sol";

// Utils
import { AugustusRFQUtils } from "../util/AugustusRFQUtils.sol";
import { BalancerV2Utils } from "../util/BalancerV2Utils.sol";
import { UniswapV2Utils } from "../util/UniswapV2Utils.sol";
import { UniswapV3Utils } from "../util/UniswapV3Utils.sol";
import { WETHUtils } from "../util/WETHUtils.sol";
import { Permit2Utils } from "../util/Permit2Utils.sol";

/// @title Routers
/// @notice A wrapper for all router contracts
contract Routers is
    AugustusFees,
    AugustusRFQRouter,
    BalancerV2SwapExactAmountOut,
    BalancerV2SwapExactAmountIn,
    CurveV1SwapExactAmountIn,
    CurveV2SwapExactAmountIn,
    GenericSwapExactAmountOut,
    GenericSwapExactAmountIn,
    UniswapV2SwapExactAmountOut,
    UniswapV2SwapExactAmountIn,
    UniswapV3SwapExactAmountOut,
    UniswapV3SwapExactAmountIn
{
    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        address _weth,
        uint256 _uniswapV3FactoryAndFF,
        uint256 _uniswapV3PoolInitCodeHash,
        uint256 _uniswapV2FactoryAndFF,
        uint256 _uniswapV2PoolInitCodeHash,
        address payable _balancerVault,
        address _permit2,
        address _rfq,
        address payable _feeVault
    )
        AugustusFees(_feeVault)
        AugustusRFQUtils(_rfq)
        BalancerV2Utils(_balancerVault)
        Permit2Utils(_permit2)
        UniswapV2Utils(_uniswapV2FactoryAndFF, _uniswapV2PoolInitCodeHash)
        UniswapV3Utils(_uniswapV3FactoryAndFF, _uniswapV3PoolInitCodeHash)
        WETHUtils(_weth)
    { }
}

File 4 of 47 : LibDiamond.sol
// SPDX-License-Identifier: MIT
/**
 * Vendored on October 12, 2023 from:
 * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/libraries/LibDiamond.sol
 */
pragma solidity ^0.8.0;

/**
 * \
 * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
 * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
 * /*****************************************************************************
 */
import { IDiamondCut } from "../interfaces/IDiamondCut.sol";

// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.
// The loupe functions are required by the EIP2535 Diamonds standard

error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata);

library LibDiamond {
    bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");

    struct FacetAddressAndPosition {
        address facetAddress;
        uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
    }

    struct FacetFunctionSelectors {
        bytes4[] functionSelectors;
        uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
    }

    struct DiamondStorage {
        // maps function selector to the facet address and
        // the position of the selector in the facetFunctionSelectors.selectors array
        mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
        // maps facet addresses to function selectors
        mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
        // facet addresses
        address[] facetAddresses;
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        // owner of the contract
        address contractOwner;
    }

    function diamondStorage() internal pure returns (DiamondStorage storage ds) {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }

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

    function setContractOwner(address _newOwner) internal {
        DiamondStorage storage ds = diamondStorage();
        address previousOwner = ds.contractOwner;
        ds.contractOwner = _newOwner;
        emit OwnershipTransferred(previousOwner, _newOwner);
    }

    function contractOwner() internal view returns (address contractOwner_) {
        contractOwner_ = diamondStorage().contractOwner;
    }

    function enforceIsContractOwner() internal view {
        require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
    }

    event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

    // Internal function version of diamondCut
    function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) internal {
        for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
            IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
            if (action == IDiamondCut.FacetCutAction.Add) {
                addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
            } else if (action == IDiamondCut.FacetCutAction.Replace) {
                replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
            } else if (action == IDiamondCut.FacetCutAction.Remove) {
                removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
            } else {
                revert("LibDiamondCut: Incorrect FacetCutAction");
            }
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        initializeDiamondCut(_init, _calldata);
    }

    function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
        require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        DiamondStorage storage ds = diamondStorage();
        require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
        uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
            require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists");
            addFunction(ds, selector, selectorPosition, _facetAddress);
            selectorPosition++;
        }
    }

    function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
        require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        DiamondStorage storage ds = diamondStorage();
        require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
        uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
            require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function");
            removeFunction(ds, oldFacetAddress, selector);
            addFunction(ds, selector, selectorPosition, _facetAddress);
            selectorPosition++;
        }
    }

    function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
        require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        DiamondStorage storage ds = diamondStorage();
        // if function does not exist then do nothing and return
        require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)");
        for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
            removeFunction(ds, oldFacetAddress, selector);
        }
    }

    function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
        enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code");
        ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
        ds.facetAddresses.push(_facetAddress);
    }

    function addFunction(
        DiamondStorage storage ds,
        bytes4 _selector,
        uint96 _selectorPosition,
        address _facetAddress
    )
        internal
    {
        ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
        ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
    }

    function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal {
        require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist");
        // an immutable function is a function defined directly in a diamond
        require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function");
        // replace selector with last selector, then delete last selector
        uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
        uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
        // if not the same then replace _selector with lastSelector
        if (selectorPosition != lastSelectorPosition) {
            bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
            ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
            ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
        }
        // delete the last selector
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
        delete ds.selectorToFacetAndPosition[_selector];

        // if no more selectors for facet address then delete the facet address
        if (lastSelectorPosition == 0) {
            // replace facet address with last facet address and delete last facet address
            uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
            uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
            if (facetAddressPosition != lastFacetAddressPosition) {
                address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
                ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
            }
            ds.facetAddresses.pop();
            delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
        }
    }

    function initializeDiamondCut(address _init, bytes memory _calldata) internal {
        if (_init == address(0)) {
            return;
        }
        enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
        (bool success, bytes memory error) = _init.delegatecall(_calldata);
        if (!success) {
            if (error.length > 0) {
                // bubble up error
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(error)
                    revert(add(32, error), returndata_size)
                }
            } else {
                revert InitializationFunctionReverted(_init, _calldata);
            }
        }
    }

    function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        require(contractSize > 0, _errorMessage);
    }
}

File 5 of 47 : IDiamondCut.sol
// SPDX-License-Identifier: MIT
/**
 * Vendored on October 12, 2023 from:
 * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/interfaces/IDiamondCut.sol
 */
pragma solidity ^0.8.0;

/**
 * \
 * Author: Nick Mudge (https://twitter.com/mudgen)
 * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
 * /*****************************************************************************
 */
interface IDiamondCut {
    enum FacetCutAction {
        Add,
        Replace,
        Remove
    }
    // Add=0, Replace=1, Remove=2

    struct FacetCut {
        address facetAddress;
        FacetCutAction action;
        bytes4[] functionSelectors;
    }

    /// @notice Add/replace/remove any number of functions and optionally execute
    ///         a function with delegatecall
    /// @param _diamondCut Contains the facet addresses and function selectors
    /// @param _init The address of the contract or facet to execute _calldata
    /// @param _calldata A function call, including function selector and arguments
    ///                  _calldata is executed with delegatecall on _init
    function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;

    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

File 6 of 47 : BalancerV2SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IBalancerV2SwapExactAmountIn } from "../../../interfaces/IBalancerV2SwapExactAmountIn.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";

// Types
import { BalancerV2Data } from "../../../AugustusV6Types.sol";

// Utils
import { BalancerV2Utils } from "../../../util/BalancerV2Utils.sol";

/// @title BalancerV2SwapExactAmountIn
/// @notice A contract for executing direct swapExactAmountIn on Balancer V2
abstract contract BalancerV2SwapExactAmountIn is IBalancerV2SwapExactAmountIn, BalancerV2Utils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IBalancerV2SwapExactAmountIn
    function swapExactAmountInOnBalancerV2(
        BalancerV2Data calldata balancerData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata data
    )
        external
        payable
        whenNotPaused
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference balancerData
        uint256 quotedAmountOut = balancerData.quotedAmount;
        uint256 beneficiaryAndApproveFlag = balancerData.beneficiaryAndApproveFlag;
        uint256 amountIn = balancerData.fromAmount;
        uint256 minAmountOut = balancerData.toAmount;

        // Decode params
        (IERC20 srcToken, IERC20 destToken, address payable beneficiary, bool approve) =
            _decodeBalancerV2Params(beneficiaryAndApproveFlag, data);

        // Check if toAmount is valid
        if (minAmountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Check if srcToken is ETH
        if (srcToken.isETH(amountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, address(this), amountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), amountIn);
            }
            // Check if approve is needed
            if (approve) {
                // Approve BALANCER_VAULT to spend srcToken
                srcToken.approve(BALANCER_VAULT);
            }
        }

        // Execute swap
        _callBalancerV2(data);

        // Check balance after swap
        receivedAmount = destToken.getBalance(address(this));

        // Check if swap succeeded
        if (receivedAmount < minAmountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken to beneficiary
        return processSwapExactAmountInFeesAndTransfer(
            beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut
        );
    }
}

File 7 of 47 : CurveV1SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { ICurveV1SwapExactAmountIn } from "../../../interfaces/ICurveV1SwapExactAmountIn.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";

// Types
import { CurveV1Data } from "../../../AugustusV6Types.sol";

// Utils
import { AugustusFees } from "../../../fees/AugustusFees.sol";
import { WETHUtils } from "../../../util/WETHUtils.sol";
import { Permit2Utils } from "../../../util/Permit2Utils.sol";
import { PauseUtils } from "../../../util/PauseUtils.sol";

/// @title CurveV1SwapExactAmountIn
/// @notice A contract for executing direct CurveV1 swaps
abstract contract CurveV1SwapExactAmountIn is
    ICurveV1SwapExactAmountIn,
    AugustusFees,
    WETHUtils,
    Permit2Utils,
    PauseUtils
{
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc ICurveV1SwapExactAmountIn
    function swapExactAmountInOnCurveV1(
        CurveV1Data calldata curveV1Data,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference curveV1Data
        IERC20 srcToken = curveV1Data.srcToken;
        IERC20 destToken = curveV1Data.destToken;
        uint256 amountIn = curveV1Data.fromAmount;
        uint256 minAmountOut = curveV1Data.toAmount;
        uint256 quotedAmountOut = curveV1Data.quotedAmount;
        address payable beneficiary = curveV1Data.beneficiary;
        uint256 curveAssets = curveV1Data.curveAssets;
        uint256 curveData = curveV1Data.curveData;

        // Check if toAmount is valid
        if (minAmountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Decode curveData
        // 160 bits for curve exchange address
        // 1 bit for approve flag
        // 2 bits for wrap flag
        // 2 bits for swap type flag

        address exchange;
        bool approveFlag;
        uint256 wrapFlag;
        uint256 swapType;

        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            exchange := and(curveData, 0xffffffffffffffffffffffffffffffffffffffff)
            approveFlag := and(shr(160, curveData), 1)
            wrapFlag := and(shr(161, curveData), 3)
            swapType := and(shr(163, curveData), 3)
        }

        // Check if srcToken is ETH
        // Transfer srcToken to augustus if not ETH
        if (srcToken.isETH(amountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, address(this), amountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), amountIn);
            }
            // Check if approve flag is set
            if (approveFlag) {
                // Approve exchange
                srcToken.approve(exchange);
            }
        } else {
            // Check if approve flag is set
            if (approveFlag) {
                // Approve exchange
                IERC20(WETH).approve(exchange);
            }
        }

        // Execute swap
        _executeSwapOnCurveV1(exchange, wrapFlag, swapType, curveAssets, amountIn);

        // Check balance after swap and unwrap if needed
        if (wrapFlag == 2) {
            // Received amount is WETH balance
            receivedAmount = IERC20(WETH).getBalance(address(this));
            // Unwrap WETH
            WETH.withdraw(receivedAmount - 1);
            // Set receivedAmount to this contract's balance
            receivedAmount = address(this).balance;
        } else {
            // Received amount is destToken balance
            receivedAmount = destToken.getBalance(address(this));
        }

        // Check if swap succeeded
        if (receivedAmount < minAmountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken to beneficiary
        return processSwapExactAmountInFeesAndTransfer(
            beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut
        );
    }

    /*//////////////////////////////////////////////////////////////
                                PRIVATE
    //////////////////////////////////////////////////////////////*/

    function _executeSwapOnCurveV1(
        address exchange,
        uint256 wrapFlag,
        uint256 swapType,
        uint256 curveAssets,
        uint256 fromAmount
    )
        private
    {
        // Load WETH address
        address weth = address(WETH);
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Load free memory pointer
            let ptr := mload(64)

            //-----------------------------------------------------------------------------------
            // Wrap ETH if needed
            //-----------------------------------------------------------------------------------

            // Check if wrap src flag is set
            if eq(wrapFlag, 1) {
                // Prepare call data for WETH.deposit()

                // Store function selector and
                mstore(ptr, 0xd0e30db000000000000000000000000000000000000000000000000000000000) // deposit()

                // Perform the external call with the prepared calldata
                // Check the outcome of the call and handle failure
                if iszero(call(gas(), weth, callvalue(), ptr, 4, 0, 0)) {
                    // 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
                }
            }

            //-----------------------------------------------------------------------------------
            // Execute swap
            //-----------------------------------------------------------------------------------

            // Prepare call data for external call

            // Check swap type
            switch swapType
            // 0x01 for EXCHANGE_UNDERLYING
            case 0x01 {
                // Store function selector for function exchange_underlying(int128,int128,uint256,uint256)
                mstore(ptr, 0xa6417ed600000000000000000000000000000000000000000000000000000000) // store selector
                mstore(add(ptr, 4), shr(128, curveAssets)) // store index i
                mstore(add(ptr, 36), and(curveAssets, 0xffffffffffffffffffffffffffffffff)) // store index j
                mstore(add(ptr, 68), fromAmount) // store fromAmount
                mstore(add(ptr, 100), 1) // store 1
                // Perform the external call with the prepared calldata
                // Check the outcome of the call and handle failure
                if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) {
                    // 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
                }
            }
            // 0x00(default) for EXCHANGE
            default {
                // check send eth wrap flag
                switch eq(wrapFlag, 0x03)
                // if it is not set, store selector for function exchange(int128,int128,uint256,uint256)
                case 1 {
                    mstore(ptr, 0x3df0212400000000000000000000000000000000000000000000000000000000) // store selector
                    mstore(add(ptr, 4), shr(128, curveAssets)) // store index i
                    mstore(add(ptr, 36), and(curveAssets, 0xffffffffffffffffffffffffffffffff)) // store index j
                    mstore(add(ptr, 68), fromAmount) // store fromAmount
                    mstore(add(ptr, 100), 1) // store 1
                    // Perform the external call with the prepared calldata
                    // Check the outcome of the call and handle failure
                    if iszero(call(gas(), exchange, callvalue(), ptr, 132, 0, 0)) {
                        // 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 it is set, store selector for function exchange(int128,int128,uint256,uint256)
                default {
                    mstore(ptr, 0x3df0212400000000000000000000000000000000000000000000000000000000) // store selector
                    mstore(add(ptr, 4), shr(128, curveAssets)) // store index i
                    mstore(add(ptr, 36), and(curveAssets, 0xffffffffffffffffffffffffffffffff)) // store index j
                    mstore(add(ptr, 68), fromAmount) // store fromAmount
                    mstore(add(ptr, 100), 1) // store 1
                    // Perform the external call with the prepared calldata
                    // Check the outcome of the call and handle failure
                    if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) {
                        // 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
                    }
                }
            }
        }
    }
}

File 8 of 47 : CurveV2SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { ICurveV2SwapExactAmountIn } from "../../../interfaces/ICurveV2SwapExactAmountIn.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";

// Types
import { CurveV2Data } from "../../../AugustusV6Types.sol";

// Utils
import { AugustusFees } from "../../../fees/AugustusFees.sol";
import { WETHUtils } from "../../../util/WETHUtils.sol";
import { Permit2Utils } from "../../../util/Permit2Utils.sol";
import { PauseUtils } from "../../../util/PauseUtils.sol";

/// @title CurveV2SwapExactAmountIn
/// @notice A contract for executing direct CurveV2 swaps
abstract contract CurveV2SwapExactAmountIn is
    ICurveV2SwapExactAmountIn,
    AugustusFees,
    WETHUtils,
    Permit2Utils,
    PauseUtils
{
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc ICurveV2SwapExactAmountIn
    function swapExactAmountInOnCurveV2(
        CurveV2Data calldata curveV2Data,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference curveData
        IERC20 srcToken = curveV2Data.srcToken;
        IERC20 destToken = curveV2Data.destToken;
        uint256 amountIn = curveV2Data.fromAmount;
        uint256 minAmountOut = curveV2Data.toAmount;
        uint256 quotedAmountOut = curveV2Data.quotedAmount;
        address payable beneficiary = curveV2Data.beneficiary;
        uint256 i = curveV2Data.i;
        uint256 j = curveV2Data.j;
        address poolAddress = curveV2Data.poolAddress;
        uint256 curveData = curveV2Data.curveData;

        // Check if toAmount is valid
        if (minAmountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Decode curveData
        // 160 bits for curve exchange address
        // 1 bit for approve flag
        // 2 bits for wrap flag
        // 2 bits for swap type flag

        address exchange;
        bool approveFlag;
        uint256 wrapFlag;
        uint256 swapType;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            exchange := and(curveData, 0xffffffffffffffffffffffffffffffffffffffff)
            approveFlag := and(shr(160, curveData), 1)
            wrapFlag := and(shr(161, curveData), 3)
            swapType := and(shr(163, curveData), 3)
        }

        // Check if srcToken is ETH
        // Transfer srcToken to augustus if not ETH
        if (srcToken.isETH(amountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, address(this), amountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), amountIn);
            }
            // Check if approve flag is set
            if (approveFlag) {
                // Approve exchange
                srcToken.approve(exchange);
            }
        } else {
            // Check if approve flag is set
            if (approveFlag) {
                // Approve exchange
                IERC20(WETH).approve(exchange);
            }
        }

        // Execute swap
        _executeSwapOnCurveV2(exchange, wrapFlag, swapType, i, j, amountIn, poolAddress);

        // Check balance after swap and unwrap if needed
        if (wrapFlag == 2) {
            // Received amount is WETH balance
            receivedAmount = IERC20(WETH).getBalance(address(this));
            // Unwrap WETH
            WETH.withdraw(receivedAmount - 1);
            // Set receivedAmount to this contract's balance
            receivedAmount = address(this).balance;
        } else {
            // Received amount is destToken balance
            receivedAmount = destToken.getBalance(address(this));
        }

        // Check if swap succeeded
        if (receivedAmount < minAmountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken to beneficiary
        return processSwapExactAmountInFeesAndTransfer(
            beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut
        );
    }

    /*//////////////////////////////////////////////////////////////
                                PRIVATE
    //////////////////////////////////////////////////////////////*/

    function _executeSwapOnCurveV2(
        address exchange,
        uint256 wrapFlag,
        uint256 swapType,
        uint256 i,
        uint256 j,
        uint256 fromAmount,
        address poolAddress
    )
        private
    {
        // Load WETH address
        address weth = address(WETH);
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Load free memory pointer
            let ptr := mload(64)

            //-----------------------------------------------------------------------------------
            // Wrap ETH if needed
            //-----------------------------------------------------------------------------------

            // Check if wrap src flag is set
            if eq(wrapFlag, 1) {
                // Prepare call data for WETH.deposit()

                // Store function selector and
                mstore(ptr, 0xd0e30db000000000000000000000000000000000000000000000000000000000) // deposit()

                // Perform the external call with the prepared calldata
                // Check the outcome of the call and handle failure
                if iszero(call(gas(), weth, callvalue(), ptr, 4, 0, 0)) {
                    // 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
                }
            }

            //-----------------------------------------------------------------------------------
            // Execute swap
            //-----------------------------------------------------------------------------------

            // Prepare call data for external call

            // Check swap type
            switch swapType
            // 0x01 for EXCHANGE_UNDERLYING
            case 0x01 {
                // Store function selector for function exchange_underlying(uint256,uint256,uint256,uint256)
                mstore(ptr, 0x65b2489b00000000000000000000000000000000000000000000000000000000) // store selector
                mstore(add(ptr, 4), i) // store index i
                mstore(add(ptr, 36), j) // store index j
                mstore(add(ptr, 68), fromAmount) // store fromAmount
                mstore(add(ptr, 100), 1) // store 1
                // Perform the external call with the prepared calldata
                // Check the outcome of the call and handle failure
                if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) {
                    // 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
                }
            }
            // 0x02 for EXCHANGE_GENERIC_FACTORY_ZAP
            case 0x02 {
                // Store function selector for function exchange(address,uint256,uint256,uint256,uint256)
                mstore(ptr, 0x64a1455800000000000000000000000000000000000000000000000000000000)
                mstore(add(ptr, 4), poolAddress) // store poolAddress
                mstore(add(ptr, 36), i) // store index i
                mstore(add(ptr, 68), j) // store index j
                mstore(add(ptr, 100), fromAmount) // store fromAmount
                mstore(add(ptr, 132), 1) // store 1
                // Perform the external call with the prepared calldata
                // Check the outcome of the call and handle failure
                if iszero(call(gas(), exchange, 0, ptr, 164, 0, 0)) {
                    // 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
                }
            }
            // 0x00(default) for EXCHANGE
            default {
                // check send eth wrap flag
                switch eq(wrapFlag, 0x03)
                // if it is not set, store selector for function exchange(uint256,uint256,uint256,uint256,bool)
                case 1 {
                    mstore(ptr, 0x394747c500000000000000000000000000000000000000000000000000000000) // store selector
                    mstore(add(ptr, 4), i) // store index i
                    mstore(add(ptr, 36), j) // store index j
                    mstore(add(ptr, 68), fromAmount) // store fromAmount
                    mstore(add(ptr, 100), 1) // store 1
                    mstore(add(ptr, 132), 1) // store true
                    // Perform the external call with the prepared calldata
                    // Check the outcome of the call and handle failure
                    if iszero(call(gas(), exchange, callvalue(), ptr, 164, 0, 0)) {
                        // 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 it is set, store selector for function exchange(uint256,uint256,uint256,uint256)
                default {
                    mstore(ptr, 0x5b41b90800000000000000000000000000000000000000000000000000000000) // store selector
                    mstore(add(ptr, 4), i) // store index i
                    mstore(add(ptr, 36), j) // store index j
                    mstore(add(ptr, 68), fromAmount) // store fromAmount
                    mstore(add(ptr, 100), 1) // store 1
                    // Perform the external call with the prepared calldata
                    // Check the outcome of the call and handle failure
                    if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) {
                        // 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
                    }
                }
            }
        }
    }
}

File 9 of 47 : UniswapV2SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IUniswapV2SwapExactAmountIn } from "../../../interfaces/IUniswapV2SwapExactAmountIn.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";

// Types
import { UniswapV2Data } from "../../../AugustusV6Types.sol";

// Utils
import { UniswapV2Utils } from "../../../util/UniswapV2Utils.sol";

/// @title UniswapV2SwapExactAmountIn
/// @notice A contract for executing direct swapExactAmountIn on UniswapV2 pools
abstract contract UniswapV2SwapExactAmountIn is IUniswapV2SwapExactAmountIn, UniswapV2Utils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                                   SWAP
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IUniswapV2SwapExactAmountIn
    function swapExactAmountInOnUniswapV2(
        UniswapV2Data calldata uniData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference uniData
        IERC20 srcToken = uniData.srcToken;
        IERC20 destToken = uniData.destToken;
        uint256 amountIn = uniData.fromAmount;
        uint256 minAmountOut = uniData.toAmount;
        uint256 quotedAmountOut = uniData.quotedAmount;
        address payable beneficiary = uniData.beneficiary;
        bytes calldata pools = uniData.pools;

        // Initialize payer
        address payer = msg.sender;

        // Check if toAmount is valid
        if (minAmountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Check if we need to wrap or permit
        if (srcToken.isETH(amountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
            }
        } else {
            // If it is ETH. wrap it to WETH
            WETH.deposit{ value: amountIn }();
            // Set srcToken to WETH
            srcToken = WETH;
            // Set payer to this contract
            payer = address(this);
        }

        // Execute swap
        _callUniswapV2PoolsSwapExactIn(amountIn, srcToken, pools, payer, permit);

        // Check if destToken is ETH and unwrap
        if (address(destToken) == address(ERC20Utils.ETH)) {
            // Check balance of WETH
            receivedAmount = IERC20(WETH).getBalance(address(this));
            // Unwrap WETH
            WETH.withdraw(receivedAmount - 1);
            // Set receivedAmount to this contract's balance
            receivedAmount = address(this).balance;
        } else {
            // Othwerwise check balance of destToken
            receivedAmount = destToken.getBalance(address(this));
        }

        // Check if swap succeeded
        if (receivedAmount < minAmountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken to beneficiary
        return processSwapExactAmountInFeesAndTransfer(
            beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut
        );
    }
}

File 10 of 47 : UniswapV3SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IUniswapV3SwapExactAmountIn } from "../../../interfaces/IUniswapV3SwapExactAmountIn.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";
import { SafeCastLib } from "@solady/utils/SafeCastLib.sol";

// Types
import { UniswapV3Data } from "../../../AugustusV6Types.sol";

// Utils
import { UniswapV3Utils } from "../../../util/UniswapV3Utils.sol";

/// @title UniswapV3SwapExactAmountIn
/// @notice A contract for executing direct swapExactAmountIn on Uniswap V3
abstract contract UniswapV3SwapExactAmountIn is IUniswapV3SwapExactAmountIn, UniswapV3Utils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;
    using SafeCastLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                   SWAP
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IUniswapV3SwapExactAmountIn
    function swapExactAmountInOnUniswapV3(
        UniswapV3Data calldata uniData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference uniData
        IERC20 srcToken = uniData.srcToken;
        IERC20 destToken = uniData.destToken;
        uint256 amountIn = uniData.fromAmount;
        uint256 minAmountOut = uniData.toAmount;
        uint256 quotedAmountOut = uniData.quotedAmount;
        address payable beneficiary = uniData.beneficiary;
        bytes calldata pools = uniData.pools;

        // Check if toAmount is valid
        if (minAmountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Address that will pay for the swap
        address fromAddress = msg.sender;

        // Check if we need to wrap or permit
        if (srcToken.isETH(amountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
            }
        } else {
            // If it is ETH. wrap it to WETH
            WETH.deposit{ value: amountIn }();
            // Swap will be paid from this contract
            fromAddress = address(this);
        }

        // Execute swap
        receivedAmount = _callUniswapV3PoolsSwapExactAmountIn(amountIn.toInt256(), pools, fromAddress, permit);

        // Check if swap succeeded
        if (receivedAmount < minAmountOut) {
            revert InsufficientReturnAmount();
        }

        // Check if destToken is ETH and unwrap
        if (address(destToken) == address(ERC20Utils.ETH)) {
            // Unwrap WETH
            WETH.withdraw(receivedAmount);
        }

        // Process fees and transfer destToken to beneficiary
        return processSwapExactAmountInFeesAndTransferUniV3(
            beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut
        );
    }
}

File 11 of 47 : BalancerV2SwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IBalancerV2SwapExactAmountOut } from "../../../interfaces/IBalancerV2SwapExactAmountOut.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";

// Types
import { BalancerV2Data } from "../../../AugustusV6Types.sol";

// Utils
import { BalancerV2Utils } from "../../../util/BalancerV2Utils.sol";

/// @title BalancerV2SwapExactAmountOut
/// @notice A contract for executing direct swapExactAmountOut on BalancerV2 pools
abstract contract BalancerV2SwapExactAmountOut is IBalancerV2SwapExactAmountOut, BalancerV2Utils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IBalancerV2SwapExactAmountOut
    function swapExactAmountOutOnBalancerV2(
        BalancerV2Data calldata balancerData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata data
    )
        external
        payable
        whenNotPaused
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference balancerData
        uint256 quotedAmountIn = balancerData.quotedAmount;
        uint256 beneficiaryAndApproveFlag = balancerData.beneficiaryAndApproveFlag;
        uint256 maxAmountIn = balancerData.fromAmount;
        uint256 amountOut = balancerData.toAmount;

        // Decode params
        (IERC20 srcToken, IERC20 destToken, address payable beneficiary, bool approve) =
            _decodeBalancerV2Params(beneficiaryAndApproveFlag, data);

        // Make sure srcToken and destToken are different
        if (srcToken == destToken) {
            revert ArbitrageNotSupported();
        }

        // Check if toAmount is valid
        if (amountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Check contract balance
        uint256 balanceBefore = srcToken.getBalance(address(this));

        // Check if srcToken is ETH
        if (srcToken.isETH(maxAmountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, address(this), maxAmountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), maxAmountIn);
            }
            // Check if approve is needed
            if (approve) {
                // Approve BALANCER_VAULT to spend srcToken
                srcToken.approve(BALANCER_VAULT);
            }
        } else {
            // If srcToken is ETH, we have to deduct msg.value from balanceBefore
            balanceBefore = balanceBefore - msg.value;
        }

        // Execute swap
        _callBalancerV2(data);

        // Check balance of destToken
        receivedAmount = destToken.getBalance(address(this));

        // Check balance of srcToken, deducting the balance before the swap if it is greater than 1
        uint256 remainingAmount = srcToken.getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0);

        // Check if swap succeeded
        if (receivedAmount < amountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken and srcToken to beneficiary
        return processSwapExactAmountOutFeesAndTransfer(
            beneficiary,
            srcToken,
            destToken,
            partnerAndFee,
            maxAmountIn,
            remainingAmount,
            receivedAmount,
            quotedAmountIn
        );
    }
}

File 12 of 47 : UniswapV2SwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IUniswapV2SwapExactAmountOut } from "../../../interfaces/IUniswapV2SwapExactAmountOut.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";

// Types
import { UniswapV2Data } from "../../../AugustusV6Types.sol";

// Utils
import { UniswapV2Utils } from "../../../util/UniswapV2Utils.sol";

/// @title UniswapV2SwapExactAmountOut
/// @notice A contract for executing direct swapExactAmountOut on UniswapV2 pools
abstract contract UniswapV2SwapExactAmountOut is IUniswapV2SwapExactAmountOut, UniswapV2Utils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IUniswapV2SwapExactAmountOut
    function swapExactAmountOutOnUniswapV2(
        UniswapV2Data calldata uniData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference uniData
        IERC20 srcToken = uniData.srcToken;
        IERC20 destToken = uniData.destToken;
        uint256 maxAmountIn = uniData.fromAmount;
        uint256 amountOut = uniData.toAmount;
        uint256 quotedAmountIn = uniData.quotedAmount;
        address payable beneficiary = uniData.beneficiary;
        bytes calldata pools = uniData.pools;

        // Check if toAmount is valid
        if (amountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Init balanceBefore
        uint256 balanceBefore;

        // Check if srcToken is ETH
        bool isFromETH = srcToken.isETH(maxAmountIn) != 0;

        // Check if we need to wrap or permit
        if (isFromETH) {
            // Check WETH balance before
            balanceBefore = IERC20(WETH).getBalance(address(this));
            // If it is ETH. wrap it to WETH
            WETH.deposit{ value: maxAmountIn }();
            // Set srcToken to WETH
            srcToken = WETH;
        } else {
            // Check srcToken balance before
            balanceBefore = srcToken.getBalance(address(this));
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, address(this), maxAmountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), maxAmountIn);
            }
        }

        // Make sure srcToken and destToken are different
        if (srcToken == destToken) {
            revert ArbitrageNotSupported();
        }

        // Execute swap
        _callUniswapV2PoolsSwapExactOut(amountOut, srcToken, pools);

        // Check if destToken is ETH and unwrap
        if (address(destToken) == address(ERC20Utils.ETH)) {
            // Make sure srcToken was not WETH
            if (srcToken == WETH) {
                revert ArbitrageNotSupported();
            }
            // Check balance of WETH
            receivedAmount = IERC20(WETH).getBalance(address(this));
            // Leave dust if receivedAmount > amountOut
            if (receivedAmount > amountOut) {
                --receivedAmount;
            }
            // Unwrap WETH
            WETH.withdraw(receivedAmount);
            // Set receivedAmount to this contract's balance
            receivedAmount = address(this).balance;
        } else {
            // Othwerwise check balance of destToken
            receivedAmount = destToken.getBalance(address(this));
        }

        // Check balance of srcToken
        uint256 remainingAmount = srcToken.getBalance(address(this));

        // Check if swap succeeded
        if (receivedAmount < amountOut) {
            revert InsufficientReturnAmount();
        }

        // Check if srcToken is ETH and unwrap if there is remaining amount
        if (isFromETH) {
            // Check native balance before
            uint256 nativeBalanceBefore = address(this).balance;
            // If balanceBefore is greater than 1, deduct it from remainingAmount
            remainingAmount = remainingAmount - (balanceBefore > 1 ? balanceBefore : 0);
            // Withdraw remaining WETH if any
            if (remainingAmount > 1) {
                WETH.withdraw(remainingAmount - 1);
            }
            srcToken = ERC20Utils.ETH;
            // If native balance before is greater than 1, deduct it from remainingAmount
            remainingAmount = address(this).balance - (nativeBalanceBefore > 1 ? nativeBalanceBefore : 0);
        } else {
            // Otherwise, if balanceBefore is greater than 1, deduct it from remainingAmount
            remainingAmount = remainingAmount - (balanceBefore > 1 ? balanceBefore : 0);
        }

        // Process fees and transfer destToken and srcToken to beneficiary
        return processSwapExactAmountOutFeesAndTransfer(
            beneficiary,
            srcToken,
            destToken,
            partnerAndFee,
            maxAmountIn,
            remainingAmount,
            receivedAmount,
            quotedAmountIn
        );
    }
}

File 13 of 47 : UniswapV3SwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IUniswapV3SwapExactAmountOut } from "../../../interfaces/IUniswapV3SwapExactAmountOut.sol";

// Libraries
import { ERC20Utils } from "../../../libraries/ERC20Utils.sol";
import { SafeCastLib } from "@solady/utils/SafeCastLib.sol";

// Types
import { UniswapV3Data } from "../../../AugustusV6Types.sol";

// Utils
import { UniswapV3Utils } from "../../../util/UniswapV3Utils.sol";

/// @title UniswapV3SwapExactAmountOut
/// @notice A contract for executing direct swapExactAmountOut on UniswapV3 pools
abstract contract UniswapV3SwapExactAmountOut is IUniswapV3SwapExactAmountOut, UniswapV3Utils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;
    using SafeCastLib for uint256;

    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IUniswapV3SwapExactAmountOut
    function swapExactAmountOutOnUniswapV3(
        UniswapV3Data calldata uniData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference uniData
        IERC20 srcToken = uniData.srcToken;
        IERC20 destToken = uniData.destToken;
        uint256 maxAmountIn = uniData.fromAmount;
        uint256 amountOut = uniData.toAmount;
        uint256 quotedAmountIn = uniData.quotedAmount;
        address payable beneficiary = uniData.beneficiary;
        bytes calldata pools = uniData.pools;

        // Check if toAmount is valid
        if (amountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Address that will pay for the swap
        address fromAddress = msg.sender;

        // Check if srcToken is ETH
        bool isFromETH = srcToken.isETH(maxAmountIn) != 0;

        // If pools.length > 96, we are going to do a multi-pool swap
        bool isMultiplePools = pools.length > 96;

        // Init balance before variables
        uint256 senderBalanceBefore;
        uint256 balanceBefore;

        // Check if we need to wrap or permit
        if (isFromETH) {
            // Check WETH balance before
            balanceBefore = IERC20(WETH).getBalance(address(this));
            // If it is ETH. wrap it to WETH
            WETH.deposit{ value: maxAmountIn }();
            // Swap will be paid from this contract
            fromAddress = address(this);
            // Set srcToken to WETH
            srcToken = WETH;
        } else {
            // Check srcToken balance before
            balanceBefore = srcToken.getBalance(address(this));
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                // if we're using multiple pools, we need to store the pre-swap balance of srcToken
                if (isMultiplePools) {
                    senderBalanceBefore = srcToken.getBalance(msg.sender);
                }
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), maxAmountIn);
                // Swap will be paid from this contract
                fromAddress = address(this);
            }
        }

        // Make sure srcToken and destToken are different
        if (srcToken == destToken) {
            revert ArbitrageNotSupported();
        }

        // Execute swap
        (spentAmount, receivedAmount) =
            _callUniswapV3PoolsSwapExactAmountOut((-amountOut.toInt256()), pools, fromAddress);

        // Check if swap succeeded
        if (receivedAmount < amountOut) {
            revert InsufficientReturnAmount();
        }

        // Check if destToken is ETH and unwrap
        if (address(destToken) == address(ERC20Utils.ETH)) {
            // Make sure srcToken was not WETH
            if (srcToken == WETH) {
                revert ArbitrageNotSupported();
            }
            // Unwrap WETH
            WETH.withdraw(receivedAmount);
        }

        // Iniiialize remainingAmount
        uint256 remainingAmount;

        // Check if payer is this contract
        if (fromAddress == address(this)) {
            // If srcTokenwas ETH, we need to withdraw remaining WETH if any
            if (isFromETH) {
                // Check native balance before
                uint256 nativeBalanceBefore = address(this).balance;
                // Check balance of WETH, If balanceBefore is greater than 1, deduct it from remainingAmount
                remainingAmount = IERC20(WETH).getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0);
                // Withdraw remaining WETH if any
                if (remainingAmount > 1) {
                    // Unwrap WETH
                    WETH.withdraw(remainingAmount - 1);
                    // If native balance before is greater than 1, deduct it from remainingAmount
                    remainingAmount = address(this).balance - (nativeBalanceBefore > 1 ? nativeBalanceBefore : 0);
                }
                // Set srcToken to ETH
                srcToken = ERC20Utils.ETH;
            } else {
                // If we have executed multi-pool swap, we need to fetch the remaining amount from balance
                if (isMultiplePools) {
                    // Calculate spent amount and remaining amount, If balanceBefore is greater than 1, deduct it from
                    // remainingAmount
                    remainingAmount = srcToken.getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0);
                } else {
                    // Otherwise, remaining amount is the difference between the spent amount and the remaining balance
                    remainingAmount = maxAmountIn - spentAmount;
                }
            }

            // Process fees using processSwapExactAmountOutFeesAndTransfer
            return processSwapExactAmountOutFeesAndTransfer(
                beneficiary,
                srcToken,
                destToken,
                partnerAndFee,
                maxAmountIn,
                remainingAmount,
                receivedAmount,
                quotedAmountIn
            );
        } else {
            // If we have executed multi-pool swap, we need to re-calculate the remaining amount and spent amount
            if (isMultiplePools) {
                // Calculate spent amount and remaining amount
                remainingAmount = srcToken.getBalance(msg.sender);
                spentAmount = senderBalanceBefore - remainingAmount;
            }
            // Process fees and transfer destToken and srcToken to feeVault or partner and
            // feeWallet if needed
            return processSwapExactAmountOutFeesAndTransferUniV3(
                beneficiary,
                srcToken,
                destToken,
                partnerAndFee,
                maxAmountIn,
                receivedAmount,
                spentAmount,
                quotedAmountIn
            );
        }
    }
}

File 14 of 47 : AugustusFees.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IAugustusFeeVault } from "../interfaces/IAugustusFeeVault.sol";
import { IAugustusFees } from "../interfaces/IAugustusFees.sol";

// Libraries
import { ERC20Utils } from "../libraries/ERC20Utils.sol";

// Storage
import { AugustusStorage } from "../storage/AugustusStorage.sol";

/// @title AugustusFees
/// @notice Contract for handling fees
contract AugustusFees is AugustusStorage, IAugustusFees {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev Fee share constants
    uint256 public constant PARTNER_SHARE_PERCENT = 8500;
    uint256 public constant MAX_FEE_PERCENT = 200;
    uint256 public constant SURPLUS_PERCENT = 100;
    uint256 public constant PARASWAP_REFERRAL_SHARE = 5000;
    uint256 public constant PARTNER_REFERRAL_SHARE = 2500;
    uint256 public constant PARASWAP_SURPLUS_SHARE = 5000;
    uint256 public constant PARASWAP_SLIPPAGE_SHARE = 10_000;
    uint256 public constant MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI = 11;

    /// @dev Masks for unpacking feeData
    uint256 private constant FEE_PERCENT_IN_BASIS_POINTS_MASK = 0x3FFF;
    uint256 private constant IS_USER_SURPLUS_MASK = 1 << 90;
    uint256 private constant IS_DIRECT_TRANSFER_MASK = 1 << 91;
    uint256 private constant IS_CAP_SURPLUS_MASK = 1 << 92;
    uint256 private constant IS_SKIP_BLACKLIST_MASK = 1 << 93;
    uint256 private constant IS_REFERRAL_MASK = 1 << 94;
    uint256 private constant IS_TAKE_SURPLUS_MASK = 1 << 95;

    /// @dev A contact that stores fees collected by the protocol
    IAugustusFeeVault public immutable FEE_VAULT; // solhint-disable-line var-name-mixedcase

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

    constructor(address _feeVault) {
        FEE_VAULT = IAugustusFeeVault(_feeVault);
    }

    /*//////////////////////////////////////////////////////////////
                       SWAP EXACT AMOUNT IN FEES
    //////////////////////////////////////////////////////////////*/

    /// @notice Process swapExactAmountIn fees and transfer the received amount to the beneficiary
    /// @param destToken The received token from the swapExactAmountIn
    /// @param partnerAndFee Packed partner and fee data
    /// @param receivedAmount The amount of destToken received from the swapExactAmountIn
    /// @param quotedAmount The quoted expected amount of destToken
    /// @return returnAmount The amount of destToken transfered to the beneficiary
    /// @return paraswapFeeShare The share of the fees for Paraswap
    /// @return partnerFeeShare The share of the fees for the partner
    function processSwapExactAmountInFeesAndTransfer(
        address beneficiary,
        IERC20 destToken,
        uint256 partnerAndFee,
        uint256 receivedAmount,
        uint256 quotedAmount
    )
        internal
        returns (uint256 returnAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare)
    {
        // initialize the surplus
        uint256 surplus;

        // parse partner and fee data
        (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee);

        // calculate the surplus, we expect there to be 1 wei dust left which we should
        // not take into account when determining if there is surplus, we only take the
        // surplus if it is greater than MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI
        if (receivedAmount > quotedAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) {
            surplus = receivedAmount - quotedAmount;
            // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount
            if (feeData & IS_CAP_SURPLUS_MASK != 0) {
                uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000;
                surplus = surplus > cappedSurplus ? cappedSurplus : surplus;
            }
        }

        // calculate remainingAmount
        uint256 remainingAmount = receivedAmount - surplus;

        // if partner address is not 0x0
        if (partner != address(0x0)) {
            // Check if skip blacklist flag is true
            bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0;
            // Check if token is blacklisted
            bool isBlacklisted = blacklistedTokens[destToken];
            // If the token is blacklisted and the skipBlacklist flag is false,
            // send the received amount to the beneficiary, we won't process fees
            if (!skipBlacklist && isBlacklisted) {
                // transfer the received amount to the beneficiary, keeping 1 wei dust
                _transferAndLeaveDust(destToken, beneficiary, receivedAmount);
                return (receivedAmount - 1, 0, 0);
            }
            // Check if direct transfer flag is true
            bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0;
            // partner takes fixed fees feePercent is greater than 0
            uint256 feePercent = _getAdjustedFeePercent(feeData);
            if (feePercent > 0) {
                // fee base = min (receivedAmount, quotedAmount + surplus)
                uint256 feeBase = receivedAmount > quotedAmount + surplus ? quotedAmount + surplus : receivedAmount;
                // calculate fixed fees
                uint256 fee = (feeBase * feePercent) / 10_000;
                partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000;
                paraswapFeeShare = fee - partnerFeeShare;
                // distrubite fees from destToken
                returnAmount = _distributeFees(
                    receivedAmount,
                    destToken,
                    partner,
                    partnerFeeShare,
                    paraswapFeeShare,
                    skipBlacklist,
                    isBlacklisted,
                    isDirectTransfer
                );
                // transfer the return amount to the beneficiary, keeping 1 wei dust
                _transferAndLeaveDust(destToken, beneficiary, returnAmount);
                return (returnAmount - 1, paraswapFeeShare, partnerFeeShare);
            }
            // if slippage is postive and referral flag is true
            else if (feeData & IS_REFERRAL_MASK != 0) {
                if (surplus > 0) {
                    // the split is 50% for paraswap, 25% for the referrer and 25% for the user
                    paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000;
                    partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000;
                    // distribute fees from destToken
                    returnAmount = _distributeFees(
                        receivedAmount,
                        destToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    );
                    // transfer the return amount to the beneficiary, keeping 1 wei dust
                    _transferAndLeaveDust(destToken, beneficiary, returnAmount);
                    return (returnAmount - 1, paraswapFeeShare, partnerFeeShare);
                }
            }
            // if slippage is positive and takeSurplus flag is true
            else if (feeData & IS_TAKE_SURPLUS_MASK != 0) {
                if (surplus > 0) {
                    // paraswap takes 50% of the surplus and partner takes the other 50%
                    paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000;
                    partnerFeeShare = surplus - paraswapFeeShare;
                    // If user surplus flag is true, transfer the partner share to the user instead of the partner
                    if (feeData & IS_USER_SURPLUS_MASK != 0) {
                        partnerFeeShare = 0;
                        // Transfer the paraswap share directly to the fee wallet
                        isDirectTransfer = true;
                    }
                    // distrubite fees from destToken, partner takes 50% of the surplus
                    // and paraswap takes the other 50%
                    returnAmount = _distributeFees(
                        receivedAmount,
                        destToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    );
                    // transfer the return amount to the beneficiary, keeping 1 wei dust
                    _transferAndLeaveDust(destToken, beneficiary, returnAmount);
                    return (returnAmount - 1, paraswapFeeShare, partnerFeeShare);
                }
            }
        }

        // if slippage is positive and partner address is 0x0 or fee percent is 0
        // paraswap will take the surplus and transfer the rest to the beneficiary
        // if there is no positive slippage, transfer the received amount to the beneficiary
        if (surplus > 0) {
            // If the token is blacklisted, send the received amount to the beneficiary
            // we won't process fees
            if (blacklistedTokens[destToken]) {
                // transfer the received amount to the beneficiary, keeping 1 wei dust
                _transferAndLeaveDust(destToken, beneficiary, receivedAmount);
                return (receivedAmount - 1, 0, 0);
            }
            // transfer the remaining amount to the beneficiary, keeping 1 wei dust
            _transferAndLeaveDust(destToken, beneficiary, remainingAmount);
            // transfer the surplus to the fee wallet
            destToken.safeTransfer(feeWallet, surplus);
            return (remainingAmount - 1, surplus, 0);
        } else {
            // transfer the received amount to the beneficiary, keeping 1 wei dust
            _transferAndLeaveDust(destToken, beneficiary, receivedAmount);
            return (receivedAmount - 1, 0, 0);
        }
    }

    /// @notice Process swapExactAmountIn fees and transfer the received amount to the beneficiary
    /// @param destToken The received token from the swapExactAmountIn
    /// @param partnerAndFee Packed partner and fee data
    /// @param receivedAmount The amount of destToken received from the swapExactAmountIn
    /// @param quotedAmount The quoted expected amount of destToken
    /// @return returnAmount The amount of destToken transfered to the beneficiary
    /// @return paraswapFeeShare The share of the fees for Paraswap
    /// @return partnerFeeShare The share of the fees for the partner
    function processSwapExactAmountInFeesAndTransferUniV3(
        address beneficiary,
        IERC20 destToken,
        uint256 partnerAndFee,
        uint256 receivedAmount,
        uint256 quotedAmount
    )
        internal
        returns (uint256 returnAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare)
    {
        // initialize the surplus
        uint256 surplus;

        // parse partner and fee data
        (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee);

        // calculate the surplus, we do not take the surplus into account if it is less than
        // MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI
        if (receivedAmount > quotedAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) {
            surplus = receivedAmount - quotedAmount;
            // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount
            if (feeData & IS_CAP_SURPLUS_MASK != 0) {
                uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000;
                surplus = surplus > cappedSurplus ? cappedSurplus : surplus;
            }
        }

        // calculate remainingAmount
        uint256 remainingAmount = receivedAmount - surplus;

        // if partner address is not 0x0
        if (partner != address(0x0)) {
            // Check if skip blacklist flag is true
            bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0;
            // Check if token is blacklisted
            bool isBlacklisted = blacklistedTokens[destToken];
            // If the token is blacklisted and the skipBlacklist flag is false,
            // send the received amount to the beneficiary, we won't process fees
            if (!skipBlacklist && isBlacklisted) {
                // transfer the received amount to the beneficiary
                destToken.safeTransfer(beneficiary, receivedAmount);
                return (receivedAmount, 0, 0);
            }
            // Check if direct transfer flag is true
            bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0;
            // partner takes fixed fees feePercent is greater than 0
            uint256 feePercent = _getAdjustedFeePercent(feeData);
            if (feePercent > 0) {
                // fee base = min (receivedAmount, quotedAmount + surplus)
                uint256 feeBase = receivedAmount > quotedAmount + surplus ? quotedAmount + surplus : receivedAmount;
                // calculate fixed fees
                uint256 fee = (feeBase * feePercent) / 10_000;
                partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000;
                paraswapFeeShare = fee - partnerFeeShare;
                // distrubite fees from destToken
                returnAmount = _distributeFees(
                    receivedAmount,
                    destToken,
                    partner,
                    partnerFeeShare,
                    paraswapFeeShare,
                    skipBlacklist,
                    isBlacklisted,
                    isDirectTransfer
                );
                // transfer the return amount to the beneficiary
                destToken.safeTransfer(beneficiary, returnAmount);
                return (returnAmount, paraswapFeeShare, partnerFeeShare);
            }
            // if slippage is postive and referral flag is true
            else if (feeData & IS_REFERRAL_MASK != 0) {
                if (surplus > 0) {
                    // the split is 50% for paraswap, 25% for the referrer and 25% for the user
                    paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000;
                    partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000;
                    // distribute fees from destToken
                    returnAmount = _distributeFees(
                        receivedAmount,
                        destToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    );
                    // transfer the return amount to the beneficiary
                    destToken.safeTransfer(beneficiary, returnAmount);
                    return (returnAmount, paraswapFeeShare, partnerFeeShare);
                }
            }
            // if slippage is positive and takeSurplus flag is true
            else if (feeData & IS_TAKE_SURPLUS_MASK != 0) {
                if (surplus > 0) {
                    // paraswap takes 50% of the surplus and partner takes the other 50%
                    paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000;
                    partnerFeeShare = surplus - paraswapFeeShare;
                    // If user surplus flag is true, transfer the partner share to the user instead of the partner
                    if (feeData & IS_USER_SURPLUS_MASK != 0) {
                        partnerFeeShare = 0;
                        // Transfer the paraswap share directly to the fee wallet
                        isDirectTransfer = true;
                    }
                    // distrubite fees from destToken, partner takes 50% of the surplus
                    // and paraswap takes the other 50%
                    returnAmount = _distributeFees(
                        receivedAmount,
                        destToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    );
                    // transfer the return amount to the beneficiary,
                    destToken.safeTransfer(beneficiary, returnAmount);
                    return (returnAmount, paraswapFeeShare, partnerFeeShare);
                }
            }
        }

        // if slippage is positive and partner address is 0x0 or fee percent is 0
        // paraswap will take the surplus and transfer the rest to the beneficiary
        // if there is no positive slippage, transfer the received amount to the beneficiary
        if (surplus > 0) {
            // If the token is blacklisted, send the received amount to the beneficiary
            // we won't process fees
            if (blacklistedTokens[destToken]) {
                // transfer the received amount to the beneficiary
                destToken.safeTransfer(beneficiary, receivedAmount);
                return (receivedAmount, 0, 0);
            }
            // transfer the remaining amount to the beneficiary
            destToken.safeTransfer(beneficiary, remainingAmount);
            // transfer the surplus to the fee wallet
            destToken.safeTransfer(feeWallet, surplus);
            return (remainingAmount, surplus, 0);
        } else {
            // transfer the received amount to the beneficiary
            destToken.safeTransfer(beneficiary, receivedAmount);
            return (receivedAmount, 0, 0);
        }
    }

    /*//////////////////////////////////////////////////////////////
                       SWAP EXACT AMOUNT OUT FEES
    //////////////////////////////////////////////////////////////*/

    /// @notice Process swapExactAmountOut fees and transfer the received amount and remaining amount to the
    /// beneficiary
    /// @param srcToken The token used to swapExactAmountOut
    /// @param destToken The token received from the swapExactAmountOut
    /// @param partnerAndFee Packed partner and fee data
    /// @param maxAmountIn The amount of srcToken passed to the swapExactAmountOut
    /// @param receivedAmount The amount of destToken received from the swapExactAmountOut
    /// @param quotedAmount The quoted expected amount of srcToken to be used to swapExactAmountOut
    /// @return spentAmount The amount of srcToken used to swapExactAmountOut
    /// @return outAmount The amount of destToken transfered to the beneficiary
    /// @return paraswapFeeShare The share of the fees for Paraswap
    /// @return partnerFeeShare The share of the fees for the partner
    function processSwapExactAmountOutFeesAndTransfer(
        address beneficiary,
        IERC20 srcToken,
        IERC20 destToken,
        uint256 partnerAndFee,
        uint256 maxAmountIn,
        uint256 remainingAmount,
        uint256 receivedAmount,
        uint256 quotedAmount
    )
        internal
        returns (uint256 spentAmount, uint256 outAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare)
    {
        // calculate the amount used to swapExactAmountOut
        spentAmount = maxAmountIn - (remainingAmount > 0 ? remainingAmount - 1 : remainingAmount);

        // initialize the surplus
        uint256 surplus;

        // initialize the return amount
        uint256 returnAmount;

        // parse partner and fee data
        (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee);

        // check if the quotedAmount is bigger than the maxAmountIn
        if (quotedAmount > maxAmountIn) {
            revert InvalidQuotedAmount();
        }

        // calculate the surplus, we do not take the surplus into account if it is less than
        // MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI
        if (quotedAmount > spentAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) {
            surplus = quotedAmount - spentAmount;
            // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount
            if (feeData & IS_CAP_SURPLUS_MASK != 0) {
                uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000;
                surplus = surplus > cappedSurplus ? cappedSurplus : surplus;
            }
        }

        // if partner address is not 0x0
        if (partner != address(0x0)) {
            // Check if skip blacklist flag is true
            bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0;
            // Check if token is blacklisted
            bool isBlacklisted = blacklistedTokens[srcToken];
            // If the token is blacklisted and the skipBlacklist flag is false,
            // send the remaining amount to the msg.sender, we won't process fees
            if (!skipBlacklist && isBlacklisted) {
                // transfer the remaining amount to msg.sender
                returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount);
                // transfer the received amount of destToken to the beneficiary
                destToken.safeTransfer(beneficiary, --receivedAmount);
                return (maxAmountIn - returnAmount, receivedAmount, 0, 0);
            }
            // Check if direct transfer flag is true
            bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0;
            // partner takes fixed fees feePercent is greater than 0
            uint256 feePercent = _getAdjustedFeePercent(feeData);
            if (feePercent > 0) {
                // fee base = min (spentAmount, quotedAmount)
                uint256 feeBase = spentAmount < quotedAmount ? spentAmount : quotedAmount;
                // calculate fixed fees
                uint256 fee = (feeBase * feePercent) / 10_000;
                partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000;
                paraswapFeeShare = fee - partnerFeeShare;
                // distrubite fees from srcToken
                returnAmount = _distributeFees(
                    remainingAmount,
                    srcToken,
                    partner,
                    partnerFeeShare,
                    paraswapFeeShare,
                    skipBlacklist,
                    isBlacklisted,
                    isDirectTransfer
                );
                // transfer the rest to msg.sender
                returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, returnAmount);
                // transfer the received amount of destToken to the beneficiary
                destToken.safeTransfer(beneficiary, --receivedAmount);
                return (maxAmountIn - returnAmount, receivedAmount, paraswapFeeShare, partnerFeeShare);
            }
            // if slippage is postive and referral flag is true
            if (feeData & IS_REFERRAL_MASK != 0) {
                if (surplus > 0) {
                    // the split is 50% for paraswap, 25% for the referrer and 25% for the user
                    paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000;
                    partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000;
                    // distribute fees from srcToken
                    returnAmount = _distributeFees(
                        remainingAmount,
                        srcToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    );
                    // transfer the rest to msg.sender
                    returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, returnAmount);
                    // transfer the received amount of destToken to the beneficiary
                    destToken.safeTransfer(beneficiary, --receivedAmount);
                    return (maxAmountIn - returnAmount, receivedAmount, paraswapFeeShare, partnerFeeShare);
                }
            }
            // if slippage is positive and takeSurplus flag is true
            else if (feeData & IS_TAKE_SURPLUS_MASK != 0) {
                if (surplus > 0) {
                    // paraswap takes 50% of the surplus and partner takes the other 50%
                    paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000;
                    partnerFeeShare = surplus - paraswapFeeShare;
                    // If user surplus flag is true, transfer the partner share to the user instead of the partner
                    if (feeData & IS_USER_SURPLUS_MASK != 0) {
                        partnerFeeShare = 0;
                        // Transfer the paraswap share directly to the fee wallet
                        isDirectTransfer = true;
                    }
                    // distrubite fees from srcToken, partner takes 50% of the surplus
                    // and paraswap takes the other 50%
                    returnAmount = _distributeFees(
                        remainingAmount,
                        srcToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    );
                    // transfer the rest to msg.sender
                    returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, returnAmount);
                    // transfer the received amount of destToken to the beneficiary
                    destToken.safeTransfer(beneficiary, --receivedAmount);
                    return (maxAmountIn - returnAmount, receivedAmount, paraswapFeeShare, partnerFeeShare);
                }
            }
        }

        // transfer the received amount of destToken to the beneficiary
        destToken.safeTransfer(beneficiary, --receivedAmount);

        // if slippage is positive and partner address is 0x0 or fee percent is 0
        // paraswap will take the surplus, and transfer the rest to msg.sender
        // if there is no positive slippage, transfer the remaining amount to msg.sender
        if (surplus > 0) {
            // If the token is blacklisted, send the remaining amount to the msg.sender
            // we won't process fees
            if (blacklistedTokens[srcToken]) {
                // transfer the remaining amount to msg.sender
                returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount);
                return (maxAmountIn - returnAmount, receivedAmount, 0, 0);
            }
            // transfer the surplus to the fee wallet
            srcToken.safeTransfer(feeWallet, surplus);
            // transfer the remaining amount to msg.sender
            returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount - surplus);
            return (maxAmountIn - returnAmount, receivedAmount, surplus, 0);
        } else {
            // transfer the remaining amount to msg.sender
            returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount);
            return (maxAmountIn - returnAmount, receivedAmount, 0, 0);
        }
    }

    /// @notice Process swapExactAmountOut fees for UniV3 swapExactAmountOut, doing a transferFrom user to the fee
    /// vault or partner and feeWallet
    /// @param beneficiary The user's address
    /// @param srcToken The token used to swapExactAmountOut
    /// @param destToken The token received from the swapExactAmountOut
    /// @param partnerAndFee Packed partner and fee data
    /// @param maxAmountIn The amount of srcToken passed to the swapExactAmountOut
    /// @param receivedAmount The amount of destToken received from the swapExactAmountOut
    /// @param spentAmount The amount of srcToken used to swapExactAmountOut
    /// @param quotedAmount The quoted expected amount of srcToken to be used to swapExactAmountOut
    /// @return totalSpentAmount The total amount of srcToken used to swapExactAmountOut
    /// @return returnAmount The amount of destToken transfered to the beneficiary
    /// @return paraswapFeeShare The share of the fees for Paraswap
    /// @return partnerFeeShare The share of the fees for the partner
    function processSwapExactAmountOutFeesAndTransferUniV3(
        address beneficiary,
        IERC20 srcToken,
        IERC20 destToken,
        uint256 partnerAndFee,
        uint256 maxAmountIn,
        uint256 receivedAmount,
        uint256 spentAmount,
        uint256 quotedAmount
    )
        internal
        returns (uint256 totalSpentAmount, uint256 returnAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare)
    {
        // initialize the surplus
        uint256 surplus;

        // calculate remaining amount
        uint256 remainingAmount = maxAmountIn - spentAmount;

        // parse partner and fee data
        (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee);

        // check if the quotedAmount is bigger than the fromAmount
        if (quotedAmount > maxAmountIn) {
            revert InvalidQuotedAmount();
        }

        // calculate the surplus, we do not take the surplus into account if it is less than
        // MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI
        if (quotedAmount > spentAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) {
            surplus = quotedAmount - spentAmount;
            // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount
            if (feeData & IS_CAP_SURPLUS_MASK != 0) {
                uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000;
                surplus = surplus > cappedSurplus ? cappedSurplus : surplus;
            }
        }

        // if partner address is not 0x0
        if (partner != address(0x0)) {
            // Check if skip blacklist flag is true
            bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0;
            // Check if token is blacklisted
            bool isBlacklisted = blacklistedTokens[srcToken];
            // If the token is blacklisted and the skipBlacklist flag is false,
            // we won't process fees
            if (!skipBlacklist && isBlacklisted) {
                // transfer the received amount of destToken to the beneficiary
                destToken.safeTransfer(beneficiary, receivedAmount);
                return (spentAmount, receivedAmount, 0, 0);
            }
            // Check if direct transfer flag is true
            bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0;
            // partner takes fixed fees feePercent is greater than 0
            uint256 feePercent = _getAdjustedFeePercent(feeData);
            if (feePercent > 0) {
                // fee base = min (spentAmount, quotedAmount)
                uint256 feeBase = spentAmount < quotedAmount ? spentAmount : quotedAmount;
                // calculate fixed fees
                uint256 fee = (feeBase * feePercent) / 10_000;
                partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000;
                paraswapFeeShare = fee - partnerFeeShare;
                // distrubite fees from srcToken
                totalSpentAmount = _distributeFeesUniV3(
                    remainingAmount,
                    msg.sender,
                    srcToken,
                    partner,
                    partnerFeeShare,
                    paraswapFeeShare,
                    skipBlacklist,
                    isBlacklisted,
                    isDirectTransfer
                ) + spentAmount;
                // transfer the received amount of destToken to the beneficiary
                destToken.safeTransfer(beneficiary, receivedAmount);
                return (totalSpentAmount, receivedAmount, paraswapFeeShare, partnerFeeShare);
            }
            // if slippage is postive and referral flag is true
            else if (feeData & IS_REFERRAL_MASK != 0) {
                if (surplus > 0) {
                    // the split is 50% for paraswap, 25% for the referrer and 25% for the user
                    paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000;
                    partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000;
                    // distribute fees from srcToken
                    totalSpentAmount = _distributeFeesUniV3(
                        remainingAmount,
                        msg.sender,
                        srcToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    ) + spentAmount;
                    // transfer the received amount of destToken to the beneficiary
                    destToken.safeTransfer(beneficiary, receivedAmount);
                    return (totalSpentAmount, receivedAmount, paraswapFeeShare, partnerFeeShare);
                }
            }
            // if slippage is positive and takeSurplus flag is true
            else if (feeData & IS_TAKE_SURPLUS_MASK != 0) {
                if (surplus > 0) {
                    // paraswap takes 50% of the surplus and partner takes the other 50%
                    paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000;
                    partnerFeeShare = surplus - paraswapFeeShare;
                    // If user surplus flag is true, transfer the partner share to the user instead of the partner
                    if (feeData & IS_USER_SURPLUS_MASK != 0) {
                        partnerFeeShare = 0;
                        // Transfer the paraswap share directly to the fee wallet
                        isDirectTransfer = true;
                    }
                    //  partner takes 50% of the surplus and paraswap takes the other 50%
                    // distrubite fees from srcToken
                    totalSpentAmount = _distributeFeesUniV3(
                        remainingAmount,
                        msg.sender,
                        srcToken,
                        partner,
                        partnerFeeShare,
                        paraswapFeeShare,
                        skipBlacklist,
                        isBlacklisted,
                        isDirectTransfer
                    ) + spentAmount;
                    // transfer the received amount of destToken to the beneficiary
                    destToken.safeTransfer(beneficiary, receivedAmount);
                    return (totalSpentAmount, receivedAmount, paraswapFeeShare, partnerFeeShare);
                }
            }
        }

        // transfer the received amount of destToken to the beneficiary
        destToken.safeTransfer(beneficiary, receivedAmount);

        // if slippage is positive and partner address is 0x0 or fee percent is 0
        // paraswap will take the surplus
        if (surplus > 0) {
            // If the token is blacklisted, we won't process fees
            if (blacklistedTokens[srcToken]) {
                return (spentAmount, receivedAmount, 0, 0);
            }
            // transfer the surplus to the fee wallet
            srcToken.safeTransferFrom(msg.sender, feeWallet, surplus);
        }
        return (spentAmount + surplus, receivedAmount, surplus, 0);
    }

    /*//////////////////////////////////////////////////////////////
                                 PUBLIC
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAugustusFees
    function parsePartnerAndFeeData(uint256 partnerAndFee)
        public
        pure
        returns (address payable partner, uint256 feeData)
    {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            partner := shr(96, partnerAndFee)
            feeData := and(partnerAndFee, 0xFFFFFFFFFFFFFFFFFFFFFFFF)
        }
    }

    /*//////////////////////////////////////////////////////////////
                                PRIVATE
    //////////////////////////////////////////////////////////////*/

    /// @notice Distribute fees to the partner and paraswap
    /// @param currentBalance The current balance of the token before distributing the fees
    /// @param token The token to distribute the fees for
    /// @param partner The partner address
    /// @param partnerShare The partner share
    /// @param paraswapShare The paraswap share
    /// @param skipBlacklist Whether to skip the blacklist and transfer the fees directly to the partner
    /// @param isBlacklisted Whether the token is blacklisted
    /// @param directTransfer Whether to transfer the fees directly to the partner instead of the fee vault
    /// @return newBalance The new balance of the token after distributing the fees
    function _distributeFees(
        uint256 currentBalance,
        IERC20 token,
        address payable partner,
        uint256 partnerShare,
        uint256 paraswapShare,
        bool skipBlacklist,
        bool isBlacklisted,
        bool directTransfer
    )
        private
        returns (uint256 newBalance)
    {
        uint256 totalFees = partnerShare + paraswapShare;
        if (totalFees == 0) {
            return currentBalance;
        } else {
            if (skipBlacklist && isBlacklisted) {
                // totalFees should be just the partner share, paraswap does not take fees
                // on blacklisted tokens, the rest of the fees are sent to sender based on
                // newBalance = currentBalance - totalFees
                totalFees = partnerShare;
                // revert if the balance is not enough to pay the fees
                if (totalFees > currentBalance) {
                    revert InsufficientBalanceToPayFees();
                }
                if (partnerShare > 0) {
                    token.safeTransfer(partner, partnerShare);
                }
            } else {
                // revert if the balance is not enough to pay the fees
                if (totalFees > currentBalance) {
                    revert InsufficientBalanceToPayFees();
                }
                if (directTransfer) {
                    // transfer the fees directly to the partner and paraswap
                    if (paraswapShare > 0) {
                        token.safeTransfer(feeWallet, paraswapShare);
                    }
                    if (partnerShare > 0) {
                        token.safeTransfer(partner, partnerShare);
                    }
                } else {
                    // transfer the fees to the fee vault
                    token.safeTransfer(address(FEE_VAULT), totalFees);
                    // Setup fee registration data
                    address[] memory feeAddresses = new address[](2);
                    uint256[] memory feeAmounts = new uint256[](2);
                    feeAddresses[0] = partner;
                    feeAmounts[0] = partnerShare;
                    feeAddresses[1] = feeWalletDelegate;
                    feeAmounts[1] = paraswapShare;
                    IAugustusFeeVault.FeeRegistration memory feeData =
                        IAugustusFeeVault.FeeRegistration({ token: token, addresses: feeAddresses, fees: feeAmounts });
                    // Register the fees
                    FEE_VAULT.registerFees(feeData);
                }
            }
        }
        newBalance = currentBalance - totalFees;
    }

    /// @notice Distribute fees for UniV3
    /// @param currentBalance The current balance of the token before distributing the fees
    /// @param payer The user's address
    /// @param token The token to distribute the fees for
    /// @param partner The partner address
    /// @param partnerShare The partner share
    /// @param paraswapShare The paraswap share
    /// @param skipBlacklist Whether to skip the blacklist and transfer the fees directly to the partner
    /// @param isBlacklisted Whether the token is blacklisted
    /// @param directTransfer Whether to transfer the fees directly to the partner instead of the fee vault
    /// @return totalFees The total fees distributed
    function _distributeFeesUniV3(
        uint256 currentBalance,
        address payer,
        IERC20 token,
        address payable partner,
        uint256 partnerShare,
        uint256 paraswapShare,
        bool skipBlacklist,
        bool isBlacklisted,
        bool directTransfer
    )
        private
        returns (uint256 totalFees)
    {
        totalFees = partnerShare + paraswapShare;
        if (totalFees != 0) {
            if (skipBlacklist && isBlacklisted) {
                // totalFees should be just the partner share, paraswap does not take fees
                // on blacklisted tokens, the rest of the fees will remain on the payer's address
                totalFees = partnerShare;
                // revert if the balance is not enough to pay the fees
                if (totalFees > currentBalance) {
                    revert InsufficientBalanceToPayFees();
                }
                // transfer the fees to the partner
                if (partnerShare > 0) {
                    // transfer the fees to the partner
                    token.safeTransferFrom(payer, partner, partnerShare);
                }
            } else {
                // revert if the balance is not enough to pay the fees
                if (totalFees > currentBalance) {
                    revert InsufficientBalanceToPayFees();
                }
                if (directTransfer) {
                    // transfer the fees directly to the partner and paraswap
                    if (paraswapShare > 0) {
                        token.safeTransferFrom(payer, feeWallet, paraswapShare);
                    }
                    if (partnerShare > 0) {
                        token.safeTransferFrom(payer, partner, partnerShare);
                    }
                } else {
                    // transfer the fees to the fee vault
                    token.safeTransferFrom(payer, address(FEE_VAULT), totalFees);
                    // Setup fee registration data
                    address[] memory feeAddresses = new address[](2);
                    uint256[] memory feeAmounts = new uint256[](2);
                    feeAddresses[0] = partner;
                    feeAmounts[0] = partnerShare;
                    feeAddresses[1] = feeWalletDelegate;
                    feeAmounts[1] = paraswapShare;
                    IAugustusFeeVault.FeeRegistration memory feeData =
                        IAugustusFeeVault.FeeRegistration({ token: token, addresses: feeAddresses, fees: feeAmounts });
                    // Register the fees
                    FEE_VAULT.registerFees(feeData);
                }
            }
            // othwerwise do not transfer the fees
        }
        return totalFees;
    }

    /// @notice Get the adjusted fee percent by masking feePercent with FEE_PERCENT_IN_BASIS_POINTS_MASK,
    /// if the fee percent is bigger than MAX_FEE_PERCENT, then set it to MAX_FEE_PERCENT
    /// @param feePercent The fee percent
    /// @return adjustedFeePercent The adjusted fee percent
    function _getAdjustedFeePercent(uint256 feePercent) private pure returns (uint256) {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            feePercent := and(feePercent, FEE_PERCENT_IN_BASIS_POINTS_MASK)
            // if feePercent is bigger than MAX_FEE_PERCENT, then set it to MAX_FEE_PERCENT
            if gt(feePercent, MAX_FEE_PERCENT) { feePercent := MAX_FEE_PERCENT }
        }
        return feePercent;
    }

    /// @notice Transfers amount to recipient if the amount is bigger than 1, leaving 1 wei dust on the contract
    /// @param token The token to transfer
    /// @param recipient The address to transfer to
    /// @param amount The amount to transfer
    function _transferIfGreaterThanOne(
        IERC20 token,
        address recipient,
        uint256 amount
    )
        private
        returns (uint256 amountOut)
    {
        if (amount > 1) {
            unchecked {
                --amount;
            }
            token.safeTransfer(recipient, amount);
            return amount;
        }
        return 0;
    }

    /// @notice Transfer amount to beneficiary, leaving 1 wei dust on the contract
    /// @param token The token to transfer
    /// @param beneficiary The address to transfer to
    /// @param amount The amount to transfer
    function _transferAndLeaveDust(IERC20 token, address beneficiary, uint256 amount) private {
        unchecked {
            --amount;
        }
        token.safeTransfer(beneficiary, amount);
    }
}

File 15 of 47 : GenericSwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Contracts
import { GenericUtils } from "../../util/GenericUtils.sol";

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IGenericSwapExactAmountIn } from "../../interfaces/IGenericSwapExactAmountIn.sol";

// Libraries
import { ERC20Utils } from "../../libraries/ERC20Utils.sol";

// Types
import { GenericData } from "../../AugustusV6Types.sol";

/// @title GenericSwapExactAmountIn
/// @notice Router for executing generic swaps with exact amount in through an executor
abstract contract GenericSwapExactAmountIn is IGenericSwapExactAmountIn, GenericUtils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IGenericSwapExactAmountIn
    function swapExactAmountIn(
        address executor,
        GenericData calldata swapData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata executorData
    )
        external
        payable
        whenNotPaused
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference swapData
        IERC20 destToken = swapData.destToken;
        IERC20 srcToken = swapData.srcToken;
        uint256 amountIn = swapData.fromAmount;
        uint256 minAmountOut = swapData.toAmount;
        uint256 quotedAmountOut = swapData.quotedAmount;
        address payable beneficiary = swapData.beneficiary;

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Check if toAmount is valid
        if (minAmountOut == 0) {
            revert InvalidToAmount();
        }

        // Check if srcToken is ETH
        if (srcToken.isETH(amountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, executor, amountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, executor, amountIn);
            }
        }

        // Execute swap
        _callSwapExactAmountInExecutor(executor, executorData, amountIn);

        // Check balance after swap
        receivedAmount = destToken.getBalance(address(this));

        // Check if swap succeeded
        if (receivedAmount < minAmountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken to beneficiary
        return processSwapExactAmountInFeesAndTransfer(
            beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut
        );
    }
}

File 16 of 47 : GenericSwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IGenericSwapExactAmountOut } from "../../interfaces/IGenericSwapExactAmountOut.sol";

// Libraries
import { ERC20Utils } from "../../libraries/ERC20Utils.sol";

// Types
import { GenericData } from "../../AugustusV6Types.sol";

// Utils
import { GenericUtils } from "../../util/GenericUtils.sol";

/// @title GenericSwapExactAmountOut
/// @notice Router for executing generic swaps with exact amount out through an executor
abstract contract GenericSwapExactAmountOut is IGenericSwapExactAmountOut, GenericUtils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IGenericSwapExactAmountOut
    function swapExactAmountOut(
        address executor,
        GenericData calldata swapData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata executorData
    )
        external
        payable
        whenNotPaused
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare)
    {
        // Dereference swapData
        IERC20 destToken = swapData.destToken;
        IERC20 srcToken = swapData.srcToken;
        uint256 maxAmountIn = swapData.fromAmount;
        uint256 amountOut = swapData.toAmount;
        uint256 quotedAmountIn = swapData.quotedAmount;
        address payable beneficiary = swapData.beneficiary;

        // Make sure srcToken and destToken are different
        if (srcToken == destToken) {
            revert ArbitrageNotSupported();
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Check if toAmount is valid
        if (amountOut == 0) {
            revert InvalidToAmount();
        }

        // Check contract balance
        uint256 balanceBefore = srcToken.getBalance(address(this));

        // Check if srcToken is ETH
        // Transfer srcToken to executor if not ETH
        if (srcToken.isETH(maxAmountIn) == 0) {
            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, executor, maxAmountIn);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, executor, maxAmountIn);
            }
        } else {
            // If srcToken is ETH, we have to deduct msg.value from balanceBefore
            balanceBefore = balanceBefore - msg.value;
        }

        // Execute swap
        _callSwapExactAmountOutExecutor(executor, executorData, maxAmountIn, amountOut);

        // Check balance of destToken
        receivedAmount = destToken.getBalance(address(this));

        // Check balance of srcToken, deducting the balance before the swap if it is greater than 1
        uint256 remainingAmount = srcToken.getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0);

        // Check if swap succeeded
        if (receivedAmount < amountOut) {
            revert InsufficientReturnAmount();
        }

        // Process fees and transfer destToken and srcToken to beneficiary
        return processSwapExactAmountOutFeesAndTransfer(
            beneficiary,
            srcToken,
            destToken,
            partnerAndFee,
            maxAmountIn,
            remainingAmount,
            receivedAmount,
            quotedAmountIn
        );
    }
}

File 17 of 47 : AugustusRFQRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IAugustusRFQRouter } from "../../interfaces/IAugustusRFQRouter.sol";

// Libraries
import { ERC20Utils } from "../../libraries/ERC20Utils.sol";

// Types
import { AugustusRFQData, OrderInfo } from "../../AugustusV6Types.sol";

// Utils
import { AugustusRFQUtils } from "../../util/AugustusRFQUtils.sol";
import { WETHUtils } from "../../util/WETHUtils.sol";
import { PauseUtils } from "../../util/PauseUtils.sol";
import { Permit2Utils } from "../../util/Permit2Utils.sol";
import { AugustusFees } from "../../fees/AugustusFees.sol";

/// @title AugustusRFQRouter
/// @notice A contract for executing direct AugustusRFQ swaps
abstract contract AugustusRFQRouter is
    IAugustusRFQRouter,
    AugustusRFQUtils,
    AugustusFees,
    WETHUtils,
    Permit2Utils,
    PauseUtils
{
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                             TRY BATCH FILL
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IAugustusRFQRouter
    // solhint-disable-next-line code-complexity
    function swapOnAugustusRFQTryBatchFill(
        AugustusRFQData calldata data,
        OrderInfo[] calldata orders,
        bytes calldata permit
    )
        external
        payable
        whenNotPaused
        returns (uint256 spentAmount, uint256 receivedAmount)
    {
        // Dereference data
        address payable beneficiary = data.beneficiary;
        uint256 ordersLength = orders.length;
        uint256 fromAmount = data.fromAmount;
        uint256 toAmount = data.toAmount;
        uint8 wrapApproveDirection = data.wrapApproveDirection;

        // Decode wrapApproveDirection
        // First 2 bits are for wrap
        // Next 1 bit is for approve
        // Last 1 bit is for direction

        uint8 wrap;
        bool approve;
        bool direction;

        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            wrap := and(3, wrapApproveDirection)
            approve := and(shr(2, wrapApproveDirection), 1)
            direction := and(shr(3, wrapApproveDirection), 1)
        }

        // Check if beneficiary is valid
        if (beneficiary == address(0)) {
            beneficiary = payable(msg.sender);
        }

        // Check if toAmount is valid
        if (toAmount == 0) {
            revert InvalidToAmount();
        }

        // Check if ordersLength is valid
        if (ordersLength == 0) {
            revert InvalidOrdersLength();
        }

        // Check if msg.sender is authorized to be the taker for all orders
        for (uint256 i = 0; i < ordersLength; ++i) {
            _checkAuthorization(orders[i].order.nonceAndMeta);
        }

        // Dereference srcToken and destToken
        IERC20 srcToken = IERC20(orders[0].order.takerAsset);
        IERC20 destToken = IERC20(orders[0].order.makerAsset);

        // Check if we need to wrap or permit
        if (wrap != 1) {
            // If msg.value is not 0, revert
            if (msg.value > 0) {
                revert IncorrectEthAmount();
            }

            // Check the length of the permit field,
            // if < 257 and > 0 we should execute regular permit
            // and if it is >= 257 we execute permit2
            if (permit.length < 257) {
                // Permit if needed
                if (permit.length > 0) {
                    srcToken.permit(permit);
                }
                srcToken.safeTransferFrom(msg.sender, address(this), fromAmount);
            } else {
                // Otherwise Permit2.permitTransferFrom
                permit2TransferFrom(permit, address(this), fromAmount);
            }
        } else {
            // Check if msg.value is equal to fromAmount
            if (fromAmount != msg.value) {
                revert IncorrectEthAmount();
            }
            // If it is ETH. wrap it to WETH
            WETH.deposit{ value: fromAmount }();
        }

        if (approve) {
            // Approve srcToken to AugustusRFQ
            srcToken.approve(address(AUGUSTUS_RFQ));
        }

        // Check if we need to execute a swapExactAmountIn or a swapExactAmountOut
        if (!direction) {
            // swapExactAmountIn
            // Unwrap WETH if needed
            if (wrap == 2) {
                // Execute tryBatchFillOrderTakerAmount
                AUGUSTUS_RFQ.tryBatchFillOrderTakerAmount(orders, fromAmount, address(this));
                // Check received amount
                receivedAmount = IERC20(WETH).getBalance(address(this));
                // Check if swap succeeded
                if (receivedAmount < toAmount) {
                    revert InsufficientReturnAmount();
                }
                // Unwrap WETH
                WETH.withdraw(--receivedAmount);
                // Transfer ETH to beneficiary
                ERC20Utils.ETH.safeTransfer(beneficiary, receivedAmount);
            } else {
                // Check balance of beneficiary before swap
                uint256 beforeBalance = destToken.getBalance(beneficiary);
                // Execute tryBatchFillOrderTakerAmount
                AUGUSTUS_RFQ.tryBatchFillOrderTakerAmount(orders, fromAmount, beneficiary);
                // set receivedAmount to afterBalance - beforeBalance
                receivedAmount = destToken.getBalance(beneficiary) - beforeBalance;
                // Check if swap succeeded
                if (receivedAmount < toAmount) {
                    revert InsufficientReturnAmount();
                }
            }

            // Return spentAmount and receivedAmount
            return (fromAmount, receivedAmount);
        } else {
            // swapExactAmountOut
            // Unwrap WETH if needed
            if (wrap == 2) {
                // Execute tryBatchFillOrderMakerAmount
                AUGUSTUS_RFQ.tryBatchFillOrderMakerAmount(orders, toAmount, address(this));
                // Check remaining WETH balance
                receivedAmount = IERC20(WETH).getBalance(address(this));
                // Unwrap WETH
                WETH.withdraw(--receivedAmount);
                // Transfer ETH to beneficiary
                ERC20Utils.ETH.safeTransfer(beneficiary, receivedAmount);
                // Set toAmount to receivedAmount
                toAmount = receivedAmount;
            } else {
                // Execute tryBatchFillOrderMakerAmount
                AUGUSTUS_RFQ.tryBatchFillOrderMakerAmount(orders, toAmount, beneficiary);
            }

            // Check remaining amount
            uint256 remainingAmount = srcToken.getBalance(address(this));

            // Send remaining srcToken to msg.sender
            if (remainingAmount > 1) {
                // If srcToken was ETH
                if (wrap == 1) {
                    // Unwrap WETH
                    WETH.withdraw(--remainingAmount);
                    // Transfer ETH to msg.sender
                    ERC20Utils.ETH.safeTransfer(msg.sender, remainingAmount);
                } else {
                    // Transfer remaining srcToken to msg.sender
                    srcToken.safeTransfer(msg.sender, --remainingAmount);
                }
            }

            // Return spentAmount and receivedAmount
            return (fromAmount - remainingAmount, toAmount);
        }
    }
}

File 18 of 47 : AugustusRFQUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IAugustusRFQ } from "../interfaces/IAugustusRFQ.sol";
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";

// Libraries
import { ERC20Utils } from "../libraries/ERC20Utils.sol";

/// @title AugustusRFQUtils
/// @notice A contract containing common utilities for AugustusRFQ swaps
contract AugustusRFQUtils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using ERC20Utils for IERC20;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when the msg.sender is not authorized to be the taker
    error UnauthorizedUser();

    /// @dev Emitted when the orders length is 0
    error InvalidOrdersLength();

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev AugustusRFQ address
    IAugustusRFQ public immutable AUGUSTUS_RFQ; // solhint-disable-line var-name-mixedcase

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

    constructor(address _augustusRFQ) {
        AUGUSTUS_RFQ = IAugustusRFQ(_augustusRFQ);
    }

    /*//////////////////////////////////////////////////////////////
                                 INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev Check if the msg.sender is authorized to be the taker
    function _checkAuthorization(uint256 nonceAndMeta) internal view {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Parse nonceAndMeta
            if xor(and(nonceAndMeta, 0xffffffffffffffffffffffffffffffffffffffff), 0) {
                // If the taker is not 0, we check if the msg.sender is authorized
                if xor(and(nonceAndMeta, 0xffffffffffffffffffffffffffffffffffffffff), caller()) {
                    // The taker does not match the originalSender, revert
                    mstore(0, 0x02a43f8b00000000000000000000000000000000000000000000000000000000) // function
                        // selector for error UnauthorizedUser();
                    revert(0, 4)
                }
            }
        }
    }
}

File 19 of 47 : BalancerV2Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Contracts
import { AugustusFees } from "../fees/AugustusFees.sol";

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

// Utils
import { Permit2Utils } from "./Permit2Utils.sol";
import { PauseUtils } from "./PauseUtils.sol";

/// @title BalancerV2Utils
/// @notice A contract containing common utilities for BalancerV2 swaps
abstract contract BalancerV2Utils is AugustusFees, Permit2Utils, PauseUtils {
    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when the passed selector is invalid
    error InvalidSelector();

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev BalancerV2 vault address
    address payable public immutable BALANCER_VAULT; // solhint-disable-line var-name-mixedcase

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

    constructor(address payable _balancerVault) {
        BALANCER_VAULT = _balancerVault;
    }

    /*//////////////////////////////////////////////////////////////
                                 INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev Decode srcToken, destToken from balancerData, beneficiary and approve flag from beneficiaryAndApproveFlag
    function _decodeBalancerV2Params(
        uint256 beneficiaryAndApproveFlag,
        bytes calldata balancerData
    )
        internal
        pure
        returns (IERC20 srcToken, IERC20 destToken, address payable beneficiary, bool approve)
    {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            // Parse beneficiaryAndApproveFlag
            beneficiary := and(beneficiaryAndApproveFlag, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
            approve := shr(255, beneficiaryAndApproveFlag)
            // Load calldata without selector
            let callDataWithoutSelector := add(4, balancerData.offset)
            // Check selector
            switch calldataload(balancerData.offset)
            // If the selector is for swap(tuple singleSwap,tuple funds,uint256 limit,uint256 deadline)
            case 0x52bbbe2900000000000000000000000000000000000000000000000000000000 {
                // Load srcToken from singleSswap.assetIn
                srcToken := calldataload(add(callDataWithoutSelector, 288))
                // Load destToken from singleSswap.assetOut
                destToken := calldataload(add(callDataWithoutSelector, 320))
            }
            // If the selector is for batchSwap(uint8 kind,tuple[] swaps,address[] assets,tuple funds,int256[]
            // limits,uint256 deadline)
            case 0x945bcec900000000000000000000000000000000000000000000000000000000 {
                // Load assetOffset from balancerData
                let assetsOffset := calldataload(add(callDataWithoutSelector, 64))
                // Load assetCount at assetOffset
                let assetsCount := calldataload(add(callDataWithoutSelector, assetsOffset))
                // Get swapExactAmountIn type from first 32 bytes of balancerData
                let swapType := calldataload(callDataWithoutSelector)
                // Set fromAmount, srcToken, toAmount and destToken based on swapType
                switch eq(swapType, 1)
                case 1 {
                    // Load srcToken as the last asset in balancerData.assets
                    srcToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, mul(assetsCount, 32))))
                    // Load destToken as the first asset in balancerData.assets
                    destToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, 32)))
                }
                default {
                    // Load srcToken as the first asset in balancerData.assets
                    srcToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, 32)))
                    // Load destToken as the last asset in balancerData.assets
                    destToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, mul(assetsCount, 32))))
                }
            }
            default {
                // If the selector is invalid, revert
                mstore(0, 0x7352d91c00000000000000000000000000000000000000000000000000000000) // store the
                    // selector for error InvalidSelector();
                revert(0, 4)
            }
            // Balancer users 0x0 as ETH address so we need to convert it
            if eq(srcToken, 0) { srcToken := 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE }
            if eq(destToken, 0) { destToken := 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE }
        }
        return (srcToken, destToken, beneficiary, approve);
    }

    /// @dev Call balancerVault with data
    function _callBalancerV2(bytes calldata balancerData) internal {
        address payable targetAddress = BALANCER_VAULT;
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            // Load free memory pointer
            let ptr := mload(64)
            // Copy the balancerData to memory
            calldatacopy(ptr, balancerData.offset, balancerData.length)
            // Execute the call on balancerVault
            if iszero(call(gas(), targetAddress, callvalue(), ptr, balancerData.length, 0, 0)) {
                returndatacopy(ptr, 0, returndatasize()) // copy the revert data to memory
                revert(ptr, returndatasize()) // revert with the revert data
            }
        }
    }
}

File 20 of 47 : UniswapV2Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Contracts
import { AugustusFees } from "../fees/AugustusFees.sol";

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

// Utils
import { WETHUtils } from "./WETHUtils.sol";
import { Permit2Utils } from "./Permit2Utils.sol";
import { PauseUtils } from "./PauseUtils.sol";

/// @title UniswapV2Utils
/// @notice A contract containing common utilities for UniswapV2 swaps
abstract contract UniswapV2Utils is AugustusFees, WETHUtils, Permit2Utils, PauseUtils {
    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev Used to caluclate pool address
    uint256 public immutable UNISWAP_V2_POOL_INIT_CODE_HASH;

    /// @dev Right padded FF + UniswapV2Factory address
    uint256 public immutable UNISWAP_V2_FACTORY_AND_FF;

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

    constructor(uint256 _uniswapV2FactoryAndFF, uint256 _uniswapV2PoolInitCodeHash) {
        UNISWAP_V2_FACTORY_AND_FF = _uniswapV2FactoryAndFF;
        UNISWAP_V2_POOL_INIT_CODE_HASH = _uniswapV2PoolInitCodeHash;
    }

    /*//////////////////////////////////////////////////////////////
                               INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev Loops through UniswapV2 pools in backword direction and swaps exact amount out
    function _callUniswapV2PoolsSwapExactOut(uint256 amountOut, IERC20 srcToken, bytes calldata pools) internal {
        uint256 uniswapV2FactoryAndFF = UNISWAP_V2_FACTORY_AND_FF;
        uint256 uniswapV2PoolInitCodeHash = UNISWAP_V2_POOL_INIT_CODE_HASH;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            function calculatePoolAddress(
                poolMemoryPtr, poolCalldataPtr, _uniswapV2FactoryAndFF, _uniswapV2PoolInitCodeHash
            ) {
                // Calculate the pool address
                // We can do this by first calling the keccak256 function on the passed pool values and then
                // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address),
                // keccak256(abi.encodePacked(token0, token1)), POOL_INIT_CODE_HASH));
                // The first 20 bytes of the computed address are the pool address

                // Store 0xff + factory address (right padded)
                mstore(poolMemoryPtr, _uniswapV2FactoryAndFF)

                // Store pools offset + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE)
                let token0ptr := add(poolMemoryPtr, 21)

                // Copy pool data (skip last bit) to free memory pointer + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE)
                calldatacopy(token0ptr, poolCalldataPtr, 40)

                // Calculate keccak256(abi.encode(address(token0), address(token1))
                mstore(token0ptr, keccak256(token0ptr, 40))

                // Store POOL_INIT_CODE_HASH
                mstore(add(token0ptr, 32), _uniswapV2PoolInitCodeHash)

                // Calculate address(keccak256(abi.encodePacked(hex'ff', address(factory_address),
                // keccak256(abi.encode(token0, token1), POOL_INIT_CODE_HASH)));
                mstore(poolMemoryPtr, and(keccak256(poolMemoryPtr, 85), 0xffffffffffffffffffffffffffffffffffffffff)) // 21
                    // + 32 + 32
            }

            // Calculate pool count
            let poolCount := div(pools.length, 64)

            // Initilize memory pointers
            let amounts := mload(64) // pointer for amounts array
            let poolAddresses := add(amounts, add(mul(poolCount, 32), 32)) // pointer for pools array
            let emptyPtr := add(poolAddresses, mul(poolCount, 32)) // pointer for empty memory

            // Initialize fromAmount
            let fromAmount := 0

            // Set the final amount in the amounts array to amountOut
            mstore(add(amounts, mul(poolCount, 0x20)), amountOut)

            //---------------------------------//
            // Calculate Pool Addresses and Amounts
            //---------------------------------//

            // Calculate pool addresses
            for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } {
                calculatePoolAddress(
                    add(poolAddresses, mul(i, 32)),
                    add(pools.offset, mul(i, 64)),
                    uniswapV2FactoryAndFF,
                    uniswapV2PoolInitCodeHash
                )
            }

            // Rerverse loop through pools and calculate amounts
            for { let i := poolCount } gt(i, 0) { i := sub(i, 1) } {
                // Use previous pool data to calculate amount in
                let indexSub1 := sub(i, 1)

                // Get pool address
                let poolAddress := mload(add(poolAddresses, mul(indexSub1, 32)))

                // Get direction
                let direction := and(1, calldataload(add(add(pools.offset, mul(indexSub1, 64)), 32)))

                // Get amount
                let amount := mload(add(amounts, mul(i, 32)))

                //---------------------------------//
                // Calculate Amount In
                //---------------------------------//

                //---------------------------------//
                // Get Reserves
                //---------------------------------//

                // Store the selector
                mstore(emptyPtr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000) // 'getReserves()'
                // selector

                // Perform the external 'getReserves' call - outputs directly to ptr
                if iszero(staticcall(gas(), poolAddress, emptyPtr, 4, emptyPtr, 64)) {
                    returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                    revert(0, returndatasize()) // Revert with the error message
                }

                // If direction is true, getReserves returns (reserve0, reserve1)
                // If direction is false, getReserves returns (reserve1, reserve0) -> swap the values

                // Load the reserve0 value returned by the 'getReserves' call.
                let reserve1 := mload(emptyPtr)

                // Load the reserve1 value returned by the 'getReserves' call.
                let reserve0 := mload(add(emptyPtr, 32))

                // Check if direction is true
                if direction {
                    // swap reserve0 and reserve1
                    let temp := reserve0
                    reserve0 := reserve1
                    reserve1 := temp
                }

                //---------------------------------//

                // Calculate numerator = reserve0 * amountOut * 10000
                let numerator := mul(mul(reserve0, amount), 10000)

                // Calculate denominator = (reserve1 - amountOut) * 9970
                let denominator := mul(sub(reserve1, amount), 9970)

                // Calculate amountIn = numerator / denominator + 1
                fromAmount := add(div(numerator, denominator), 1)

                // Store amountIn for the previous pool
                mstore(add(amounts, mul(indexSub1, 32)), fromAmount)
            }

            //---------------------------------//

            // Initialize variables
            let poolAddress := 0
            let nextPoolAddress := 0

            //---------------------------------//
            // Loop Swap Through Pools
            //---------------------------------//

            // Loop for each pool
            for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } {
                // Check if it is the first pool
                if iszero(poolAddress) {
                    // If it is the first pool, we need to transfer amount of srcToken to poolAddress
                    // Load first pool address
                    poolAddress := mload(poolAddresses)

                    //---------------------------------//
                    // Transfer amount of srcToken to poolAddress
                    //---------------------------------//

                    // Transfer fromAmount of srcToken to poolAddress
                    mstore(emptyPtr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the
                        // selector
                        // (function transfer(address recipient, uint256 amount))
                    mstore(add(emptyPtr, 4), poolAddress) // store the recipient
                    mstore(add(emptyPtr, 36), fromAmount) // store the amount
                    pop(call(gas(), srcToken, 0, emptyPtr, 68, 0, 32)) // call transfer

                    //---------------------------------//
                }

                // Adjust toAddress depending on if it is the last pool in the array
                let toAddress := address()

                // Check if it is not the last pool
                if lt(add(i, 1), poolCount) {
                    // Load next pool address
                    nextPoolAddress := mload(add(poolAddresses, mul(add(i, 1), 32)))

                    // Adjust toAddress to next pool address
                    toAddress := nextPoolAddress
                }

                // Check direction
                let direction := and(1, calldataload(add(add(pools.offset, mul(i, 64)), 32)))

                // if direction is 1, amount0out is 0 and amount1out is amount[i+1]
                // if direction is 0, amount0out is amount[i+1] and amount1out is 0

                // Load amount[i+1]
                let amount := mload(add(amounts, mul(add(i, 1), 32)))

                // Initialize amount0Out and amount1Out
                let amount0Out := amount
                let amount1Out := 0

                // Check if direction is true
                if direction {
                    // swap amount0Out and amount1Out
                    let temp := amount0Out
                    amount0Out := amount1Out
                    amount1Out := temp
                }

                //---------------------------------//
                // Perform Swap
                //---------------------------------//

                // Load the 'swap' selector, amount0Out, amount1Out, toAddress and data("") into memory.
                mstore(emptyPtr, 0x022c0d9f00000000000000000000000000000000000000000000000000000000)
                // 'swap()' selector
                mstore(add(emptyPtr, 4), amount0Out) // amount0Out
                mstore(add(emptyPtr, 36), amount1Out) // amount1Out
                mstore(add(emptyPtr, 68), toAddress) // toAddress
                mstore(add(emptyPtr, 100), 0x80) // data length
                mstore(add(emptyPtr, 132), 0) // data

                // Perform the external 'swap' call
                if iszero(call(gas(), poolAddress, 0, emptyPtr, 164, 0, 64)) {
                    // 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
                }

                //---------------------------------//

                // Set poolAddress to nextPoolAddress
                poolAddress := nextPoolAddress
            }

            //---------------------------------//
        }
    }

    /// @dev Loops through UniswapV2 pools and swaps exact amount in
    function _callUniswapV2PoolsSwapExactIn(
        uint256 fromAmount,
        IERC20 srcToken,
        bytes calldata pools,
        address payer,
        bytes calldata permit2
    )
        internal
    {
        uint256 uniswapV2FactoryAndFF = UNISWAP_V2_FACTORY_AND_FF;
        uint256 uniswapV2PoolInitCodeHash = UNISWAP_V2_POOL_INIT_CODE_HASH;
        address permit2Address = PERMIT2;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            //---------------------------------//
            // Loop Swap Through Pools
            //---------------------------------//

            // Calculate pool count
            let poolCount := div(pools.length, 64)

            // Initialize variables
            let p := 0
            let poolAddress := 0
            let nextPoolAddress := 0
            let direction := 0

            // Loop for each pool
            for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } {
                // Check if it is the first pool
                if iszero(p) {
                    //---------------------------------//
                    // Calculate Pool Address
                    //---------------------------------//

                    // Calculate the pool address
                    // We can do this by first calling the keccak256 function on the passed pool values and then
                    // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address),
                    // keccak256(abi.encodePacked(token0,token1)), POOL_INIT_CODE_HASH));
                    // The first 20 bytes of the computed address are the pool address

                    // Get free memory pointer
                    let ptr := mload(64)

                    // Store 0xff + factory address (right padded)
                    mstore(ptr, uniswapV2FactoryAndFF)

                    // Store pools offset + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE)
                    let token0ptr := add(ptr, 21)

                    // Copy pool data (skip last bit) to free memory pointer + 21 bytes (UNISWAP_V2_FACTORY_AND_FF
                    // SIZE)
                    calldatacopy(token0ptr, pools.offset, 40)

                    // Calculate keccak256(abi.encodePacked(address(token0), address(token1))
                    mstore(token0ptr, keccak256(token0ptr, 40))

                    // Store POOL_INIT_CODE_HASH
                    mstore(add(token0ptr, 32), uniswapV2PoolInitCodeHash)

                    // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address),
                    // keccak256(abi.encode(token0,
                    // token1, fee)), POOL_INIT_CODE_HASH));
                    mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32

                    // Load pool
                    p := mload(ptr)

                    // Get the first 20 bytes of the computed address
                    poolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff)

                    //---------------------------------//

                    //---------------------------------//
                    // Transfer fromAmount of srcToken to poolAddress
                    //---------------------------------//

                    switch eq(payer, address())
                    // if payer is this contract, transfer fromAmount of srcToken to poolAddress
                    case 1 {
                        // Transfer fromAmount of srcToken to poolAddress
                        mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the
                            // selector
                            // (function transfer(address recipient, uint256 amount))
                        mstore(add(ptr, 4), poolAddress) // store the recipient
                        mstore(add(ptr, 36), fromAmount) // store the amount
                        pop(call(gas(), srcToken, 0, ptr, 68, 0, 32)) // call transfer
                    }
                    // othwerwise transferFrom fromAmount of srcToken to poolAddress from payer
                    default {
                        switch gt(permit2.length, 256)
                        case 0 {
                            // Transfer fromAmount of srcToken to poolAddress
                            mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // store
                                // the selector
                            // (function transferFrom(address sender, address recipient,
                            // uint256 amount))
                            mstore(add(ptr, 4), payer) // store the sender
                            mstore(add(ptr, 36), poolAddress) // store the recipient
                            mstore(add(ptr, 68), fromAmount) // store the amount
                            pop(call(gas(), srcToken, 0, ptr, 100, 0, 32)) // call transferFrom
                        }
                        default {
                            // Otherwise Permit2.permitTransferFrom
                            // Store function selector
                            mstore(ptr, 0x30f28b7a00000000000000000000000000000000000000000000000000000000)
                            // permitTransferFrom()
                            calldatacopy(add(ptr, 4), permit2.offset, permit2.length) // Copy data to memory
                            mstore(add(ptr, 132), poolAddress) // Store recipient
                            mstore(add(ptr, 164), fromAmount) // Store amount
                            mstore(add(ptr, 196), payer) // Store payer
                            // Call permit2.permitTransferFrom and revert if call failed
                            if iszero(call(gas(), permit2Address, 0, ptr, add(permit2.length, 4), 0, 0)) {
                                mstore(0, 0x6b836e6b00000000000000000000000000000000000000000000000000000000) // Store
                                    // error selector
                                    // error Permit2Failed()
                                revert(0, 4)
                            }
                        }
                    }

                    //---------------------------------//
                }

                // Direction is the first bit of the pool data
                direction := and(1, calldataload(add(add(pools.offset, mul(i, 64)), 32)))

                //---------------------------------//
                // Calculate Amount Out
                //---------------------------------//

                //---------------------------------//
                // Get Reserves
                //---------------------------------//

                // Get free memory pointer
                let ptr := mload(64)

                // Store the selector
                mstore(ptr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000) // 'getReserves()'
                // selector

                // Perform the external 'getReserves' call - outputs directly to ptr
                if iszero(staticcall(gas(), poolAddress, ptr, 4, ptr, 64)) {
                    returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
                    revert(0, returndatasize()) // Revert with the error message
                }

                // If direction is true, getReserves returns (reserve0, reserve1)
                // If direction is false, getReserves returns (reserve1, reserve0) -> swap the values

                // Load the reserve0 value returned by the 'getReserves' call.
                let reserve1 := mload(ptr)

                // Load the reserve1 value returned by the 'getReserves' call.
                let reserve0 := mload(add(ptr, 32))

                // Check if direction is true
                if direction {
                    // swap reserve0 and reserve1
                    let temp := reserve0
                    reserve0 := reserve1
                    reserve1 := temp
                }

                //---------------------------------//

                // Calculate amount based on fee
                let amountWithFee := mul(fromAmount, 9970)

                // Calculate numerator = amountWithFee * reserve1
                let numerator := mul(amountWithFee, reserve1)

                // Calculate denominator = reserve0 * 10000 + amountWithFee
                let denominator := add(mul(reserve0, 10000), amountWithFee)

                // Calculate amountOut = numerator / denominator
                let amountOut := div(numerator, denominator)

                fromAmount := amountOut

                // if direction is true, amount0Out is 0 and amount1Out is fromAmount,
                // otherwise amount0Out is fromAmount and amount1Out is 0

                let amount0Out := fromAmount
                let amount1Out := 0

                // swap amount0Out and amount1Out if direction is false
                if direction {
                    amount0Out := 0
                    amount1Out := fromAmount
                }

                //---------------------------------//

                // Adjust toAddress depending on if it is the last pool in the array
                let toAddress := address()

                // Check if it is not the last pool
                if lt(add(i, 1), poolCount) {
                    //---------------------------------//
                    // Calculate Next Pool Address
                    //---------------------------------//

                    // Store 0xff + factory address (right padded)
                    mstore(ptr, uniswapV2FactoryAndFF)

                    // Store pools offset + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE)
                    let token0ptr := add(ptr, 21)

                    // Copy next pool data to free memory pointer + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE)
                    calldatacopy(token0ptr, add(pools.offset, mul(add(i, 1), 64)), 40)

                    // Calculate keccak256(abi.encodePacked(address(token0), address(token1))
                    mstore(token0ptr, keccak256(token0ptr, 40))

                    // Store POOL_INIT_CODE_HASH
                    mstore(add(token0ptr, 32), uniswapV2PoolInitCodeHash)

                    // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address),
                    // keccak256(abi.encode(token0,
                    // token1), POOL_INIT_CODE_HASH));
                    mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32

                    // Load pool
                    p := mload(ptr)

                    // Get the first 20 bytes of the computed address
                    nextPoolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff)

                    // Adjust toAddress to next pool address
                    toAddress := nextPoolAddress

                    //---------------------------------//
                }

                //---------------------------------//
                // Perform Swap
                //---------------------------------//

                // Load the 'swap' selector, amount0Out, amount1Out, toAddress and data("") into memory.
                mstore(ptr, 0x022c0d9f00000000000000000000000000000000000000000000000000000000)
                // 'swap()' selector
                mstore(add(ptr, 4), amount0Out) // amount0Out
                mstore(add(ptr, 36), amount1Out) // amount1Out
                mstore(add(ptr, 68), toAddress) // toAddress
                mstore(add(ptr, 100), 0x80) // data length
                mstore(add(ptr, 132), 0) // data

                // Perform the external 'swap' call
                if iszero(call(gas(), poolAddress, 0, ptr, 164, 0, 64)) {
                    // 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
                }

                //---------------------------------//

                // Set poolAddress to nextPoolAddress
                poolAddress := nextPoolAddress
            }

            //---------------------------------//
        }
    }
}

File 21 of 47 : UniswapV3Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Contracts
import { AugustusFees } from "../fees/AugustusFees.sol";

// Interfaces
import { IUniswapV3SwapCallback } from "../interfaces/IUniswapV3SwapCallback.sol";

// Libraries
import { SafeCastLib } from "@solady/utils/SafeCastLib.sol";

// Utils
import { WETHUtils } from "./WETHUtils.sol";
import { Permit2Utils } from "./Permit2Utils.sol";
import { PauseUtils } from "./PauseUtils.sol";

/// @title UniswapV3Utils
/// @notice A contract containing common utilities for UniswapV3 swaps
abstract contract UniswapV3Utils is IUniswapV3SwapCallback, AugustusFees, WETHUtils, Permit2Utils, PauseUtils {
    /*//////////////////////////////////////////////////////////////
                               LIBRARIES
    //////////////////////////////////////////////////////////////*/

    using SafeCastLib for int256;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Error emitted if the caller is not a Uniswap V3 pool
    error InvalidCaller();
    /// @notice Error emitted if the transfer of tokens to the pool inside the callback failed
    error CallbackTransferFailed();

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev Used to caluclate pool address
    uint256 public immutable UNISWAP_V3_POOL_INIT_CODE_HASH;

    /// @dev Right padded FF + UniswapV3Factory address
    uint256 public immutable UNISWAP_V3_FACTORY_AND_FF;

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    uint256 private constant UNISWAP_V3_MIN_SQRT = 4_295_128_740;
    uint256 private constant UNISWAP_V3_MAX_SQRT = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_341;

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

    constructor(uint256 _uniswapV3FactoryAndFF, uint256 _uniswapV3PoolInitCodeHash) {
        UNISWAP_V3_FACTORY_AND_FF = _uniswapV3FactoryAndFF;
        UNISWAP_V3_POOL_INIT_CODE_HASH = _uniswapV3PoolInitCodeHash;
    }

    /*//////////////////////////////////////////////////////////////
                                EXTERNAL
    //////////////////////////////////////////////////////////////*/

    // @inheritdoc IUniswapV3SwapCallback
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    )
        external
        whenNotPaused
    {
        // Initialize variables
        uint256 uniswapV3FactoryAndFF = UNISWAP_V3_FACTORY_AND_FF;
        uint256 uniswapV3PoolInitCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH;
        address permit2Address = PERMIT2;
        address poolAddress;

        // 160 (single pool data) + 352 (permit2 length)
        bool isPermit2 = data.length == 512;

        // Check if the caller is a UniswapV3Pool deployed by the canonical UniswapV3Factory
        //solhint-disable-next-line no-inline-assembly
        assembly {
            // Pool address
            poolAddress := caller()

            // Get free memory pointer
            let ptr := mload(64)

            // We need make sure the caller is a UniswapV3Pool deployed by the canonical UniswapV3Factory
            // 1. Prepare data for calculating the pool address
            // Store ff+factory address, Load token0, token1, fee from bytes calldata and store pool init code hash

            // Store 0xff + factory address (right padded)
            mstore(ptr, uniswapV3FactoryAndFF)

            // Store data offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE)
            let token0Offset := add(ptr, 21)

            // Copy token0, token1, fee to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) + 1 byte
            // (direction)
            calldatacopy(add(token0Offset, 1), add(data.offset, 65), 95)

            // 2. Calculate the pool address
            // We can do this by first calling the keccak256 function on the fetched values and then
            // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address),
            // keccak256(abi.encode(token0,
            // token1, fee)), POOL_INIT_CODE_HASH));
            // The first 20 bytes of the computed address are the pool address

            // Calculate keccak256(abi.encode(address(token0), address(token1), fee))
            mstore(token0Offset, keccak256(token0Offset, 96))
            // Store POOL_INIT_CODE_HASH
            mstore(add(token0Offset, 32), uniswapV3PoolInitCodeHash)
            // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), keccak256(abi.encode(token0,
            // token1, fee)), POOL_INIT_CODE_HASH));
            mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32

            // Get the first 20 bytes of the computed address
            let computedAddress := and(mload(ptr), 0xffffffffffffffffffffffffffffffffffffffff)

            // Check if the caller matches the computed address (and revert if not)
            if xor(poolAddress, computedAddress) {
                mstore(0, 0x48f5c3ed00000000000000000000000000000000000000000000000000000000) // store the selector
                    // (error InvalidCaller())
                revert(0, 4) // revert with error selector
            }
        }

        // Check if data length is greater than 160 bytes (1 pool)
        // If the data length is greater than 160 bytes, we know that we are executing a multi-hop swapExactAmountOut
        // by recursively calling swapExactAmountOut on the next pool, until we reach the last pool in the data and
        // then we will transfer the tokens to the pool
        if (data.length > 160 && !isPermit2) {
            // Initialize recursive variables
            address payer;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                // Copy payer address from calldata
                payer := calldataload(164)
            }
            // Recursive call swapExactAmountOut
            _callUniswapV3PoolsSwapExactAmountOut(amount0Delta > 0 ? -amount0Delta : -amount1Delta, data, payer);
        } else {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                // Token to send to the pool
                let token
                // Amount to send to the pool
                let amount

                // Get free memory pointer
                let ptr := mload(64)

                // If the caller is the computed address, then we can safely assume that the caller is a UniswapV3Pool
                // deployed by the canonical UniswapV3Factory

                // 3. Transfer amount to the pool

                // Check if amount0Delta or amount1Delta is positive and which token we need to send to the pool
                if sgt(amount0Delta, 0) {
                    // If amount0Delta is positive, we need to send amount0Delta token0 to the pool
                    token := and(calldataload(add(data.offset, 64)), 0xffffffffffffffffffffffffffffffffffffffff)
                    amount := amount0Delta
                }
                if sgt(amount1Delta, 0) {
                    // If amount1Delta is positive, we need to send amount1Delta token1 to the pool
                    token := calldataload(add(data.offset, 96))
                    amount := amount1Delta
                }

                // Based on the data passed to the callback, we know the fromAddress that will pay for the
                // swap, if it is this contract, we will execute the transfer() function,
                // otherwise, we will execute transferFrom()

                // Check if fromAddress is this contract
                let fromAddress := calldataload(164)

                switch eq(fromAddress, address())
                // If fromAddress is this contract, execute transfer()
                case 1 {
                    // Prepare external call data
                    mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the
                        // selector
                        // (function transfer(address recipient, uint256 amount))
                    mstore(add(ptr, 4), poolAddress) // store the recipient
                    mstore(add(ptr, 36), amount) // store the amount
                    let success := call(gas(), token, 0, ptr, 68, 0, 32) // call transfer
                    if success {
                        switch returndatasize()
                        // check the return data size
                        case 0 { success := gt(extcodesize(token), 0) }
                        default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
                    }

                    if iszero(success) {
                        mstore(0, 0x1bbb4abe00000000000000000000000000000000000000000000000000000000) // store the
                            // selector
                            // (error CallbackTransferFailed())
                        revert(0, 4) // revert with error selector
                    }
                }
                // If fromAddress is not this contract, execute transferFrom() or permitTransferFrom()
                default {
                    switch isPermit2
                    // If permit2 is not present, execute transferFrom()
                    case 0 {
                        mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // store the
                            // selector
                            // (function transferFrom(address sender, address recipient,
                            // uint256 amount))
                        mstore(add(ptr, 4), fromAddress) // store the sender
                        mstore(add(ptr, 36), poolAddress) // store the recipient
                        mstore(add(ptr, 68), amount) // store the amount
                        let success := call(gas(), token, 0, ptr, 100, 0, 32) // call transferFrom
                        if success {
                            switch returndatasize()
                            // check the return data size
                            case 0 { success := gt(extcodesize(token), 0) }
                            default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
                        }
                        if iszero(success) {
                            mstore(0, 0x1bbb4abe00000000000000000000000000000000000000000000000000000000) // store the
                                // selector
                                // (error CallbackTransferFailed())
                            revert(0, 4) // revert with error selector
                        }
                    }
                    // If permit2 is present, execute permitTransferFrom()
                    default {
                        // Otherwise Permit2.permitTransferFrom
                        // Store function selector
                        mstore(ptr, 0x30f28b7a00000000000000000000000000000000000000000000000000000000)
                        // permitTransferFrom()
                        calldatacopy(add(ptr, 4), 292, 352) // Copy data to memory
                        mstore(add(ptr, 132), poolAddress) // Store pool address as recipient
                        mstore(add(ptr, 164), amount) // Store amount as amount
                        mstore(add(ptr, 196), fromAddress) // Store payer
                        // Call permit2.permitTransferFrom and revert if call failed
                        if iszero(call(gas(), permit2Address, 0, ptr, 356, 0, 0)) {
                            mstore(0, 0x6b836e6b00000000000000000000000000000000000000000000000000000000) // Store
                                // error selector
                                // error Permit2Failed()
                            revert(0, 4)
                        }
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                               INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev Loops through pools and performs swaps
    function _callUniswapV3PoolsSwapExactAmountIn(
        int256 fromAmount,
        bytes calldata pools,
        address fromAddress,
        bytes calldata permit2
    )
        internal
        returns (uint256 receivedAmount)
    {
        uint256 uniswapV3FactoryAndFF = UNISWAP_V3_FACTORY_AND_FF;
        uint256 uniswapV3PoolInitCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            //---------------------------------//
            // Loop Swap Through Pools
            //---------------------------------//

            // Calculate pool count
            let poolCount := div(pools.length, 96)

            // Initialize variables
            let p := 0
            let poolAddress := 0
            let nextPoolAddress := 0
            let direction := 0
            let isPermit2 := gt(permit2.length, 256)

            // Get free memory pointer
            let ptr := mload(64)

            // Loop through pools
            for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } {
                // Check if it is the first pool
                if iszero(p) {
                    //---------------------------------//
                    // Calculate Pool Address
                    //---------------------------------//

                    // Calculate the pool address
                    // We can do this by first calling the keccak256 function on the passed pool values and then
                    // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address),
                    // keccak256(abi.encode(token0,
                    // token1, fee)), POOL_INIT_CODE_HASH));
                    // The first 20 bytes of the computed address are the pool address

                    // Store 0xff + factory address (right padded)
                    mstore(ptr, uniswapV3FactoryAndFF)

                    // Store pools offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE)
                    let token0ptr := add(ptr, 21)

                    // Copy pool data (skip first byte) to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF
                    // SIZE)
                    calldatacopy(add(token0ptr, 1), add(pools.offset, 1), 95)

                    // Calculate keccak256(abi.encode(address(token0), address(token1), fee))
                    mstore(token0ptr, keccak256(token0ptr, 96))

                    // Store POOL_INIT_CODE_HASH
                    mstore(add(token0ptr, 32), uniswapV3PoolInitCodeHash)

                    // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address),
                    // keccak256(abi.encode(token0,
                    // token1, fee)), POOL_INIT_CODE_HASH));
                    mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32

                    // Load pool
                    p := mload(ptr)

                    // Get the first 20 bytes of the computed address
                    poolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff)

                    //---------------------------------//
                }

                // Direction is the first bit of the pool data
                direction := shr(255, calldataload(add(pools.offset, mul(i, 96))))

                // Check if it is not the last pool
                if lt(add(i, 1), poolCount) {
                    //---------------------------------//
                    // Calculate Next Pool Address
                    //---------------------------------//

                    // Store 0xff + factory address (right padded)
                    mstore(ptr, uniswapV3FactoryAndFF)

                    // Store pools offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE)
                    let token0ptr := add(ptr, 21)

                    // Copy next pool data to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE)
                    calldatacopy(add(token0ptr, 1), add(add(pools.offset, 1), mul(add(i, 1), 96)), 95)

                    // Calculate keccak256(abi.encode(address(token0), address(token1), fee))
                    mstore(token0ptr, keccak256(token0ptr, 96))

                    // Store POOL_INIT_CODE_HASH
                    mstore(add(token0ptr, 32), uniswapV3PoolInitCodeHash)

                    // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address),
                    // keccak256(abi.encode(token0,
                    // token1, fee)), POOL_INIT_CODE_HASH));
                    mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32

                    // Load pool
                    p := mload(ptr)

                    // Get the first 20 bytes of the computed address
                    nextPoolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff)

                    //---------------------------------//
                }

                // Adjust fromAddress and fromAmount if it's not the first pool
                if gt(i, 0) { fromAddress := address() }

                //---------------------------------//
                // Perform Swap
                //---------------------------------//

                //---------------------------------//
                // Return based on direction
                //---------------------------------//

                // Initialize data length
                let dataLength := 0xa0

                // Initialize total data length
                let totalDataLength := 356

                // If permit2 is present include permit2 data length in total data length
                if eq(isPermit2, 1) {
                    totalDataLength := add(totalDataLength, permit2.length)
                    dataLength := add(dataLength, permit2.length)
                }

                // Return amount0 or amount1 depending on direction
                switch direction
                case 0 {
                    // Prepare external call data
                    // Store swap selector (0x128acb08)
                    mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000)
                    // Store toAddress
                    mstore(add(ptr, 4), address())
                    // Store direction
                    mstore(add(ptr, 36), 0)
                    // Store fromAmount
                    mstore(add(ptr, 68), fromAmount)
                    // Store sqrtPriceLimitX96
                    mstore(add(ptr, 100), UNISWAP_V3_MAX_SQRT)
                    // Store data offset
                    mstore(add(ptr, 132), 0xa0)
                    /// Store data length
                    mstore(add(ptr, 164), dataLength)
                    // Store fromAddress
                    mstore(add(ptr, 228), fromAddress)
                    // Store token0, token1, fee
                    calldatacopy(add(ptr, 260), add(pools.offset, mul(i, 96)), 96)
                    // If permit2 is present, store permit2 data
                    if eq(isPermit2, 1) {
                        // Store permit2 data
                        calldatacopy(add(ptr, 356), permit2.offset, permit2.length)
                    }
                    // Perform the external 'swap' call
                    if iszero(call(gas(), poolAddress, 0, ptr, totalDataLength, 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
                    fromAmount := mload(ptr)
                }
                default {
                    // Prepare external call data
                    // Store swap selector (0x128acb08)
                    mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000)
                    // Store toAddress
                    mstore(add(ptr, 4), address())
                    // Store direction
                    mstore(add(ptr, 36), 1)
                    // Store fromAmount
                    mstore(add(ptr, 68), fromAmount)
                    // Store sqrtPriceLimitX96
                    mstore(add(ptr, 100), UNISWAP_V3_MIN_SQRT)
                    // Store data offset
                    mstore(add(ptr, 132), 0xa0)
                    /// Store data length
                    mstore(add(ptr, 164), dataLength)
                    // Store fromAddress
                    mstore(add(ptr, 228), fromAddress)
                    // Store token0, token1, fee
                    calldatacopy(add(ptr, 260), add(pools.offset, mul(i, 96)), 96)
                    // If permit2 is present, store permit2 data
                    if eq(isPermit2, 1) {
                        // Store permit2 data
                        calldatacopy(add(ptr, 356), permit2.offset, permit2.length)
                    }
                    // Perform the external 'swap' call
                    if iszero(call(gas(), poolAddress, 0, ptr, totalDataLength, 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
                    fromAmount := mload(add(ptr, 32))
                }
                //---------------------------------//

                //---------------------------------//

                // The next pool address was already calculated so we can set it as the current pool address for the
                // next iteration of the loop
                poolAddress := nextPoolAddress

                // fromAmount = -fromAmount
                fromAmount := sub(0, fromAmount)
            }

            //---------------------------------//
        }
        return fromAmount.toUint256();
    }

    /// @dev Recursively loops through pools and performs swaps
    function _callUniswapV3PoolsSwapExactAmountOut(
        int256 fromAmount,
        bytes calldata pools,
        address fromAddress
    )
        internal
        returns (uint256 spentAmount, uint256 receivedAmount)
    {
        uint256 uniswapV3FactoryAndFF = UNISWAP_V3_FACTORY_AND_FF;
        uint256 uniswapV3PoolInitCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            //---------------------------------//
            // Adjust data received from recursive call
            //---------------------------------//

            // Initialize variables
            let poolsStartOffset := pools.offset
            let poolsLength := pools.length
            let previousPoolAddress := 0

            // Check if pools length is not divisible by 96
            if gt(mod(pools.length, 96), 0) {
                // Check if pools length is greater than 128 bytes (1 pool)
                if gt(pools.length, 160) {
                    // Get the previous pool address from the first 20 bytes of pool data
                    previousPoolAddress := and(calldataload(pools.offset), 0xffffffffffffffffffffffffffffffffffffffff)
                    // Relculate the offset to skip data
                    poolsStartOffset := add(pools.offset, 160)
                    // Recalculate the length to skip data
                    poolsLength := sub(pools.length, 160)
                }
            }

            // Get free memory pointer
            let ptr := mload(64)

            //---------------------------------//
            // Calculate Pool Address
            //---------------------------------//

            // Calculate the pool address
            // We can do this by first calling the keccak256 function on the passed pool values and then
            // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address),
            // keccak256(abi.encode(token0,
            // token1, fee)), POOL_INIT_CODE_HASH));
            // The first 20 bytes of the computed address are the pool address

            // Store 0xff + factory address (right padded)
            mstore(ptr, uniswapV3FactoryAndFF)

            // Store pools offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE)
            let token0ptr := add(ptr, 21)

            // Copy pool data (skip first byte) to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF
            // SIZE)
            calldatacopy(add(token0ptr, 1), add(poolsStartOffset, 1), 95)

            // Calculate keccak256(abi.encode(address(token0), address(token1), fee))
            mstore(token0ptr, keccak256(token0ptr, 96))

            // Store POOL_INIT_CODE_HASH
            mstore(add(token0ptr, 32), uniswapV3PoolInitCodeHash)

            // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address),
            // keccak256(abi.encode(token0,
            // token1, fee)), POOL_INIT_CODE_HASH));
            mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32

            // Load pool
            let p := mload(ptr)

            // Get the first 20 bytes of the computed address
            let poolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff)

            //---------------------------------//

            //---------------------------------//
            // Adjust toAddress
            //---------------------------------//

            let toAddress := address()

            // If it's not the first entry to recursion, we use the pool address from the previous pool as
            // the toAddress
            if xor(previousPoolAddress, 0) { toAddress := previousPoolAddress }

            //---------------------------------//

            // Direction is the first bit of the pool data
            let direction := shr(255, calldataload(poolsStartOffset))

            //---------------------------------//
            // Perform Swap
            //---------------------------------//

            //---------------------------------//
            // Return based on direction
            //---------------------------------//

            // Return amount0 or amount1 depending on direction
            switch direction
            case 0 {
                // Prepare external call data
                // Store swap selector (0x128acb08)
                mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000)
                // Store toAddress
                mstore(add(ptr, 4), toAddress)
                // Store direction
                mstore(add(ptr, 36), 0)
                // Store fromAmount
                mstore(add(ptr, 68), fromAmount)
                // Store sqrtPriceLimitX96
                mstore(add(ptr, 100), UNISWAP_V3_MAX_SQRT)
                // Store data offset
                mstore(add(ptr, 132), 0xa0)
                /// Store data length
                mstore(add(ptr, 164), add(64, poolsLength))
                // Store poolAddress
                mstore(add(ptr, 196), poolAddress)
                // Store fromAddress
                mstore(add(ptr, 228), fromAddress)
                // Store token0, token1, fee
                calldatacopy(add(ptr, 260), poolsStartOffset, poolsLength)

                // Perform the external 'swap' call
                if iszero(call(gas(), poolAddress, 0, ptr, add(poolsLength, 260), 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 0, return amount0 as fromAmount
                fromAmount := mload(ptr)
                // return amount1 as spentAmount
                spentAmount := mload(add(ptr, 32))
            }
            default {
                // Prepare external call data
                // Store swap selector (0x128acb08)
                mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000)
                // Store toAddress
                mstore(add(ptr, 4), toAddress)
                // Store direction
                mstore(add(ptr, 36), 1)
                // Store fromAmount
                mstore(add(ptr, 68), fromAmount)
                // Store sqrtPriceLimitX96
                mstore(add(ptr, 100), UNISWAP_V3_MIN_SQRT)
                // Store data offset
                mstore(add(ptr, 132), 0xa0)
                /// Store data length
                mstore(add(ptr, 164), add(64, poolsLength))
                // Store poolAddress
                mstore(add(ptr, 196), poolAddress)
                // Store fromAddress
                mstore(add(ptr, 228), fromAddress)
                // Store token0, token1, fee
                calldatacopy(add(ptr, 260), poolsStartOffset, poolsLength)

                // Perform the external 'swap' call
                if iszero(call(gas(), poolAddress, 0, ptr, add(poolsLength, 260), 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 as fromAmount
                fromAmount := mload(add(ptr, 32))
                // return amount0 as spentAmount
                spentAmount := mload(ptr)
            }
            //---------------------------------//

            //---------------------------------//

            // fromAmount = -fromAmount
            fromAmount := sub(0, fromAmount)
        }
        return (spentAmount, fromAmount.toUint256());
    }
}

File 22 of 47 : WETHUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IWETH } from "../interfaces/IWETH.sol";

/// @title WETHUtils
/// @notice A contract containing common utilities for WETH
abstract contract WETHUtils {
    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev WETH address
    IWETH public immutable WETH;

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

    constructor(address _weth) {
        WETH = IWETH(_weth);
    }
}

File 23 of 47 : Permit2Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

/// @title Permit2Utils
/// @notice A contract containing common utilities for Permit2
abstract contract Permit2Utils {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error Permit2Failed();

    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev Permit2 address
    address public immutable PERMIT2; // solhint-disable-line var-name-mixedcase

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

    constructor(address _permit2) {
        PERMIT2 = _permit2;
    }

    /*//////////////////////////////////////////////////////////////
                                INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev Parses data and executes permit2.permitTransferFrom, reverts if it fails
    function permit2TransferFrom(bytes calldata data, address recipient, uint256 amount) internal {
        address targetAddress = PERMIT2;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Get free memory pointer
            let ptr := mload(64)
            // Store function selector
            mstore(ptr, 0x30f28b7a00000000000000000000000000000000000000000000000000000000) // permitTransferFrom()
            // Copy data to memory
            calldatacopy(add(ptr, 4), data.offset, data.length)
            // Store recipient
            mstore(add(ptr, 132), recipient)
            // Store amount
            mstore(add(ptr, 164), amount)
            // Store owner
            mstore(add(ptr, 196), caller())
            // Call permit2.permitTransferFrom and revert if call failed
            if iszero(call(gas(), targetAddress, 0, ptr, add(data.length, 4), 0, 0)) {
                mstore(0, 0x6b836e6b00000000000000000000000000000000000000000000000000000000) // Store error selector
                    // error Permit2Failed()
                revert(0, 4)
            }
        }
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 25 of 47 : IBalancerV2SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { BalancerV2Data } from "../AugustusV6Types.sol";

/// @title IBalancerV2SwapExactAmountIn
/// @notice Interface for executing swapExactAmountIn directly on Balancer V2 pools
interface IBalancerV2SwapExactAmountIn is IErrors {
    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountIn on Balancer V2 pools
    /// @param balancerData Struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit Permit data for the swap
    /// @param data The calldata to execute
    /// the first 20 bytes are the beneficiary address and the left most bit is the approve flag
    /// @return receivedAmount The amount of destToken received after fees
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountInOnBalancerV2(
        BalancerV2Data calldata balancerData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata data
    )
        external
        payable
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 26 of 47 : ERC20Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

/// @title ERC20Utils
/// @notice Optimized functions for ERC20 tokens
library ERC20Utils {
    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    error IncorrectEthAmount();
    error PermitFailed();
    error TransferFromFailed();
    error TransferFailed();
    error ApprovalFailed();

    /*//////////////////////////////////////////////////////////////
                               CONSTANTS
    //////////////////////////////////////////////////////////////*/

    IERC20 internal constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);

    /*//////////////////////////////////////////////////////////////
                                APPROVE
    //////////////////////////////////////////////////////////////*/

    /// @dev Vendored from Solady by @vectorized - SafeTransferLib.approveWithRetry
    /// https://github.com/Vectorized/solady/src/utils/SafeTransferLib.sol#L325
    /// Instead of approving a specific amount, this function approves for uint256(-1) (type(uint256).max).
    function approve(IERC20 token, address to) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // Store the `amount`
                // argument (type(uint256).max).
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // Store
                    // type(uint256).max for the `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0, 0x8164f84200000000000000000000000000000000000000000000000000000000)
                    // store the selector (error ApprovalFailed())
                    revert(0, 4) // revert with error selector
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /*//////////////////////////////////////////////////////////////
                                PERMIT
    //////////////////////////////////////////////////////////////*/

    /// @dev Executes an ERC20 permit and reverts if invalid length is provided
    function permit(IERC20 token, bytes calldata data) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            // check the permit length
            switch data.length
            // 32 * 7 = 224 EIP2612 Permit
            case 224 {
                let x := mload(64) // get the free memory pointer
                mstore(x, 0xd505accf00000000000000000000000000000000000000000000000000000000) // store the selector
                    // function permit(address owner, address spender, uint256
                    // amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                calldatacopy(add(x, 4), data.offset, 224) // store the args
                pop(call(gas(), token, 0, x, 228, 0, 32)) // call ERC20 permit, skip checking return data
            }
            // 32 * 8 = 256 DAI-Style Permit
            case 256 {
                let x := mload(64) // get the free memory pointer
                mstore(x, 0x8fcbaf0c00000000000000000000000000000000000000000000000000000000) // store the selector
                    // function permit(address holder, address spender, uint256
                    // nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                calldatacopy(add(x, 4), data.offset, 256) // store the args
                pop(call(gas(), token, 0, x, 260, 0, 32)) // call ERC20 permit, skip checking return data
            }
            default {
                mstore(0, 0xb78cb0dd00000000000000000000000000000000000000000000000000000000) // store the selector
                    // (error PermitFailed())
                revert(0, 4)
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                                 ETH
    //////////////////////////////////////////////////////////////*/

    /// @dev Returns 1 if the token is ETH, 0 if not ETH
    function isETH(IERC20 token, uint256 amount) internal view returns (uint256 fromETH) {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            // If token is ETH
            if eq(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
                // if msg.value is not equal to fromAmount, then revert
                if xor(amount, callvalue()) {
                    mstore(0, 0x8b6ebb4d00000000000000000000000000000000000000000000000000000000) // store the selector
                        // (error IncorrectEthAmount())
                    revert(0, 4) // revert with error selector
                }
                // return 1 if ETH
                fromETH := 1
            }
            // If token is not ETH
            if xor(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
                // if msg.value is not equal to 0, then revert
                if gt(callvalue(), 0) {
                    mstore(0, 0x8b6ebb4d00000000000000000000000000000000000000000000000000000000) // store the selector
                    // (error IncorrectEthAmount())
                    revert(0, 4) // revert with error selector
                }
            }
        }
        // return 0 if not ETH
    }

    /*//////////////////////////////////////////////////////////////
                                TRANSFER
    //////////////////////////////////////////////////////////////*/

    /// @dev Executes transfer and reverts if it fails, works for both ETH and ERC20 transfers
    function safeTransfer(IERC20 token, address recipient, uint256 amount) internal returns (bool success) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            switch eq(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
            // ETH
            case 1 {
                // transfer ETH
                // Cap gas at 10000 to avoid reentrancy
                success := call(10000, recipient, amount, 0, 0, 0, 0)
            }
            // ERC20
            default {
                let x := mload(64) // get the free memory pointer
                mstore(x, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the selector
                    // (function transfer(address recipient, uint256 amount))
                mstore(add(x, 4), recipient) // store the recipient
                mstore(add(x, 36), amount) // store the amount
                success := call(gas(), token, 0, x, 68, 0, 32) // call transfer
                if success {
                    switch returndatasize()
                    // check the return data size
                    case 0 { success := gt(extcodesize(token), 0) }
                    default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
                }
            }
            if iszero(success) {
                mstore(0, 0x90b8ec1800000000000000000000000000000000000000000000000000000000) // store the selector
                    // (error TransferFailed())
                revert(0, 4) // revert with error selector
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                             TRANSFER FROM
    //////////////////////////////////////////////////////////////*/

    /// @dev Executes transferFrom and reverts if it fails
    function safeTransferFrom(
        IERC20 srcToken,
        address sender,
        address recipient,
        uint256 amount
    )
        internal
        returns (bool success)
    {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let x := mload(64) // get the free memory pointer
            mstore(x, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // store the selector
                // (function transferFrom(address sender, address recipient,
                // uint256 amount))
            mstore(add(x, 4), sender) // store the sender
            mstore(add(x, 36), recipient) // store the recipient
            mstore(add(x, 68), amount) // store the amount
            success := call(gas(), srcToken, 0, x, 100, 0, 32) // call transferFrom
            if success {
                switch returndatasize()
                // check the return data size
                case 0 { success := gt(extcodesize(srcToken), 0) }
                default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
            }
            if iszero(success) {
                mstore(x, 0x7939f42400000000000000000000000000000000000000000000000000000000) // store the selector
                    // (error TransferFromFailed())
                revert(x, 4) // revert with error selector
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                                BALANCE
    //////////////////////////////////////////////////////////////*/

    /// @dev Returns the balance of an account, works for both ETH and ERC20 tokens
    function getBalance(IERC20 token, address account) internal view returns (uint256 balanceOf) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            switch eq(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
            // ETH
            case 1 { balanceOf := balance(account) }
            // ERC20
            default {
                let x := mload(64) // get the free memory pointer
                mstore(x, 0x70a0823100000000000000000000000000000000000000000000000000000000) // store the selector
                    // (function balanceOf(address account))
                mstore(add(x, 4), account) // store the account
                let success := staticcall(gas(), token, x, 36, x, 32) // call balanceOf
                if success { balanceOf := mload(x) } // load the balance
            }
        }
    }
}

File 27 of 47 : AugustusV6Types.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

/*//////////////////////////////////////////////////////////////
                        GENERIC SWAP DATA
//////////////////////////////////////////////////////////////*/

/// @notice Struct containg data for generic swapExactAmountIn/swapExactAmountOut
/// @param srcToken The token to swap from
/// @param destToken The token to swap to
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount of destToken to receive
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param quotedAmount The quoted expected amount of destToken/srcToken
/// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut
/// @param metadata Packed uuid and additional metadata
/// @param beneficiary The address to send the swapped tokens to
struct GenericData {
    IERC20 srcToken;
    IERC20 destToken;
    uint256 fromAmount;
    uint256 toAmount;
    uint256 quotedAmount;
    bytes32 metadata;
    address payable beneficiary;
}

/*//////////////////////////////////////////////////////////////
                            UNISWAPV2
//////////////////////////////////////////////////////////////*/

/// @notice Struct for UniswapV2 swapExactAmountIn/swapExactAmountOut data
/// @param srcToken The token to swap from
/// @param destToken The token to swap to
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param quotedAmount The quoted expected amount of destToken/srcToken
/// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount of destToken to receive
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param metadata Packed uuid and additional metadata
/// @param beneficiary The address to send the swapped tokens to
/// @param pools data consisting of concatenated token0 and token1 address for each pool with the direction flag being
/// the right most bit of the packed token0-token1 pair bytes used in the path
struct UniswapV2Data {
    IERC20 srcToken;
    IERC20 destToken;
    uint256 fromAmount;
    uint256 toAmount;
    uint256 quotedAmount;
    bytes32 metadata;
    address payable beneficiary;
    bytes pools;
}

/*//////////////////////////////////////////////////////////////
                            UNISWAPV3
//////////////////////////////////////////////////////////////*/

/// @notice Struct for UniswapV3 swapExactAmountIn/swapExactAmountOut data
/// @param srcToken The token to swap from
/// @param destToken The token to swap to
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param quotedAmount The quoted expected amount of destToken/srcToken
/// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount of destToken to receive
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param metadata Packed uuid and additional metadata
/// @param beneficiary The address to send the swapped tokens to
/// @param pools data consisting of concatenated token0-
/// token1-fee bytes for each pool used in the path, with the direction flag being the left most bit of token0 in the
/// concatenated bytes
struct UniswapV3Data {
    IERC20 srcToken;
    IERC20 destToken;
    uint256 fromAmount;
    uint256 toAmount;
    uint256 quotedAmount;
    bytes32 metadata;
    address payable beneficiary;
    bytes pools;
}

/*//////////////////////////////////////////////////////////////
                            CURVE V1
//////////////////////////////////////////////////////////////*/

/// @notice Struct for CurveV1 swapExactAmountIn data
/// @param curveData Packed data for the Curve pool, first 160 bits is the target exchange address,
/// the 161st bit is the approve flag, bits from (162 - 163) are used for the wrap flag,
//// bits from (164 - 165) are used for the swapType flag and the last 91 bits are unused:
/// Approve Flag - a) 0 -> do not approve b) 1 -> approve
/// Wrap Flag - a) 0 -> do not wrap b) 1 -> wrap native & srcToken == eth
/// c) 2 -> unwrap and destToken == eth d) 3 - >srcToken == eth && do not wrap
/// Swap Type Flag -  a) 0 -> EXCHANGE b) 1 -> EXCHANGE_UNDERLYING
/// @param curveAssets Packed uint128 index i and uint128 index j of the pool
/// The first 128 bits is the index i and the second 128 bits is the index j
/// @param srcToken The token to swap from
/// @param destToken The token to swap to
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount that must be recieved
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param quotedAmount The expected amount of destToken to be recieved
/// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut
/// @param metadata Packed uuid and additional metadata
/// @param beneficiary The address to send the swapped tokens to
struct CurveV1Data {
    uint256 curveData;
    uint256 curveAssets;
    IERC20 srcToken;
    IERC20 destToken;
    uint256 fromAmount;
    uint256 toAmount;
    uint256 quotedAmount;
    bytes32 metadata;
    address payable beneficiary;
}

/*//////////////////////////////////////////////////////////////
                            CURVE V2
//////////////////////////////////////////////////////////////*/

/// @notice Struct for CurveV2 swapExactAmountIn data
/// @param curveData Packed data for the Curve pool, first 160 bits is the target exchange address,
/// the 161st bit is the approve flag, bits from (162 - 163) are used for the wrap flag,
//// bits from (164 - 165) are used for the swapType flag and the last 91 bits are unused
/// Approve Flag - a) 0 -> do not approve b) 1 -> approve
/// Approve Flag - a) 0 -> do not approve b) 1 -> approve
/// Wrap Flag - a) 0 -> do not wrap b) 1 -> wrap native & srcToken == eth
/// c) 2 -> unwrap and destToken == eth d) 3 - >srcToken == eth && do not wrap
/// Swap Type Flag -  a) 0 -> EXCHANGE b) 1 -> EXCHANGE_UNDERLYING c) 2 -> EXCHANGE_UNDERLYING_FACTORY_ZAP
/// @param i The index of the srcToken
/// @param j The index of the destToken
/// The first 128 bits is the index i and the second 128 bits is the index j
/// @param poolAddress The address of the CurveV2 pool (only used for EXCHANGE_UNDERLYING_FACTORY_ZAP)
/// @param srcToken The token to swap from
/// @param destToken The token to swap to
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount that must be recieved
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param quotedAmount The expected amount of destToken to be recieved
/// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut
/// @param metadata Packed uuid and additional metadata
/// @param beneficiary The address to send the swapped tokens to
struct CurveV2Data {
    uint256 curveData;
    uint256 i;
    uint256 j;
    address poolAddress;
    IERC20 srcToken;
    IERC20 destToken;
    uint256 fromAmount;
    uint256 toAmount;
    uint256 quotedAmount;
    bytes32 metadata;
    address payable beneficiary;
}

/*//////////////////////////////////////////////////////////////
                            BALANCER V2
//////////////////////////////////////////////////////////////*/

/// @notice Struct for BalancerV2 swapExactAmountIn data
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount of destToken to receive
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param quotedAmount The quoted expected amount of destToken/srcToken
/// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut
/// @param metadata Packed uuid and additional metadata
/// @param beneficiaryAndApproveFlag The beneficiary address and approve flag packed into one uint256,
/// the first 20 bytes are the beneficiary address and the left most bit is the approve flag
struct BalancerV2Data {
    uint256 fromAmount;
    uint256 toAmount;
    uint256 quotedAmount;
    bytes32 metadata;
    uint256 beneficiaryAndApproveFlag;
}

/*//////////////////////////////////////////////////////////////
                            MAKERPSM
//////////////////////////////////////////////////////////////*/

/// @notice Struct for Maker PSM swapExactAmountIn data
/// @param srcToken The token to swap from
/// @param destToken The token to swap to
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount of destToken to receive
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param toll Used to calculate gem amount for the swapExactAmountIn
/// @param to18ConversionFactor Used to calculate gem amount for the swapExactAmountIn
/// @param gemJoinAddress The address of the gemJoin contract
/// @param exchange The address of the exchange contract
/// @param metadata Packed uuid and additional metadata
/// @param beneficiaryDirectionApproveFlag The beneficiary address, swap direction and approve flag packed
/// into one uint256, the first 20 bytes are the beneficiary address, the left most bit is the approve flag and the
/// second left most bit is the swap direction flag, 0 for swapExactAmountIn and 1 for swapExactAmountOut
struct MakerPSMData {
    IERC20 srcToken;
    IERC20 destToken;
    uint256 fromAmount;
    uint256 toAmount;
    uint256 toll;
    uint256 to18ConversionFactor;
    address exchange;
    address gemJoinAddress;
    bytes32 metadata;
    uint256 beneficiaryDirectionApproveFlag;
}

/*//////////////////////////////////////////////////////////////
                            AUGUSTUS RFQ
//////////////////////////////////////////////////////////////*/

/// @notice Order struct for Augustus RFQ
/// @param nonceAndMeta The nonce and meta data packed into one uint256,
/// the first 160 bits is the user address and the last 96 bits is the nonce
/// @param expiry The expiry of the order
/// @param makerAsset The address of the maker asset
/// @param takerAsset The address of the taker asset
/// @param maker The address of the maker
/// @param taker The address of the taker, if the taker is address(0) anyone can take the order
/// @param makerAmount The amount of makerAsset
/// @param takerAmount The amount of takerAsset
struct Order {
    uint256 nonceAndMeta;
    uint128 expiry;
    address makerAsset;
    address takerAsset;
    address maker;
    address taker;
    uint256 makerAmount;
    uint256 takerAmount;
}

/// @notice Struct containing order info for Augustus RFQ
/// @param order The order struct
/// @param signature The signature for the order
/// @param takerTokenFillAmount The amount of takerToken to fill
/// @param permitTakerAsset The permit data for the taker asset
/// @param permitMakerAsset The permit data for the maker asset
struct OrderInfo {
    Order order;
    bytes signature;
    uint256 takerTokenFillAmount;
    bytes permitTakerAsset;
    bytes permitMakerAsset;
}

/// @notice Struct containing common data for executing swaps on Augustus RFQ
/// @param fromAmount The amount of srcToken to swap
/// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut
/// @param toAmount The minimum amount of destToken to receive
/// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut
/// @param wrapApproveDirection The wrap, approve and direction flag packed into one uint8,
/// the first 2 bits is wrap flag (10 for wrap dest, 01 for wrap src, 00 for no wrap), the next bit is the approve flag
/// (1 for approve, 0 for no approve) and the last bit is the direction flag (0 for swapExactAmountIn and 1 for
/// swapExactAmountOut)
/// @param metadata Packed uuid and additional metadata
struct AugustusRFQData {
    uint256 fromAmount;
    uint256 toAmount;
    uint8 wrapApproveDirection;
    bytes32 metadata;
    address payable beneficiary;
}

File 28 of 47 : ICurveV1SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { CurveV1Data } from "../AugustusV6Types.sol";

/// @title ICurveV1SwapExactAmountIn
/// @notice Interface for direct swaps on Curve V1
interface ICurveV1SwapExactAmountIn is IErrors {
    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountIn on Curve V1 pools
    /// @param curveV1Data Struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit Permit data for the swap
    /// @return receivedAmount The amount of destToken received after fees
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountInOnCurveV1(
        CurveV1Data calldata curveV1Data,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 29 of 47 : PauseUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Storage
import { AugustusStorage } from "../storage/AugustusStorage.sol";

/// @title PauseUtils
/// @notice Provides a modifier to check if the contract is paused
abstract contract PauseUtils is AugustusStorage {
    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Error emitted when the contract is paused
    error ContractPaused();

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    // Check if the contract is paused, if it is, revert
    modifier whenNotPaused() {
        if (paused) {
            revert ContractPaused();
        }
        _;
    }
}

File 30 of 47 : ICurveV2SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { CurveV2Data } from "../AugustusV6Types.sol";

/// @title ICurveV2SwapExactAmountIn
/// @notice Interface for direct swaps on Curve V2
interface ICurveV2SwapExactAmountIn is IErrors {
    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountIn on Curve V2 pools
    /// @param curveV2Data Struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit Permit data for the swap
    /// @return receivedAmount The amount of destToken received after fees
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountInOnCurveV2(
        CurveV2Data calldata curveV2Data,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 31 of 47 : IUniswapV2SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { UniswapV2Data } from "../AugustusV6Types.sol";

/// @title IUniswapV2SwapExactAmountIn
/// @notice Interface for direct swaps on Uniswap V2
interface IUniswapV2SwapExactAmountIn is IErrors {
    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountIn on Uniswap V2 pools
    /// @param uniData struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit The permit data
    /// @return receivedAmount The amount of destToken received after fees
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountInOnUniswapV2(
        UniswapV2Data calldata uniData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 32 of 47 : IUniswapV3SwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { UniswapV3Data } from "../AugustusV6Types.sol";

/// @title IUniswapV3SwapExactAmountIn
/// @notice Interface for executing direct swapExactAmountIn on Uniswap V3
interface IUniswapV3SwapExactAmountIn is IErrors {
    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountIn on Uniswap V3 pools
    /// @param uniData struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit The permit data
    /// @return receivedAmount The amount of destToken received after fees
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountInOnUniswapV3(
        UniswapV3Data calldata uniData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 33 of 47 : SafeCastLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    error Overflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          UNSIGNED INTEGER SAFE CASTING OPERATIONS          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function toUint8(uint256 x) internal pure returns (uint8) {
        if (x >= 1 << 8) _revertOverflow();
        return uint8(x);
    }

    function toUint16(uint256 x) internal pure returns (uint16) {
        if (x >= 1 << 16) _revertOverflow();
        return uint16(x);
    }

    function toUint24(uint256 x) internal pure returns (uint24) {
        if (x >= 1 << 24) _revertOverflow();
        return uint24(x);
    }

    function toUint32(uint256 x) internal pure returns (uint32) {
        if (x >= 1 << 32) _revertOverflow();
        return uint32(x);
    }

    function toUint40(uint256 x) internal pure returns (uint40) {
        if (x >= 1 << 40) _revertOverflow();
        return uint40(x);
    }

    function toUint48(uint256 x) internal pure returns (uint48) {
        if (x >= 1 << 48) _revertOverflow();
        return uint48(x);
    }

    function toUint56(uint256 x) internal pure returns (uint56) {
        if (x >= 1 << 56) _revertOverflow();
        return uint56(x);
    }

    function toUint64(uint256 x) internal pure returns (uint64) {
        if (x >= 1 << 64) _revertOverflow();
        return uint64(x);
    }

    function toUint72(uint256 x) internal pure returns (uint72) {
        if (x >= 1 << 72) _revertOverflow();
        return uint72(x);
    }

    function toUint80(uint256 x) internal pure returns (uint80) {
        if (x >= 1 << 80) _revertOverflow();
        return uint80(x);
    }

    function toUint88(uint256 x) internal pure returns (uint88) {
        if (x >= 1 << 88) _revertOverflow();
        return uint88(x);
    }

    function toUint96(uint256 x) internal pure returns (uint96) {
        if (x >= 1 << 96) _revertOverflow();
        return uint96(x);
    }

    function toUint104(uint256 x) internal pure returns (uint104) {
        if (x >= 1 << 104) _revertOverflow();
        return uint104(x);
    }

    function toUint112(uint256 x) internal pure returns (uint112) {
        if (x >= 1 << 112) _revertOverflow();
        return uint112(x);
    }

    function toUint120(uint256 x) internal pure returns (uint120) {
        if (x >= 1 << 120) _revertOverflow();
        return uint120(x);
    }

    function toUint128(uint256 x) internal pure returns (uint128) {
        if (x >= 1 << 128) _revertOverflow();
        return uint128(x);
    }

    function toUint136(uint256 x) internal pure returns (uint136) {
        if (x >= 1 << 136) _revertOverflow();
        return uint136(x);
    }

    function toUint144(uint256 x) internal pure returns (uint144) {
        if (x >= 1 << 144) _revertOverflow();
        return uint144(x);
    }

    function toUint152(uint256 x) internal pure returns (uint152) {
        if (x >= 1 << 152) _revertOverflow();
        return uint152(x);
    }

    function toUint160(uint256 x) internal pure returns (uint160) {
        if (x >= 1 << 160) _revertOverflow();
        return uint160(x);
    }

    function toUint168(uint256 x) internal pure returns (uint168) {
        if (x >= 1 << 168) _revertOverflow();
        return uint168(x);
    }

    function toUint176(uint256 x) internal pure returns (uint176) {
        if (x >= 1 << 176) _revertOverflow();
        return uint176(x);
    }

    function toUint184(uint256 x) internal pure returns (uint184) {
        if (x >= 1 << 184) _revertOverflow();
        return uint184(x);
    }

    function toUint192(uint256 x) internal pure returns (uint192) {
        if (x >= 1 << 192) _revertOverflow();
        return uint192(x);
    }

    function toUint200(uint256 x) internal pure returns (uint200) {
        if (x >= 1 << 200) _revertOverflow();
        return uint200(x);
    }

    function toUint208(uint256 x) internal pure returns (uint208) {
        if (x >= 1 << 208) _revertOverflow();
        return uint208(x);
    }

    function toUint216(uint256 x) internal pure returns (uint216) {
        if (x >= 1 << 216) _revertOverflow();
        return uint216(x);
    }

    function toUint224(uint256 x) internal pure returns (uint224) {
        if (x >= 1 << 224) _revertOverflow();
        return uint224(x);
    }

    function toUint232(uint256 x) internal pure returns (uint232) {
        if (x >= 1 << 232) _revertOverflow();
        return uint232(x);
    }

    function toUint240(uint256 x) internal pure returns (uint240) {
        if (x >= 1 << 240) _revertOverflow();
        return uint240(x);
    }

    function toUint248(uint256 x) internal pure returns (uint248) {
        if (x >= 1 << 248) _revertOverflow();
        return uint248(x);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*           SIGNED INTEGER SAFE CASTING OPERATIONS           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function toInt8(int256 x) internal pure returns (int8) {
        int8 y = int8(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt16(int256 x) internal pure returns (int16) {
        int16 y = int16(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt24(int256 x) internal pure returns (int24) {
        int24 y = int24(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt32(int256 x) internal pure returns (int32) {
        int32 y = int32(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt40(int256 x) internal pure returns (int40) {
        int40 y = int40(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt48(int256 x) internal pure returns (int48) {
        int48 y = int48(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt56(int256 x) internal pure returns (int56) {
        int56 y = int56(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt64(int256 x) internal pure returns (int64) {
        int64 y = int64(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt72(int256 x) internal pure returns (int72) {
        int72 y = int72(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt80(int256 x) internal pure returns (int80) {
        int80 y = int80(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt88(int256 x) internal pure returns (int88) {
        int88 y = int88(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt96(int256 x) internal pure returns (int96) {
        int96 y = int96(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt104(int256 x) internal pure returns (int104) {
        int104 y = int104(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt112(int256 x) internal pure returns (int112) {
        int112 y = int112(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt120(int256 x) internal pure returns (int120) {
        int120 y = int120(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt128(int256 x) internal pure returns (int128) {
        int128 y = int128(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt136(int256 x) internal pure returns (int136) {
        int136 y = int136(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt144(int256 x) internal pure returns (int144) {
        int144 y = int144(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt152(int256 x) internal pure returns (int152) {
        int152 y = int152(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt160(int256 x) internal pure returns (int160) {
        int160 y = int160(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt168(int256 x) internal pure returns (int168) {
        int168 y = int168(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt176(int256 x) internal pure returns (int176) {
        int176 y = int176(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt184(int256 x) internal pure returns (int184) {
        int184 y = int184(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt192(int256 x) internal pure returns (int192) {
        int192 y = int192(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt200(int256 x) internal pure returns (int200) {
        int200 y = int200(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt208(int256 x) internal pure returns (int208) {
        int208 y = int208(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt216(int256 x) internal pure returns (int216) {
        int216 y = int216(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt224(int256 x) internal pure returns (int224) {
        int224 y = int224(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt232(int256 x) internal pure returns (int232) {
        int232 y = int232(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt240(int256 x) internal pure returns (int240) {
        int240 y = int240(x);
        if (x != y) _revertOverflow();
        return y;
    }

    function toInt248(int256 x) internal pure returns (int248) {
        int248 y = int248(x);
        if (x != y) _revertOverflow();
        return y;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               OTHER SAFE CASTING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function toInt256(uint256 x) internal pure returns (int256) {
        if (x >= 1 << 255) _revertOverflow();
        return int256(x);
    }

    function toUint256(int256 x) internal pure returns (uint256) {
        if (x < 0) _revertOverflow();
        return uint256(x);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function _revertOverflow() private pure {
        /// @solidity memory-safe-assembly
        assembly {
            // Store the function selector of `Overflow()`.
            mstore(0x00, 0x35278d12)
            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
    }
}

File 34 of 47 : IBalancerV2SwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { BalancerV2Data } from "../AugustusV6Types.sol";

/// @title IBalancerV2SwapExactAmountOut
/// @notice Interface for executing swapExactAmountOut directly on Balancer V2 pools
interface IBalancerV2SwapExactAmountOut is IErrors {
    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountOut on Balancer V2 pools
    /// @param balancerData Struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit Permit data for the swap
    /// @param data The calldata to execute
    /// @return spentAmount The actual amount of tokens used to swap
    /// @return receivedAmount The amount of tokens received
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountOutOnBalancerV2(
        BalancerV2Data calldata balancerData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata data
    )
        external
        payable
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 35 of 47 : IUniswapV2SwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { UniswapV2Data } from "../AugustusV6Types.sol";

/// @title IUniswapV2SwapExactAmountOut
/// @notice Interface for direct swapExactAmountOut on Uniswap V2
interface IUniswapV2SwapExactAmountOut is IErrors {
    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountOut on Uniswap V2 pools
    /// @param swapData struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit The permit data
    /// @return spentAmount The actual amount of tokens used to swap
    /// @return receivedAmount The amount of tokens received
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountOutOnUniswapV2(
        UniswapV2Data calldata swapData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 36 of 47 : IUniswapV3SwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { UniswapV3Data } from "../AugustusV6Types.sol";

/// @title IUniswapV3SwapExactAmountOut
/// @notice Interface for executing direct swapExactAmountOut on Uniswap V3
interface IUniswapV3SwapExactAmountOut is IErrors {
    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swapExactAmountOut on Uniswap V3 pools
    /// @param swapData struct containing data for the swap
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit The permit data
    /// @return spentAmount The actual amount of tokens used to swap
    /// @return receivedAmount The amount of tokens received
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountOutOnUniswapV3(
        UniswapV3Data calldata swapData,
        uint256 partnerAndFee,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 37 of 47 : IAugustusFeeVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

/// @title IAugustusFeeVault
/// @notice Interface for the AugustusFeeVault contract
interface IAugustusFeeVault {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Error emitted when withdraw amount is zero or exceeds the stored amount
    error InvalidWithdrawAmount();

    /// @notice Error emmitted when caller is not an approved augustus contract
    error UnauthorizedCaller();

    /// @notice Error emitted when an invalid parameter length is passed
    error InvalidParameterLength();

    /// @notice Error emitted when batch withdraw fails
    error BatchCollectFailed();

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when an augustus contract approval status is set
    /// @param augustus The augustus contract address
    /// @param approved The approval status
    event AugustusApprovalSet(address indexed augustus, bool approved);

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Struct to register fees
    /// @param addresses The addresses to register fees for
    /// @param token The token to register fees for
    /// @param fees The fees to register
    struct FeeRegistration {
        address[] addresses;
        IERC20 token;
        uint256[] fees;
    }

    /*//////////////////////////////////////////////////////////////
                                COLLECT
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows partners to withdraw fees allocated to them and stored in the vault
    /// @param token The token to withdraw fees in
    /// @param amount The amount of fees to withdraw
    /// @param recipient The address to send the fees to
    /// @return success Whether the transfer was successful or not
    function withdrawSomeERC20(IERC20 token, uint256 amount, address recipient) external returns (bool success);

    /// @notice Allows partners to withdraw all fees allocated to them and stored in the vault for a given token
    /// @param token The token to withdraw fees in
    /// @param recipient The address to send the fees to
    /// @return success Whether the transfer was successful or not
    function withdrawAllERC20(IERC20 token, address recipient) external returns (bool success);

    /// @notice Allows partners to withdraw all fees allocated to them and stored in the vault for multiple tokens
    /// @param tokens The tokens to withdraw fees i
    /// @param recipient The address to send the fees to
    /// @return success Whether the transfer was successful or not
    function batchWithdrawAllERC20(IERC20[] calldata tokens, address recipient) external returns (bool success);

    /// @notice Allows partners to withdraw fees allocated to them and stored in the vault
    /// @param tokens The tokens to withdraw fees in
    /// @param amounts The amounts of fees to withdraw
    /// @param recipient The address to send the fees to
    /// @return success Whether the transfer was successful or not
    function batchWithdrawSomeERC20(
        IERC20[] calldata tokens,
        uint256[] calldata amounts,
        address recipient
    )
        external
        returns (bool success);

    /*//////////////////////////////////////////////////////////////
                            BALANCE GETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the balance of a given token for a given partner
    /// @param token The token to get the balance of
    /// @param partner The partner to get the balance for
    /// @return feeBalance The balance of the given token for the given partner
    function getBalance(IERC20 token, address partner) external view returns (uint256 feeBalance);

    /// @notice Get the balances of a given partner for multiple tokens
    /// @param tokens The tokens to get the balances of
    /// @param partner The partner to get the balances for
    /// @return feeBalances The balances of the given tokens for the given partner
    function batchGetBalance(
        IERC20[] calldata tokens,
        address partner
    )
        external
        view
        returns (uint256[] memory feeBalances);

    /// @notice Returns the unallocated fees for a given token
    /// @param token The token to get the unallocated fees for
    /// @return unallocatedFees The unallocated fees for the given token
    function getUnallocatedFees(IERC20 token) external view returns (uint256 unallocatedFees);

    /*//////////////////////////////////////////////////////////////
                                 OWNER
    //////////////////////////////////////////////////////////////*/

    /// @notice Registers the given feeData to the vault
    /// @param feeData The fee registration data
    function registerFees(FeeRegistration memory feeData) external;

    /// @notice Sets the augustus contract approval status
    /// @param augustus The augustus contract address
    /// @param approved The approval status
    function setAugustusApproval(address augustus, bool approved) external;

    /// @notice Sets the contract pause state
    /// @param _isPaused The new pause state
    function setContractPauseState(bool _isPaused) external;
}

File 38 of 47 : IAugustusFees.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

/// @title IAugustusFees
/// @notice Interface for the AugustusFees contract, which handles the fees for the Augustus aggregator
interface IAugustusFees {
    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Error emmited when the balance is not enough to pay the fees
    error InsufficientBalanceToPayFees();

    /// @notice Error emmited when the quotedAmount is bigger than the fromAmount
    error InvalidQuotedAmount();

    /*//////////////////////////////////////////////////////////////
                                 PUBLIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Parses the `partnerAndFee` parameter to extract the partner address and fee data.
    /// @dev `partnerAndFee` is a uint256 value where data is packed in a specific bit layout.
    ///
    ///      The bit layout for `partnerAndFee` is as follows:
    ///      - The most significant 160 bits (positions 255 to 96) represent the partner address.
    ///      - Bits 95 to 92 are reserved for flags indicating various fee processing conditions:
    ///          - 95th bit: `IS_TAKE_SURPLUS_MASK` - Partner takes surplus
    ///          - 94th bit: `IS_REFERRAL_MASK` - Referral takes surplus
    ///          - 93rd bit: `IS_SKIP_BLACKLIST_MASK` - Bypass token blacklist when processing fees
    ///          - 92nd bit: `IS_CAP_SURPLUS_MASK` - Cap surplus to 1% of quoted amount
    ///      - The least significant 16 bits (positions 15 to 0) encode the fee percentage.
    ///
    /// @param partnerAndFee Packed uint256 containing both partner address and fee data.
    /// @return partner The extracted partner address as a payable address.
    /// @return feeData The extracted fee data containing the fee percentage and flags.
    function parsePartnerAndFeeData(uint256 partnerAndFee)
        external
        pure
        returns (address payable partner, uint256 feeData);
}

File 39 of 47 : AugustusStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

// @title AugustusStorage
// @notice Inherited storage layout for AugustusV6,
// contracts should inherit this contract to access the storage layout
contract AugustusStorage {
    /*//////////////////////////////////////////////////////////////
                               FEES
    //////////////////////////////////////////////////////////////*/

    // @dev Mapping of tokens to boolean indicating if token is blacklisted for fee collection
    mapping(IERC20 token => bool isBlacklisted) public blacklistedTokens;

    // @dev Fee wallet to directly transfer paraswap share to
    address payable public feeWallet;

    // @dev Fee wallet address to register the paraswap share to in the fee vault
    address payable public feeWalletDelegate;

    /*//////////////////////////////////////////////////////////////
                                CONTROL
    //////////////////////////////////////////////////////////////*/

    // @dev Contract paused state
    bool public paused;
}

File 40 of 47 : GenericUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Contracts
import { AugustusFees } from "../fees/AugustusFees.sol";

// Utils
import { Permit2Utils } from "./Permit2Utils.sol";
import { PauseUtils } from "./PauseUtils.sol";

/// @title GenericUtils
/// @notice A contract containing common utilities for Generic swaps
abstract contract GenericUtils is AugustusFees, Permit2Utils, PauseUtils {
    /*//////////////////////////////////////////////////////////////
                                INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev Call executor with executorData and amountIn
    function _callSwapExactAmountInExecutor(
        address executor,
        bytes calldata executorData,
        uint256 amountIn
    )
        internal
    {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // get the length of the executorData
            // + 4 bytes for the selector
            // + 32 bytes for fromAmount
            // + 32 bytes for sender
            let totalLength := add(executorData.length, 68)
            calldatacopy(add(0x7c, 4), executorData.offset, executorData.length) // store the executorData
            mstore(add(0x7c, add(4, executorData.length)), amountIn) // store the amountIn
            mstore(add(0x7c, add(36, executorData.length)), caller()) // store the sender
            // call executor and forward call value
            if iszero(call(gas(), executor, callvalue(), 0x7c, totalLength, 0, 0)) {
                returndatacopy(0x7c, 0, returndatasize()) // copy the revert data to memory
                revert(0x7c, returndatasize()) // revert with the revert data
            }
        }
    }

    /// @dev Call executor with executorData, maxAmountIn, amountOut
    function _callSwapExactAmountOutExecutor(
        address executor,
        bytes calldata executorData,
        uint256 maxAmountIn,
        uint256 amountOut
    )
        internal
    {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // get the length of the executorData
            // + 4 bytes for the selector
            // + 32 bytes for fromAmount
            // + 32 bytes for toAmount
            // + 32 bytes for sender
            let totalLength := add(executorData.length, 100)
            calldatacopy(add(0x7c, 4), executorData.offset, executorData.length) // store the executorData
            mstore(add(0x7c, add(4, executorData.length)), maxAmountIn) // store the maxAmountIn
            mstore(add(0x7c, add(36, executorData.length)), amountOut) // store the amountOut
            mstore(add(0x7c, add(68, executorData.length)), caller()) // store the sender
            // call executor and forward call value
            if iszero(call(gas(), executor, callvalue(), 0x7c, totalLength, 0, 0)) {
                returndatacopy(0x7c, 0, returndatasize()) // copy the revert data to memory
                revert(0x7c, returndatasize()) // revert with the revert data
            }
        }
    }
}

File 41 of 47 : IGenericSwapExactAmountIn.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { GenericData } from "../AugustusV6Types.sol";

/// @title IGenericSwapExactAmountIn
/// @notice Interface for executing a generic swapExactAmountIn through an Augustus executor
interface IGenericSwapExactAmountIn is IErrors {
    /*//////////////////////////////////////////////////////////////
                          SWAP EXACT AMOUNT IN
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a generic swapExactAmountIn using the given executorData on the given executor
    /// @param executor The address of the executor contract to use
    /// @param swapData Generic data containing the swap information
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit The permit data
    /// @param executorData The data to execute on the executor
    /// @return receivedAmount The amount of destToken received after fees
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountIn(
        address executor,
        GenericData calldata swapData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata executorData
    )
        external
        payable
        returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 42 of 47 : IGenericSwapExactAmountOut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { GenericData } from "../AugustusV6Types.sol";

/// @title IGenericSwapExactAmountOut
/// @notice Interface for executing a generic swapExactAmountOut through an Augustus executor
interface IGenericSwapExactAmountOut is IErrors {
    /*//////////////////////////////////////////////////////////////
                         SWAP EXACT AMOUNT OUT
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a generic swapExactAmountOut using the given executorData on the given executor
    /// @param executor The address of the executor contract to use
    /// @param swapData Generic data containing the swap information
    /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last
    /// 20 bytes is the partner address
    /// @param permit The permit data
    /// @param executorData The data to execute on the executor
    /// @return spentAmount The actual amount of tokens used to swap
    /// @return receivedAmount The amount of tokens received from the swap
    /// @return paraswapShare The share of the fees for Paraswap
    /// @return partnerShare The share of the fees for the partner
    function swapExactAmountOut(
        address executor,
        GenericData calldata swapData,
        uint256 partnerAndFee,
        bytes calldata permit,
        bytes calldata executorData
    )
        external
        payable
        returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare);
}

File 43 of 47 : IAugustusRFQRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Interfaces
import { IErrors } from "./IErrors.sol";

// Types
import { AugustusRFQData, OrderInfo } from "../AugustusV6Types.sol";

/// @title IAugustusRFQRouter
/// @notice Interface for direct swaps on AugustusRFQ
interface IAugustusRFQRouter is IErrors {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when the passed msg.value is not equal to the fromAmount
    error IncorrectEthAmount();

    /*//////////////////////////////////////////////////////////////
                             TRY BATCH FILL
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a tryBatchFillTakerAmount or tryBatchFillMakerAmount call on AugustusRFQ
    /// the function that is executed is defined by the direction flag in the data param
    /// @param data Struct containing common data for AugustusRFQ
    /// @param orders An array containing AugustusRFQ orderInfo data
    /// @param permit Permit data for the swap
    /// @return spentAmount The amount of tokens spent
    /// @return receivedAmount The amount of tokens received
    function swapOnAugustusRFQTryBatchFill(
        AugustusRFQData calldata data,
        OrderInfo[] calldata orders,
        bytes calldata permit
    )
        external
        payable
        returns (uint256 spentAmount, uint256 receivedAmount);
}

File 44 of 47 : IAugustusRFQ.sol
// SPDX-License-Identifier: ISC

pragma solidity 0.8.22;
pragma abicoder v2;

// Types
import { Order, OrderInfo } from "../AugustusV6Types.sol";

interface IAugustusRFQ {
    /// @dev Allows taker to fill an order
    /// @param order Order quote to fill
    /// @param signature Signature of the maker corresponding to the order
    function fillOrder(Order calldata order, bytes calldata signature) external;

    /// @dev The same as fillOrder but allows sender to specify the target beneficiary address
    /// @param order Order quote to fill
    /// @param signature Signature of the maker corresponding to the order
    /// @param target Address of the receiver
    function fillOrderWithTarget(Order calldata order, bytes calldata signature, address target) external;

    /// @dev Allows taker to fill an order partially
    /// @param order Order quote to fill
    /// @param signature Signature of the maker corresponding to the order
    /// @param takerTokenFillAmount Maximum taker token to fill this order with.
    function partialFillOrder(
        Order calldata order,
        bytes calldata signature,
        uint256 takerTokenFillAmount
    )
        external
        returns (uint256 makerTokenFilledAmount);

    /// @dev Same as `partialFillOrder` but it allows to specify the destination address
    ///  @param order Order quote to fill
    ///  @param signature Signature of the maker corresponding to the order
    ///  @param takerTokenFillAmount Maximum taker token to fill this order with.
    ///  @param target Address that will receive swap funds
    function partialFillOrderWithTarget(
        Order calldata order,
        bytes calldata signature,
        uint256 takerTokenFillAmount,
        address target
    )
        external
        returns (uint256 makerTokenFilledAmount);

    /// @dev Same as `partialFillOrderWithTarget` but it allows to pass permit
    ///  @param order Order quote to fill
    ///  @param signature Signature of the maker corresponding to the order
    ///  @param takerTokenFillAmount Maximum taker token to fill this order with.
    ///  @param target Address that will receive swap funds
    ///  @param permitTakerAsset Permit calldata for taker
    ///  @param permitMakerAsset Permit calldata for maker
    function partialFillOrderWithTargetPermit(
        Order calldata order,
        bytes calldata signature,
        uint256 takerTokenFillAmount,
        address target,
        bytes calldata permitTakerAsset,
        bytes calldata permitMakerAsset
    )
        external
        returns (uint256 makerTokenFilledAmount);

    /// @dev batch fills orders until the takerFillAmount is swapped
    /// @dev skip the order if it fails
    /// @param orderInfos OrderInfo to fill
    /// @param takerFillAmount total taker amount to fill
    /// @param target Address of receiver

    function tryBatchFillOrderTakerAmount(
        OrderInfo[] calldata orderInfos,
        uint256 takerFillAmount,
        address target
    )
        external;

    /// @dev batch fills orders until the makerFillAmount is swapped
    /// @dev skip the order if it fails
    /// @param orderInfos OrderInfo to fill
    /// @param makerFillAmount total maker amount to fill
    /// @param target Address of receiver
    function tryBatchFillOrderMakerAmount(
        OrderInfo[] calldata orderInfos,
        uint256 makerFillAmount,
        address target
    )
        external;
}

File 45 of 47 : IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

File 46 of 47 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

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

/// @title IWETH
/// @notice An interface for WETH IERC20
interface IWETH is IERC20 {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

File 47 of 47 : IErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

/// @title IErrors
/// @notice Common interface for errors
interface IErrors {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when the returned amount is less than the minimum amount
    error InsufficientReturnAmount();

    /// @notice Emitted when the specified toAmount is less than the minimum amount (2)
    error InvalidToAmount();

    /// @notice Emmited when the srcToken and destToken are the same
    error ArbitrageNotSupported();
}

Settings
{
  "remappings": [
    "@prb/test/=lib/prb-test/src/",
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "@solady/=lib/solady/src/",
    "@create3/=lib/create3-factory/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "create3-factory/=lib/create3-factory/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "prb-test/=lib/prb-test/src/",
    "solady/=lib/solady/",
    "solidity-bytes-utils/=lib/solidity-bytes-utils/contracts/",
    "solmate/=lib/create3-factory/lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_diamondCutFacet","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address payable","name":"_balancerVault","type":"address"},{"internalType":"uint256","name":"_uniV3FactoryAndFF","type":"uint256"},{"internalType":"uint256","name":"_uniswapV3PoolInitCodeHash","type":"uint256"},{"internalType":"uint256","name":"_uniswapV2FactoryAndFF","type":"uint256"},{"internalType":"uint256","name":"_uniswapV2PoolInitCodeHash","type":"uint256"},{"internalType":"address","name":"_rfq","type":"address"},{"internalType":"address payable","name":"_feeVault","type":"address"},{"internalType":"address","name":"_permit2","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArbitrageNotSupported","type":"error"},{"inputs":[],"name":"CallbackTransferFailed","type":"error"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[],"name":"DiamondFunctionDoesNotExist","type":"error"},{"inputs":[],"name":"IncorrectEthAmount","type":"error"},{"inputs":[{"internalType":"address","name":"_initializationContractAddress","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"InitializationFunctionReverted","type":"error"},{"inputs":[],"name":"InsufficientBalanceToPayFees","type":"error"},{"inputs":[],"name":"InsufficientReturnAmount","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidOrdersLength","type":"error"},{"inputs":[],"name":"InvalidQuotedAmount","type":"error"},{"inputs":[],"name":"InvalidSelector","type":"error"},{"inputs":[],"name":"InvalidToAmount","type":"error"},{"inputs":[],"name":"Permit2Failed","type":"error"},{"inputs":[],"name":"UnauthorizedUser","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"facetAddress","type":"address"},{"internalType":"enum IDiamondCut.FacetCutAction","name":"action","type":"uint8"},{"internalType":"bytes4[]","name":"functionSelectors","type":"bytes4[]"}],"indexed":false,"internalType":"struct IDiamondCut.FacetCut[]","name":"_diamondCut","type":"tuple[]"},{"indexed":false,"internalType":"address","name":"_init","type":"address"},{"indexed":false,"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"DiamondCut","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"AUGUSTUS_RFQ","outputs":[{"internalType":"contract IAugustusRFQ","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BALANCER_VAULT","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_VAULT","outputs":[{"internalType":"contract IAugustusFeeVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARASWAP_REFERRAL_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARASWAP_SLIPPAGE_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARASWAP_SURPLUS_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARTNER_REFERRAL_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARTNER_SHARE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SURPLUS_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_FACTORY_AND_FF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_POOL_INIT_CODE_HASH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_FACTORY_AND_FF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_POOL_INIT_CODE_HASH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"blacklistedTokens","outputs":[{"internalType":"bool","name":"isBlacklisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeWallet","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeWalletDelegate","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"partnerAndFee","type":"uint256"}],"name":"parsePartnerAndFeeData","outputs":[{"internalType":"address payable","name":"partner","type":"address"},{"internalType":"uint256","name":"feeData","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct GenericData","name":"swapData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"executorData","type":"bytes"}],"name":"swapExactAmountIn","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"uint256","name":"beneficiaryAndApproveFlag","type":"uint256"}],"internalType":"struct BalancerV2Data","name":"balancerData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapExactAmountInOnBalancerV2","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"curveData","type":"uint256"},{"internalType":"uint256","name":"curveAssets","type":"uint256"},{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct CurveV1Data","name":"curveV1Data","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnCurveV1","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"curveData","type":"uint256"},{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"j","type":"uint256"},{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct CurveV2Data","name":"curveV2Data","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnCurveV2","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV2Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnUniswapV2","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV3Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnUniswapV3","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct GenericData","name":"swapData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"executorData","type":"bytes"}],"name":"swapExactAmountOut","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"uint256","name":"beneficiaryAndApproveFlag","type":"uint256"}],"internalType":"struct BalancerV2Data","name":"balancerData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapExactAmountOutOnBalancerV2","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV2Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountOutOnUniswapV2","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV3Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountOutOnUniswapV3","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint8","name":"wrapApproveDirection","type":"uint8"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct AugustusRFQData","name":"data","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"nonceAndMeta","type":"uint256"},{"internalType":"uint128","name":"expiry","type":"uint128"},{"internalType":"address","name":"makerAsset","type":"address"},{"internalType":"address","name":"takerAsset","type":"address"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"}],"internalType":"struct Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"takerTokenFillAmount","type":"uint256"},{"internalType":"bytes","name":"permitTakerAsset","type":"bytes"},{"internalType":"bytes","name":"permitMakerAsset","type":"bytes"}],"internalType":"struct OrderInfo[]","name":"orders","type":"tuple[]"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapOnAugustusRFQTryBatchFill","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

34620009f057620070e3388190036101a0601f8201601f19168101906001600160401b03821190821017620009b2576101609282916040526101a03912620009f0576200004e6101a062000a34565b6200005b6101c062000a34565b90620000696101e062000a34565b906200007761020062000a34565b610220516102405161026051610280519294919390916200009a6102a062000a34565b96620000a86102c062000a34565b620000b56102e062000a34565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039586166001600160a01b03198216811790925591949091167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36200012662000a14565b9960018b525f5b60208110620009c657506200014162000a14565b60018152602036818301376307e4c70760e21b6200015f8262000a49565b526200016a620009f4565b6001600160a01b0390921682525f602083015260408201526200018d8b62000a49565b52620001998a62000a49565b50604051986001600160401b0360208b01908111908b1117620009b25794898989898e94602085016040525f85525f995b86518b1015620006c2576020620001e28c8962000a6b565b5101516003811015620006ae57806200038957506001600160a01b036200020a8c8962000a6b565b5151169960406200021c8d8a62000a6b565b510151996200022e8b51151562000ac0565b6200023b8c151562000b21565b6001600160a01b038c165f9081525f80516020620070c383398151915260205260409020546001600160601b0316998a1562000378575b5f9a5b8c518c10156200034e576001600160e01b0319620002948d8f62000a6b565b51165f8181525f805160206200708383398151915260205260409020546001600160a01b0316620002e357818f620002d490600194620002da9462000fda565b62000b83565b9b019a62000275565b60405162461bcd60e51b815260206004820152603560248201527f4c69624469616d6f6e644375743a2043616e2774206164642066756e6374696f60448201527f6e207468617420616c72656164792065786973747300000000000000000000006064820152608490fd5b5094995094995094995094996001909b91969b5b0199949a95909a989398979297969196620001ca565b620003838d62000eff565b62000272565b6001819b939597999b9a929496989a145f146200053b5750918a97959391620003d39c9a99979593604060018060a01b03620003c68c8c62000a6b565b5151169e8f9b8b62000a6b565b5101519a620003e58c51151562000ac0565b620003f28b151562000b21565b6001600160a01b038b165f9081525f80516020620070c383398151915260205260409020546001600160601b03169a8b1562000529575b505f9a5b8c518c101562000511578f908d6200044e8e63ffffffff60e01b9262000a6b565b51165f8181525f805160206200708383398151915260205260409020546001600160a01b03169190838314620004a657600193828262000497620002d4946200049d9762000bf7565b62000fda565b9b019a6200042d565b60405162461bcd60e51b815260206004820152603860248201527f4c69624469616d6f6e644375743a2043616e2774207265706c6163652066756e60448201527f6374696f6e20776974682073616d652066756e6374696f6e00000000000000006064820152608490fd5b50949950949950949991969b50949960019062000362565b620005349062000eff565b8e62000429565b60020362000659576001600160a01b03620005578c8b62000a6b565b515116996040620005698d8c62000a6b565b5101519a6200057b8c51151562000ac0565b620005ee575f5b8b51811015620005d85780620005d18d620005a860019463ffffffff60e01b9262000a6b565b5116805f525f8051602062007083833981519152602052838060a01b0360405f20541662000bf7565b0162000582565b509295989b9194979a60019194979a5062000362565b60405162461bcd60e51b815260206004820152603660248201527f4c69624469616d6f6e644375743a2052656d6f7665206661636574206164647260448201527f657373206d7573742062652061646472657373283029000000000000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152602760248201527f4c69624469616d6f6e644375743a20496e636f727265637420466163657443756044820152663a20b1ba34b7b760c91b6064820152608490fd5b634e487b7160e01b5f52602160045260245ffd5b8989898e898b60405191606083016060845282518091526080840190602060808260051b8701019401915f905b828210620009185750505050916200073881927f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb673945f6020850152838203604085015262000a80565b0390a16001600160a01b0390811660805290811660a0521660c05260e05261010052610140526101205261018091825261016052604051615ff2918262001091833960805182818161063701528181612eb30152818161302c015281816131100152818161332e01526133c0015260a051828181610f1701528181615bef0152615e76015260c05182818161038d015281816104ec01528181610d2701528181610e1401528181611046015281816110ef01528181611215015281816114ac01528181611a8301528181611b8a01528181611e0301528181611ed5015281816125fb0152818161274a015281816128e301528181612f2d01528181613188015281816132540152818161344301528181613a9b01526155e3015260e05182818161093c015281816121c40152818161376801526158b8015261010051828181611572015281816117180152818161185d01526152d101526101205182818161150501528181614ece01528181615786015261581c0152610140518281816108cf01528181614ead0152818161574d01526157e901526101605182818161143f015281816120bc01528181613f8c01528181614ab70152614b3c01525181818161083e0152818161207f01528181613f5401528181614a780152614b040152f35b868603607f19018152835180516001600160a01b031687526020810151949693949293919260608301916003821015620006ae57604060809160209384870152015193606060408201528451809452019201905f905b8082106200098e57505050602080600192970192019201909291620006ef565b82516001600160e01b0319168452602093840193909201916001909101906200096e565b634e487b7160e01b5f52604160045260245ffd5b808c60208093620009d6620009f4565b925f84525f8385015260606040850152010152016200012d565b5f80fd5b60405190606082016001600160401b03811183821017620009b257604052565b60408051919082016001600160401b03811183821017620009b257604052565b51906001600160a01b0382168203620009f057565b80511562000a575760200190565b634e487b7160e01b5f52603260045260245ffd5b805182101562000a575760209160051b010190565b91908251928382525f5b84811062000aab575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520162000a8a565b1562000ac857565b60405162461bcd60e51b815260206004820152602b60248201527f4c69624469616d6f6e644375743a204e6f2073656c6563746f727320696e206660448201526a1858d95d081d1bc818dd5d60aa1b6064820152608490fd5b1562000b2957565b60405162461bcd60e51b815260206004820152602c60248201527f4c69624469616d6f6e644375743a204164642066616365742063616e2774206260448201526b65206164647265737328302960a01b6064820152608490fd5b6001600160601b0390811690811462000b9c5760010190565b634e487b7160e01b5f52601160045260245ffd5b919091805483101562000a57575f52601c60205f208360031c019260021b1690565b5f80516020620070a3833981519152805482101562000a57575f5260205f2001905f90565b6001600160a01b0390811691821562000e945730831462000e385763ffffffff60e01b809116805f525f805160206200708383398151915293602090858252604093845f205460a01c96825f525f80516020620070c383398151915294858552865f2054925f19998a850194851162000b9c57889187898888850362000da6575b9450505050505f52858552865f208054801562000d3c578a019062000c9e828262000bb0565b63ffffffff82549160031b1b19169055555f5283525f858120551562000cc7575b505050505050565b5f80516020620070a383398151915294855487810190811162000b9c57825f52848452816001875f2001549180830362000d50575b505050855495861562000d3c575f97600197019162000d1b8362000bd2565b909182549160031b1b1916905555855252822001555f808080808062000cbf565b634e487b7160e01b5f52603160045260245ffd5b62000d5b9062000bd2565b90549060031b1c1662000d918162000d738462000bd2565b90919060018060a01b038084549260031b9316831b921b1916179055565b5f528484526001865f2001555f818162000cfc565b62000de18562000e2c9762000dfe94845f5280875262000dc98d835f2062000bb0565b90549060031b1c60e01b9687955f52525f2062000bb0565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b165f90815284885289902080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b865f8087898862000c78565b60405162461bcd60e51b815260206004820152602e60248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f766520696d6d7560448201526d3a30b1363290333ab731ba34b7b760911b6064820152608490fd5b60405162461bcd60e51b815260206004820152603760248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f76652066756e6360448201527f74696f6e207468617420646f65736e27742065786973740000000000000000006064820152608490fd5b62000f09620009f4565b602481527f4c69624469616d6f6e644375743a204e657720666163657420686173206e6f20602082015263636f646560e01b6040820152813b1562000fb057505f80516020620070a383398151915280546001600160a01b0383165f9081525f80516020620070c383398151915260205260409020600101819055919068010000000000000000831015620009b2578262000d7391600162000fae9501905562000bd2565b565b60405162461bcd60e51b81526020600482015290819062000fd690602483019062000a80565b0390fd5b6001600160e01b031981165f8181525f80516020620070838339815191526020819052604090912080546001600160a01b031660a09590951b6001600160a01b0319169490941790935590926001600160a01b03165f8181525f80516020620070c383398151915260205260409020805491949068010000000000000000831015620009b2578262000de1916001620010769501815562000bb0565b5f5260205260405f209060018060a01b031982541617905556fe6080604052600436101561001d575b366135435761001b61353a565b005b5f3560e01c80631a01c5321461023757806342f3b24114610232578063523819fb1461022d57806355c0bff0146102285780635c8b5f44146102235780635c975abb1461021e5780635e94e28d1461021957806366c31b841461021457806367d817401461020f5780636a6f511a1461020a5780636afdd850146102055780637f45767514610200578063838bf44d146101fb578063876a02f6146101f65780638940192a146101f157806390a0c0ea146101ce5780639facd044146101ec578063a76f4eb6146101e7578063aad8a491146101e2578063ad5c4648146101dd578063b613cc8a146101d8578063bc163846146101d3578063c8e416dd146101ce578063d6ed22e6146101c9578063d85ca173146101c4578063da35bb0d146101bf578063e37ed256146101ba578063e3ead59e146101b5578063e65dc2f2146101b0578063e8bb3b6c146101ab578063ed386afa146101a6578063f25f4b56146101a1578063fa461e331461019c5763fe12941f0361000e5761236c565b612015565b611fc4565b611f8b565b611d3d565b611cec565b611be5565b611958565b6118b2565b611787565b61163a565b610f3b565b611528565b6114d0565b611462565b61140a565b610faf565b610f75565b610ecd565b610c20565b610be6565b610a15565b6108f2565b61089a565b610861565b610809565b610796565b6106e4565b610679565b6105ed565b6105b4565b61057a565b61026e565b9181601f8401121561026a5782359167ffffffffffffffff831161026a576020838186019501011161026a57565b5f80fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601610160811261026a576101201361026a576101443567ffffffffffffffff811161026a576102c46004913690830161023c565b9060ff60025460a01c16610547576102da6123be565b926102e36123cd565b906084359360a435946102f46123fb565b94833590871561051e5773ffffffffffffffffffffffffffffffffffffffff9889881615610516575b916002949391610385938b8316809260018560a01c169060038660a11c169861034688866135f5565b6104db57876101018210156104c95750806104b8575b505061036a8630338661386a565b505b6104a8575b50505b8460036024359360a31c169161390c565b0361049857847f0000000000000000000000000000000000000000000000000000000000000000166103bf6103ba3083613acb565b612442565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af180156104935761047a575b5047935b84106104525761044e61043160c435866101243586888b16613bb9565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761048d926124ae565b80610570565b5f610410565b6124e3565b6104a23083613acb565b93610414565b6104b191613660565b5f81610371565b6104c291856137b8565b5f8061035c565b90916104d692309161371f565b61036c565b50509150501561037457610511818c7f000000000000000000000000000000000000000000000000000000000000000016613660565b610374565b33975061031d565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b5f91031261026a57565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516121348152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160648152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026a57565b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5773ffffffffffffffffffffffffffffffffffffffff6004356106c98161065b565b165f525f602052602060ff60405f2054166040519015158152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060ff60025460a01c166040519015158152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9160608383011261026a5760043567ffffffffffffffff9384821161026a5761010090828503011261026a57600401926024359260443591821161026a576107929160040161023c565b9091565b61079f36610727565b9060ff60029493945460a01c166107df5761044e936107bd9361256b565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160c88152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b359061096b8261065b565b565b906101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261026a576004356109a58161065b565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026a57602491610104359167ffffffffffffffff916101243583811161026a57826109fa9160040161023c565b939093926101443591821161026a576107929160040161023c565b610a1e3661096d565b909192959360ff60025460a01c166107df57610a3c602087016123f1565b610a45876123f1565b604088013593606089013595610a5d60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610bbc578c8b1615610bb4575b8815610b8a57610a983086613acb565b99610aa389876135f5565b610b6f579282610ada9592858b80966101018f99105f14610b5f57505080610b4e575b5050610ad48482338a61386a565b50614865565b610b02610ae73084613acb565b96610af23084613acb565b906001811115610b465790612474565b948610610b1c5761044e9860806107bd99013597166144dd565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b505f90612474565b610b5891896137b8565b5f80610ac6565b9091610b6a9361371f565b614865565b505097610ada928892610b8489933490612474565b9a614865565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a50610a88565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516127108152f35b610c2936610727565b60ff60025460a01c166107df57610c3f846123f1565b92610c4c602086016123f1565b60409485870135606088013594610c6560c08a016123f1565b96610c7360e08b018b6124ee565b8895919515610ea45773ffffffffffffffffffffffffffffffffffffffff95868b1615610e9c575b3392610ca787826135f5565b610e1157610cc896610cc3916101018810610df1575b50613ef9565b61489b565b938410610dc85773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610d20575b90610d0494939291608061044e9801359416614b83565b9251918252602082015260408101919091529081906060820190565b93929190847f00000000000000000000000000000000000000000000000000000000000000001694853b1561026a575f875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610d8c8a600483019190602083019252565b03925af180156104935761044e98610d0497608092610db5575b50985050909192939450610ced565b80610487610dc2926124ae565b5f610da6565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610e00575b5050610cbd565b610e09926137b8565b5f8787610df9565b507f000000000000000000000000000000000000000000000000000000000000000087169250823b1561026a575f869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561049357610cc896610e89575b50610cc33093613ef9565b80610487610e96926124ae565b5f610e7e565b339a50610c9b565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516113888152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516109c48152f35b610fb836610727565b9260ff60025460a01c166107df57610fcf816123f1565b93610fdc602083016123f1565b936040948584013592606085013597610ff760c087016123f1565b9861100560e08801886124ee565b9190938115610ea45773ffffffffffffffffffffffffffffffffffffffff93848d1615611402575b61103789826135f5565b1580159b906113aa57505050827f000000000000000000000000000000000000000000000000000000000000000016936110713086613acb565b94803b1561026a578b517fd0e30db00000000000000000000000000000000000000000000000000000000081525f816004818d865af1801561049357611397575b50905b848716938583169185831461136e57906110d0918486614ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361135d57847f000000000000000000000000000000000000000000000000000000000000000016809114611334576111203082613acb565b90838211611324575b803b1561026a578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611311575b5047995b6111833083613acb565b928b106112e857156112bc57506111a6904794600181115f14610b465790612474565b60018111611206575b50916111e29795939160806111d761044e9c999795934790600181115f14610b465790612474565b965b013597166144dd565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161123b817f0000000000000000000000000000000000000000000000000000000000000000169b612442565b9a803b1561026a578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c525f908c90602490829084905af19a8b15610493576111d76111e29a60809261044e9e6112a9575b50939597999c505050919395976111af565b806104876112b6926124ae565b5f611297565b91509160806112e26111e29a98969461044e9d9a9896600181115f14610b465790612474565b966111d9565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761131e926124ae565b5f611175565b9061132e90612a14565b90611129565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506113683087613acb565b99611179565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b806104876113a4926124ae565b5f6110b2565b6113b8969192963084613acb565b968a6101018210156113f05750806113df575b50506113d98930338561386a565b506110b5565b6113e991846137b8565b5f806113cb565b90916113fd92309161371f565b6110b5565b339c5061102d565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261026a57600490565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8101610100811261026a5760a01361026a5760049160a4359167ffffffffffffffff9160c43583811161026a57826116209160040161023c565b9390939260e43591821161026a576107929160040161023c565b611643366115c5565b949360ff60025460a01c166107df5784359060208601359261166981608089013561518d565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610bbc578915610b8a578d161561177f575b6116a93086613acb565b996116b489876135f5565b611766576116eb949392919089610101821015611754575080611743575b50506116e08830338861386a565b505b611712576152c2565b6116f8610ae73084613acb565b948610610b1c5761044e9860406107bd99013597166144dd565b61173e8c7f00000000000000000000000000000000000000000000000000000000000000001685613660565b6152c2565b61174d91876137b8565b5f806116d2565b909161176192309161371f565b6116e2565b50505096906117796116eb923490612474565b976152c2565b339a5061169f565b611790366115c5565b90949360ff60025460a01c166107df578435926020860135926117b788608089013561518d565b909a9291948b988815610b8a5773ffffffffffffffffffffffffffffffffffffffff809d16156118aa575b611804969798999a6117f482866135f5565b15611828575b50505050506152c2565b61180e3082613acb565b928310610b1c5761044e9560406104319601359416613bb9565b61010183101561189a578261184693611889575b505030338561386a565b505b611856575b808080806117fa565b611883908a7f00000000000000000000000000000000000000000000000000000000000000001690613660565b5f61184d565b61189391866137b8565b5f8061183c565b6118a592309161371f565b611848565b3399506117e2565b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576118e536611596565b67ffffffffffffffff60a43581811161026a573660238201121561026a57806004013582811161026a573660248260051b8401011161026a5760c43592831161026a576119469361193c602494369060040161023c565b9490930190612a3f565b60408051928352602083019190915290f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36016101a0811261026a576101601361026a576101843567ffffffffffffffff811161026a576119ae6004913690830161023c565b60ff60025460a01c16610547576119c36123d9565b926119cc6123e5565b9060c4359260e435946119dd612408565b946119e66123cd565b9184358815611bbc5773ffffffffffffffffffffffffffffffffffffffff998a891615611bb4575b918160029695938c611a7b969416809360018460a01c169060038560a11c1699611a3888866135f5565b611b795787610101821015611b67575080611b56575b5050611a5c8630338661386a565b505b611b46575b50505b604435918660036024359360a31c1691615431565b03611b3657847f000000000000000000000000000000000000000000000000000000000000000016611ab06103ba3083613acb565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af1801561049357611b23575b5047935b84106104525761044e61043161010435866101643586888b16613bb9565b80610487611b30926124ae565b5f611b01565b611b403083613acb565b93611b05565b611b4f91613660565b5f82611a63565b611b6091856137b8565b5f80611a4e565b9091611b7492309161371f565b611a5e565b505091505015611a6657611baf828d7f000000000000000000000000000000000000000000000000000000000000000016613660565b611a66565b339850611a0e565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b611bee3661096d565b959260ff60029593955460a01c166107df57611c0c602087016123f1565b91611c16876123f1565b97604088013591606089013596611c2f60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611ce4575b8915610b8a578281611c7698611c668980956135f5565b15611c9a575b5050505050615616565b611c803082613acb565b928310610b1c5761044e9560806104319601359416613bb9565b610101851015611cd45784611cb795611cc3575b5050339061386a565b505b5f84828280611c6c565b611ccd91836137b8565b5f80611cae565b611cdf94915061371f565b611cb9565b339a50611c4f565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611d4636610727565b9160ff60025460a01c166107df57611d5d846123f1565b93611d6a602082016123f1565b906040948582013590606083013595611d8560c085016123f1565b98611d9360e08601866124ee565b9033928a15611f625773ffffffffffffffffffffffffffffffffffffffff9796959493929190888e1615611f5a575b611dcc87826135f5565b611ed257611de3966101018710611ec1575b61563c565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611eb157817f000000000000000000000000000000000000000000000000000000000000000016611e306103ba3083613acb565b90803b1561026a5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611e9e575b5047945b8510610dc8579161044e9693916080610d04969401359416613bb9565b80610487611eab926124ae565b5f611e7d565b611ebb3084613acb565b94611e81565b8615611dde57611dde8787846137b8565b507f00000000000000000000000000000000000000000000000000000000000000008816959250853b1561026a578b51957fd0e30db00000000000000000000000000000000000000000000000000000000087525f8760048187855af196871561049357611de397611f47575b50309361563c565b80610487611f54926124ae565b5f611f3f565b339d50611dc2565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576020604051600b8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026a5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576004602435813560443567ffffffffffffffff811161026a5761206a903690850161023c565b91909260ff60025460a01c16612343576040517f00000000000000000000000000000000000000000000000000000000000000008152610200841460158201605f6041880160168501376060812090527f000000000000000000000000000000000000000000000000000000000000000060358301526055822080925273ffffffffffffffffffffffffffffffffffffffff809216331861231c5760a0851180612314575b15612148575061001b9550505f821315612138575061212d9061253f565b915b60a43592613f31565b612142915061253f565b9161212f565b935093905f915f93604051965f8213612302575b50505f82136122f2575b505060a435923084146001146122a8571561221257507f30f28b7a0000000000000000000000000000000000000000000000000000000083525f928392610164926101606101248885013733608484015260a483015260c4820152827f00000000000000000000000000000000000000000000000000000000000000005af1156121ec57005b7f6b836e6b000000000000000000000000000000000000000000000000000000005f525ffd5b92805f926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081612286575b501561226057005b7f1bbb4abe000000000000000000000000000000000000000000000000000000005f525ffd5b90503d156122a0575060015f5114601f3d11165b5f612258565b3b151561229a565b509260209250805f927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af1908161228657501561226057005b9092506060915001355f80612166565b90945060408201351692505f8061215c565b50801561210f565b867f48f5c3ed000000000000000000000000000000000000000000000000000000005f525ffd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356123ca8161065b565b90565b6064356123ca8161065b565b6084356123ca8161065b565b60a4356123ca8161065b565b356123ca8161065b565b610104356123ca8161065b565b610144356123ca8161065b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161246f57565b612415565b9190820391821161246f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116124c257604052565b612481565b6060810190811067ffffffffffffffff8211176124c257604052565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026a570180359067ffffffffffffffff821161026a5760200191813603831361026a57565b7f8000000000000000000000000000000000000000000000000000000000000000811461246f575f0390565b91612575836123f1565b91612582602085016123f1565b9061258f60c086016123f1565b9161259d60e08701876124ee565b97606088013515610b8a5773ffffffffffffffffffffffffffffffffffffffff851615612a0c575b336125d460408a0135896135f5565b1515975f97895f146129a0575050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016946126263087613acb565b98863b1561026a5760405f8a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af180156104935761298d575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610bbc5787826126c5926126c06126bb60608f0135613ef9565b61253f565b613f31565b8099919360608c01358210610b1c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146128cc575b73ffffffffffffffffffffffffffffffffffffffff1630036128755750501561284257505050479361277573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001698610af2308b613acb565b94600186116127b4575b506127ac97505b73ffffffffffffffffffffffffffffffffffffffff6040608089013598013594166144dd565b929391929091565b90946127bf90612442565b97803b1561026a576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099525f908990602490829084905af1908115610493576127ac986128289261282f575b5047906001811115610b465790612474565b935f61277f565b8061048761283c926124ae565b5f612816565b6127ac9992965060601015612866575061286090610af23087613acb565b93612786565b61286091506040880135612474565b9399509750506127ac99506060106128af575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614144565b6128c59196506128bf3388613acb565b90612474565b945f612888565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168073ffffffffffffffffffffffffffffffffffffffff891614610bbc57803b1561026a575f60405180927f2e1a7d4d0000000000000000000000000000000000000000000000000000000082528183816129658a600483019190602083019252565b03925af180156104935761297a575b50612708565b80610487612987926124ae565b5f612974565b8061048761299a926124ae565b5f61266d565b6129b29b9298939b9491943086613acb565b9b6101018110156129f157806129e0575b505060608211156126725791506129da3384613acb565b91612672565b6129ea91866137b8565b5f806129c3565b90612a0592995060408c013591309161371f565b3096612672565b3394506125c5565b801561246f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c166107df5761079294612db2565b3560ff8116810361026a5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9015612acd578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b612a67565b9190811015612acd5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026a57016020813591019167ffffffffffffffff821161026a57813603831361026a57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481945f925b848410612bfc575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026a5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026a5784612d82612d5b8695612d188f612cf18e612da1998b60019e0152612cca6040612cae818c01610960565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b612cd5818a01610960565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b612cfc818801610960565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612d2860a0612cfc818801610960565b60c0808601359085015260e080860135908501526101009080612d4d83880188612b12565b929093870152850191612b62565b6101208085013590840152610140612d7581860186612b12565b9185840390860152612b62565b91612d936101609182810190612b12565b929091818503910152612b62565b990193019401929195949390612bc9565b9493929192612dc3608087016123f1565b91863594602088013594612dda6040809a01612a59565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613532575b8815611f62578415613509575f5b8581106134ed575050612e45612e2c6060612e268789612a94565b016123f1565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612e57612e2c8d612e26888a612a94565b936003811693600185149283613437573461340e5760019291908d6101018210156133fc5750806133eb575b5050612e918c30338761386a565b505b818160021c166133ba575b60031c166130fb5750506002036130165750827f00000000000000000000000000000000000000000000000000000000000000001691823b1561026a57612f1a92875f80948c51968795869485937f1c64b820000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613003575b50807f00000000000000000000000000000000000000000000000000000000000000001692612f583085613acb565b908110612fda57612f6890612a14565b95833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052925f908490602490829084905af191821561049357612fc2938793612fc7575b5016615350565b509190565b80610487612fd4926124ae565b5f612fbb565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b80610487613010926124ae565b5f612f29565b9190838599969916916130298385613acb565b947f000000000000000000000000000000000000000000000000000000000000000016803b1561026a575f92838a936130908b519a8b96879586947f1c64b82000000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af1918215610493576130b3946130ae936130e8575b50613acb565b612474565b9384106130bf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b806104876130f5926124ae565b5f6130a8565b9793925099979593906002145f1461332757847f000000000000000000000000000000000000000000000000000000000000000016803b1561026a57613175935f80948b51968795869485937f01fb36ba000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613314575b50817f000000000000000000000000000000000000000000000000000000000000000016906131bb6131b63084613acb565b612a14565b91803b1561026a575f875180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161320089600483019190602083019252565b03925af180156104935761321d9284928692612fc7575016615350565b50955b61322a3082613acb565b9360018511613245575b5050506132419250612474565b9190565b939493156132f3575061327a907f00000000000000000000000000000000000000000000000000000000000000001693612a14565b91833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052925f908490602490829084905af192831561049357613241936132e0575b506132d68233615350565b505b5f8080613234565b806104876132ed926124ae565b5f6132cb565b92505061330e61330561324194612a14565b8093339061538b565b506132d8565b80610487613321926124ae565b5f613184565b84999392997f000000000000000000000000000000000000000000000000000000000000000016803b1561026a575f92838c936133928c51978896879586947f01fb36ba00000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af18015610493576133a7575b50613220565b806104876133b4926124ae565b5f6133a1565b6133e6897f00000000000000000000000000000000000000000000000000000000000000001685613660565b612e9e565b6133f591866137b8565b5f80612e83565b909161340992309161371f565b612e93565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036134c457877f00000000000000000000000000000000000000000000000000000000000000001690813b1561026a575f8c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610493576001926134b1575b50612e93565b806104876134be926124ae565b5f6134ab565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806135036134fd8493898b612ad2565b35615302565b01612e0b565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612dfd565b333b1561026a57565b7fffffffff000000000000000000000000000000000000000000000000000000005f35165f527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60405f20541680156135cb575f8091368280378136915af43d5f803e156135c7573d5ff35b3d5ffd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b91905f9273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9182821461364e575b501861361f57565b3461362657565b7f8b6ebb4d000000000000000000000000000000000000000000000000000000005f5260045ffd5b9093503418613626576001925f613617565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90816034526f095ea7b300000000000000000000000090815f5260205f6044601082855af13d1560015f51141716156136c2575b5050505f603452565b60105f60449260209582958360345283528238868683865af1506034525af13d1560015f51141716156136f7575f80806136b9565b7f8164f842000000000000000000000000000000000000000000000000000000005f5260045ffd5b906004905f94859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f00000000000000000000000000000000000000000000000000000000000000005af11561379057565b7f6b836e6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b918060e01461382e57610100146137f1577fb78cb0dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101045f9182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e45f918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015260205f60648382875af192836138e7575b5082156138bf5750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d15613903575060015f5114601f3d1116915b5f6138b5565b3b1515916138fd565b919293906040519360018214613a57575b915f95608494928796946001146139fb5760031460011461399b577f3df02124000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b3d5f803e3d5ffd5b6fffffffffffffffffffffffffffffffff907f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af161096b573d5f803e3d5ffd5b507fa6417ed6000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b929093917fd0e30db00000000000000000000000000000000000000000000000000000000083525f806004853473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af115613993579193909261391d565b5f9291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613b2f576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613b2a575b50565b519150565b31925050565b90600b820180921161246f57565b9190820180921161246f57565b908160640291606483040361246f57565b906113889182810292818404149015171561246f57565b906109c49182810292818404149015171561246f57565b906121349182810292818404149015171561246f57565b8181029291811591840414171561246f57565b9091949392613bda5f96906bffffffffffffffffffffffff8260601c921690565b9490613be582613b35565b8311613eb3575b613bf68884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216613cb5575b505086159050613c9b57613c4e613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b5460ff1690565b613c9b57508482613c6785613c8f94613c959796615968565b613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b9061538b565b50612442565b91905f90565b9250613cae93945082906103ba92615968565b905f905f90565b6b2000000000000000000000008199949395979699161594851594613cfa613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680613eac575b613e93576b0800000000000000000000008316151597613d2084615995565b938415613da1575050948a94613d8c94613d6e613d66613d949b9686613d849f9e9c979b613d51816103ba9e613b43565b871115613d9957613d6191613b43565b613ba6565b612710900490565b90613d7b613d6683613b8f565b9c8d8093612474565b9d8e93615ad2565b928391615968565b929190565b505084613ba6565b80929c99969794506b400000000000000000000000919a989593501615155f14613e0f575089613dd857505050505b5f8080613c13565b8698975091613d949693918a6103ba96613d8c95613e07613dff613d669f613d6690613b61565b9e8f94613b78565b9c8d92615ad2565b939291906b8000000000000000000000008516613e31575b5050505050613dd0565b8a15613e27579092948a92949998506b040000000000000000000000613e5f613d66613e689d9a999a613b61565b9b8c8095612474565b9a16613e87575b926103ba95928a889693613d949a99613d8c97615ad2565b5f995060019550613e6f565b505050509390506103ba9250839150613cae9495615968565b5086613d01565b9650613ebf8183612474565b966b100000000000000000000000861615613bec5796613ee1613d6683613b50565b9081811115613ef257505b96613bec565b9050613eec565b7f8000000000000000000000000000000000000000000000000000000000000000811015613f245790565b6335278d125f526004601cfd5b939192908094845f92606082066140ee575b50505f6040949596855197889586947f0000000000000000000000000000000000000000000000000000000000000000865260158601605f6001860160168901376060812090527f0000000000000000000000000000000000000000000000000000000000000000603587015273ffffffffffffffffffffffffffffffffffffffff6055872016968030916140e6575b50843560ff1c861461405d577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca60208301519251925b5f03615d3d565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca6020835193015192614056565b90505f613fd3565b60a0821115613f435760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff1691505f6040613f43565b919093979594979692965f9761417561415d8884612474565b91906bffffffffffffffffffffffff8260601c921690565b909286116144b3578a9561418889613b35565b811161446d575b73ffffffffffffffffffffffffffffffffffffffff8416614249575b50505050916141bc9187949361538b565b50816141d4575b506141cd91613b43565b9291905f90565b9050614200613c478273ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b61423f576141cd91614238858093614230612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b90339061386a565b50916141c3565b509291505f908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591614292613c478d73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9380614466575b614448576b08000000000000000000000082161515946142b883615995565b92831561431d575050906142d6918088105f14613d61575086613ba6565b6127109004996142e58b613b8f565b61271090046142f581809d612474565b9c8d923361430298615d47565b9061430c91613b43565b926143169261538b565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156143995750614363575050505050916141bc918794935b91939481935f6141ab565b909192939998949695979a61437781613b61565b61271090049b6143878d92613b78565b61271090049b8c913361430298615d47565b946b800000000000000000000000869b9a999897929b166143c6575b505050505050906141bc9291614358565b909192939495969798996143df578c99989796956143b5565b988c929394959b9a96996b04000000000000000000000061440661440f9f613d6690613b61565b9e8f8096612474565b9d1661443c575b928c6143169a99989693614431969361443699963390615d47565b613b43565b9361538b565b5f9c5060019650614416565b5050505050509450919061445d93955061538b565b5091905f905f90565b5083614299565b9950614479888b612474565b996b10000000000000000000000082161561418f579961449b613d668c613b50565b90818111156144ac57505b9961418f565b90506144a6565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315155f1461485c576144ff6144f985612442565b87612474565b5f98606081901c906bffffffffffffffffffffffff1690928881116144b35761452783613b35565b8111614816575b73ffffffffffffffffffffffffffffffffffffffff84166145f8575b505050509061455b61456392612a14565b97889161538b565b5084156145df57614594613c478373ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b6145df576141cd92916145d7866128bf936145d1826145cb612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8661538b565b50612474565b903390615fa1565b6145f09394506128bf913390615fa1565b91905f905f90565b6b20000000000000000000000082161591821591614636613c478b73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b938061480f575b6147e7576b080000000000000000000000821615159461465c83615995565b9182614798575050506b4000000000000000000000008116156146f757508a61469557505050509061455b614563925b91925f8061454a565b8a6146a78996989c9b9a99959b613b61565b61271090049b6146b78d92613b78565b61271090049b6146c8968d92615ad2565b336146d292615fa1565b916146dc90612a14565b80976146e79261538b565b506146f191612474565b93929190565b9291936b8000000000000000000000008416614720575b50505050509061455b6145639261468c565b8b1561470e578b919293999695976b04000000000000000000000061474e613d666147579f9e9c989e613b61565b9d8e8095612474565b9b1661478c575b938a9b61477e9487946146f19c9d6145d7956145d19b9a6147849a615ad2565b94612a14565b98899161538b565b5f9a506001945061475e565b8b989a9d9c9b979e506147b593508082105f146147e05750613ba6565b6127109004996147c48b613b8f565b61271090046147d481809d612474565b9c6146c8968e93615ad2565b9050613ba6565b50505050505093926145f095965061477e6145d193614807923390615fa1565b96879161538b565b508361463d565b9950614822828b612474565b996b10000000000000000000000082161561452e5799614844613d668c613b50565b908181111561485557505b9961452e565b905061484f565b6144ff846144f9565b91805f958695606493607c95608037608083015260a08201523360c0820152019134905af11561489157565b3d5f607c3e3d607cfd5b94919392935f925f945f93604051975f975b6060860489106148ca57505050505050505050506123ca90615d3d565b909192939495969798809b9a8915614afe575b6060880460018c0110614a71575b8a614a69575b8a60a061016492896001610100821114614a5b575b506060830288013560ff1c156149be579460608086945f946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146149b1575b5af1156139935760208a0151945b600187965f0399019796959a98999a9493929190946148ad565b8989610164850137614989565b9460608086945f946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614a4e575b5af11561399357895194614997565b8989610164850137614a3f565b809192940193019089614906565b3093506148f1565b97505096507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916976148eb565b985050507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f6001840160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916916148dd565b9091949392614ba45f96906bffffffffffffffffffffffff8260601c921690565b9490614baf82613b35565b8311614e2b575b614bc08884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216614c67575b505086159050614c5057614c11613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b614c50575081614c26848793614c499561538b565b50613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b5091905f90565b939450614c5f9250839161538b565b50905f905f90565b6b2000000000000000000000008199949395979699161594851594614cac613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680614e24575b614e0e576b0800000000000000000000008316151597614cd284615995565b938415614d2c57505093614d1699989793614d00613d66614d269995858f9a9699613d5181614d1e9c613b43565b90614d0d613d6683613b8f565b9b8c8093612474565b9c8d93615ad2565b92839161538b565b50929190565b80929c99969794506b400000000000000000000000919a989593501615155f14614d97575089614d6357505050505b5f8080614bdd565b869897509089614d26969795614d1e959493614d8f614d87613d66613d669f613b61565b9d8e94613b78565b9b8c92615ad2565b939291906b8000000000000000000000008516614db9575b5050505050614d5b565b8a15614daf579092948a92949998506b040000000000000000000000613e5f613d66614de79d9a999a613b61565b9a16614e02575b918987969492614d2698614d1e9795615ad2565b5f995060019550614dee565b5050505094959050849250614c5f93915061538b565b5086614cb3565b9650614e378183612474565b966b100000000000000000000000861615614bb65796614e59613d6683613b50565b9081811115614e6a57505b96614bb6565b9050614e64565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000091604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16935f97858501525f5b8360061c8110615169575050508060061c805b61507957505f935f965f955b8360061c8710614f5557505050505050505050565b8015615027575b308460061c600189011061500f575b60a46040925f928a6001810160051b8a01518b8b826020899560061b8d010135600116615004575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561399357600188960195614f40565b935087925081614f93565b508486016001880160051b0160200151985088614f6b565b508385016020818101517fa9059cbb0000000000000000000000000000000000000000000000000000000092880180830193845260248101829052604490810185905290925f9190828c5af150614f5c565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156139935760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c010135851661515e575b50906126f28161271093030292020204018098838301901b8501520180614f34565b9092506126f261513c565b80615187838560019460061b8b0160208560051b8c8c010101614e71565b01614f21565b9190918235807f52bbbe2900000000000000000000000000000000000000000000000000000000146152b0577f945bcec90000000000000000000000000000000000000000000000000000000014615207577f7352d91c000000000000000000000000000000000000000000000000000000005f5260045ffd5b60448301359283810193600485013594600183600401351460011461529d5760059590951b01016004013592602401355b8015615282575b8315615266575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350615246565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61523f565b602401359460051b010160040135615238565b50610144830135926101240135615238565b905f80918060405194853783347f00000000000000000000000000000000000000000000000000000000000000005af1156152fa5750565b3d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff16806153205750565b331861532857565b7f02a43f8b000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8080938193612710f190811561536357565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee841460011461541d5760446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af191826153f8575b505b811561536357565b9091503d15615414575060015f5114601f3d1116905b5f6153ee565b3b15159061540e565b5f809394508092918192612710f1906153f0565b929490936040519460019384821461559c575b905f988998979695949392806001146155515760021461550157506003146001146154b15790869392916084967f5b41b90800000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b60a4957f394747c5000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152806064840152608483015234905af161096b573d5f803e3d5ffd5b889594939291509660a4977f64a14558000000000000000000000000000000000000000000000000000000008852600488015260248701526044860152606485015260848401525af11561399357565b50505090869392916084967f65b2489b00000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b969594939291907fd0e30db00000000000000000000000000000000000000000000000000000000086525f806004883473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1156139935790919293949596615444565b906044835f958695607c9460803760808201523360a0820152019134905af11561489157565b9095929193955f955f945f985f965b8660061c8810615662575050505050505050505050565b89156157e3575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156139935783519084015190818a6157da575b506126f291612710838502910201920202049687905f906157d1575b30918a60061c60018d0110615744575b5f9360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156139935760018a97019661564b565b92939d509b50507f00000000000000000000000000000000000000000000000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f000000000000000000000000000000000000000000000000000000000000000060358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d919093926156e8565b50505f876156d8565b9150905f6156bc565b506040517f000000000000000000000000000000000000000000000000000000000000000081529850601589016028808a8337812090527f000000000000000000000000000000000000000000000000000000000000000060358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a1690308314600114615929576101008411156158e4577f30f28b7a0000000000000000000000000000000000000000000000000000000081525f806004928688858301378460848201528960a48201528560c482015283870190827f00000000000000000000000000000000000000000000000000000000000000005af1156121ec5750615669565b5f6064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615669565b5f6044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615669565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613b2793019161538b565b613fff1660c881116159a45790565b5060c890565b6040519061096b826124c7565b604051906159c4826124c7565b600282526040366020840137565b805115612acd5760200190565b805160011015612acd5760400190565b9081518082526020808093019301915f5b828110615a0e575050505090565b835185529381019392810192600101615a00565b6020808252825160608284015280516080840181905293949360a084019392918201905f5b818110615aa8575050508473ffffffffffffffffffffffffffffffffffffffff6040926123ca96970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526159ef565b825173ffffffffffffffffffffffffffffffffffffffff1686529483019491830191600101615a47565b96909591939294615ae38487613b43565b9283615af457505050505050505090565b9697959681615d35575b5015615b745750505082828111615b4a576123ca9481615b21575b505050612474565b73ffffffffffffffffffffffffffffffffffffffff615b4193169061538b565b505f8080615b19565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b84829693979211615b4a5715615bcf57856123ca96615b9c575b5081615b2157505050612474565b615bc890615bc2612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8361538b565b505f615b8e565b615cc09073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000000000000000000000000000000000000000001694615c1b88878561538b565b50615c56615c276159b7565b99615c306159b7565b9616615c3b8b6159d2565b9073ffffffffffffffffffffffffffffffffffffffff169052565b615c5f856159d2565b52615c8e615c85612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b8a6159df565b615c97846159df565b52615ca06159aa565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026a57615d095f949185926040519687809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1928315610493576123ca93615d225750612474565b80610487615d2f926124ae565b5f6145d1565b90505f615afe565b5f8112613f245790565b969790929594919394615d5a8387613b43565b9889615d6d575b50505050505050505090565b819994959697989991615f99575b5015615dcf5750505082948311615b4a5782615da5575b505050505b5f8080808080808080615d61565b73ffffffffffffffffffffffffffffffffffffffff615dc594169161386a565b505f808080615d92565b9091968711615b4a5715615e515780615e1d575b5082615df3575b50505050615d97565b73ffffffffffffffffffffffffffffffffffffffff615e1394169161386a565b505f808080615dea565b615e4a90615e43612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b838561386a565b505f615de3565b91615f289193949273ffffffffffffffffffffffffffffffffffffffff95615e9e88887f00000000000000000000000000000000000000000000000000000000000000001680988661386a565b50615ebe615eaa6159b7565b97615eb36159b7565b9616615c3b896159d2565b615ec7856159d2565b52615ef6615eed612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b886159df565b615eff846159df565b52615f086159aa565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026a57615f715f929183926040519485809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1801561049357615f86575b50615d97565b80610487615f93926124ae565b5f615f80565b90505f615d7b565b91909160018211615fb3575050505f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615fe1920192839161538b565b509056fea164736f6c6343000816000ac8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a2903263300000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e00000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

Deployed Bytecode

0x6080604052600436101561001d575b366135435761001b61353a565b005b5f3560e01c80631a01c5321461023757806342f3b24114610232578063523819fb1461022d57806355c0bff0146102285780635c8b5f44146102235780635c975abb1461021e5780635e94e28d1461021957806366c31b841461021457806367d817401461020f5780636a6f511a1461020a5780636afdd850146102055780637f45767514610200578063838bf44d146101fb578063876a02f6146101f65780638940192a146101f157806390a0c0ea146101ce5780639facd044146101ec578063a76f4eb6146101e7578063aad8a491146101e2578063ad5c4648146101dd578063b613cc8a146101d8578063bc163846146101d3578063c8e416dd146101ce578063d6ed22e6146101c9578063d85ca173146101c4578063da35bb0d146101bf578063e37ed256146101ba578063e3ead59e146101b5578063e65dc2f2146101b0578063e8bb3b6c146101ab578063ed386afa146101a6578063f25f4b56146101a1578063fa461e331461019c5763fe12941f0361000e5761236c565b612015565b611fc4565b611f8b565b611d3d565b611cec565b611be5565b611958565b6118b2565b611787565b61163a565b610f3b565b611528565b6114d0565b611462565b61140a565b610faf565b610f75565b610ecd565b610c20565b610be6565b610a15565b6108f2565b61089a565b610861565b610809565b610796565b6106e4565b610679565b6105ed565b6105b4565b61057a565b61026e565b9181601f8401121561026a5782359167ffffffffffffffff831161026a576020838186019501011161026a57565b5f80fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601610160811261026a576101201361026a576101443567ffffffffffffffff811161026a576102c46004913690830161023c565b9060ff60025460a01c16610547576102da6123be565b926102e36123cd565b906084359360a435946102f46123fb565b94833590871561051e5773ffffffffffffffffffffffffffffffffffffffff9889881615610516575b916002949391610385938b8316809260018560a01c169060038660a11c169861034688866135f5565b6104db57876101018210156104c95750806104b8575b505061036a8630338661386a565b505b6104a8575b50505b8460036024359360a31c169161390c565b0361049857847f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9166103bf6103ba3083613acb565b612442565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af180156104935761047a575b5047935b84106104525761044e61043160c435866101243586888b16613bb9565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761048d926124ae565b80610570565b5f610410565b6124e3565b6104a23083613acb565b93610414565b6104b191613660565b5f81610371565b6104c291856137b8565b5f8061035c565b90916104d692309161371f565b61036c565b50509150501561037457610511818c7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916613660565b610374565b33975061031d565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b5f91031261026a57565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516121348152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160648152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026a57565b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5773ffffffffffffffffffffffffffffffffffffffff6004356106c98161065b565b165f525f602052602060ff60405f2054166040519015158152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060ff60025460a01c166040519015158152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9160608383011261026a5760043567ffffffffffffffff9384821161026a5761010090828503011261026a57600401926024359260443591821161026a576107929160040161023c565b9091565b61079f36610727565b9060ff60029493945460a01c166107df5761044e936107bd9361256b565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160c88152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3168152f35b359061096b8261065b565b565b906101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261026a576004356109a58161065b565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026a57602491610104359167ffffffffffffffff916101243583811161026a57826109fa9160040161023c565b939093926101443591821161026a576107929160040161023c565b610a1e3661096d565b909192959360ff60025460a01c166107df57610a3c602087016123f1565b610a45876123f1565b604088013593606089013595610a5d60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610bbc578c8b1615610bb4575b8815610b8a57610a983086613acb565b99610aa389876135f5565b610b6f579282610ada9592858b80966101018f99105f14610b5f57505080610b4e575b5050610ad48482338a61386a565b50614865565b610b02610ae73084613acb565b96610af23084613acb565b906001811115610b465790612474565b948610610b1c5761044e9860806107bd99013597166144dd565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b505f90612474565b610b5891896137b8565b5f80610ac6565b9091610b6a9361371f565b614865565b505097610ada928892610b8489933490612474565b9a614865565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a50610a88565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516127108152f35b610c2936610727565b60ff60025460a01c166107df57610c3f846123f1565b92610c4c602086016123f1565b60409485870135606088013594610c6560c08a016123f1565b96610c7360e08b018b6124ee565b8895919515610ea45773ffffffffffffffffffffffffffffffffffffffff95868b1615610e9c575b3392610ca787826135f5565b610e1157610cc896610cc3916101018810610df1575b50613ef9565b61489b565b938410610dc85773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610d20575b90610d0494939291608061044e9801359416614b83565b9251918252602082015260408101919091529081906060820190565b93929190847f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e91694853b1561026a575f875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610d8c8a600483019190602083019252565b03925af180156104935761044e98610d0497608092610db5575b50985050909192939450610ced565b80610487610dc2926124ae565b5f610da6565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610e00575b5050610cbd565b610e09926137b8565b5f8787610df9565b507f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e987169250823b1561026a575f869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561049357610cc896610e89575b50610cc33093613ef9565b80610487610e96926124ae565b5f610e7e565b339a50610c9b565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516113888152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516109c48152f35b610fb836610727565b9260ff60025460a01c166107df57610fcf816123f1565b93610fdc602083016123f1565b936040948584013592606085013597610ff760c087016123f1565b9861100560e08801886124ee565b9190938115610ea45773ffffffffffffffffffffffffffffffffffffffff93848d1615611402575b61103789826135f5565b1580159b906113aa57505050827f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916936110713086613acb565b94803b1561026a578b517fd0e30db00000000000000000000000000000000000000000000000000000000081525f816004818d865af1801561049357611397575b50905b848716938583169185831461136e57906110d0918486614ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361135d57847f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916809114611334576111203082613acb565b90838211611324575b803b1561026a578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611311575b5047995b6111833083613acb565b928b106112e857156112bc57506111a6904794600181115f14610b465790612474565b60018111611206575b50916111e29795939160806111d761044e9c999795934790600181115f14610b465790612474565b965b013597166144dd565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161123b817f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9169b612442565b9a803b1561026a578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c525f908c90602490829084905af19a8b15610493576111d76111e29a60809261044e9e6112a9575b50939597999c505050919395976111af565b806104876112b6926124ae565b5f611297565b91509160806112e26111e29a98969461044e9d9a9896600181115f14610b465790612474565b966111d9565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761131e926124ae565b5f611175565b9061132e90612a14565b90611129565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506113683087613acb565b99611179565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b806104876113a4926124ae565b5f6110b2565b6113b8969192963084613acb565b968a6101018210156113f05750806113df575b50506113d98930338561386a565b506110b5565b6113e991846137b8565b5f806113cb565b90916113fd92309161371f565b6110b5565b339c5061102d565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8168152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261026a57600490565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8101610100811261026a5760a01361026a5760049160a4359167ffffffffffffffff9160c43583811161026a57826116209160040161023c565b9390939260e43591821161026a576107929160040161023c565b611643366115c5565b949360ff60025460a01c166107df5784359060208601359261166981608089013561518d565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610bbc578915610b8a578d161561177f575b6116a93086613acb565b996116b489876135f5565b611766576116eb949392919089610101821015611754575080611743575b50506116e08830338861386a565b505b611712576152c2565b6116f8610ae73084613acb565b948610610b1c5761044e9860406107bd99013597166144dd565b61173e8c7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81685613660565b6152c2565b61174d91876137b8565b5f806116d2565b909161176192309161371f565b6116e2565b50505096906117796116eb923490612474565b976152c2565b339a5061169f565b611790366115c5565b90949360ff60025460a01c166107df578435926020860135926117b788608089013561518d565b909a9291948b988815610b8a5773ffffffffffffffffffffffffffffffffffffffff809d16156118aa575b611804969798999a6117f482866135f5565b15611828575b50505050506152c2565b61180e3082613acb565b928310610b1c5761044e9560406104319601359416613bb9565b61010183101561189a578261184693611889575b505030338561386a565b505b611856575b808080806117fa565b611883908a7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81690613660565b5f61184d565b61189391866137b8565b5f8061183c565b6118a592309161371f565b611848565b3399506117e2565b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576118e536611596565b67ffffffffffffffff60a43581811161026a573660238201121561026a57806004013582811161026a573660248260051b8401011161026a5760c43592831161026a576119469361193c602494369060040161023c565b9490930190612a3f565b60408051928352602083019190915290f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36016101a0811261026a576101601361026a576101843567ffffffffffffffff811161026a576119ae6004913690830161023c565b60ff60025460a01c16610547576119c36123d9565b926119cc6123e5565b9060c4359260e435946119dd612408565b946119e66123cd565b9184358815611bbc5773ffffffffffffffffffffffffffffffffffffffff998a891615611bb4575b918160029695938c611a7b969416809360018460a01c169060038560a11c1699611a3888866135f5565b611b795787610101821015611b67575080611b56575b5050611a5c8630338661386a565b505b611b46575b50505b604435918660036024359360a31c1691615431565b03611b3657847f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916611ab06103ba3083613acb565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af1801561049357611b23575b5047935b84106104525761044e61043161010435866101643586888b16613bb9565b80610487611b30926124ae565b5f611b01565b611b403083613acb565b93611b05565b611b4f91613660565b5f82611a63565b611b6091856137b8565b5f80611a4e565b9091611b7492309161371f565b611a5e565b505091505015611a6657611baf828d7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916613660565b611a66565b339850611a0e565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b611bee3661096d565b959260ff60029593955460a01c166107df57611c0c602087016123f1565b91611c16876123f1565b97604088013591606089013596611c2f60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611ce4575b8915610b8a578281611c7698611c668980956135f5565b15611c9a575b5050505050615616565b611c803082613acb565b928310610b1c5761044e9560806104319601359416613bb9565b610101851015611cd45784611cb795611cc3575b5050339061386a565b505b5f84828280611c6c565b611ccd91836137b8565b5f80611cae565b611cdf94915061371f565b611cb9565b339a50611c4f565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611d4636610727565b9160ff60025460a01c166107df57611d5d846123f1565b93611d6a602082016123f1565b906040948582013590606083013595611d8560c085016123f1565b98611d9360e08601866124ee565b9033928a15611f625773ffffffffffffffffffffffffffffffffffffffff9796959493929190888e1615611f5a575b611dcc87826135f5565b611ed257611de3966101018710611ec1575b61563c565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611eb157817f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916611e306103ba3083613acb565b90803b1561026a5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611e9e575b5047945b8510610dc8579161044e9693916080610d04969401359416613bb9565b80610487611eab926124ae565b5f611e7d565b611ebb3084613acb565b94611e81565b8615611dde57611dde8787846137b8565b507f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e98816959250853b1561026a578b51957fd0e30db00000000000000000000000000000000000000000000000000000000087525f8760048187855af196871561049357611de397611f47575b50309361563c565b80610487611f54926124ae565b5f611f3f565b339d50611dc2565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576020604051600b8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026a5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576004602435813560443567ffffffffffffffff811161026a5761206a903690850161023c565b91909260ff60025460a01c16612343576040517f00000000000000000000000000000000000000000000000000000000000000008152610200841460158201605f6041880160168501376060812090527f000000000000000000000000000000000000000000000000000000000000000060358301526055822080925273ffffffffffffffffffffffffffffffffffffffff809216331861231c5760a0851180612314575b15612148575061001b9550505f821315612138575061212d9061253f565b915b60a43592613f31565b612142915061253f565b9161212f565b935093905f915f93604051965f8213612302575b50505f82136122f2575b505060a435923084146001146122a8571561221257507f30f28b7a0000000000000000000000000000000000000000000000000000000083525f928392610164926101606101248885013733608484015260a483015260c4820152827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af1156121ec57005b7f6b836e6b000000000000000000000000000000000000000000000000000000005f525ffd5b92805f926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081612286575b501561226057005b7f1bbb4abe000000000000000000000000000000000000000000000000000000005f525ffd5b90503d156122a0575060015f5114601f3d11165b5f612258565b3b151561229a565b509260209250805f927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af1908161228657501561226057005b9092506060915001355f80612166565b90945060408201351692505f8061215c565b50801561210f565b867f48f5c3ed000000000000000000000000000000000000000000000000000000005f525ffd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356123ca8161065b565b90565b6064356123ca8161065b565b6084356123ca8161065b565b60a4356123ca8161065b565b356123ca8161065b565b610104356123ca8161065b565b610144356123ca8161065b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161246f57565b612415565b9190820391821161246f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116124c257604052565b612481565b6060810190811067ffffffffffffffff8211176124c257604052565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026a570180359067ffffffffffffffff821161026a5760200191813603831361026a57565b7f8000000000000000000000000000000000000000000000000000000000000000811461246f575f0390565b91612575836123f1565b91612582602085016123f1565b9061258f60c086016123f1565b9161259d60e08701876124ee565b97606088013515610b8a5773ffffffffffffffffffffffffffffffffffffffff851615612a0c575b336125d460408a0135896135f5565b1515975f97895f146129a0575050505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916946126263087613acb565b98863b1561026a5760405f8a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af180156104935761298d575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610bbc5787826126c5926126c06126bb60608f0135613ef9565b61253f565b613f31565b8099919360608c01358210610b1c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146128cc575b73ffffffffffffffffffffffffffffffffffffffff1630036128755750501561284257505050479361277573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e91698610af2308b613acb565b94600186116127b4575b506127ac97505b73ffffffffffffffffffffffffffffffffffffffff6040608089013598013594166144dd565b929391929091565b90946127bf90612442565b97803b1561026a576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099525f908990602490829084905af1908115610493576127ac986128289261282f575b5047906001811115610b465790612474565b935f61277f565b8061048761283c926124ae565b5f612816565b6127ac9992965060601015612866575061286090610af23087613acb565b93612786565b61286091506040880135612474565b9399509750506127ac99506060106128af575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614144565b6128c59196506128bf3388613acb565b90612474565b945f612888565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9168073ffffffffffffffffffffffffffffffffffffffff891614610bbc57803b1561026a575f60405180927f2e1a7d4d0000000000000000000000000000000000000000000000000000000082528183816129658a600483019190602083019252565b03925af180156104935761297a575b50612708565b80610487612987926124ae565b5f612974565b8061048761299a926124ae565b5f61266d565b6129b29b9298939b9491943086613acb565b9b6101018110156129f157806129e0575b505060608211156126725791506129da3384613acb565b91612672565b6129ea91866137b8565b5f806129c3565b90612a0592995060408c013591309161371f565b3096612672565b3394506125c5565b801561246f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c166107df5761079294612db2565b3560ff8116810361026a5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9015612acd578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b612a67565b9190811015612acd5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026a57016020813591019167ffffffffffffffff821161026a57813603831361026a57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481945f925b848410612bfc575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026a5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026a5784612d82612d5b8695612d188f612cf18e612da1998b60019e0152612cca6040612cae818c01610960565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b612cd5818a01610960565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b612cfc818801610960565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612d2860a0612cfc818801610960565b60c0808601359085015260e080860135908501526101009080612d4d83880188612b12565b929093870152850191612b62565b6101208085013590840152610140612d7581860186612b12565b9185840390860152612b62565b91612d936101609182810190612b12565b929091818503910152612b62565b990193019401929195949390612bc9565b9493929192612dc3608087016123f1565b91863594602088013594612dda6040809a01612a59565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613532575b8815611f62578415613509575f5b8581106134ed575050612e45612e2c6060612e268789612a94565b016123f1565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612e57612e2c8d612e26888a612a94565b936003811693600185149283613437573461340e5760019291908d6101018210156133fc5750806133eb575b5050612e918c30338761386a565b505b818160021c166133ba575b60031c166130fb5750506002036130165750827f0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e1691823b1561026a57612f1a92875f80948c51968795869485937f1c64b820000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613003575b50807f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e91692612f583085613acb565b908110612fda57612f6890612a14565b95833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052925f908490602490829084905af191821561049357612fc2938793612fc7575b5016615350565b509190565b80610487612fd4926124ae565b5f612fbb565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b80610487613010926124ae565b5f612f29565b9190838599969916916130298385613acb565b947f0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e16803b1561026a575f92838a936130908b519a8b96879586947f1c64b82000000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af1918215610493576130b3946130ae936130e8575b50613acb565b612474565b9384106130bf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b806104876130f5926124ae565b5f6130a8565b9793925099979593906002145f1461332757847f0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e16803b1561026a57613175935f80948b51968795869485937f01fb36ba000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613314575b50817f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e916906131bb6131b63084613acb565b612a14565b91803b1561026a575f875180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161320089600483019190602083019252565b03925af180156104935761321d9284928692612fc7575016615350565b50955b61322a3082613acb565b9360018511613245575b5050506132419250612474565b9190565b939493156132f3575061327a907f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e91693612a14565b91833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052925f908490602490829084905af192831561049357613241936132e0575b506132d68233615350565b505b5f8080613234565b806104876132ed926124ae565b5f6132cb565b92505061330e61330561324194612a14565b8093339061538b565b506132d8565b80610487613321926124ae565b5f613184565b84999392997f0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e16803b1561026a575f92838c936133928c51978896879586947f01fb36ba00000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af18015610493576133a7575b50613220565b806104876133b4926124ae565b5f6133a1565b6133e6897f0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e1685613660565b612e9e565b6133f591866137b8565b5f80612e83565b909161340992309161371f565b612e93565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036134c457877f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e91690813b1561026a575f8c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610493576001926134b1575b50612e93565b806104876134be926124ae565b5f6134ab565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806135036134fd8493898b612ad2565b35615302565b01612e0b565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612dfd565b333b1561026a57565b7fffffffff000000000000000000000000000000000000000000000000000000005f35165f527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60405f20541680156135cb575f8091368280378136915af43d5f803e156135c7573d5ff35b3d5ffd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b91905f9273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9182821461364e575b501861361f57565b3461362657565b7f8b6ebb4d000000000000000000000000000000000000000000000000000000005f5260045ffd5b9093503418613626576001925f613617565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90816034526f095ea7b300000000000000000000000090815f5260205f6044601082855af13d1560015f51141716156136c2575b5050505f603452565b60105f60449260209582958360345283528238868683865af1506034525af13d1560015f51141716156136f7575f80806136b9565b7f8164f842000000000000000000000000000000000000000000000000000000005f5260045ffd5b906004905f94859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af11561379057565b7f6b836e6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b918060e01461382e57610100146137f1577fb78cb0dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101045f9182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e45f918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015260205f60648382875af192836138e7575b5082156138bf5750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d15613903575060015f5114601f3d1116915b5f6138b5565b3b1515916138fd565b919293906040519360018214613a57575b915f95608494928796946001146139fb5760031460011461399b577f3df02124000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b3d5f803e3d5ffd5b6fffffffffffffffffffffffffffffffff907f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af161096b573d5f803e3d5ffd5b507fa6417ed6000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b929093917fd0e30db00000000000000000000000000000000000000000000000000000000083525f806004853473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9165af115613993579193909261391d565b5f9291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613b2f576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613b2a575b50565b519150565b31925050565b90600b820180921161246f57565b9190820180921161246f57565b908160640291606483040361246f57565b906113889182810292818404149015171561246f57565b906109c49182810292818404149015171561246f57565b906121349182810292818404149015171561246f57565b8181029291811591840414171561246f57565b9091949392613bda5f96906bffffffffffffffffffffffff8260601c921690565b9490613be582613b35565b8311613eb3575b613bf68884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216613cb5575b505086159050613c9b57613c4e613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b5460ff1690565b613c9b57508482613c6785613c8f94613c959796615968565b613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b9061538b565b50612442565b91905f90565b9250613cae93945082906103ba92615968565b905f905f90565b6b2000000000000000000000008199949395979699161594851594613cfa613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680613eac575b613e93576b0800000000000000000000008316151597613d2084615995565b938415613da1575050948a94613d8c94613d6e613d66613d949b9686613d849f9e9c979b613d51816103ba9e613b43565b871115613d9957613d6191613b43565b613ba6565b612710900490565b90613d7b613d6683613b8f565b9c8d8093612474565b9d8e93615ad2565b928391615968565b929190565b505084613ba6565b80929c99969794506b400000000000000000000000919a989593501615155f14613e0f575089613dd857505050505b5f8080613c13565b8698975091613d949693918a6103ba96613d8c95613e07613dff613d669f613d6690613b61565b9e8f94613b78565b9c8d92615ad2565b939291906b8000000000000000000000008516613e31575b5050505050613dd0565b8a15613e27579092948a92949998506b040000000000000000000000613e5f613d66613e689d9a999a613b61565b9b8c8095612474565b9a16613e87575b926103ba95928a889693613d949a99613d8c97615ad2565b5f995060019550613e6f565b505050509390506103ba9250839150613cae9495615968565b5086613d01565b9650613ebf8183612474565b966b100000000000000000000000861615613bec5796613ee1613d6683613b50565b9081811115613ef257505b96613bec565b9050613eec565b7f8000000000000000000000000000000000000000000000000000000000000000811015613f245790565b6335278d125f526004601cfd5b939192908094845f92606082066140ee575b50505f6040949596855197889586947f0000000000000000000000000000000000000000000000000000000000000000865260158601605f6001860160168901376060812090527f0000000000000000000000000000000000000000000000000000000000000000603587015273ffffffffffffffffffffffffffffffffffffffff6055872016968030916140e6575b50843560ff1c861461405d577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca60208301519251925b5f03615d3d565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca6020835193015192614056565b90505f613fd3565b60a0821115613f435760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff1691505f6040613f43565b919093979594979692965f9761417561415d8884612474565b91906bffffffffffffffffffffffff8260601c921690565b909286116144b3578a9561418889613b35565b811161446d575b73ffffffffffffffffffffffffffffffffffffffff8416614249575b50505050916141bc9187949361538b565b50816141d4575b506141cd91613b43565b9291905f90565b9050614200613c478273ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b61423f576141cd91614238858093614230612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b90339061386a565b50916141c3565b509291505f908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591614292613c478d73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9380614466575b614448576b08000000000000000000000082161515946142b883615995565b92831561431d575050906142d6918088105f14613d61575086613ba6565b6127109004996142e58b613b8f565b61271090046142f581809d612474565b9c8d923361430298615d47565b9061430c91613b43565b926143169261538b565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156143995750614363575050505050916141bc918794935b91939481935f6141ab565b909192939998949695979a61437781613b61565b61271090049b6143878d92613b78565b61271090049b8c913361430298615d47565b946b800000000000000000000000869b9a999897929b166143c6575b505050505050906141bc9291614358565b909192939495969798996143df578c99989796956143b5565b988c929394959b9a96996b04000000000000000000000061440661440f9f613d6690613b61565b9e8f8096612474565b9d1661443c575b928c6143169a99989693614431969361443699963390615d47565b613b43565b9361538b565b5f9c5060019650614416565b5050505050509450919061445d93955061538b565b5091905f905f90565b5083614299565b9950614479888b612474565b996b10000000000000000000000082161561418f579961449b613d668c613b50565b90818111156144ac57505b9961418f565b90506144a6565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315155f1461485c576144ff6144f985612442565b87612474565b5f98606081901c906bffffffffffffffffffffffff1690928881116144b35761452783613b35565b8111614816575b73ffffffffffffffffffffffffffffffffffffffff84166145f8575b505050509061455b61456392612a14565b97889161538b565b5084156145df57614594613c478373ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b6145df576141cd92916145d7866128bf936145d1826145cb612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8661538b565b50612474565b903390615fa1565b6145f09394506128bf913390615fa1565b91905f905f90565b6b20000000000000000000000082161591821591614636613c478b73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b938061480f575b6147e7576b080000000000000000000000821615159461465c83615995565b9182614798575050506b4000000000000000000000008116156146f757508a61469557505050509061455b614563925b91925f8061454a565b8a6146a78996989c9b9a99959b613b61565b61271090049b6146b78d92613b78565b61271090049b6146c8968d92615ad2565b336146d292615fa1565b916146dc90612a14565b80976146e79261538b565b506146f191612474565b93929190565b9291936b8000000000000000000000008416614720575b50505050509061455b6145639261468c565b8b1561470e578b919293999695976b04000000000000000000000061474e613d666147579f9e9c989e613b61565b9d8e8095612474565b9b1661478c575b938a9b61477e9487946146f19c9d6145d7956145d19b9a6147849a615ad2565b94612a14565b98899161538b565b5f9a506001945061475e565b8b989a9d9c9b979e506147b593508082105f146147e05750613ba6565b6127109004996147c48b613b8f565b61271090046147d481809d612474565b9c6146c8968e93615ad2565b9050613ba6565b50505050505093926145f095965061477e6145d193614807923390615fa1565b96879161538b565b508361463d565b9950614822828b612474565b996b10000000000000000000000082161561452e5799614844613d668c613b50565b908181111561485557505b9961452e565b905061484f565b6144ff846144f9565b91805f958695606493607c95608037608083015260a08201523360c0820152019134905af11561489157565b3d5f607c3e3d607cfd5b94919392935f925f945f93604051975f975b6060860489106148ca57505050505050505050506123ca90615d3d565b909192939495969798809b9a8915614afe575b6060880460018c0110614a71575b8a614a69575b8a60a061016492896001610100821114614a5b575b506060830288013560ff1c156149be579460608086945f946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146149b1575b5af1156139935760208a0151945b600187965f0399019796959a98999a9493929190946148ad565b8989610164850137614989565b9460608086945f946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614a4e575b5af11561399357895194614997565b8989610164850137614a3f565b809192940193019089614906565b3093506148f1565b97505096507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916976148eb565b985050507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f6001840160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916916148dd565b9091949392614ba45f96906bffffffffffffffffffffffff8260601c921690565b9490614baf82613b35565b8311614e2b575b614bc08884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216614c67575b505086159050614c5057614c11613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b614c50575081614c26848793614c499561538b565b50613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b5091905f90565b939450614c5f9250839161538b565b50905f905f90565b6b2000000000000000000000008199949395979699161594851594614cac613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680614e24575b614e0e576b0800000000000000000000008316151597614cd284615995565b938415614d2c57505093614d1699989793614d00613d66614d269995858f9a9699613d5181614d1e9c613b43565b90614d0d613d6683613b8f565b9b8c8093612474565b9c8d93615ad2565b92839161538b565b50929190565b80929c99969794506b400000000000000000000000919a989593501615155f14614d97575089614d6357505050505b5f8080614bdd565b869897509089614d26969795614d1e959493614d8f614d87613d66613d669f613b61565b9d8e94613b78565b9b8c92615ad2565b939291906b8000000000000000000000008516614db9575b5050505050614d5b565b8a15614daf579092948a92949998506b040000000000000000000000613e5f613d66614de79d9a999a613b61565b9a16614e02575b918987969492614d2698614d1e9795615ad2565b5f995060019550614dee565b5050505094959050849250614c5f93915061538b565b5086614cb3565b9650614e378183612474565b966b100000000000000000000000861615614bb65796614e59613d6683613b50565b9081811115614e6a57505b96614bb6565b9050614e64565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000091604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16935f97858501525f5b8360061c8110615169575050508060061c805b61507957505f935f965f955b8360061c8710614f5557505050505050505050565b8015615027575b308460061c600189011061500f575b60a46040925f928a6001810160051b8a01518b8b826020899560061b8d010135600116615004575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561399357600188960195614f40565b935087925081614f93565b508486016001880160051b0160200151985088614f6b565b508385016020818101517fa9059cbb0000000000000000000000000000000000000000000000000000000092880180830193845260248101829052604490810185905290925f9190828c5af150614f5c565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156139935760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c010135851661515e575b50906126f28161271093030292020204018098838301901b8501520180614f34565b9092506126f261513c565b80615187838560019460061b8b0160208560051b8c8c010101614e71565b01614f21565b9190918235807f52bbbe2900000000000000000000000000000000000000000000000000000000146152b0577f945bcec90000000000000000000000000000000000000000000000000000000014615207577f7352d91c000000000000000000000000000000000000000000000000000000005f5260045ffd5b60448301359283810193600485013594600183600401351460011461529d5760059590951b01016004013592602401355b8015615282575b8315615266575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350615246565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61523f565b602401359460051b010160040135615238565b50610144830135926101240135615238565b905f80918060405194853783347f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c85af1156152fa5750565b3d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff16806153205750565b331861532857565b7f02a43f8b000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8080938193612710f190811561536357565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee841460011461541d5760446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af191826153f8575b505b811561536357565b9091503d15615414575060015f5114601f3d1116905b5f6153ee565b3b15159061540e565b5f809394508092918192612710f1906153f0565b929490936040519460019384821461559c575b905f988998979695949392806001146155515760021461550157506003146001146154b15790869392916084967f5b41b90800000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b60a4957f394747c5000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152806064840152608483015234905af161096b573d5f803e3d5ffd5b889594939291509660a4977f64a14558000000000000000000000000000000000000000000000000000000008852600488015260248701526044860152606485015260848401525af11561399357565b50505090869392916084967f65b2489b00000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b969594939291907fd0e30db00000000000000000000000000000000000000000000000000000000086525f806004883473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9165af1156139935790919293949596615444565b906044835f958695607c9460803760808201523360a0820152019134905af11561489157565b9095929193955f955f945f985f965b8660061c8810615662575050505050505050505050565b89156157e3575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156139935783519084015190818a6157da575b506126f291612710838502910201920202049687905f906157d1575b30918a60061c60018d0110615744575b5f9360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156139935760018a97019661564b565b92939d509b50507f00000000000000000000000000000000000000000000000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f000000000000000000000000000000000000000000000000000000000000000060358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d919093926156e8565b50505f876156d8565b9150905f6156bc565b506040517f000000000000000000000000000000000000000000000000000000000000000081529850601589016028808a8337812090527f000000000000000000000000000000000000000000000000000000000000000060358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a1690308314600114615929576101008411156158e4577f30f28b7a0000000000000000000000000000000000000000000000000000000081525f806004928688858301378460848201528960a48201528560c482015283870190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af1156121ec5750615669565b5f6064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615669565b5f6044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615669565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613b2793019161538b565b613fff1660c881116159a45790565b5060c890565b6040519061096b826124c7565b604051906159c4826124c7565b600282526040366020840137565b805115612acd5760200190565b805160011015612acd5760400190565b9081518082526020808093019301915f5b828110615a0e575050505090565b835185529381019392810192600101615a00565b6020808252825160608284015280516080840181905293949360a084019392918201905f5b818110615aa8575050508473ffffffffffffffffffffffffffffffffffffffff6040926123ca96970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526159ef565b825173ffffffffffffffffffffffffffffffffffffffff1686529483019491830191600101615a47565b96909591939294615ae38487613b43565b9283615af457505050505050505090565b9697959681615d35575b5015615b745750505082828111615b4a576123ca9481615b21575b505050612474565b73ffffffffffffffffffffffffffffffffffffffff615b4193169061538b565b505f8080615b19565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b84829693979211615b4a5715615bcf57856123ca96615b9c575b5081615b2157505050612474565b615bc890615bc2612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8361538b565b505f615b8e565b615cc09073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc1694615c1b88878561538b565b50615c56615c276159b7565b99615c306159b7565b9616615c3b8b6159d2565b9073ffffffffffffffffffffffffffffffffffffffff169052565b615c5f856159d2565b52615c8e615c85612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b8a6159df565b615c97846159df565b52615ca06159aa565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026a57615d095f949185926040519687809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1928315610493576123ca93615d225750612474565b80610487615d2f926124ae565b5f6145d1565b90505f615afe565b5f8112613f245790565b969790929594919394615d5a8387613b43565b9889615d6d575b50505050505050505090565b819994959697989991615f99575b5015615dcf5750505082948311615b4a5782615da5575b505050505b5f8080808080808080615d61565b73ffffffffffffffffffffffffffffffffffffffff615dc594169161386a565b505f808080615d92565b9091968711615b4a5715615e515780615e1d575b5082615df3575b50505050615d97565b73ffffffffffffffffffffffffffffffffffffffff615e1394169161386a565b505f808080615dea565b615e4a90615e43612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b838561386a565b505f615de3565b91615f289193949273ffffffffffffffffffffffffffffffffffffffff95615e9e88887f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc1680988661386a565b50615ebe615eaa6159b7565b97615eb36159b7565b9616615c3b896159d2565b615ec7856159d2565b52615ef6615eed612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b886159df565b615eff846159df565b52615f086159aa565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026a57615f715f929183926040519485809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1801561049357615f86575b50615d97565b80610487615f93926124ae565b5f615f80565b90505f615d7b565b91909160018211615fb3575050505f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615fe1920192839161538b565b509056fea164736f6c6343000816000a

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

000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a2903263300000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e00000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

-----Decoded View---------------
Arg [0] : _owner (address): 0xD7e24A49944F7972cEb826C7557580658F9C3303
Arg [1] : _diamondCutFacet (address): 0xfa39c1c670b48956eeF9fd0BbD0E81A290326330
Arg [2] : _weth (address): 0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9
Arg [3] : _balancerVault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
Arg [4] : _uniV3FactoryAndFF (uint256): 0
Arg [5] : _uniswapV3PoolInitCodeHash (uint256): 0
Arg [6] : _uniswapV2FactoryAndFF (uint256): 0
Arg [7] : _uniswapV2PoolInitCodeHash (uint256): 0
Arg [8] : _rfq (address): 0x7Ee1F7fa4C0b2eDB0Fdd5944c14A07167700486E
Arg [9] : _feeVault (address): 0x00700052c0608F670705380a4900e0a8080010CC
Arg [10] : _permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303
Arg [1] : 000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330
Arg [2] : 0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9
Arg [3] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000007ee1f7fa4c0b2edb0fdd5944c14a07167700486e
Arg [9] : 00000000000000000000000000700052c0608f670705380a4900e0a8080010cc
Arg [10] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Chain Token Portfolio % Price Amount Value
ETH36.11%$3.821$3.82
ETH20.68%$2.191$2.19
ETH1.72%$18.180.01$0.1818
ETH<0.01%$3,912.510.000000000000000001<$0.000001
GNO39.66%$4.21$4.2
GNO<0.01%$10.000000000000000001<$0.000001
OP1.83%$0.1937611$0.1937
OP<0.01%$3,911.770.000000000000000001<$0.000001
BASE<0.01%$3,913.440.000000000000000001<$0.000001
ARB<0.01%$3,911.960.000000000000000001<$0.000001
BSC<0.01%$733.40.000000000000000001<$0.000001
AVAX<0.01%$52.250.000000000000000001<$0.000001
FTM<0.01%$1.240.000000000000000001<$0.000001
POL<0.01%$0.7151630.000000000000000001<$0.000001
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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