More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 63,494 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Swap Exact Amoun... | 100278241 | 49 secs ago | IN | 31.12665699 FTM | 0.00143422 | ||||
Swap Exact Amoun... | 100276864 | 23 mins ago | IN | 12 FTM | 0.00112758 | ||||
Swap Exact Amoun... | 100274323 | 1 hr ago | IN | 0 FTM | 0.00092916 | ||||
Swap Exact Amoun... | 100270447 | 2 hrs ago | IN | 0 FTM | 0.00066639 | ||||
Swap Exact Amoun... | 100267049 | 2 hrs ago | IN | 0 FTM | 0.00177285 | ||||
Swap Exact Amoun... | 100266893 | 2 hrs ago | IN | 500 FTM | 0.00234011 | ||||
Swap Exact Amoun... | 100266450 | 3 hrs ago | IN | 0 FTM | 0.00086827 | ||||
Swap Exact Amoun... | 100263676 | 3 hrs ago | IN | 1.76298631 FTM | 0.00066639 | ||||
Swap Exact Amoun... | 100255025 | 6 hrs ago | IN | 0 FTM | 0.0054608 | ||||
Swap Exact Amoun... | 100254980 | 6 hrs ago | IN | 0 FTM | 0.0007565 | ||||
Swap Exact Amoun... | 100251418 | 7 hrs ago | IN | 0 FTM | 0.00295976 | ||||
Swap Exact Amoun... | 100250531 | 7 hrs ago | IN | 0 FTM | 0.00078647 | ||||
Swap Exact Amoun... | 100250396 | 7 hrs ago | IN | 0 FTM | 0.00176956 | ||||
Swap Exact Amoun... | 100250182 | 7 hrs ago | IN | 0 FTM | 0.00133492 | ||||
Swap Exact Amoun... | 100249001 | 7 hrs ago | IN | 0 FTM | 0.00245666 | ||||
Swap Exact Amoun... | 100246021 | 8 hrs ago | IN | 0 FTM | 0.00220657 | ||||
Swap Exact Amoun... | 100245381 | 9 hrs ago | IN | 0 FTM | 0.00446444 | ||||
Swap Exact Amoun... | 100244267 | 9 hrs ago | IN | 0 FTM | 0.00405246 | ||||
Swap Exact Amoun... | 100244028 | 9 hrs ago | IN | 0 FTM | 0.00106153 | ||||
Swap Exact Amoun... | 100242634 | 9 hrs ago | IN | 20,000 FTM | 0.00072328 | ||||
Swap Exact Amoun... | 100241623 | 10 hrs ago | IN | 0 FTM | 0.00328749 | ||||
Swap Exact Amoun... | 100240040 | 10 hrs ago | IN | 0 FTM | 0.00493 | ||||
Swap Exact Amoun... | 100239487 | 10 hrs ago | IN | 0 FTM | 0.0018417 | ||||
Swap Exact Amoun... | 100239185 | 10 hrs ago | IN | 0 FTM | 0.00903856 | ||||
Swap Exact Amoun... | 100235946 | 11 hrs ago | IN | 0 FTM | 0.00348188 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
100278241 | 49 secs ago | 31.12665699 FTM | ||||
100276864 | 23 mins ago | 12 FTM | ||||
100270447 | 2 hrs ago | 49.03644518 FTM | ||||
100270447 | 2 hrs ago | 49.03644518 FTM | ||||
100267323 | 2 hrs ago | 18 FTM | ||||
100267323 | 2 hrs ago | 18 FTM | ||||
100266893 | 2 hrs ago | 500 FTM | ||||
100266450 | 3 hrs ago | 0.27086848 FTM | ||||
100266450 | 3 hrs ago | 0.27086848 FTM | ||||
100263676 | 3 hrs ago | 1.76298631 FTM | ||||
100255025 | 6 hrs ago | 4,139.21628747 FTM | ||||
100255025 | 6 hrs ago | 10.58676181 FTM | ||||
100255025 | 6 hrs ago | 1.86825208 FTM | ||||
100255025 | 6 hrs ago | 4,151.67130138 FTM | ||||
100254980 | 6 hrs ago | 157.65169852 FTM | ||||
100254980 | 6 hrs ago | 157.65169852 FTM | ||||
100251418 | 7 hrs ago | 28.34332384 FTM | ||||
100251418 | 7 hrs ago | 0.07249295 FTM | ||||
100251418 | 7 hrs ago | 0.01279287 FTM | ||||
100251418 | 7 hrs ago | 28.42860967 FTM | ||||
100250182 | 7 hrs ago | 264.59850874 FTM | ||||
100250182 | 7 hrs ago | 0.67675646 FTM | ||||
100250182 | 7 hrs ago | 0.11942761 FTM | ||||
100250182 | 7 hrs ago | 265.39469282 FTM | ||||
100244028 | 9 hrs ago | 32.753591 FTM |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
AugustusV6
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 50000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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) } } } }
// 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 { } }
// 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) { } }
// 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); } }
// 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); }
// 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 ); } }
// 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 } } } } } }
// 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 } } } } } }
// 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 ); } }
// 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 ); } }
// 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 ); } }
// 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 ); } }
// 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 ); } } }
// 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); } }
// 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 ); } }
// 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 ); } }
// 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); } } }
// 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) } } } } }
// 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 } } } }
// 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 } //---------------------------------// } } }
// 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()); } }
// 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); } }
// 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) } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.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); }
// 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 } } } }
// 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; }
// 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); }
// 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(); } _; } }
// 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); }
// 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); }
// 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); }
// 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) } } }
// 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); }
// 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); }
// 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); }
// 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; }
// 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); }
// 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; }
// 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 } } } }
// 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); }
// 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); }
// 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); }
// 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; }
// 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; }
// 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; }
// 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(); }
{ "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": 50000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
3462000a0d5762006f1b388190036101a0601f8201601f19168101906001600160401b03821190821017620009cb576101609282916040526101a0391262000a0d576200004e6101a062000a52565b6200005b6101c062000a52565b90620000696101e062000a52565b906200007761020062000a52565b610220516102405161026051610280519294919390916200009a6102a062000a52565b96620000a86102c062000a52565b620000b56102e062000a52565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039586166001600160a01b03198216811790925591949091167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a36200012762000a32565b9960018b5260005b60208110620009e157506200014362000a32565b60018152602036818301376307e4c70760e21b620001618262000a67565b526200016c62000a12565b6001600160a01b039092168252600060208301526040820152620001908b62000a67565b526200019c8a62000a67565b50604051986001600160401b0360208b01908111908b1117620009cb5794898989898e9460208501604052600085526000995b86518b1015620006d8576020620001e78c8962000a8b565b5101516003811015620006c257806200039357506001600160a01b036200020f8c8962000a8b565b515116996040620002218d8a62000a8b565b51015199620002338b51151562000ae2565b620002408c151562000b43565b6001600160a01b038c16600090815260008051602062006efb83398151915260205260409020546001600160601b0316998a1562000382575b60009a5b8c518c101562000358576001600160e01b03196200029c8d8f62000a8b565b5116600081815260008051602062006ebb83398151915260205260409020546001600160a01b0316620002ed57818f620002de90600194620002e49462001051565b62000ba5565b9b019a6200027d565b60405162461bcd60e51b815260206004820152603560248201527f4c69624469616d6f6e644375743a2043616e2774206164642066756e6374696f60448201527f6e207468617420616c72656164792065786973747300000000000000000000006064820152608490fd5b5094995094995094995094996001909b91969b5b0199949a95909a989398979297969196620001cf565b6200038d8d62000f73565b62000279565b6001819b939597999b9a929496989a146000146200054b5750918a97959391620003de9c9a99979593604060018060a01b03620003d18c8c62000a8b565b5151169e8f9b8b62000a8b565b5101519a620003f08c51151562000ae2565b620003fd8b151562000b43565b6001600160a01b038b16600090815260008051602062006efb83398151915260205260409020546001600160601b03169a8b1562000539575b5060009a5b8c518c101562000521578f908d6200045c8e63ffffffff60e01b9262000a8b565b5116600081815260008051602062006ebb83398151915260205260409020546001600160a01b03169190838314620004b6576001938282620004a7620002de94620004ad9762000c21565b62001051565b9b019a6200043b565b60405162461bcd60e51b815260206004820152603860248201527f4c69624469616d6f6e644375743a2043616e2774207265706c6163652066756e60448201527f6374696f6e20776974682073616d652066756e6374696f6e00000000000000006064820152608490fd5b50949950949950949991969b5094996001906200036c565b620005449062000f73565b8e62000436565b6002036200066d576001600160a01b03620005678c8b62000a8b565b515116996040620005798d8c62000a8b565b5101519a6200058b8c51151562000ae2565b620006025760005b8b51811015620005ec5780620005e58d620005b960019463ffffffff60e01b9262000a8b565b51168060005260008051602062006ebb833981519152602052838060a01b036040600020541662000c21565b0162000593565b509295989b9194979a60019194979a506200036c565b60405162461bcd60e51b815260206004820152603660248201527f4c69624469616d6f6e644375743a2052656d6f7665206661636574206164647260448201527f657373206d7573742062652061646472657373283029000000000000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152602760248201527f4c69624469616d6f6e644375743a20496e636f727265637420466163657443756044820152663a20b1ba34b7b760c91b6064820152608490fd5b634e487b7160e01b600052602160045260246000fd5b8989898e898b60405191606083016060845282518091526080840190602060808260051b8701019401916000905b828210620009305750505050916200075081927f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb6739460006020850152838203604085015262000aa0565b0390a16001600160a01b0390811660805290811660a0521660c05260e05261010052610140526101205261018091825261016052604051615d9591826200112683396080518281816105c601528181612ba001528181612d1b01528181612e010152818161302201526130b5015260a051828181610d880152818161598e0152615c17015260c051828181610371015281816104d101528181610bb301528181610ca101528181610e8301528181610f2b01528181611054015281816112e0015281816118050152818161190d01528181611b6f01528181611c45015281816122e001528181612430015281816125ca01528181612c1b01528181612e7a01528181612f4701528181613138015281816137b4015261537d015260e05182818161080101528181611eb601528181613468015261565301526101005182818161136c015281816114d60152818161161b015261502c01526101205182818161131c01528181614c240152818161552101526155b60152610140518281816107b101528181614c03015281816154e8015261558301526101605182818161129001528181611daa01528181613cb501528181614801015261488601525181818161075a01528181611d6d01528181613c7d015281816147c2015261484e0152f35b868603607f19018152835180516001600160a01b0316875260208101519496939492939192906003821015620006c25760409160208401520151906060604082015260206080835192836060820152019201906000905b808210620009a75750505060208060019297019201920190929162000706565b82516001600160e01b03191684526020938401939092019160019091019062000987565b634e487b7160e01b600052604160045260246000fd5b808c60208093620009f162000a12565b926000845260008385015260606040850152010152016200012f565b600080fd5b60405190606082016001600160401b03811183821017620009cb57604052565b60408051919082016001600160401b03811183821017620009cb57604052565b51906001600160a01b038216820362000a0d57565b80511562000a755760200190565b634e487b7160e01b600052603260045260246000fd5b805182101562000a755760209160051b010190565b919082519283825260005b84811062000acd575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000aab565b1562000aea57565b60405162461bcd60e51b815260206004820152602b60248201527f4c69624469616d6f6e644375743a204e6f2073656c6563746f727320696e206660448201526a1858d95d081d1bc818dd5d60aa1b6064820152608490fd5b1562000b4b57565b60405162461bcd60e51b815260206004820152602c60248201527f4c69624469616d6f6e644375743a204164642066616365742063616e2774206260448201526b65206164647265737328302960a01b6064820152608490fd5b6001600160601b0390811690811462000bbe5760010190565b634e487b7160e01b600052601160045260246000fd5b919091805483101562000a7557600052601c60206000208360031c019260021b1690565b60008051602062006edb833981519152805482101562000a755760005260206000200190600090565b9091906001600160a01b03908116801562000f085730811462000eac5763ffffffff60e01b80941660009281845260008051602062006ebb833981519152926020918483526040948587205460a01c9083885260008051602062006efb8339815191529586865287892054926000199b8c850194851162000e9857908991888c898c8980870362000e06575b50509052505050878752508789208054801562000df2578c019062000cd3828262000bd4565b63ffffffff82549160031b1b19169055558852845286868120551562000cfe575b5050505050509050565b60008051602062006edb833981519152805489810190811162000dde57838852858552826001888a2001549180830362000d88575b505050805498891562000d74576001979899019162000d528362000bf8565b909182549160031b1b1916905555855252822001558038808080808062000cf4565b634e487b7160e01b88526031600452602488fd5b62000d939062000bf8565b90549060031b1c1662000dc98162000dab8462000bf8565b90919060018060a01b038084549260031b9316831b921b1916179055565b88528585526001878920015538828162000d33565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b8b52603160045260248bfd5b62000e8a978462000e419362000e2a8a948762000e5e9952828a5284842062000bd4565b90549060031b1c60e01b9788968352522062000bd4565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b168b52838852898b2080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b873880888c898c8962000cad565b634e487b7160e01b8b52601160045260248bfd5b60405162461bcd60e51b815260206004820152602e60248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f766520696d6d7560448201526d3a30b1363290333ab731ba34b7b760911b6064820152608490fd5b60405162461bcd60e51b815260206004820152603760248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f76652066756e6360448201527f74696f6e207468617420646f65736e27742065786973740000000000000000006064820152608490fd5b62000f7d62000a12565b602481527f4c69624469616d6f6e644375743a204e657720666163657420686173206e6f20602082015263636f646560e01b6040820152813b1562001027575060008051602062006edb83398151915280546001600160a01b038316600090815260008051602062006efb83398151915260205260409020600101819055919068010000000000000000831015620009cb578262000dab916001620010259501905562000bf8565b565b60405162461bcd60e51b8152602060048201529081906200104d90602483019062000aa0565b0390fd5b6001600160e01b03198116600081815260008051602062006ebb83398151915260208190526040822080546001600160a01b031660a09690961b6001600160a01b031916959095179094559194939092906001600160a01b031680835260008051602062006efb833981519152602052604083208054919491906801000000000000000082101562001111579662000e418260409798996001620010f89501815562000bd4565b82526020522080546001600160a01b0319169091179055565b634e487b7160e01b85526041600452602485fdfe6080604052600436101561001d575b366132395761001b613230565b005b60003560e01c80631a01c5321461023857806342f3b24114610233578063523819fb1461022e57806355c0bff0146102295780635c8b5f44146102245780635c975abb1461021f5780635e94e28d1461021a57806366c31b841461021557806367d81740146102105780636a6f511a1461020b5780636afdd850146102065780637f45767514610201578063838bf44d146101fc578063876a02f6146101f75780638940192a146101f257806390a0c0ea146101cf5780639facd044146101ed578063a76f4eb6146101e8578063aad8a491146101e3578063ad5c4648146101de578063b613cc8a146101d9578063bc163846146101d4578063c8e416dd146101cf578063d6ed22e6146101ca578063d85ca173146101c5578063da35bb0d146101c0578063e37ed256146101bb578063e3ead59e146101b6578063e65dc2f2146101b1578063e8bb3b6c146101ac578063ed386afa146101a7578063f25f4b56146101a2578063fa461e331461019d5763fe12941f0361000e57612067565b611d21565b611ced565b611cd1565b611aa3565b611a6f565b611968565b6116f8565b611670565b611545565b6113f8565b610dac565b61133f565b611304565b6112b3565b611278565b610de6565b610dc9565b610d5b565b610aac565b610a8f565b6108bc565b6107d4565b610799565b61077d565b610742565b6106cf565b610658565b610608565b610599565b61057d565b610560565b610270565b9181601f8401121561026b5782359167ffffffffffffffff831161026b576020838186019501011161026b57565b600080fd5b6003193601610160811261026b576101201361026b576101443567ffffffffffffffff811161026b576102a86004913690830161023d565b9060ff60025460a01c1661052c576102be61209b565b926102c76120aa565b906084359360a435946102d86120d8565b9483359087156105035773ffffffffffffffffffffffffffffffffffffffff98898816156104fb575b916002949391610369938b8316809260018560a01c169060038660a11c169861032a88866132ed565b6104c057876101018210156104ae57508061049d575b505061034e86303386613570565b505b61048d575b50505b8460036024359360a31c1691613614565b0361047d57847f0000000000000000000000000000000000000000000000000000000000000000166103a361039e30836137e4565b612121565b90803b1561026b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815283810192835291600091839182908490829060200103925af180156104785761045f575b5047935b84106104375761043361041660c435866101243586888b166138d3565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c6104729261218f565b80610555565b386103f5565b6121c4565b61048730836137e4565b936103f9565b6104969161335b565b3881610355565b6104a791856134ba565b3880610340565b90916104bb92309161341e565b610350565b505091505015610358576104f6818c7f00000000000000000000000000000000000000000000000000000000000000001661335b565b610358565b339750610301565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b600091031261026b57565b3461026b57600060031936011261026b5760206040516121348152f35b3461026b57600060031936011261026b57602060405160648152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026b57565b3461026b57602060031936011261026b5773ffffffffffffffffffffffffffffffffffffffff60043561063a816105ea565b166000526000602052602060ff604060002054166040519015158152f35b3461026b57600060031936011261026b57602060ff60025460a01c166040519015158152f35b6003199160608383011261026b5760043567ffffffffffffffff9384821161026b5761010090828503011261026b57600401926024359260443591821161026b576106cb9160040161023d565b9091565b6106d83661067e565b9060ff60029493945460a01c1661071857610433936106f69361224e565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405160c88152f35b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3590610830826105ea565b565b9061016060031983011261026b5760043561084c816105ea565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026b57602491610104359167ffffffffffffffff916101243583811161026b57826108a19160040161023d565b939093926101443591821161026b576106cb9160040161023d565b6108c536610832565b909192959360ff60025460a01c16610718576108e3602087016120ce565b6108ec876120ce565b60408801359360608901359561090460c08b016120ce565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610a65578c8b1615610a5d575b8815610a335761093f30866137e4565b9961094a89876132ed565b610a185792826109829592858b80966101018f9910600014610a08575050806109f7575b505061097c8482338a613570565b506145a9565b6109aa61098f30846137e4565b9661099a30846137e4565b9060018111156109ee5790612153565b9486106109c4576104339860806106f69901359716614215565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b50600090612153565b610a0191896134ba565b388061096e565b9091610a139361341e565b6145a9565b505097610982928892610a2d89933490612153565b9a6145a9565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a5061092f565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b5760206040516127108152f35b610ab53661067e565b60ff60025460a01c1661071857610acb846120ce565b92610ad8602086016120ce565b60409485870135606088013594610af160c08a016120ce565b96610aff60e08b018b6121d0565b8895919515610d325773ffffffffffffffffffffffffffffffffffffffff95868b1615610d2a575b3392610b3387826132ed565b610c9e57610b5496610b4f916101018810610c7e575b50613c1f565b6145e1565b938410610c555773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610bac575b90610b9094939291608061043398013594166148cd565b9251918252602082015260408101919091529081906060820190565b93929190847f00000000000000000000000000000000000000000000000000000000000000001694853b1561026b576000875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610c198a600483019190602083019252565b03925af180156104785761043398610b9097608092610c42575b50985050909192939450610b79565b8061046c610c4f9261218f565b38610c33565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610c8d575b5050610b49565b610c96926134ba565b388787610c86565b507f000000000000000000000000000000000000000000000000000000000000000087169250823b1561026b576000869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561047857610b5496610d17575b50610b4f3093613c1f565b8061046c610d249261218f565b38610d0c565b339a50610b27565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026b57600060031936011261026b5760206040516113888152f35b3461026b57600060031936011261026b5760206040516109c48152f35b610def3661067e565b919260ff60025460a01c1661071857600092610e0a826120ce565b94610e17602084016120ce565b60409586850135936060860135610e3060c088016120ce565b99610e3e60e08901896121d0565b939091831561124f5773ffffffffffffffffffffffffffffffffffffffff9493929190858e1615611247575b610e748a826132ed565b1580159c906111ee57505050837f000000000000000000000000000000000000000000000000000000000000000016610ead30826137e4565b95813b156111eb578c51907fd0e30db0000000000000000000000000000000000000000000000000000000008252816004818d865af18015610478576111d8575b50905b84871693858316918583146111af5790610f0c918486614bfd565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361119e57847f00000000000000000000000000000000000000000000000000000000000000001680911461117557610f5c30826137e4565b90838211611165575b803b1561026b578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092526000908290602490829084905af1801561047857611152575b5047995b610fc030836137e4565b928b1061112957156110fc5750610fe4904794600181116000146109ee5790612153565b60018111611045575b50916110219795939160806110166104339c999795934790600181116000146109ee5790612153565b965b01359716614215565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161107a817f0000000000000000000000000000000000000000000000000000000000000000169b612121565b9a803b1561026b578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c526000908c90602490829084905af19a8b15610478576110166110219a6080926104339e6110e9575b50939597999c50505091939597610fed565b8061046c6110f69261218f565b386110d7565b91509160806111236110219a9896946104339d9a9896600181116000146109ee5790612153565b96611018565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c61115f9261218f565b38610fb2565b9061116f906126fc565b90610f65565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506111a930876137e4565b99610fb6565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b8061046c6111e59261218f565b38610eee565b80fd5b90929196506111fd30846137e4565b968a610101821015611235575080611224575b505061121e89303385613570565b50610ef1565b61122e91846134ba565b3880611210565b909161124292309161341e565b610ef1565b339d50610e6a565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b60031960a091011261026b57600490565b6003198101610100811261026b5760a01361026b5760049160a4359167ffffffffffffffff9160c43583811161026b57826113de9160040161023d565b9390939260e43591821161026b576106cb9160040161023d565b611401366113a1565b949360ff60025460a01c1661071857843590602086013592611427816080890135614ee5565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610a65578915610a33578d161561153d575b61146730866137e4565b9961147289876132ed565b611524576114a9949392919089610101821015611512575080611501575b505061149e88303388613570565b505b6114d05761501c565b6114b661098f30846137e4565b9486106109c4576104339860406106f69901359716614215565b6114fc8c7f0000000000000000000000000000000000000000000000000000000000000000168561335b565b61501c565b61150b91876134ba565b3880611490565b909161151f92309161341e565b6114a0565b50505096906115376114a9923490612153565b9761501c565b339a5061145d565b61154e366113a1565b90949360ff60025460a01c1661071857843592602086013592611575886080890135614ee5565b909a9291948b988815610a335773ffffffffffffffffffffffffffffffffffffffff809d1615611668575b6115c2969798999a6115b282866132ed565b156115e6575b505050505061501c565b6115cc30826137e4565b9283106109c45761043395604061041696013594166138d3565b610101831015611658578261160493611647575b5050303385613570565b505b611614575b808080806115b8565b611641908a7f0000000000000000000000000000000000000000000000000000000000000000169061335b565b3861160b565b61165191866134ba565b38806115fa565b61166392309161341e565b611606565b3399506115a0565b60e060031936011261026b5761168536611390565b67ffffffffffffffff60a43581811161026b573660238201121561026b57806004013582811161026b573660248260051b8401011161026b5760c43592831161026b576116e6936116dc602494369060040161023d565b9490930190612727565b60408051928352602083019190915290f35b60031936016101a0811261026b576101601361026b576101843567ffffffffffffffff811161026b576117306004913690830161023d565b60ff60025460a01c1661052c576117456120b6565b9261174e6120c2565b9060c4359260e4359461175f6120e5565b946117686120aa565b918435881561193f5773ffffffffffffffffffffffffffffffffffffffff998a891615611937575b918160029695938c6117fd969416809360018460a01c169060038560a11c16996117ba88866132ed565b6118fc57876101018210156118ea5750806118d9575b50506117de86303386613570565b505b6118c9575b50505b604435918660036024359360a31c16916151c2565b036118b957847f00000000000000000000000000000000000000000000000000000000000000001661183261039e30836137e4565b90803b1561026b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815283810192835291600091839182908490829060200103925af18015610478576118a6575b5047935b84106104375761043361041661010435866101643586888b166138d3565b8061046c6118b39261218f565b38611884565b6118c330836137e4565b93611888565b6118d29161335b565b38826117e5565b6118e391856134ba565b38806117d0565b90916118f792309161341e565b6117e0565b5050915050156117e857611932828d7f00000000000000000000000000000000000000000000000000000000000000001661335b565b6117e8565b339850611790565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b61197136610832565b959260ff60029593955460a01c166107185761198f602087016120ce565b91611999876120ce565b976040880135916060890135966119b260c08b016120ce565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611a67575b8915610a335782816119f9986119e98980956132ed565b15611a1d575b50505050506153ad565b611a0330826137e4565b9283106109c45761043395608061041696013594166138d3565b610101851015611a575784611a3a95611a46575b50503390613570565b505b38848282806119ef565b611a5091836134ba565b3880611a31565b611a6294915061341e565b611a3c565b339a506119d2565b3461026b57600060031936011261026b57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611aac3661067e565b92919060ff60025460a01c1661071857600091611ac8846120ce565b93611ad5602082016120ce565b906040948582013597606083013595611af060c085016120ce565b98611afe60e08601866121d0565b9033928a1561124f578c9d73ffffffffffffffffffffffffffffffffffffffff9d999a9b9c9d809a1615611cc9575b611b3781836132ed565b611c3f57611b4f97506101018710611c2e575b6153d4565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611c1e57817f000000000000000000000000000000000000000000000000000000000000000016611b9c61039e30836137e4565b90803b1561026b5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092526000908290602490829084905af1801561047857611c0b575b5047945b8510610c5557916104339693916080610b909694013594166138d3565b8061046c611c189261218f565b38611bea565b611c2830846137e4565b94611bee565b8615611b4a57611b4a8787846134ba565b935050877f000000000000000000000000000000000000000000000000000000000000000016803b15611cc5578c51967fd0e30db00000000000000000000000000000000000000000000000000000000088528760048187855af196871561047857611b4f97611cb2575b5030936153d4565b8061046c611cbf9261218f565b38611caa565b8680fd5b339e50611b2d565b3461026b57600060031936011261026b576020604051600b8152f35b3461026b57600060031936011261026b57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026b57606060031936011261026b576004602435813560443567ffffffffffffffff811161026b57611d58903690850161023d565b91909260ff60025460a01c1661203e576040517f00000000000000000000000000000000000000000000000000000000000000008152610200841460158201605f6041880160168501376060812090527f000000000000000000000000000000000000000000000000000000000000000060358301526055822080925273ffffffffffffffffffffffffffffffffffffffff80921633186120155760a085118061200d575b15611e37575061001b9550506000821315611e275750611e1c90612221565b915b60a43592613c58565b611e319150612221565b91611e1e565b93509390600091829360405196848213611ffb575b505060008213611feb575b505060a43592308414600114611fa05715611f0657507f30f28b7a0000000000000000000000000000000000000000000000000000000083526000928392610164926101606101248885013733608484015260a483015260c4820152827f00000000000000000000000000000000000000000000000000000000000000005af115611ede57005b7f6b836e6b000000000000000000000000000000000000000000000000000000006000526000fd5b92806000926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081611f7d575b5015611f5557005b7f1bbb4abe000000000000000000000000000000000000000000000000000000006000526000fd5b90503d15611f985750600160005114601f3d11165b38611f4d565b3b1515611f92565b509260209250806000927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af19081611f7d575015611f5557005b9092506060915001353880611e57565b90945060408201351692503880611e4c565b508015611dfd565b867f48f5c3ed000000000000000000000000000000000000000000000000000000006000526000fd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026b57602060031936011261026b5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356120a7816105ea565b90565b6064356120a7816105ea565b6084356120a7816105ea565b60a4356120a7816105ea565b356120a7816105ea565b610104356120a7816105ea565b610144356120a7816105ea565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161214e57565b6120f2565b9190820391821161214e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116121a357604052565b612160565b6060810190811067ffffffffffffffff8211176121a357604052565b6040513d6000823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026b570180359067ffffffffffffffff821161026b5760200191813603831361026b57565b7f8000000000000000000000000000000000000000000000000000000000000000811461214e5760000390565b91612258836120ce565b91612265602085016120ce565b9061227260c086016120ce565b9161228060e08701876121d0565b97606088013515610a335773ffffffffffffffffffffffffffffffffffffffff8516156126f4575b336122b760408a0135896132ed565b15159760009789600014612688575050505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169461230b30876137e4565b98863b1561026b57604060008a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af1801561047857612675575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610a655787826123ab926123a66123a160608f0135613c1f565b612221565b613c58565b8099919360608c013582106109c45773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146125b3575b73ffffffffffffffffffffffffffffffffffffffff16300361255c5750501561252957505050479361245b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169861099a308b6137e4565b946001861161249a575b5061249297505b73ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614215565b929391929091565b90946124a590612121565b97803b1561026b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099526000908990602490829084905af1908115610478576124929861250f92612516575b50479060018111156109ee5790612153565b9338612465565b8061046c6125239261218f565b386124fd565b612492999296506060101561254d57506125479061099a30876137e4565b9361246c565b61254791506040880135612153565b9399509750506124929950606010612596575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416613e6f565b6125ac9196506125a633886137e4565b90612153565b943861256f565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168073ffffffffffffffffffffffffffffffffffffffff891614610a6557803b1561026b57600060405180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161264d8a600483019190602083019252565b03925af1801561047857612662575b506123ee565b8061046c61266f9261218f565b3861265c565b8061046c6126829261218f565b38612353565b61269a9b9298939b94919430866137e4565b9b6101018110156126d957806126c8575b505060608211156123585791506126c233846137e4565b91612358565b6126d291866134ba565b38806126ab565b906126ed92995060408c013591309161341e565b3096612358565b3394506122a8565b801561214e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c16610718576106cb94612a9e565b3560ff8116810361026b5790565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90156127b7578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026b570190565b61274f565b91908110156127b75760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026b570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026b57016020813591019167ffffffffffffffff821161026b57813603831361026b57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481946000925b8484106128e8575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026b5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026b5784612a6e612a478695612a048f6129dd8e612a8d998b60019e01526129b6604061299a818c01610825565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b6129c1818a01610825565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b6129e8818801610825565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612a1460a06129e8818801610825565b60c0808601359085015260e080860135908501526101009080612a39838801886127fc565b92909387015285019161284c565b6101208085013590840152610140612a61818601866127fc565b918584039086015261284c565b91612a7f61016091828101906127fc565b92909181850391015261284c565b9901930194019291959493906128b5565b9493929192612aaf608087016120ce565b91863594602088013594612ac66040809a01612741565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613228575b881561124f5784156131ff5760005b8581106131e3575050612b32612b196060612b13878961277e565b016120ce565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612b44612b198d612b13888a61277e565b93600381169360018514928361312c57346131035760019291908d6101018210156130f15750806130e0575b5050612b7e8c303387613570565b505b818160021c166130af575b60031c16612deb575050600203612d055750827f00000000000000000000000000000000000000000000000000000000000000001691823b1561026b57612c089287600080948c51968795869485937f1c64b82000000000000000000000000000000000000000000000000000000000855230926004860161288b565b03925af1801561047857612cf2575b50807f00000000000000000000000000000000000000000000000000000000000000001692612c4630856137e4565b908110612cc957612c56906126fc565b95833b1561026b57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052926000908490602490829084905af191821561047857612cb1938793612cb6575b50166150ae565b509190565b8061046c612cc39261218f565b38612caa565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c612cff9261218f565b38612c17565b919083859996991691612d1883856137e4565b947f000000000000000000000000000000000000000000000000000000000000000016803b1561026b57600092838a93612d808b519a8b96879586947f1c64b8200000000000000000000000000000000000000000000000000000000086526004860161288b565b03925af191821561047857612da394612d9e93612dd8575b506137e4565b612153565b938410612daf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c612de59261218f565b38612d98565b97939250999795939060021460001461301b57847f000000000000000000000000000000000000000000000000000000000000000016803b1561026b57612e6793600080948b51968795869485937f01fb36ba00000000000000000000000000000000000000000000000000000000855230926004860161288b565b03925af1801561047857613008575b50817f00000000000000000000000000000000000000000000000000000000000000001690612ead612ea830846137e4565b6126fc565b91803b1561026b576000875180927f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381612ef389600483019190602083019252565b03925af1801561047857612f109284928692612cb65750166150ae565b50955b612f1d30826137e4565b9360018511612f38575b505050612f349250612153565b9190565b93949315612fe75750612f6d907f000000000000000000000000000000000000000000000000000000000000000016936126fc565b91833b1561026b57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052926000908490602490829084905af192831561047857612f3493612fd4575b50612fca82336150ae565b505b388080612f27565b8061046c612fe19261218f565b38612fbf565b925050613002612ff9612f34946126fc565b809333906150ef565b50612fcc565b8061046c6130159261218f565b38612e76565b84999392997f000000000000000000000000000000000000000000000000000000000000000016803b1561026b57600092838c936130878c51978896879586947f01fb36ba0000000000000000000000000000000000000000000000000000000086526004860161288b565b03925af180156104785761309c575b50612f13565b8061046c6130a99261218f565b38613096565b6130db897f0000000000000000000000000000000000000000000000000000000000000000168561335b565b612b8b565b6130ea91866134ba565b3880612b70565b90916130fe92309161341e565b612b80565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036131ba57877f00000000000000000000000000000000000000000000000000000000000000001690813b1561026b5760008c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610478576001926131a7575b50612b80565b8061046c6131b49261218f565b386131a1565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806131f96131f38493898b6127bc565b3561505e565b01612af8565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612ae9565b333b1561026b57565b60007fffffffff0000000000000000000000000000000000000000000000000000000081351681527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60408220541680156132c357818091368280378136915af43d82803e156132bf573d90f35b3d90fd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b919060009273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee91828214613349575b501861331857565b3461331f57565b7f8b6ebb4d0000000000000000000000000000000000000000000000000000000060005260046000fd5b909350341861331f5760019238613310565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff806034526f095ea7b30000000000000000000000009060009283918383526020836044601082865af13d1560018551141716156133c1575b50505050603452565b826044926010926020968360345283528238868683865af1506034525af13d1560018351141716156133f657388181806133b8565b807f8164f8420000000000000000000000000000000000000000000000000000000060049252fd5b90600490600094859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f00000000000000000000000000000000000000000000000000000000000000005af11561349057565b7f6b836e6b0000000000000000000000000000000000000000000000000000000060005260046000fd5b918060e01461353357610100146134f5577fb78cb0dd0000000000000000000000000000000000000000000000000000000060005260046000fd5b61010460009182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e46000918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd0000000000000000000000000000000000000000000000000000000084526004840152602483015260448201526020600060648382875af192836135ee575b5082156135c65750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d1561360b5750600160005114601f3d1116915b386135bc565b3b151591613605565b9192906040519360018214613773575b60011461370d576003146001146136a257926fffffffffffffffffffffffffffffffff6084947f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015260009283928380935af11561369a5750565b3d81803e3d90fd5b916084916000946fffffffffffffffffffffffffffffffff86957f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af1610830575b3d6000803e3d6000fd5b50926fffffffffffffffffffffffffffffffff6084947fa6417ed60000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015260009283928380935af11561369a5750565b7fd0e30db00000000000000000000000000000000000000000000000000000000085526000806004873473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1613624573d6000803e3d6000fd5b60009291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613849576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613844575b50565b519150565b31925050565b90600b820180921161214e57565b9190820180921161214e57565b908160640291606483040361214e57565b906113889182810292818404149015171561214e57565b906109c49182810292818404149015171561214e57565b906121349182810292818404149015171561214e57565b8181029291811591840414171561214e57565b90919493926138f5600096906bffffffffffffffffffffffff8260601c921690565b94906139008261384f565b8311613bd9575b6139118884612153565b9573ffffffffffffffffffffffffffffffffffffffff82166139d6575b5050861590506139ba5761396c6139658473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b6139ba57508482613985856139ad946139b39796615705565b6139a7612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b906150ef565b50612121565b9190600090565b92506139cd939450829061039e92615705565b90600090600090565b6b2000000000000000000000008199949395979699161594851594613a1e6139658c73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9680613bd2575b613bb9576b0800000000000000000000008316151597613a4484615732565b938415613ac5575050948a94613ab094613a92613a8a613ab89b9686613aa89f9e9c979b613a758161039e9e61385d565b871115613abd57613a859161385d565b6138c0565b612710900490565b90613a9f613a8a836138a9565b9c8d8093612153565b9d8e93615871565b928391615705565b929190565b5050846138c0565b80929c99969794506b400000000000000000000000919a98959350161515600014613b34575089613afd57505050505b38808061392e565b8698975091613ab89693918a61039e96613ab095613b2c613b24613a8a9f613a8a9061387b565b9e8f94613892565b9c8d92615871565b939291906b8000000000000000000000008516613b56575b5050505050613af5565b8a15613b4c579092948a92949998506b040000000000000000000000613b84613a8a613b8d9d9a999a61387b565b9b8c8095612153565b9a16613bac575b9261039e95928a889693613ab89a99613ab097615871565b6000995060019550613b94565b5050505093905061039e92508391506139cd9495615705565b5086613a25565b9650613be58183612153565b966b1000000000000000000000008616156139075796613c07613a8a8361386a565b9081811115613c1857505b96613907565b9050613c12565b7f8000000000000000000000000000000000000000000000000000000000000000811015613c4a5790565b6335278d126000526004601cfd5b9391929080948460009260608206613e18575b505060006040949596855197889586947f0000000000000000000000000000000000000000000000000000000000000000865260158601605f6001860160168901376060812090527f0000000000000000000000000000000000000000000000000000000000000000603587015273ffffffffffffffffffffffffffffffffffffffff605587201696803091613e10575b50843560ff1c8614613d87577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613703576120a760208301519251925b600003615add565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613703576120a76020835193015192613d7f565b905038613cfc565b60a0821115613c6b5760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff16915060006040613c6b565b91909397959497969296600097613ea1613e898884612153565b91906bffffffffffffffffffffffff8260601c921690565b909286116141eb578a95613eb48961384f565b81116141a5575b73ffffffffffffffffffffffffffffffffffffffff8416613f7a575b5050505091613ee8918794936150ef565b5081613f01575b50613ef99161385d565b929190600090565b9050613f306139658273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b613f6f57613ef991613f68858093613f60612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b903390613570565b5091613eef565b509291506000908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591613fc66139658d73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b938061419e575b61417e576b0800000000000000000000008216151594613fec83615732565b9283156140525750509061400b91808810600014613a855750866138c0565b61271090049961401a8b6138a9565b612710900461402a81809d612153565b9c8d923361403798615ae8565b906140419161385d565b9261404b926150ef565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156140ce575061409857505050505091613ee8918794935b919394819338613ed7565b909192939998949695979a6140ac8161387b565b61271090049b6140bc8d92613892565b61271090049b8c913361403798615ae8565b946b800000000000000000000000869b9a999897929b166140fb575b50505050505090613ee8929161408d565b90919293949596979899614114578c99989796956140ea565b988c929394959b9a96996b04000000000000000000000061413b6141449f613a8a9061387b565b9e8f8096612153565b9d16614171575b928c61404b9a99989693614166969361416b99963390615ae8565b61385d565b936150ef565b60009c506001965061414b565b505050505050945091906141939395506150ef565b509190600090600090565b5083613fcd565b99506141b1888b612153565b996b100000000000000000000000821615613ebb57996141d3613a8a8c61386a565b90818111156141e457505b99613ebb565b90506141de565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315156000146145a05761423861423285612121565b87612153565b600098606081901c906bffffffffffffffffffffffff1690928881116141eb576142618361384f565b811161455a575b73ffffffffffffffffffffffffffffffffffffffff8416614337575b505050509061429561429d926126fc565b9788916150ef565b50841561431c576142d16139658373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61431c57613ef99291614314866125a69361430e82614308612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b866150ef565b50612153565b903390615d43565b61432d9394506125a6913390615d43565b9190600090600090565b6b200000000000000000000000821615918215916143786139658b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9380614553575b61452b576b080000000000000000000000821615159461439e83615732565b91826144db575050506b40000000000000000000000081161561443957508a6143d757505050509061429561429d925b91923880614284565b8a6143e98996989c9b9a99959b61387b565b61271090049b6143f98d92613892565b61271090049b61440a968d92615871565b3361441492615d43565b9161441e906126fc565b8097614429926150ef565b5061443391612153565b93929190565b9291936b8000000000000000000000008416614462575b50505050509061429561429d926143ce565b8b15614450578b919293999695976b040000000000000000000000614490613a8a6144999f9e9c989e61387b565b9d8e8095612153565b9b166144ce575b938a9b6144c09487946144339c9d6143149561430e9b9a6144c69a615871565b946126fc565b9889916150ef565b60009a50600194506144a0565b8b989a9d9c9b979e506144f9935080821060001461452457506138c0565b6127109004996145088b6138a9565b612710900461451881809d612153565b9c61440a968e93615871565b90506138c0565b505050505050939261432d9596506144c061430e9361454b923390615d43565b9687916150ef565b508361437f565b9950614566828b612153565b996b1000000000000000000000008216156142685799614588613a8a8c61386a565b908181111561459957505b99614268565b9050614593565b61423884614232565b91806000958695606493607c95608037608083015260a08201523360c0820152019134905af1156145d657565b3d6000607c3e3d607cfd5b9491939293600092839484936040519786975b60608604891061461157505050505050505050506120a790615add565b909192939495969798809b9a8915614848575b6060880460018c01106147bb575b8a6147b3575b8a60a0610164928960016101008211146147a5575b506060830288013560ff1c15614707579460608086946000946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146146fa575b5af1156137035760208a0151945b6001879660000399019796959a98999a9493929190946145f4565b89896101648501376146d1565b9460608086946000946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614798575b5af115613703578951946146df565b8989610164850137614789565b80919294019301908961464d565b309350614638565b97505096507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff891697614632565b985050507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f6001840160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff891691614624565b90919493926148ef600096906bffffffffffffffffffffffff8260601c921690565b94906148fa8261384f565b8311614b81575b61490b8884612153565b9573ffffffffffffffffffffffffffffffffffffffff82166149b8575b50508615905061499f5761495f6139658473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61499f575081614974848793614997956150ef565b506139a7612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b509190600090565b9394506149ae925083916150ef565b5090600090600090565b6b2000000000000000000000008199949395979699161594851594614a006139658c73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9680614b7a575b614b64576b0800000000000000000000008316151597614a2684615732565b938415614a8057505093614a6a99989793614a54613a8a614a7a9995858f9a9699613a7581614a729c61385d565b90614a61613a8a836138a9565b9b8c8093612153565b9c8d93615871565b9283916150ef565b50929190565b80929c99969794506b400000000000000000000000919a98959350161515600014614aec575089614ab857505050505b388080614928565b869897509089614a7a969795614a72959493614ae4614adc613a8a613a8a9f61387b565b9d8e94613892565b9b8c92615871565b939291906b8000000000000000000000008516614b0e575b5050505050614ab0565b8a15614b04579092948a92949998506b040000000000000000000000613b84613a8a614b3c9d9a999a61387b565b9a16614b57575b918987969492614a7a98614a729795615871565b6000995060019550614b43565b50505050949590508492506149ae9391506150ef565b5086614a07565b9650614b8d8183612153565b966b1000000000000000000000008616156149015796614baf613a8a8361386a565b9081811115614bc057505b96614901565b9050614bba565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000091604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16936000978585015260005b8360061c8110614ec1575050508060061c805b614dd157506000938496855b8360061c8710614cad57505050505050505050565b8015614d7e575b308460061c6001890110614d66575b60a46040926000928a6001810160051b8a01518b8b826020899560061b8d010135600116614d5d575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561370357600188960195614c98565b93925081614cec565b508486016001880160051b0160200151985088614cc3565b508385016020818101517fa9059cbb00000000000000000000000000000000000000000000000000000000928801808301938452602481018290526044908101859052909260009190828c5af150614cb4565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156137035760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c0101358516614eb6575b50906126f28161271093030292020204018098838301901b8501520180614c8c565b9092506126f2614e94565b80614edf838560019460061b8b0160208560051b8c8c010101614bc7565b01614c79565b9190918235807f52bbbe29000000000000000000000000000000000000000000000000000000001461500a577f945bcec90000000000000000000000000000000000000000000000000000000014614f61577f7352d91c0000000000000000000000000000000000000000000000000000000060005260046000fd5b604483013592838101936004850135946001836004013514600114614ff75760059590951b01016004013592602401355b8015614fdc575b8315614fc0575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350614fa0565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee614f99565b602401359460051b010160040135614f92565b50610144830135926101240135614f92565b90600080918060405194853783347f00000000000000000000000000000000000000000000000000000000000000005af1156150555750565b3d6000823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff168061507c5750565b331861508457565b7f02a43f8b0000000000000000000000000000000000000000000000000000000060005260046000fd5b9190916000808080958194612710f19182156150c75750565b807f90b8ec180000000000000000000000000000000000000000000000000000000060049252fd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee84146001146151ad576044602092600092604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af19182615187575b505b811561515d57565b7f90b8ec180000000000000000000000000000000000000000000000000000000060005260046000fd5b9091503d156151a45750600160005114601f3d1116905b38615153565b3b15159061519e565b6000809394508092918192612710f190615155565b9395949192906040519560019485831461533c575b806001146152ec57600214615298575060031460011461523e57946084957f5b41b908000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152606483015260009283928380935af11561369a5750565b9160a4939160009686947f394747c500000000000000000000000000000000000000000000000000000000899852600486015260248501526044840152806064840152608483015234905af1610830573d6000803e3d6000fd5b60a4979291507f64a145580000000000000000000000000000000000000000000000000000000087526004870152602486015260448501526064840152608483015260009283928380935af11561369a5750565b505050946084957f65b2489b000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152606483015260009283928380935af11561369a5750565b7fd0e30db000000000000000000000000000000000000000000000000000000000885260008060048a3473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af16151d7573d6000803e3d6000fd5b906044836000958695607c9460803760808201523360a0820152019134905af1156145d657565b9095929193956000958694879888965b8660061c88106153fb575050505050505050505050565b891561557d575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156137035783519084015190818a615574575b506126f2916127108385029102019202020496879060009061556c575b30918a60061c60018d01106154df575b60009360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156137035760018a9701966153e4565b92939d509b50507f00000000000000000000000000000000000000000000000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f000000000000000000000000000000000000000000000000000000000000000060358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d91909392615482565b905087615472565b91509038615455565b506040517f000000000000000000000000000000000000000000000000000000000000000081529850601589016028808a8337812090527f000000000000000000000000000000000000000000000000000000000000000060358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a16903083146001146156c55761010084111561567f577f30f28b7a0000000000000000000000000000000000000000000000000000000081526000806004928688858301378460848201528960a48201528560c482015283870190827f00000000000000000000000000000000000000000000000000000000000000005af115611ede5750615402565b60006064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615402565b60006044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615402565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6138419301916150ef565b613fff1660c881116157415790565b5060c890565b60405190610830826121a8565b60405190615761826121a8565b600282526040366020840137565b8051156127b75760200190565b8051600110156127b75760400190565b90815180825260208080930193019160005b8281106157ac575050505090565b83518552938101939281019260010161579e565b6020808252825160608284015280516080840181905293949360a0840193929182019060005b818110615847575050508473ffffffffffffffffffffffffffffffffffffffff6040926120a796970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08285030191015261578c565b825173ffffffffffffffffffffffffffffffffffffffff16865294830194918301916001016157e6565b96909591939294615882848761385d565b928361589357505050505050505090565b9697959681615ad5575b501561591357505050828281116158e9576120a794816158c0575b505050612153565b73ffffffffffffffffffffffffffffffffffffffff6158e09316906150ef565b503880806158b8565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b848296939792116158e9571561596e57856120a79661593b575b50816158c057505050612153565b61596790615961612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b836150ef565b503861592d565b615a5f9073ffffffffffffffffffffffffffffffffffffffff93929396877f000000000000000000000000000000000000000000000000000000000000000016946159ba8887856150ef565b506159f56159c6615754565b996159cf615754565b96166159da8b61576f565b9073ffffffffffffffffffffffffffffffffffffffff169052565b6159fe8561576f565b52615a2d615a24612b1960025473ffffffffffffffffffffffffffffffffffffffff1690565b6159da8a61577c565b615a368461577c565b52615a3f615747565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026b57615aa96000949185926040519687809481937f45f32b0b000000000000000000000000000000000000000000000000000000008352600483016157c0565b03925af1928315610478576120a793615ac25750612153565b8061046c615acf9261218f565b3861430e565b90503861589d565b60008112613c4a5790565b969790929594919394615afb838761385d565b9889615b0e575b50505050505050505090565b819994959697989991615d3b575b5015615b7057505050829483116158e95782615b46575b505050505b388080808080808080615b02565b73ffffffffffffffffffffffffffffffffffffffff615b66941691613570565b5038808080615b33565b90919687116158e95715615bf25780615bbe575b5082615b94575b50505050615b38565b73ffffffffffffffffffffffffffffffffffffffff615bb4941691613570565b5038808080615b8b565b615beb90615be4612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b8385613570565b5038615b84565b91615cc99193949273ffffffffffffffffffffffffffffffffffffffff95615c3f88887f000000000000000000000000000000000000000000000000000000000000000016809886613570565b50615c5f615c4b615754565b97615c54615754565b96166159da8961576f565b615c688561576f565b52615c97615c8e612b1960025473ffffffffffffffffffffffffffffffffffffffff1690565b6159da8861577c565b615ca08461577c565b52615ca9615747565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026b57615d136000929183926040519485809481937f45f32b0b000000000000000000000000000000000000000000000000000000008352600483016157c0565b03925af1801561047857615d28575b50615b38565b8061046c615d359261218f565b38615d22565b905038615b1c565b91909160018211615d5657505050600090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615d8492019283916150ef565b509056fea164736f6c6343000816000ac8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a29032633000000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8300000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab8934700000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Deployed Bytecode
0x6080604052600436101561001d575b366132395761001b613230565b005b60003560e01c80631a01c5321461023857806342f3b24114610233578063523819fb1461022e57806355c0bff0146102295780635c8b5f44146102245780635c975abb1461021f5780635e94e28d1461021a57806366c31b841461021557806367d81740146102105780636a6f511a1461020b5780636afdd850146102065780637f45767514610201578063838bf44d146101fc578063876a02f6146101f75780638940192a146101f257806390a0c0ea146101cf5780639facd044146101ed578063a76f4eb6146101e8578063aad8a491146101e3578063ad5c4648146101de578063b613cc8a146101d9578063bc163846146101d4578063c8e416dd146101cf578063d6ed22e6146101ca578063d85ca173146101c5578063da35bb0d146101c0578063e37ed256146101bb578063e3ead59e146101b6578063e65dc2f2146101b1578063e8bb3b6c146101ac578063ed386afa146101a7578063f25f4b56146101a2578063fa461e331461019d5763fe12941f0361000e57612067565b611d21565b611ced565b611cd1565b611aa3565b611a6f565b611968565b6116f8565b611670565b611545565b6113f8565b610dac565b61133f565b611304565b6112b3565b611278565b610de6565b610dc9565b610d5b565b610aac565b610a8f565b6108bc565b6107d4565b610799565b61077d565b610742565b6106cf565b610658565b610608565b610599565b61057d565b610560565b610270565b9181601f8401121561026b5782359167ffffffffffffffff831161026b576020838186019501011161026b57565b600080fd5b6003193601610160811261026b576101201361026b576101443567ffffffffffffffff811161026b576102a86004913690830161023d565b9060ff60025460a01c1661052c576102be61209b565b926102c76120aa565b906084359360a435946102d86120d8565b9483359087156105035773ffffffffffffffffffffffffffffffffffffffff98898816156104fb575b916002949391610369938b8316809260018560a01c169060038660a11c169861032a88866132ed565b6104c057876101018210156104ae57508061049d575b505061034e86303386613570565b505b61048d575b50505b8460036024359360a31c1691613614565b0361047d57847f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83166103a361039e30836137e4565b612121565b90803b1561026b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815283810192835291600091839182908490829060200103925af180156104785761045f575b5047935b84106104375761043361041660c435866101243586888b166138d3565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c6104729261218f565b80610555565b386103f5565b6121c4565b61048730836137e4565b936103f9565b6104969161335b565b3881610355565b6104a791856134ba565b3880610340565b90916104bb92309161341e565b610350565b505091505015610358576104f6818c7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831661335b565b610358565b339750610301565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b600091031261026b57565b3461026b57600060031936011261026b5760206040516121348152f35b3461026b57600060031936011261026b57602060405160648152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab89347168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026b57565b3461026b57602060031936011261026b5773ffffffffffffffffffffffffffffffffffffffff60043561063a816105ea565b166000526000602052602060ff604060002054166040519015158152f35b3461026b57600060031936011261026b57602060ff60025460a01c166040519015158152f35b6003199160608383011261026b5760043567ffffffffffffffff9384821161026b5761010090828503011261026b57600401926024359260443591821161026b576106cb9160040161023d565b9091565b6106d83661067e565b9060ff60029493945460a01c1661071857610433936106f69361224e565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405160c88152f35b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3168152f35b3590610830826105ea565b565b9061016060031983011261026b5760043561084c816105ea565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026b57602491610104359167ffffffffffffffff916101243583811161026b57826108a19160040161023d565b939093926101443591821161026b576106cb9160040161023d565b6108c536610832565b909192959360ff60025460a01c16610718576108e3602087016120ce565b6108ec876120ce565b60408801359360608901359561090460c08b016120ce565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610a65578c8b1615610a5d575b8815610a335761093f30866137e4565b9961094a89876132ed565b610a185792826109829592858b80966101018f9910600014610a08575050806109f7575b505061097c8482338a613570565b506145a9565b6109aa61098f30846137e4565b9661099a30846137e4565b9060018111156109ee5790612153565b9486106109c4576104339860806106f69901359716614215565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b50600090612153565b610a0191896134ba565b388061096e565b9091610a139361341e565b6145a9565b505097610982928892610a2d89933490612153565b9a6145a9565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a5061092f565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b5760206040516127108152f35b610ab53661067e565b60ff60025460a01c1661071857610acb846120ce565b92610ad8602086016120ce565b60409485870135606088013594610af160c08a016120ce565b96610aff60e08b018b6121d0565b8895919515610d325773ffffffffffffffffffffffffffffffffffffffff95868b1615610d2a575b3392610b3387826132ed565b610c9e57610b5496610b4f916101018810610c7e575b50613c1f565b6145e1565b938410610c555773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610bac575b90610b9094939291608061043398013594166148cd565b9251918252602082015260408101919091529081906060820190565b93929190847f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831694853b1561026b576000875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610c198a600483019190602083019252565b03925af180156104785761043398610b9097608092610c42575b50985050909192939450610b79565b8061046c610c4f9261218f565b38610c33565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610c8d575b5050610b49565b610c96926134ba565b388787610c86565b507f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8387169250823b1561026b576000869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561047857610b5496610d17575b50610b4f3093613c1f565b8061046c610d249261218f565b38610d0c565b339a50610b27565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc168152f35b3461026b57600060031936011261026b5760206040516113888152f35b3461026b57600060031936011261026b5760206040516109c48152f35b610def3661067e565b919260ff60025460a01c1661071857600092610e0a826120ce565b94610e17602084016120ce565b60409586850135936060860135610e3060c088016120ce565b99610e3e60e08901896121d0565b939091831561124f5773ffffffffffffffffffffffffffffffffffffffff9493929190858e1615611247575b610e748a826132ed565b1580159c906111ee57505050837f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8316610ead30826137e4565b95813b156111eb578c51907fd0e30db0000000000000000000000000000000000000000000000000000000008252816004818d865af18015610478576111d8575b50905b84871693858316918583146111af5790610f0c918486614bfd565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361119e57847f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831680911461117557610f5c30826137e4565b90838211611165575b803b1561026b578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092526000908290602490829084905af1801561047857611152575b5047995b610fc030836137e4565b928b1061112957156110fc5750610fe4904794600181116000146109ee5790612153565b60018111611045575b50916110219795939160806110166104339c999795934790600181116000146109ee5790612153565b965b01359716614215565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161107a817f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83169b612121565b9a803b1561026b578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c526000908c90602490829084905af19a8b15610478576110166110219a6080926104339e6110e9575b50939597999c50505091939597610fed565b8061046c6110f69261218f565b386110d7565b91509160806111236110219a9896946104339d9a9896600181116000146109ee5790612153565b96611018565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c61115f9261218f565b38610fb2565b9061116f906126fc565b90610f65565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506111a930876137e4565b99610fb6565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b8061046c6111e59261218f565b38610eee565b80fd5b90929196506111fd30846137e4565b968a610101821015611235575080611224575b505061121e89303385613570565b50610ef1565b61122e91846134ba565b3880611210565b909161124292309161341e565b610ef1565b339d50610e6a565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83168152f35b3461026b57600060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026b57600060031936011261026b57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce168152f35b60031960a091011261026b57600490565b6003198101610100811261026b5760a01361026b5760049160a4359167ffffffffffffffff9160c43583811161026b57826113de9160040161023d565b9390939260e43591821161026b576106cb9160040161023d565b611401366113a1565b949360ff60025460a01c1661071857843590602086013592611427816080890135614ee5565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610a65578915610a33578d161561153d575b61146730866137e4565b9961147289876132ed565b611524576114a9949392919089610101821015611512575080611501575b505061149e88303388613570565b505b6114d05761501c565b6114b661098f30846137e4565b9486106109c4576104339860406106f69901359716614215565b6114fc8c7f00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce168561335b565b61501c565b61150b91876134ba565b3880611490565b909161151f92309161341e565b6114a0565b50505096906115376114a9923490612153565b9761501c565b339a5061145d565b61154e366113a1565b90949360ff60025460a01c1661071857843592602086013592611575886080890135614ee5565b909a9291948b988815610a335773ffffffffffffffffffffffffffffffffffffffff809d1615611668575b6115c2969798999a6115b282866132ed565b156115e6575b505050505061501c565b6115cc30826137e4565b9283106109c45761043395604061041696013594166138d3565b610101831015611658578261160493611647575b5050303385613570565b505b611614575b808080806115b8565b611641908a7f00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce169061335b565b3861160b565b61165191866134ba565b38806115fa565b61166392309161341e565b611606565b3399506115a0565b60e060031936011261026b5761168536611390565b67ffffffffffffffff60a43581811161026b573660238201121561026b57806004013582811161026b573660248260051b8401011161026b5760c43592831161026b576116e6936116dc602494369060040161023d565b9490930190612727565b60408051928352602083019190915290f35b60031936016101a0811261026b576101601361026b576101843567ffffffffffffffff811161026b576117306004913690830161023d565b60ff60025460a01c1661052c576117456120b6565b9261174e6120c2565b9060c4359260e4359461175f6120e5565b946117686120aa565b918435881561193f5773ffffffffffffffffffffffffffffffffffffffff998a891615611937575b918160029695938c6117fd969416809360018460a01c169060038560a11c16996117ba88866132ed565b6118fc57876101018210156118ea5750806118d9575b50506117de86303386613570565b505b6118c9575b50505b604435918660036024359360a31c16916151c2565b036118b957847f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831661183261039e30836137e4565b90803b1561026b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815283810192835291600091839182908490829060200103925af18015610478576118a6575b5047935b84106104375761043361041661010435866101643586888b166138d3565b8061046c6118b39261218f565b38611884565b6118c330836137e4565b93611888565b6118d29161335b565b38826117e5565b6118e391856134ba565b38806117d0565b90916118f792309161341e565b6117e0565b5050915050156117e857611932828d7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831661335b565b6117e8565b339850611790565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b61197136610832565b959260ff60029593955460a01c166107185761198f602087016120ce565b91611999876120ce565b976040880135916060890135966119b260c08b016120ce565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611a67575b8915610a335782816119f9986119e98980956132ed565b15611a1d575b50505050506153ad565b611a0330826137e4565b9283106109c45761043395608061041696013594166138d3565b610101851015611a575784611a3a95611a46575b50503390613570565b505b38848282806119ef565b611a5091836134ba565b3880611a31565b611a6294915061341e565b611a3c565b339a506119d2565b3461026b57600060031936011261026b57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611aac3661067e565b92919060ff60025460a01c1661071857600091611ac8846120ce565b93611ad5602082016120ce565b906040948582013597606083013595611af060c085016120ce565b98611afe60e08601866121d0565b9033928a1561124f578c9d73ffffffffffffffffffffffffffffffffffffffff9d999a9b9c9d809a1615611cc9575b611b3781836132ed565b611c3f57611b4f97506101018710611c2e575b6153d4565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611c1e57817f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8316611b9c61039e30836137e4565b90803b1561026b5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092526000908290602490829084905af1801561047857611c0b575b5047945b8510610c5557916104339693916080610b909694013594166138d3565b8061046c611c189261218f565b38611bea565b611c2830846137e4565b94611bee565b8615611b4a57611b4a8787846134ba565b935050877f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8316803b15611cc5578c51967fd0e30db00000000000000000000000000000000000000000000000000000000088528760048187855af196871561047857611b4f97611cb2575b5030936153d4565b8061046c611cbf9261218f565b38611caa565b8680fd5b339e50611b2d565b3461026b57600060031936011261026b576020604051600b8152f35b3461026b57600060031936011261026b57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026b57606060031936011261026b576004602435813560443567ffffffffffffffff811161026b57611d58903690850161023d565b91909260ff60025460a01c1661203e576040517f00000000000000000000000000000000000000000000000000000000000000008152610200841460158201605f6041880160168501376060812090527f000000000000000000000000000000000000000000000000000000000000000060358301526055822080925273ffffffffffffffffffffffffffffffffffffffff80921633186120155760a085118061200d575b15611e37575061001b9550506000821315611e275750611e1c90612221565b915b60a43592613c58565b611e319150612221565b91611e1e565b93509390600091829360405196848213611ffb575b505060008213611feb575b505060a43592308414600114611fa05715611f0657507f30f28b7a0000000000000000000000000000000000000000000000000000000083526000928392610164926101606101248885013733608484015260a483015260c4820152827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af115611ede57005b7f6b836e6b000000000000000000000000000000000000000000000000000000006000526000fd5b92806000926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081611f7d575b5015611f5557005b7f1bbb4abe000000000000000000000000000000000000000000000000000000006000526000fd5b90503d15611f985750600160005114601f3d11165b38611f4d565b3b1515611f92565b509260209250806000927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af19081611f7d575015611f5557005b9092506060915001353880611e57565b90945060408201351692503880611e4c565b508015611dfd565b867f48f5c3ed000000000000000000000000000000000000000000000000000000006000526000fd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026b57602060031936011261026b5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356120a7816105ea565b90565b6064356120a7816105ea565b6084356120a7816105ea565b60a4356120a7816105ea565b356120a7816105ea565b610104356120a7816105ea565b610144356120a7816105ea565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161214e57565b6120f2565b9190820391821161214e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116121a357604052565b612160565b6060810190811067ffffffffffffffff8211176121a357604052565b6040513d6000823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026b570180359067ffffffffffffffff821161026b5760200191813603831361026b57565b7f8000000000000000000000000000000000000000000000000000000000000000811461214e5760000390565b91612258836120ce565b91612265602085016120ce565b9061227260c086016120ce565b9161228060e08701876121d0565b97606088013515610a335773ffffffffffffffffffffffffffffffffffffffff8516156126f4575b336122b760408a0135896132ed565b15159760009789600014612688575050505073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83169461230b30876137e4565b98863b1561026b57604060008a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af1801561047857612675575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610a655787826123ab926123a66123a160608f0135613c1f565b612221565b613c58565b8099919360608c013582106109c45773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146125b3575b73ffffffffffffffffffffffffffffffffffffffff16300361255c5750501561252957505050479361245b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83169861099a308b6137e4565b946001861161249a575b5061249297505b73ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614215565b929391929091565b90946124a590612121565b97803b1561026b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099526000908990602490829084905af1908115610478576124929861250f92612516575b50479060018111156109ee5790612153565b9338612465565b8061046c6125239261218f565b386124fd565b612492999296506060101561254d57506125479061099a30876137e4565b9361246c565b61254791506040880135612153565b9399509750506124929950606010612596575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416613e6f565b6125ac9196506125a633886137e4565b90612153565b943861256f565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83168073ffffffffffffffffffffffffffffffffffffffff891614610a6557803b1561026b57600060405180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161264d8a600483019190602083019252565b03925af1801561047857612662575b506123ee565b8061046c61266f9261218f565b3861265c565b8061046c6126829261218f565b38612353565b61269a9b9298939b94919430866137e4565b9b6101018110156126d957806126c8575b505060608211156123585791506126c233846137e4565b91612358565b6126d291866134ba565b38806126ab565b906126ed92995060408c013591309161341e565b3096612358565b3394506122a8565b801561214e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c16610718576106cb94612a9e565b3560ff8116810361026b5790565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90156127b7578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026b570190565b61274f565b91908110156127b75760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026b570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026b57016020813591019167ffffffffffffffff821161026b57813603831361026b57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481946000925b8484106128e8575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026b5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026b5784612a6e612a478695612a048f6129dd8e612a8d998b60019e01526129b6604061299a818c01610825565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b6129c1818a01610825565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b6129e8818801610825565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612a1460a06129e8818801610825565b60c0808601359085015260e080860135908501526101009080612a39838801886127fc565b92909387015285019161284c565b6101208085013590840152610140612a61818601866127fc565b918584039086015261284c565b91612a7f61016091828101906127fc565b92909181850391015261284c565b9901930194019291959493906128b5565b9493929192612aaf608087016120ce565b91863594602088013594612ac66040809a01612741565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613228575b881561124f5784156131ff5760005b8581106131e3575050612b32612b196060612b13878961277e565b016120ce565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612b44612b198d612b13888a61277e565b93600381169360018514928361312c57346131035760019291908d6101018210156130f15750806130e0575b5050612b7e8c303387613570565b505b818160021c166130af575b60031c16612deb575050600203612d055750827f0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab893471691823b1561026b57612c089287600080948c51968795869485937f1c64b82000000000000000000000000000000000000000000000000000000000855230926004860161288b565b03925af1801561047857612cf2575b50807f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831692612c4630856137e4565b908110612cc957612c56906126fc565b95833b1561026b57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052926000908490602490829084905af191821561047857612cb1938793612cb6575b50166150ae565b509190565b8061046c612cc39261218f565b38612caa565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c612cff9261218f565b38612c17565b919083859996991691612d1883856137e4565b947f0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab8934716803b1561026b57600092838a93612d808b519a8b96879586947f1c64b8200000000000000000000000000000000000000000000000000000000086526004860161288b565b03925af191821561047857612da394612d9e93612dd8575b506137e4565b612153565b938410612daf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061046c612de59261218f565b38612d98565b97939250999795939060021460001461301b57847f0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab8934716803b1561026b57612e6793600080948b51968795869485937f01fb36ba00000000000000000000000000000000000000000000000000000000855230926004860161288b565b03925af1801561047857613008575b50817f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831690612ead612ea830846137e4565b6126fc565b91803b1561026b576000875180927f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381612ef389600483019190602083019252565b03925af1801561047857612f109284928692612cb65750166150ae565b50955b612f1d30826137e4565b9360018511612f38575b505050612f349250612153565b9190565b93949315612fe75750612f6d907f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8316936126fc565b91833b1561026b57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052926000908490602490829084905af192831561047857612f3493612fd4575b50612fca82336150ae565b505b388080612f27565b8061046c612fe19261218f565b38612fbf565b925050613002612ff9612f34946126fc565b809333906150ef565b50612fcc565b8061046c6130159261218f565b38612e76565b84999392997f0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab8934716803b1561026b57600092838c936130878c51978896879586947f01fb36ba0000000000000000000000000000000000000000000000000000000086526004860161288b565b03925af180156104785761309c575b50612f13565b8061046c6130a99261218f565b38613096565b6130db897f0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab89347168561335b565b612b8b565b6130ea91866134ba565b3880612b70565b90916130fe92309161341e565b612b80565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036131ba57877f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c831690813b1561026b5760008c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610478576001926131a7575b50612b80565b8061046c6131b49261218f565b386131a1565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806131f96131f38493898b6127bc565b3561505e565b01612af8565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612ae9565b333b1561026b57565b60007fffffffff0000000000000000000000000000000000000000000000000000000081351681527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60408220541680156132c357818091368280378136915af43d82803e156132bf573d90f35b3d90fd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b919060009273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee91828214613349575b501861331857565b3461331f57565b7f8b6ebb4d0000000000000000000000000000000000000000000000000000000060005260046000fd5b909350341861331f5760019238613310565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff806034526f095ea7b30000000000000000000000009060009283918383526020836044601082865af13d1560018551141716156133c1575b50505050603452565b826044926010926020968360345283528238868683865af1506034525af13d1560018351141716156133f657388181806133b8565b807f8164f8420000000000000000000000000000000000000000000000000000000060049252fd5b90600490600094859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af11561349057565b7f6b836e6b0000000000000000000000000000000000000000000000000000000060005260046000fd5b918060e01461353357610100146134f5577fb78cb0dd0000000000000000000000000000000000000000000000000000000060005260046000fd5b61010460009182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e46000918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd0000000000000000000000000000000000000000000000000000000084526004840152602483015260448201526020600060648382875af192836135ee575b5082156135c65750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d1561360b5750600160005114601f3d1116915b386135bc565b3b151591613605565b9192906040519360018214613773575b60011461370d576003146001146136a257926fffffffffffffffffffffffffffffffff6084947f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015260009283928380935af11561369a5750565b3d81803e3d90fd5b916084916000946fffffffffffffffffffffffffffffffff86957f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af1610830575b3d6000803e3d6000fd5b50926fffffffffffffffffffffffffffffffff6084947fa6417ed60000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015260009283928380935af11561369a5750565b7fd0e30db00000000000000000000000000000000000000000000000000000000085526000806004873473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83165af1613624573d6000803e3d6000fd5b60009291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613849576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613844575b50565b519150565b31925050565b90600b820180921161214e57565b9190820180921161214e57565b908160640291606483040361214e57565b906113889182810292818404149015171561214e57565b906109c49182810292818404149015171561214e57565b906121349182810292818404149015171561214e57565b8181029291811591840414171561214e57565b90919493926138f5600096906bffffffffffffffffffffffff8260601c921690565b94906139008261384f565b8311613bd9575b6139118884612153565b9573ffffffffffffffffffffffffffffffffffffffff82166139d6575b5050861590506139ba5761396c6139658473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b6139ba57508482613985856139ad946139b39796615705565b6139a7612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b906150ef565b50612121565b9190600090565b92506139cd939450829061039e92615705565b90600090600090565b6b2000000000000000000000008199949395979699161594851594613a1e6139658c73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9680613bd2575b613bb9576b0800000000000000000000008316151597613a4484615732565b938415613ac5575050948a94613ab094613a92613a8a613ab89b9686613aa89f9e9c979b613a758161039e9e61385d565b871115613abd57613a859161385d565b6138c0565b612710900490565b90613a9f613a8a836138a9565b9c8d8093612153565b9d8e93615871565b928391615705565b929190565b5050846138c0565b80929c99969794506b400000000000000000000000919a98959350161515600014613b34575089613afd57505050505b38808061392e565b8698975091613ab89693918a61039e96613ab095613b2c613b24613a8a9f613a8a9061387b565b9e8f94613892565b9c8d92615871565b939291906b8000000000000000000000008516613b56575b5050505050613af5565b8a15613b4c579092948a92949998506b040000000000000000000000613b84613a8a613b8d9d9a999a61387b565b9b8c8095612153565b9a16613bac575b9261039e95928a889693613ab89a99613ab097615871565b6000995060019550613b94565b5050505093905061039e92508391506139cd9495615705565b5086613a25565b9650613be58183612153565b966b1000000000000000000000008616156139075796613c07613a8a8361386a565b9081811115613c1857505b96613907565b9050613c12565b7f8000000000000000000000000000000000000000000000000000000000000000811015613c4a5790565b6335278d126000526004601cfd5b9391929080948460009260608206613e18575b505060006040949596855197889586947f0000000000000000000000000000000000000000000000000000000000000000865260158601605f6001860160168901376060812090527f0000000000000000000000000000000000000000000000000000000000000000603587015273ffffffffffffffffffffffffffffffffffffffff605587201696803091613e10575b50843560ff1c8614613d87577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613703576120a760208301519251925b600003615add565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613703576120a76020835193015192613d7f565b905038613cfc565b60a0821115613c6b5760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff16915060006040613c6b565b91909397959497969296600097613ea1613e898884612153565b91906bffffffffffffffffffffffff8260601c921690565b909286116141eb578a95613eb48961384f565b81116141a5575b73ffffffffffffffffffffffffffffffffffffffff8416613f7a575b5050505091613ee8918794936150ef565b5081613f01575b50613ef99161385d565b929190600090565b9050613f306139658273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b613f6f57613ef991613f68858093613f60612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b903390613570565b5091613eef565b509291506000908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591613fc66139658d73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b938061419e575b61417e576b0800000000000000000000008216151594613fec83615732565b9283156140525750509061400b91808810600014613a855750866138c0565b61271090049961401a8b6138a9565b612710900461402a81809d612153565b9c8d923361403798615ae8565b906140419161385d565b9261404b926150ef565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156140ce575061409857505050505091613ee8918794935b919394819338613ed7565b909192939998949695979a6140ac8161387b565b61271090049b6140bc8d92613892565b61271090049b8c913361403798615ae8565b946b800000000000000000000000869b9a999897929b166140fb575b50505050505090613ee8929161408d565b90919293949596979899614114578c99989796956140ea565b988c929394959b9a96996b04000000000000000000000061413b6141449f613a8a9061387b565b9e8f8096612153565b9d16614171575b928c61404b9a99989693614166969361416b99963390615ae8565b61385d565b936150ef565b60009c506001965061414b565b505050505050945091906141939395506150ef565b509190600090600090565b5083613fcd565b99506141b1888b612153565b996b100000000000000000000000821615613ebb57996141d3613a8a8c61386a565b90818111156141e457505b99613ebb565b90506141de565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315156000146145a05761423861423285612121565b87612153565b600098606081901c906bffffffffffffffffffffffff1690928881116141eb576142618361384f565b811161455a575b73ffffffffffffffffffffffffffffffffffffffff8416614337575b505050509061429561429d926126fc565b9788916150ef565b50841561431c576142d16139658373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61431c57613ef99291614314866125a69361430e82614308612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b866150ef565b50612153565b903390615d43565b61432d9394506125a6913390615d43565b9190600090600090565b6b200000000000000000000000821615918215916143786139658b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9380614553575b61452b576b080000000000000000000000821615159461439e83615732565b91826144db575050506b40000000000000000000000081161561443957508a6143d757505050509061429561429d925b91923880614284565b8a6143e98996989c9b9a99959b61387b565b61271090049b6143f98d92613892565b61271090049b61440a968d92615871565b3361441492615d43565b9161441e906126fc565b8097614429926150ef565b5061443391612153565b93929190565b9291936b8000000000000000000000008416614462575b50505050509061429561429d926143ce565b8b15614450578b919293999695976b040000000000000000000000614490613a8a6144999f9e9c989e61387b565b9d8e8095612153565b9b166144ce575b938a9b6144c09487946144339c9d6143149561430e9b9a6144c69a615871565b946126fc565b9889916150ef565b60009a50600194506144a0565b8b989a9d9c9b979e506144f9935080821060001461452457506138c0565b6127109004996145088b6138a9565b612710900461451881809d612153565b9c61440a968e93615871565b90506138c0565b505050505050939261432d9596506144c061430e9361454b923390615d43565b9687916150ef565b508361437f565b9950614566828b612153565b996b1000000000000000000000008216156142685799614588613a8a8c61386a565b908181111561459957505b99614268565b9050614593565b61423884614232565b91806000958695606493607c95608037608083015260a08201523360c0820152019134905af1156145d657565b3d6000607c3e3d607cfd5b9491939293600092839484936040519786975b60608604891061461157505050505050505050506120a790615add565b909192939495969798809b9a8915614848575b6060880460018c01106147bb575b8a6147b3575b8a60a0610164928960016101008211146147a5575b506060830288013560ff1c15614707579460608086946000946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146146fa575b5af1156137035760208a0151945b6001879660000399019796959a98999a9493929190946145f4565b89896101648501376146d1565b9460608086946000946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614798575b5af115613703578951946146df565b8989610164850137614789565b80919294019301908961464d565b309350614638565b97505096507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff891697614632565b985050507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f6001840160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff891691614624565b90919493926148ef600096906bffffffffffffffffffffffff8260601c921690565b94906148fa8261384f565b8311614b81575b61490b8884612153565b9573ffffffffffffffffffffffffffffffffffffffff82166149b8575b50508615905061499f5761495f6139658473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61499f575081614974848793614997956150ef565b506139a7612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b509190600090565b9394506149ae925083916150ef565b5090600090600090565b6b2000000000000000000000008199949395979699161594851594614a006139658c73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9680614b7a575b614b64576b0800000000000000000000008316151597614a2684615732565b938415614a8057505093614a6a99989793614a54613a8a614a7a9995858f9a9699613a7581614a729c61385d565b90614a61613a8a836138a9565b9b8c8093612153565b9c8d93615871565b9283916150ef565b50929190565b80929c99969794506b400000000000000000000000919a98959350161515600014614aec575089614ab857505050505b388080614928565b869897509089614a7a969795614a72959493614ae4614adc613a8a613a8a9f61387b565b9d8e94613892565b9b8c92615871565b939291906b8000000000000000000000008516614b0e575b5050505050614ab0565b8a15614b04579092948a92949998506b040000000000000000000000613b84613a8a614b3c9d9a999a61387b565b9a16614b57575b918987969492614a7a98614a729795615871565b6000995060019550614b43565b50505050949590508492506149ae9391506150ef565b5086614a07565b9650614b8d8183612153565b966b1000000000000000000000008616156149015796614baf613a8a8361386a565b9081811115614bc057505b96614901565b9050614bba565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000091604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16936000978585015260005b8360061c8110614ec1575050508060061c805b614dd157506000938496855b8360061c8710614cad57505050505050505050565b8015614d7e575b308460061c6001890110614d66575b60a46040926000928a6001810160051b8a01518b8b826020899560061b8d010135600116614d5d575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561370357600188960195614c98565b93925081614cec565b508486016001880160051b0160200151985088614cc3565b508385016020818101517fa9059cbb00000000000000000000000000000000000000000000000000000000928801808301938452602481018290526044908101859052909260009190828c5af150614cb4565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156137035760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c0101358516614eb6575b50906126f28161271093030292020204018098838301901b8501520180614c8c565b9092506126f2614e94565b80614edf838560019460061b8b0160208560051b8c8c010101614bc7565b01614c79565b9190918235807f52bbbe29000000000000000000000000000000000000000000000000000000001461500a577f945bcec90000000000000000000000000000000000000000000000000000000014614f61577f7352d91c0000000000000000000000000000000000000000000000000000000060005260046000fd5b604483013592838101936004850135946001836004013514600114614ff75760059590951b01016004013592602401355b8015614fdc575b8315614fc0575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350614fa0565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee614f99565b602401359460051b010160040135614f92565b50610144830135926101240135614f92565b90600080918060405194853783347f00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce5af1156150555750565b3d6000823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff168061507c5750565b331861508457565b7f02a43f8b0000000000000000000000000000000000000000000000000000000060005260046000fd5b9190916000808080958194612710f19182156150c75750565b807f90b8ec180000000000000000000000000000000000000000000000000000000060049252fd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee84146001146151ad576044602092600092604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af19182615187575b505b811561515d57565b7f90b8ec180000000000000000000000000000000000000000000000000000000060005260046000fd5b9091503d156151a45750600160005114601f3d1116905b38615153565b3b15159061519e565b6000809394508092918192612710f190615155565b9395949192906040519560019485831461533c575b806001146152ec57600214615298575060031460011461523e57946084957f5b41b908000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152606483015260009283928380935af11561369a5750565b9160a4939160009686947f394747c500000000000000000000000000000000000000000000000000000000899852600486015260248501526044840152806064840152608483015234905af1610830573d6000803e3d6000fd5b60a4979291507f64a145580000000000000000000000000000000000000000000000000000000087526004870152602486015260448501526064840152608483015260009283928380935af11561369a5750565b505050946084957f65b2489b000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152606483015260009283928380935af11561369a5750565b7fd0e30db000000000000000000000000000000000000000000000000000000000885260008060048a3473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83165af16151d7573d6000803e3d6000fd5b906044836000958695607c9460803760808201523360a0820152019134905af1156145d657565b9095929193956000958694879888965b8660061c88106153fb575050505050505050505050565b891561557d575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156137035783519084015190818a615574575b506126f2916127108385029102019202020496879060009061556c575b30918a60061c60018d01106154df575b60009360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156137035760018a9701966153e4565b92939d509b50507f00000000000000000000000000000000000000000000000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f000000000000000000000000000000000000000000000000000000000000000060358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d91909392615482565b905087615472565b91509038615455565b506040517f000000000000000000000000000000000000000000000000000000000000000081529850601589016028808a8337812090527f000000000000000000000000000000000000000000000000000000000000000060358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a16903083146001146156c55761010084111561567f577f30f28b7a0000000000000000000000000000000000000000000000000000000081526000806004928688858301378460848201528960a48201528560c482015283870190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af115611ede5750615402565b60006064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615402565b60006044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615402565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6138419301916150ef565b613fff1660c881116157415790565b5060c890565b60405190610830826121a8565b60405190615761826121a8565b600282526040366020840137565b8051156127b75760200190565b8051600110156127b75760400190565b90815180825260208080930193019160005b8281106157ac575050505090565b83518552938101939281019260010161579e565b6020808252825160608284015280516080840181905293949360a0840193929182019060005b818110615847575050508473ffffffffffffffffffffffffffffffffffffffff6040926120a796970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08285030191015261578c565b825173ffffffffffffffffffffffffffffffffffffffff16865294830194918301916001016157e6565b96909591939294615882848761385d565b928361589357505050505050505090565b9697959681615ad5575b501561591357505050828281116158e9576120a794816158c0575b505050612153565b73ffffffffffffffffffffffffffffffffffffffff6158e09316906150ef565b503880806158b8565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b848296939792116158e9571561596e57856120a79661593b575b50816158c057505050612153565b61596790615961612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b836150ef565b503861592d565b615a5f9073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc16946159ba8887856150ef565b506159f56159c6615754565b996159cf615754565b96166159da8b61576f565b9073ffffffffffffffffffffffffffffffffffffffff169052565b6159fe8561576f565b52615a2d615a24612b1960025473ffffffffffffffffffffffffffffffffffffffff1690565b6159da8a61577c565b615a368461577c565b52615a3f615747565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026b57615aa96000949185926040519687809481937f45f32b0b000000000000000000000000000000000000000000000000000000008352600483016157c0565b03925af1928315610478576120a793615ac25750612153565b8061046c615acf9261218f565b3861430e565b90503861589d565b60008112613c4a5790565b969790929594919394615afb838761385d565b9889615b0e575b50505050505050505090565b819994959697989991615d3b575b5015615b7057505050829483116158e95782615b46575b505050505b388080808080808080615b02565b73ffffffffffffffffffffffffffffffffffffffff615b66941691613570565b5038808080615b33565b90919687116158e95715615bf25780615bbe575b5082615b94575b50505050615b38565b73ffffffffffffffffffffffffffffffffffffffff615bb4941691613570565b5038808080615b8b565b615beb90615be4612b1960015473ffffffffffffffffffffffffffffffffffffffff1690565b8385613570565b5038615b84565b91615cc99193949273ffffffffffffffffffffffffffffffffffffffff95615c3f88887f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc16809886613570565b50615c5f615c4b615754565b97615c54615754565b96166159da8961576f565b615c688561576f565b52615c97615c8e612b1960025473ffffffffffffffffffffffffffffffffffffffff1690565b6159da8861577c565b615ca08461577c565b52615ca9615747565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026b57615d136000929183926040519485809481937f45f32b0b000000000000000000000000000000000000000000000000000000008352600483016157c0565b03925af1801561047857615d28575b50615b38565b8061046c615d359261218f565b38615d22565b905038615b1c565b91909160018211615d5657505050600090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615d8492019283916150ef565b509056fea164736f6c6343000816000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a29032633000000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c8300000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab8934700000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
-----Decoded View---------------
Arg [0] : _owner (address): 0xD7e24A49944F7972cEb826C7557580658F9C3303
Arg [1] : _diamondCutFacet (address): 0xfa39c1c670b48956eeF9fd0BbD0E81A290326330
Arg [2] : _weth (address): 0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83
Arg [3] : _balancerVault (address): 0x20dd72Ed959b6147912C2e529F0a0C651c33c9ce
Arg [4] : _uniV3FactoryAndFF (uint256): 0
Arg [5] : _uniswapV3PoolInitCodeHash (uint256): 0
Arg [6] : _uniswapV2FactoryAndFF (uint256): 0
Arg [7] : _uniswapV2PoolInitCodeHash (uint256): 0
Arg [8] : _rfq (address): 0x2DF17455B96Dde3618FD6B1C3a9AA06D6aB89347
Arg [9] : _feeVault (address): 0x00700052c0608F670705380a4900e0a8080010CC
Arg [10] : _permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303
Arg [1] : 000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330
Arg [2] : 00000000000000000000000021be370d5312f44cb42ce377bc9b8a0cef1a4c83
Arg [3] : 00000000000000000000000020dd72ed959b6147912c2e529f0a0c651c33c9ce
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000002df17455b96dde3618fd6b1c3a9aa06d6ab89347
Arg [9] : 00000000000000000000000000700052c0608f670705380a4900e0a8080010cc
Arg [10] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 30.37% | $3.82 | 1 | $3.82 | |
ETH | 22.63% | $2.85 | 1 | $2.85 | |
ETH | 5.03% | $0.000251 | 2,525.77 | $0.6336 | |
ETH | 3.04% | $38.27 | 0.01 | $0.3827 | |
ETH | <0.01% | $4,009.62 | 0.000000000000000001 | <$0.000001 | |
GNO | 32.87% | $4.14 | 1 | $4.14 | |
GNO | <0.01% | $0.999651 | 0.000000000000000001 | <$0.000001 | |
BASE | 6.07% | $38.21 | 0.02 | $0.7642 | |
BASE | <0.01% | $4,009.18 | 0.000000000000000001 | <$0.000001 | |
ARB | <0.01% | $4,009.62 | 0.000000000000000001 | <$0.000001 | |
OP | <0.01% | $4,009.18 | 0.000000000000000001 | <$0.000001 | |
BSC | <0.01% | $721.32 | 0.000000000000000001 | <$0.000001 | |
AVAX | <0.01% | $50.17 | 0.000000000000000001 | <$0.000001 | |
FTM | Fantom (FTM) | <0.01% | $1.41 | 0.000000000000000001 | <$0.000001 |
POL | <0.01% | $0.59508 | 0.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.