| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| 0x61e13bd2d883988ecc96ca1d6503b11d0ea85530743a94cc59edb2409b85b8ed | Swap Exact Amoun... | (pending) | 10 mins ago | IN | 0 ETH | (Pending) | |||
| 0xb44c5f794fc0dfe669d668eb82d7ba86ab43001f841779d577ff7f59ef6f2eb2 | Swap Exact Amoun... | (pending) | 1 hr ago | IN | 0 ETH | (Pending) | |||
| Swap Exact Amoun... | 23837629 | 12 mins ago | IN | 0 ETH | 0.00020299 | ||||
| Swap Exact Amoun... | 23837628 | 13 mins ago | IN | 0 ETH | 0.00032069 | ||||
| Swap Exact Amoun... | 23837625 | 13 mins ago | IN | 0 ETH | 0.00029171 | ||||
| Swap Exact Amoun... | 23837620 | 14 mins ago | IN | 0 ETH | 0.00017252 | ||||
| Swap Exact Amoun... | 23837616 | 15 mins ago | IN | 0 ETH | 0.00016005 | ||||
| Swap Exact Amoun... | 23837615 | 15 mins ago | IN | 0 ETH | 0.00022607 | ||||
| Swap Exact Amoun... | 23837584 | 22 mins ago | IN | 0 ETH | 0.00013756 | ||||
| Swap Exact Amoun... | 23837570 | 25 mins ago | IN | 0 ETH | 0.00009135 | ||||
| Swap Exact Amoun... | 23837567 | 25 mins ago | IN | 0 ETH | 0.00010274 | ||||
| Swap Exact Amoun... | 23837556 | 27 mins ago | IN | 0 ETH | 0.0001764 | ||||
| Swap Exact Amoun... | 23837552 | 28 mins ago | IN | 0.62 ETH | 0.00066499 | ||||
| Swap Exact Amoun... | 23837551 | 28 mins ago | IN | 0 ETH | 0.00007263 | ||||
| Swap Exact Amoun... | 23837549 | 29 mins ago | IN | 0 ETH | 0.00025965 | ||||
| Swap Exact Amoun... | 23837547 | 29 mins ago | IN | 0 ETH | 0.00008366 | ||||
| Swap Exact Amoun... | 23837547 | 29 mins ago | IN | 0 ETH | 0.00028826 | ||||
| Swap Exact Amoun... | 23837545 | 30 mins ago | IN | 0 ETH | 0.00017588 | ||||
| Swap Exact Amoun... | 23837545 | 30 mins ago | IN | 0 ETH | 0.00007455 | ||||
| Swap Exact Amoun... | 23837541 | 30 mins ago | IN | 0 ETH | 0.00007887 | ||||
| Swap Exact Amoun... | 23837536 | 31 mins ago | IN | 0.105 ETH | 0.00020152 | ||||
| Swap Exact Amoun... | 23837528 | 33 mins ago | IN | 0 ETH | 0.00005041 | ||||
| Swap Exact Amoun... | 23837528 | 33 mins ago | IN | 0 ETH | 0.00011852 | ||||
| Swap Exact Amoun... | 23837522 | 34 mins ago | IN | 0 ETH | 0.00007453 | ||||
| Swap Exact Amoun... | 23837521 | 34 mins ago | IN | 0 ETH | 0.00034591 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 23837629 | 12 mins ago | 0.22118669 ETH | ||||
| Transfer | 23837629 | 12 mins ago | 0.22118669 ETH | ||||
| Transfer | 23837553 | 28 mins ago | 0.05904763 ETH | ||||
| Transfer | 23837553 | 28 mins ago | 0.05904763 ETH | ||||
| Deposit | 23837552 | 28 mins ago | 0.62 ETH | ||||
| Transfer | 23837545 | 30 mins ago | 0.00532696 ETH | ||||
| Transfer | 23837545 | 30 mins ago | 0.00532696 ETH | ||||
| 0x00000000 | 23837536 | 31 mins ago | 0.105 ETH | ||||
| Transfer | 23837507 | 37 mins ago | 0.00180868 ETH | ||||
| Transfer | 23837507 | 37 mins ago | 0.00180868 ETH | ||||
| 0x00000000 | 23837499 | 39 mins ago | 0.00363558 ETH | ||||
| Swap Exact Amoun... | 23837499 | 39 mins ago | 0.00363558 ETH | ||||
| Transfer | 23837456 | 47 mins ago | 0.00401619 ETH | ||||
| Transfer | 23837456 | 47 mins ago | 0.00401619 ETH | ||||
| Deposit | 23837434 | 52 mins ago | 0.003 ETH | ||||
| 0x00000000 | 23837431 | 52 mins ago | 0.032881 ETH | ||||
| Swap Exact Amoun... | 23837431 | 52 mins ago | 0.032881 ETH | ||||
| Transfer | 23837423 | 54 mins ago | 0.0136289 ETH | ||||
| Transfer | 23837423 | 54 mins ago | 0.0136289 ETH | ||||
| 0x00000000 | 23837347 | 1 hr ago | 0.6 ETH | ||||
| 0x00000000 | 23837284 | 1 hr ago | 0.015 ETH | ||||
| Transfer | 23837271 | 1 hr ago | 0.0364063 ETH | ||||
| Transfer | 23837271 | 1 hr ago | 0.0364063 ETH | ||||
| Deposit | 23837250 | 1 hr ago | 0.0007 ETH | ||||
| Deposit | 23837220 | 1 hr ago | 0.26296999 ETH |
Loading...
Loading
Cross-Chain Transactions
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 1000000 runs
Other Settings:
shanghai 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": 1000000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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
34620009f057620070e3388190036101a0601f8201601f19168101906001600160401b03821190821017620009b2576101609282916040526101a03912620009f0576200004e6101a062000a34565b6200005b6101c062000a34565b90620000696101e062000a34565b906200007761020062000a34565b610220516102405161026051610280519294919390916200009a6102a062000a34565b96620000a86102c062000a34565b620000b56102e062000a34565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039586166001600160a01b03198216811790925591949091167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36200012662000a14565b9960018b525f5b60208110620009c657506200014162000a14565b60018152602036818301376307e4c70760e21b6200015f8262000a49565b526200016a620009f4565b6001600160a01b0390921682525f602083015260408201526200018d8b62000a49565b52620001998a62000a49565b50604051986001600160401b0360208b01908111908b1117620009b25794898989898e94602085016040525f85525f995b86518b1015620006c2576020620001e28c8962000a6b565b5101516003811015620006ae57806200038957506001600160a01b036200020a8c8962000a6b565b5151169960406200021c8d8a62000a6b565b510151996200022e8b51151562000ac0565b6200023b8c151562000b21565b6001600160a01b038c165f9081525f80516020620070c383398151915260205260409020546001600160601b0316998a1562000378575b5f9a5b8c518c10156200034e576001600160e01b0319620002948d8f62000a6b565b51165f8181525f805160206200708383398151915260205260409020546001600160a01b0316620002e357818f620002d490600194620002da9462000fda565b62000b83565b9b019a62000275565b60405162461bcd60e51b815260206004820152603560248201527f4c69624469616d6f6e644375743a2043616e2774206164642066756e6374696f60448201527f6e207468617420616c72656164792065786973747300000000000000000000006064820152608490fd5b5094995094995094995094996001909b91969b5b0199949a95909a989398979297969196620001ca565b620003838d62000eff565b62000272565b6001819b939597999b9a929496989a145f146200053b5750918a97959391620003d39c9a99979593604060018060a01b03620003c68c8c62000a6b565b5151169e8f9b8b62000a6b565b5101519a620003e58c51151562000ac0565b620003f28b151562000b21565b6001600160a01b038b165f9081525f80516020620070c383398151915260205260409020546001600160601b03169a8b1562000529575b505f9a5b8c518c101562000511578f908d6200044e8e63ffffffff60e01b9262000a6b565b51165f8181525f805160206200708383398151915260205260409020546001600160a01b03169190838314620004a657600193828262000497620002d4946200049d9762000bf7565b62000fda565b9b019a6200042d565b60405162461bcd60e51b815260206004820152603860248201527f4c69624469616d6f6e644375743a2043616e2774207265706c6163652066756e60448201527f6374696f6e20776974682073616d652066756e6374696f6e00000000000000006064820152608490fd5b50949950949950949991969b50949960019062000362565b620005349062000eff565b8e62000429565b60020362000659576001600160a01b03620005578c8b62000a6b565b515116996040620005698d8c62000a6b565b5101519a6200057b8c51151562000ac0565b620005ee575f5b8b51811015620005d85780620005d18d620005a860019463ffffffff60e01b9262000a6b565b5116805f525f8051602062007083833981519152602052838060a01b0360405f20541662000bf7565b0162000582565b509295989b9194979a60019194979a5062000362565b60405162461bcd60e51b815260206004820152603660248201527f4c69624469616d6f6e644375743a2052656d6f7665206661636574206164647260448201527f657373206d7573742062652061646472657373283029000000000000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152602760248201527f4c69624469616d6f6e644375743a20496e636f727265637420466163657443756044820152663a20b1ba34b7b760c91b6064820152608490fd5b634e487b7160e01b5f52602160045260245ffd5b8989898e898b60405191606083016060845282518091526080840190602060808260051b8701019401915f905b828210620009185750505050916200073881927f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb673945f6020850152838203604085015262000a80565b0390a16001600160a01b0390811660805290811660a0521660c05260e05261010052610140526101205261018091825261016052604051615ff2918262001091833960805182818161063701528181612eb30152818161302c015281816131100152818161332e01526133c0015260a051828181610f1701528181615bef0152615e76015260c05182818161038d015281816104ec01528181610d2701528181610e1401528181611046015281816110ef01528181611215015281816114ac01528181611a8301528181611b8a01528181611e0301528181611ed5015281816125fb0152818161274a015281816128e301528181612f2d01528181613188015281816132540152818161344301528181613a9b01526155e3015260e05182818161093c015281816121c40152818161376801526158b8015261010051828181611572015281816117180152818161185d01526152d101526101205182818161150501528181614ece01528181615786015261581c0152610140518281816108cf01528181614ead0152818161574d01526157e901526101605182818161143f015281816120bc01528181613f8c01528181614ab70152614b3c01525181818161083e0152818161207f01528181613f5401528181614a780152614b040152f35b868603607f19018152835180516001600160a01b031687526020810151949693949293919260608301916003821015620006ae57604060809160209384870152015193606060408201528451809452019201905f905b8082106200098e57505050602080600192970192019201909291620006ef565b82516001600160e01b0319168452602093840193909201916001909101906200096e565b634e487b7160e01b5f52604160045260245ffd5b808c60208093620009d6620009f4565b925f84525f8385015260606040850152010152016200012d565b5f80fd5b60405190606082016001600160401b03811183821017620009b257604052565b60408051919082016001600160401b03811183821017620009b257604052565b51906001600160a01b0382168203620009f057565b80511562000a575760200190565b634e487b7160e01b5f52603260045260245ffd5b805182101562000a575760209160051b010190565b91908251928382525f5b84811062000aab575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520162000a8a565b1562000ac857565b60405162461bcd60e51b815260206004820152602b60248201527f4c69624469616d6f6e644375743a204e6f2073656c6563746f727320696e206660448201526a1858d95d081d1bc818dd5d60aa1b6064820152608490fd5b1562000b2957565b60405162461bcd60e51b815260206004820152602c60248201527f4c69624469616d6f6e644375743a204164642066616365742063616e2774206260448201526b65206164647265737328302960a01b6064820152608490fd5b6001600160601b0390811690811462000b9c5760010190565b634e487b7160e01b5f52601160045260245ffd5b919091805483101562000a57575f52601c60205f208360031c019260021b1690565b5f80516020620070a3833981519152805482101562000a57575f5260205f2001905f90565b6001600160a01b0390811691821562000e945730831462000e385763ffffffff60e01b809116805f525f805160206200708383398151915293602090858252604093845f205460a01c96825f525f80516020620070c383398151915294858552865f2054925f19998a850194851162000b9c57889187898888850362000da6575b9450505050505f52858552865f208054801562000d3c578a019062000c9e828262000bb0565b63ffffffff82549160031b1b19169055555f5283525f858120551562000cc7575b505050505050565b5f80516020620070a383398151915294855487810190811162000b9c57825f52848452816001875f2001549180830362000d50575b505050855495861562000d3c575f97600197019162000d1b8362000bd2565b909182549160031b1b1916905555855252822001555f808080808062000cbf565b634e487b7160e01b5f52603160045260245ffd5b62000d5b9062000bd2565b90549060031b1c1662000d918162000d738462000bd2565b90919060018060a01b038084549260031b9316831b921b1916179055565b5f528484526001865f2001555f818162000cfc565b62000de18562000e2c9762000dfe94845f5280875262000dc98d835f2062000bb0565b90549060031b1c60e01b9687955f52525f2062000bb0565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b165f90815284885289902080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b865f8087898862000c78565b60405162461bcd60e51b815260206004820152602e60248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f766520696d6d7560448201526d3a30b1363290333ab731ba34b7b760911b6064820152608490fd5b60405162461bcd60e51b815260206004820152603760248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f76652066756e6360448201527f74696f6e207468617420646f65736e27742065786973740000000000000000006064820152608490fd5b62000f09620009f4565b602481527f4c69624469616d6f6e644375743a204e657720666163657420686173206e6f20602082015263636f646560e01b6040820152813b1562000fb057505f80516020620070a383398151915280546001600160a01b0383165f9081525f80516020620070c383398151915260205260409020600101819055919068010000000000000000831015620009b2578262000d7391600162000fae9501905562000bd2565b565b60405162461bcd60e51b81526020600482015290819062000fd690602483019062000a80565b0390fd5b6001600160e01b031981165f8181525f80516020620070838339815191526020819052604090912080546001600160a01b031660a09590951b6001600160a01b0319169490941790935590926001600160a01b03165f8181525f80516020620070c383398151915260205260409020805491949068010000000000000000831015620009b2578262000de1916001620010769501815562000bb0565b5f5260205260405f209060018060a01b031982541617905556fe6080604052600436101561001d575b366135435761001b61353a565b005b5f3560e01c80631a01c5321461023757806342f3b24114610232578063523819fb1461022d57806355c0bff0146102285780635c8b5f44146102235780635c975abb1461021e5780635e94e28d1461021957806366c31b841461021457806367d817401461020f5780636a6f511a1461020a5780636afdd850146102055780637f45767514610200578063838bf44d146101fb578063876a02f6146101f65780638940192a146101f157806390a0c0ea146101ce5780639facd044146101ec578063a76f4eb6146101e7578063aad8a491146101e2578063ad5c4648146101dd578063b613cc8a146101d8578063bc163846146101d3578063c8e416dd146101ce578063d6ed22e6146101c9578063d85ca173146101c4578063da35bb0d146101bf578063e37ed256146101ba578063e3ead59e146101b5578063e65dc2f2146101b0578063e8bb3b6c146101ab578063ed386afa146101a6578063f25f4b56146101a1578063fa461e331461019c5763fe12941f0361000e5761236c565b612015565b611fc4565b611f8b565b611d3d565b611cec565b611be5565b611958565b6118b2565b611787565b61163a565b610f3b565b611528565b6114d0565b611462565b61140a565b610faf565b610f75565b610ecd565b610c20565b610be6565b610a15565b6108f2565b61089a565b610861565b610809565b610796565b6106e4565b610679565b6105ed565b6105b4565b61057a565b61026e565b9181601f8401121561026a5782359167ffffffffffffffff831161026a576020838186019501011161026a57565b5f80fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601610160811261026a576101201361026a576101443567ffffffffffffffff811161026a576102c46004913690830161023c565b9060ff60025460a01c16610547576102da6123be565b926102e36123cd565b906084359360a435946102f46123fb565b94833590871561051e5773ffffffffffffffffffffffffffffffffffffffff9889881615610516575b916002949391610385938b8316809260018560a01c169060038660a11c169861034688866135f5565b6104db57876101018210156104c95750806104b8575b505061036a8630338661386a565b505b6104a8575b50505b8460036024359360a31c169161390c565b0361049857847f0000000000000000000000000000000000000000000000000000000000000000166103bf6103ba3083613acb565b612442565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af180156104935761047a575b5047935b84106104525761044e61043160c435866101243586888b16613bb9565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761048d926124ae565b80610570565b5f610410565b6124e3565b6104a23083613acb565b93610414565b6104b191613660565b5f81610371565b6104c291856137b8565b5f8061035c565b90916104d692309161371f565b61036c565b50509150501561037457610511818c7f000000000000000000000000000000000000000000000000000000000000000016613660565b610374565b33975061031d565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b5f91031261026a57565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516121348152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160648152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026a57565b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5773ffffffffffffffffffffffffffffffffffffffff6004356106c98161065b565b165f525f602052602060ff60405f2054166040519015158152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060ff60025460a01c166040519015158152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9160608383011261026a5760043567ffffffffffffffff9384821161026a5761010090828503011261026a57600401926024359260443591821161026a576107929160040161023c565b9091565b61079f36610727565b9060ff60029493945460a01c166107df5761044e936107bd9361256b565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160c88152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b359061096b8261065b565b565b906101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261026a576004356109a58161065b565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026a57602491610104359167ffffffffffffffff916101243583811161026a57826109fa9160040161023c565b939093926101443591821161026a576107929160040161023c565b610a1e3661096d565b909192959360ff60025460a01c166107df57610a3c602087016123f1565b610a45876123f1565b604088013593606089013595610a5d60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610bbc578c8b1615610bb4575b8815610b8a57610a983086613acb565b99610aa389876135f5565b610b6f579282610ada9592858b80966101018f99105f14610b5f57505080610b4e575b5050610ad48482338a61386a565b50614865565b610b02610ae73084613acb565b96610af23084613acb565b906001811115610b465790612474565b948610610b1c5761044e9860806107bd99013597166144dd565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b505f90612474565b610b5891896137b8565b5f80610ac6565b9091610b6a9361371f565b614865565b505097610ada928892610b8489933490612474565b9a614865565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a50610a88565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516127108152f35b610c2936610727565b60ff60025460a01c166107df57610c3f846123f1565b92610c4c602086016123f1565b60409485870135606088013594610c6560c08a016123f1565b96610c7360e08b018b6124ee565b8895919515610ea45773ffffffffffffffffffffffffffffffffffffffff95868b1615610e9c575b3392610ca787826135f5565b610e1157610cc896610cc3916101018810610df1575b50613ef9565b61489b565b938410610dc85773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610d20575b90610d0494939291608061044e9801359416614b83565b9251918252602082015260408101919091529081906060820190565b93929190847f00000000000000000000000000000000000000000000000000000000000000001694853b1561026a575f875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610d8c8a600483019190602083019252565b03925af180156104935761044e98610d0497608092610db5575b50985050909192939450610ced565b80610487610dc2926124ae565b5f610da6565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610e00575b5050610cbd565b610e09926137b8565b5f8787610df9565b507f000000000000000000000000000000000000000000000000000000000000000087169250823b1561026a575f869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561049357610cc896610e89575b50610cc33093613ef9565b80610487610e96926124ae565b5f610e7e565b339a50610c9b565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516113888152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516109c48152f35b610fb836610727565b9260ff60025460a01c166107df57610fcf816123f1565b93610fdc602083016123f1565b936040948584013592606085013597610ff760c087016123f1565b9861100560e08801886124ee565b9190938115610ea45773ffffffffffffffffffffffffffffffffffffffff93848d1615611402575b61103789826135f5565b1580159b906113aa57505050827f000000000000000000000000000000000000000000000000000000000000000016936110713086613acb565b94803b1561026a578b517fd0e30db00000000000000000000000000000000000000000000000000000000081525f816004818d865af1801561049357611397575b50905b848716938583169185831461136e57906110d0918486614ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361135d57847f000000000000000000000000000000000000000000000000000000000000000016809114611334576111203082613acb565b90838211611324575b803b1561026a578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611311575b5047995b6111833083613acb565b928b106112e857156112bc57506111a6904794600181115f14610b465790612474565b60018111611206575b50916111e29795939160806111d761044e9c999795934790600181115f14610b465790612474565b965b013597166144dd565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161123b817f0000000000000000000000000000000000000000000000000000000000000000169b612442565b9a803b1561026a578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c525f908c90602490829084905af19a8b15610493576111d76111e29a60809261044e9e6112a9575b50939597999c505050919395976111af565b806104876112b6926124ae565b5f611297565b91509160806112e26111e29a98969461044e9d9a9896600181115f14610b465790612474565b966111d9565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761131e926124ae565b5f611175565b9061132e90612a14565b90611129565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506113683087613acb565b99611179565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b806104876113a4926124ae565b5f6110b2565b6113b8969192963084613acb565b968a6101018210156113f05750806113df575b50506113d98930338561386a565b506110b5565b6113e991846137b8565b5f806113cb565b90916113fd92309161371f565b6110b5565b339c5061102d565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261026a57600490565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8101610100811261026a5760a01361026a5760049160a4359167ffffffffffffffff9160c43583811161026a57826116209160040161023c565b9390939260e43591821161026a576107929160040161023c565b611643366115c5565b949360ff60025460a01c166107df5784359060208601359261166981608089013561518d565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610bbc578915610b8a578d161561177f575b6116a93086613acb565b996116b489876135f5565b611766576116eb949392919089610101821015611754575080611743575b50506116e08830338861386a565b505b611712576152c2565b6116f8610ae73084613acb565b948610610b1c5761044e9860406107bd99013597166144dd565b61173e8c7f00000000000000000000000000000000000000000000000000000000000000001685613660565b6152c2565b61174d91876137b8565b5f806116d2565b909161176192309161371f565b6116e2565b50505096906117796116eb923490612474565b976152c2565b339a5061169f565b611790366115c5565b90949360ff60025460a01c166107df578435926020860135926117b788608089013561518d565b909a9291948b988815610b8a5773ffffffffffffffffffffffffffffffffffffffff809d16156118aa575b611804969798999a6117f482866135f5565b15611828575b50505050506152c2565b61180e3082613acb565b928310610b1c5761044e9560406104319601359416613bb9565b61010183101561189a578261184693611889575b505030338561386a565b505b611856575b808080806117fa565b611883908a7f00000000000000000000000000000000000000000000000000000000000000001690613660565b5f61184d565b61189391866137b8565b5f8061183c565b6118a592309161371f565b611848565b3399506117e2565b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576118e536611596565b67ffffffffffffffff60a43581811161026a573660238201121561026a57806004013582811161026a573660248260051b8401011161026a5760c43592831161026a576119469361193c602494369060040161023c565b9490930190612a3f565b60408051928352602083019190915290f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36016101a0811261026a576101601361026a576101843567ffffffffffffffff811161026a576119ae6004913690830161023c565b60ff60025460a01c16610547576119c36123d9565b926119cc6123e5565b9060c4359260e435946119dd612408565b946119e66123cd565b9184358815611bbc5773ffffffffffffffffffffffffffffffffffffffff998a891615611bb4575b918160029695938c611a7b969416809360018460a01c169060038560a11c1699611a3888866135f5565b611b795787610101821015611b67575080611b56575b5050611a5c8630338661386a565b505b611b46575b50505b604435918660036024359360a31c1691615431565b03611b3657847f000000000000000000000000000000000000000000000000000000000000000016611ab06103ba3083613acb565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af1801561049357611b23575b5047935b84106104525761044e61043161010435866101643586888b16613bb9565b80610487611b30926124ae565b5f611b01565b611b403083613acb565b93611b05565b611b4f91613660565b5f82611a63565b611b6091856137b8565b5f80611a4e565b9091611b7492309161371f565b611a5e565b505091505015611a6657611baf828d7f000000000000000000000000000000000000000000000000000000000000000016613660565b611a66565b339850611a0e565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b611bee3661096d565b959260ff60029593955460a01c166107df57611c0c602087016123f1565b91611c16876123f1565b97604088013591606089013596611c2f60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611ce4575b8915610b8a578281611c7698611c668980956135f5565b15611c9a575b5050505050615616565b611c803082613acb565b928310610b1c5761044e9560806104319601359416613bb9565b610101851015611cd45784611cb795611cc3575b5050339061386a565b505b5f84828280611c6c565b611ccd91836137b8565b5f80611cae565b611cdf94915061371f565b611cb9565b339a50611c4f565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611d4636610727565b9160ff60025460a01c166107df57611d5d846123f1565b93611d6a602082016123f1565b906040948582013590606083013595611d8560c085016123f1565b98611d9360e08601866124ee565b9033928a15611f625773ffffffffffffffffffffffffffffffffffffffff9796959493929190888e1615611f5a575b611dcc87826135f5565b611ed257611de3966101018710611ec1575b61563c565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611eb157817f000000000000000000000000000000000000000000000000000000000000000016611e306103ba3083613acb565b90803b1561026a5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611e9e575b5047945b8510610dc8579161044e9693916080610d04969401359416613bb9565b80610487611eab926124ae565b5f611e7d565b611ebb3084613acb565b94611e81565b8615611dde57611dde8787846137b8565b507f00000000000000000000000000000000000000000000000000000000000000008816959250853b1561026a578b51957fd0e30db00000000000000000000000000000000000000000000000000000000087525f8760048187855af196871561049357611de397611f47575b50309361563c565b80610487611f54926124ae565b5f611f3f565b339d50611dc2565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576020604051600b8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026a5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576004602435813560443567ffffffffffffffff811161026a5761206a903690850161023c565b91909260ff60025460a01c16612343576040517f00000000000000000000000000000000000000000000000000000000000000008152610200841460158201605f6041880160168501376060812090527f000000000000000000000000000000000000000000000000000000000000000060358301526055822080925273ffffffffffffffffffffffffffffffffffffffff809216331861231c5760a0851180612314575b15612148575061001b9550505f821315612138575061212d9061253f565b915b60a43592613f31565b612142915061253f565b9161212f565b935093905f915f93604051965f8213612302575b50505f82136122f2575b505060a435923084146001146122a8571561221257507f30f28b7a0000000000000000000000000000000000000000000000000000000083525f928392610164926101606101248885013733608484015260a483015260c4820152827f00000000000000000000000000000000000000000000000000000000000000005af1156121ec57005b7f6b836e6b000000000000000000000000000000000000000000000000000000005f525ffd5b92805f926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081612286575b501561226057005b7f1bbb4abe000000000000000000000000000000000000000000000000000000005f525ffd5b90503d156122a0575060015f5114601f3d11165b5f612258565b3b151561229a565b509260209250805f927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af1908161228657501561226057005b9092506060915001355f80612166565b90945060408201351692505f8061215c565b50801561210f565b867f48f5c3ed000000000000000000000000000000000000000000000000000000005f525ffd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356123ca8161065b565b90565b6064356123ca8161065b565b6084356123ca8161065b565b60a4356123ca8161065b565b356123ca8161065b565b610104356123ca8161065b565b610144356123ca8161065b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161246f57565b612415565b9190820391821161246f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116124c257604052565b612481565b6060810190811067ffffffffffffffff8211176124c257604052565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026a570180359067ffffffffffffffff821161026a5760200191813603831361026a57565b7f8000000000000000000000000000000000000000000000000000000000000000811461246f575f0390565b91612575836123f1565b91612582602085016123f1565b9061258f60c086016123f1565b9161259d60e08701876124ee565b97606088013515610b8a5773ffffffffffffffffffffffffffffffffffffffff851615612a0c575b336125d460408a0135896135f5565b1515975f97895f146129a0575050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016946126263087613acb565b98863b1561026a5760405f8a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af180156104935761298d575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610bbc5787826126c5926126c06126bb60608f0135613ef9565b61253f565b613f31565b8099919360608c01358210610b1c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146128cc575b73ffffffffffffffffffffffffffffffffffffffff1630036128755750501561284257505050479361277573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001698610af2308b613acb565b94600186116127b4575b506127ac97505b73ffffffffffffffffffffffffffffffffffffffff6040608089013598013594166144dd565b929391929091565b90946127bf90612442565b97803b1561026a576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099525f908990602490829084905af1908115610493576127ac986128289261282f575b5047906001811115610b465790612474565b935f61277f565b8061048761283c926124ae565b5f612816565b6127ac9992965060601015612866575061286090610af23087613acb565b93612786565b61286091506040880135612474565b9399509750506127ac99506060106128af575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614144565b6128c59196506128bf3388613acb565b90612474565b945f612888565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168073ffffffffffffffffffffffffffffffffffffffff891614610bbc57803b1561026a575f60405180927f2e1a7d4d0000000000000000000000000000000000000000000000000000000082528183816129658a600483019190602083019252565b03925af180156104935761297a575b50612708565b80610487612987926124ae565b5f612974565b8061048761299a926124ae565b5f61266d565b6129b29b9298939b9491943086613acb565b9b6101018110156129f157806129e0575b505060608211156126725791506129da3384613acb565b91612672565b6129ea91866137b8565b5f806129c3565b90612a0592995060408c013591309161371f565b3096612672565b3394506125c5565b801561246f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c166107df5761079294612db2565b3560ff8116810361026a5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9015612acd578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b612a67565b9190811015612acd5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026a57016020813591019167ffffffffffffffff821161026a57813603831361026a57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481945f925b848410612bfc575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026a5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026a5784612d82612d5b8695612d188f612cf18e612da1998b60019e0152612cca6040612cae818c01610960565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b612cd5818a01610960565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b612cfc818801610960565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612d2860a0612cfc818801610960565b60c0808601359085015260e080860135908501526101009080612d4d83880188612b12565b929093870152850191612b62565b6101208085013590840152610140612d7581860186612b12565b9185840390860152612b62565b91612d936101609182810190612b12565b929091818503910152612b62565b990193019401929195949390612bc9565b9493929192612dc3608087016123f1565b91863594602088013594612dda6040809a01612a59565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613532575b8815611f62578415613509575f5b8581106134ed575050612e45612e2c6060612e268789612a94565b016123f1565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612e57612e2c8d612e26888a612a94565b936003811693600185149283613437573461340e5760019291908d6101018210156133fc5750806133eb575b5050612e918c30338761386a565b505b818160021c166133ba575b60031c166130fb5750506002036130165750827f00000000000000000000000000000000000000000000000000000000000000001691823b1561026a57612f1a92875f80948c51968795869485937f1c64b820000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613003575b50807f00000000000000000000000000000000000000000000000000000000000000001692612f583085613acb565b908110612fda57612f6890612a14565b95833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052925f908490602490829084905af191821561049357612fc2938793612fc7575b5016615350565b509190565b80610487612fd4926124ae565b5f612fbb565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b80610487613010926124ae565b5f612f29565b9190838599969916916130298385613acb565b947f000000000000000000000000000000000000000000000000000000000000000016803b1561026a575f92838a936130908b519a8b96879586947f1c64b82000000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af1918215610493576130b3946130ae936130e8575b50613acb565b612474565b9384106130bf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b806104876130f5926124ae565b5f6130a8565b9793925099979593906002145f1461332757847f000000000000000000000000000000000000000000000000000000000000000016803b1561026a57613175935f80948b51968795869485937f01fb36ba000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613314575b50817f000000000000000000000000000000000000000000000000000000000000000016906131bb6131b63084613acb565b612a14565b91803b1561026a575f875180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161320089600483019190602083019252565b03925af180156104935761321d9284928692612fc7575016615350565b50955b61322a3082613acb565b9360018511613245575b5050506132419250612474565b9190565b939493156132f3575061327a907f00000000000000000000000000000000000000000000000000000000000000001693612a14565b91833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052925f908490602490829084905af192831561049357613241936132e0575b506132d68233615350565b505b5f8080613234565b806104876132ed926124ae565b5f6132cb565b92505061330e61330561324194612a14565b8093339061538b565b506132d8565b80610487613321926124ae565b5f613184565b84999392997f000000000000000000000000000000000000000000000000000000000000000016803b1561026a575f92838c936133928c51978896879586947f01fb36ba00000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af18015610493576133a7575b50613220565b806104876133b4926124ae565b5f6133a1565b6133e6897f00000000000000000000000000000000000000000000000000000000000000001685613660565b612e9e565b6133f591866137b8565b5f80612e83565b909161340992309161371f565b612e93565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036134c457877f00000000000000000000000000000000000000000000000000000000000000001690813b1561026a575f8c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610493576001926134b1575b50612e93565b806104876134be926124ae565b5f6134ab565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806135036134fd8493898b612ad2565b35615302565b01612e0b565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612dfd565b333b1561026a57565b7fffffffff000000000000000000000000000000000000000000000000000000005f35165f527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60405f20541680156135cb575f8091368280378136915af43d5f803e156135c7573d5ff35b3d5ffd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b91905f9273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9182821461364e575b501861361f57565b3461362657565b7f8b6ebb4d000000000000000000000000000000000000000000000000000000005f5260045ffd5b9093503418613626576001925f613617565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90816034526f095ea7b300000000000000000000000090815f5260205f6044601082855af13d1560015f51141716156136c2575b5050505f603452565b60105f60449260209582958360345283528238868683865af1506034525af13d1560015f51141716156136f7575f80806136b9565b7f8164f842000000000000000000000000000000000000000000000000000000005f5260045ffd5b906004905f94859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f00000000000000000000000000000000000000000000000000000000000000005af11561379057565b7f6b836e6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b918060e01461382e57610100146137f1577fb78cb0dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101045f9182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e45f918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015260205f60648382875af192836138e7575b5082156138bf5750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d15613903575060015f5114601f3d1116915b5f6138b5565b3b1515916138fd565b919293906040519360018214613a57575b915f95608494928796946001146139fb5760031460011461399b577f3df02124000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b3d5f803e3d5ffd5b6fffffffffffffffffffffffffffffffff907f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af161096b573d5f803e3d5ffd5b507fa6417ed6000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b929093917fd0e30db00000000000000000000000000000000000000000000000000000000083525f806004853473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af115613993579193909261391d565b5f9291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613b2f576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613b2a575b50565b519150565b31925050565b90600b820180921161246f57565b9190820180921161246f57565b908160640291606483040361246f57565b906113889182810292818404149015171561246f57565b906109c49182810292818404149015171561246f57565b906121349182810292818404149015171561246f57565b8181029291811591840414171561246f57565b9091949392613bda5f96906bffffffffffffffffffffffff8260601c921690565b9490613be582613b35565b8311613eb3575b613bf68884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216613cb5575b505086159050613c9b57613c4e613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b5460ff1690565b613c9b57508482613c6785613c8f94613c959796615968565b613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b9061538b565b50612442565b91905f90565b9250613cae93945082906103ba92615968565b905f905f90565b6b2000000000000000000000008199949395979699161594851594613cfa613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680613eac575b613e93576b0800000000000000000000008316151597613d2084615995565b938415613da1575050948a94613d8c94613d6e613d66613d949b9686613d849f9e9c979b613d51816103ba9e613b43565b871115613d9957613d6191613b43565b613ba6565b612710900490565b90613d7b613d6683613b8f565b9c8d8093612474565b9d8e93615ad2565b928391615968565b929190565b505084613ba6565b80929c99969794506b400000000000000000000000919a989593501615155f14613e0f575089613dd857505050505b5f8080613c13565b8698975091613d949693918a6103ba96613d8c95613e07613dff613d669f613d6690613b61565b9e8f94613b78565b9c8d92615ad2565b939291906b8000000000000000000000008516613e31575b5050505050613dd0565b8a15613e27579092948a92949998506b040000000000000000000000613e5f613d66613e689d9a999a613b61565b9b8c8095612474565b9a16613e87575b926103ba95928a889693613d949a99613d8c97615ad2565b5f995060019550613e6f565b505050509390506103ba9250839150613cae9495615968565b5086613d01565b9650613ebf8183612474565b966b100000000000000000000000861615613bec5796613ee1613d6683613b50565b9081811115613ef257505b96613bec565b9050613eec565b7f8000000000000000000000000000000000000000000000000000000000000000811015613f245790565b6335278d125f526004601cfd5b939192908094845f92606082066140ee575b50505f6040949596855197889586947f0000000000000000000000000000000000000000000000000000000000000000865260158601605f6001860160168901376060812090527f0000000000000000000000000000000000000000000000000000000000000000603587015273ffffffffffffffffffffffffffffffffffffffff6055872016968030916140e6575b50843560ff1c861461405d577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca60208301519251925b5f03615d3d565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca6020835193015192614056565b90505f613fd3565b60a0821115613f435760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff1691505f6040613f43565b919093979594979692965f9761417561415d8884612474565b91906bffffffffffffffffffffffff8260601c921690565b909286116144b3578a9561418889613b35565b811161446d575b73ffffffffffffffffffffffffffffffffffffffff8416614249575b50505050916141bc9187949361538b565b50816141d4575b506141cd91613b43565b9291905f90565b9050614200613c478273ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b61423f576141cd91614238858093614230612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b90339061386a565b50916141c3565b509291505f908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591614292613c478d73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9380614466575b614448576b08000000000000000000000082161515946142b883615995565b92831561431d575050906142d6918088105f14613d61575086613ba6565b6127109004996142e58b613b8f565b61271090046142f581809d612474565b9c8d923361430298615d47565b9061430c91613b43565b926143169261538b565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156143995750614363575050505050916141bc918794935b91939481935f6141ab565b909192939998949695979a61437781613b61565b61271090049b6143878d92613b78565b61271090049b8c913361430298615d47565b946b800000000000000000000000869b9a999897929b166143c6575b505050505050906141bc9291614358565b909192939495969798996143df578c99989796956143b5565b988c929394959b9a96996b04000000000000000000000061440661440f9f613d6690613b61565b9e8f8096612474565b9d1661443c575b928c6143169a99989693614431969361443699963390615d47565b613b43565b9361538b565b5f9c5060019650614416565b5050505050509450919061445d93955061538b565b5091905f905f90565b5083614299565b9950614479888b612474565b996b10000000000000000000000082161561418f579961449b613d668c613b50565b90818111156144ac57505b9961418f565b90506144a6565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315155f1461485c576144ff6144f985612442565b87612474565b5f98606081901c906bffffffffffffffffffffffff1690928881116144b35761452783613b35565b8111614816575b73ffffffffffffffffffffffffffffffffffffffff84166145f8575b505050509061455b61456392612a14565b97889161538b565b5084156145df57614594613c478373ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b6145df576141cd92916145d7866128bf936145d1826145cb612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8661538b565b50612474565b903390615fa1565b6145f09394506128bf913390615fa1565b91905f905f90565b6b20000000000000000000000082161591821591614636613c478b73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b938061480f575b6147e7576b080000000000000000000000821615159461465c83615995565b9182614798575050506b4000000000000000000000008116156146f757508a61469557505050509061455b614563925b91925f8061454a565b8a6146a78996989c9b9a99959b613b61565b61271090049b6146b78d92613b78565b61271090049b6146c8968d92615ad2565b336146d292615fa1565b916146dc90612a14565b80976146e79261538b565b506146f191612474565b93929190565b9291936b8000000000000000000000008416614720575b50505050509061455b6145639261468c565b8b1561470e578b919293999695976b04000000000000000000000061474e613d666147579f9e9c989e613b61565b9d8e8095612474565b9b1661478c575b938a9b61477e9487946146f19c9d6145d7956145d19b9a6147849a615ad2565b94612a14565b98899161538b565b5f9a506001945061475e565b8b989a9d9c9b979e506147b593508082105f146147e05750613ba6565b6127109004996147c48b613b8f565b61271090046147d481809d612474565b9c6146c8968e93615ad2565b9050613ba6565b50505050505093926145f095965061477e6145d193614807923390615fa1565b96879161538b565b508361463d565b9950614822828b612474565b996b10000000000000000000000082161561452e5799614844613d668c613b50565b908181111561485557505b9961452e565b905061484f565b6144ff846144f9565b91805f958695606493607c95608037608083015260a08201523360c0820152019134905af11561489157565b3d5f607c3e3d607cfd5b94919392935f925f945f93604051975f975b6060860489106148ca57505050505050505050506123ca90615d3d565b909192939495969798809b9a8915614afe575b6060880460018c0110614a71575b8a614a69575b8a60a061016492896001610100821114614a5b575b506060830288013560ff1c156149be579460608086945f946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146149b1575b5af1156139935760208a0151945b600187965f0399019796959a98999a9493929190946148ad565b8989610164850137614989565b9460608086945f946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614a4e575b5af11561399357895194614997565b8989610164850137614a3f565b809192940193019089614906565b3093506148f1565b97505096507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916976148eb565b985050507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f6001840160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916916148dd565b9091949392614ba45f96906bffffffffffffffffffffffff8260601c921690565b9490614baf82613b35565b8311614e2b575b614bc08884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216614c67575b505086159050614c5057614c11613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b614c50575081614c26848793614c499561538b565b50613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b5091905f90565b939450614c5f9250839161538b565b50905f905f90565b6b2000000000000000000000008199949395979699161594851594614cac613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680614e24575b614e0e576b0800000000000000000000008316151597614cd284615995565b938415614d2c57505093614d1699989793614d00613d66614d269995858f9a9699613d5181614d1e9c613b43565b90614d0d613d6683613b8f565b9b8c8093612474565b9c8d93615ad2565b92839161538b565b50929190565b80929c99969794506b400000000000000000000000919a989593501615155f14614d97575089614d6357505050505b5f8080614bdd565b869897509089614d26969795614d1e959493614d8f614d87613d66613d669f613b61565b9d8e94613b78565b9b8c92615ad2565b939291906b8000000000000000000000008516614db9575b5050505050614d5b565b8a15614daf579092948a92949998506b040000000000000000000000613e5f613d66614de79d9a999a613b61565b9a16614e02575b918987969492614d2698614d1e9795615ad2565b5f995060019550614dee565b5050505094959050849250614c5f93915061538b565b5086614cb3565b9650614e378183612474565b966b100000000000000000000000861615614bb65796614e59613d6683613b50565b9081811115614e6a57505b96614bb6565b9050614e64565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000091604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16935f97858501525f5b8360061c8110615169575050508060061c805b61507957505f935f965f955b8360061c8710614f5557505050505050505050565b8015615027575b308460061c600189011061500f575b60a46040925f928a6001810160051b8a01518b8b826020899560061b8d010135600116615004575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561399357600188960195614f40565b935087925081614f93565b508486016001880160051b0160200151985088614f6b565b508385016020818101517fa9059cbb0000000000000000000000000000000000000000000000000000000092880180830193845260248101829052604490810185905290925f9190828c5af150614f5c565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156139935760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c010135851661515e575b50906126f28161271093030292020204018098838301901b8501520180614f34565b9092506126f261513c565b80615187838560019460061b8b0160208560051b8c8c010101614e71565b01614f21565b9190918235807f52bbbe2900000000000000000000000000000000000000000000000000000000146152b0577f945bcec90000000000000000000000000000000000000000000000000000000014615207577f7352d91c000000000000000000000000000000000000000000000000000000005f5260045ffd5b60448301359283810193600485013594600183600401351460011461529d5760059590951b01016004013592602401355b8015615282575b8315615266575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350615246565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61523f565b602401359460051b010160040135615238565b50610144830135926101240135615238565b905f80918060405194853783347f00000000000000000000000000000000000000000000000000000000000000005af1156152fa5750565b3d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff16806153205750565b331861532857565b7f02a43f8b000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8080938193612710f190811561536357565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee841460011461541d5760446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af191826153f8575b505b811561536357565b9091503d15615414575060015f5114601f3d1116905b5f6153ee565b3b15159061540e565b5f809394508092918192612710f1906153f0565b929490936040519460019384821461559c575b905f988998979695949392806001146155515760021461550157506003146001146154b15790869392916084967f5b41b90800000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b60a4957f394747c5000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152806064840152608483015234905af161096b573d5f803e3d5ffd5b889594939291509660a4977f64a14558000000000000000000000000000000000000000000000000000000008852600488015260248701526044860152606485015260848401525af11561399357565b50505090869392916084967f65b2489b00000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b969594939291907fd0e30db00000000000000000000000000000000000000000000000000000000086525f806004883473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1156139935790919293949596615444565b906044835f958695607c9460803760808201523360a0820152019134905af11561489157565b9095929193955f955f945f985f965b8660061c8810615662575050505050505050505050565b89156157e3575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156139935783519084015190818a6157da575b506126f291612710838502910201920202049687905f906157d1575b30918a60061c60018d0110615744575b5f9360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156139935760018a97019661564b565b92939d509b50507f00000000000000000000000000000000000000000000000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f000000000000000000000000000000000000000000000000000000000000000060358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d919093926156e8565b50505f876156d8565b9150905f6156bc565b506040517f000000000000000000000000000000000000000000000000000000000000000081529850601589016028808a8337812090527f000000000000000000000000000000000000000000000000000000000000000060358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a1690308314600114615929576101008411156158e4577f30f28b7a0000000000000000000000000000000000000000000000000000000081525f806004928688858301378460848201528960a48201528560c482015283870190827f00000000000000000000000000000000000000000000000000000000000000005af1156121ec5750615669565b5f6064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615669565b5f6044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615669565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613b2793019161538b565b613fff1660c881116159a45790565b5060c890565b6040519061096b826124c7565b604051906159c4826124c7565b600282526040366020840137565b805115612acd5760200190565b805160011015612acd5760400190565b9081518082526020808093019301915f5b828110615a0e575050505090565b835185529381019392810192600101615a00565b6020808252825160608284015280516080840181905293949360a084019392918201905f5b818110615aa8575050508473ffffffffffffffffffffffffffffffffffffffff6040926123ca96970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526159ef565b825173ffffffffffffffffffffffffffffffffffffffff1686529483019491830191600101615a47565b96909591939294615ae38487613b43565b9283615af457505050505050505090565b9697959681615d35575b5015615b745750505082828111615b4a576123ca9481615b21575b505050612474565b73ffffffffffffffffffffffffffffffffffffffff615b4193169061538b565b505f8080615b19565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b84829693979211615b4a5715615bcf57856123ca96615b9c575b5081615b2157505050612474565b615bc890615bc2612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8361538b565b505f615b8e565b615cc09073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000000000000000000000000000000000000000001694615c1b88878561538b565b50615c56615c276159b7565b99615c306159b7565b9616615c3b8b6159d2565b9073ffffffffffffffffffffffffffffffffffffffff169052565b615c5f856159d2565b52615c8e615c85612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b8a6159df565b615c97846159df565b52615ca06159aa565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026a57615d095f949185926040519687809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1928315610493576123ca93615d225750612474565b80610487615d2f926124ae565b5f6145d1565b90505f615afe565b5f8112613f245790565b969790929594919394615d5a8387613b43565b9889615d6d575b50505050505050505090565b819994959697989991615f99575b5015615dcf5750505082948311615b4a5782615da5575b505050505b5f8080808080808080615d61565b73ffffffffffffffffffffffffffffffffffffffff615dc594169161386a565b505f808080615d92565b9091968711615b4a5715615e515780615e1d575b5082615df3575b50505050615d97565b73ffffffffffffffffffffffffffffffffffffffff615e1394169161386a565b505f808080615dea565b615e4a90615e43612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b838561386a565b505f615de3565b91615f289193949273ffffffffffffffffffffffffffffffffffffffff95615e9e88887f00000000000000000000000000000000000000000000000000000000000000001680988661386a565b50615ebe615eaa6159b7565b97615eb36159b7565b9616615c3b896159d2565b615ec7856159d2565b52615ef6615eed612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b886159df565b615eff846159df565b52615f086159aa565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026a57615f715f929183926040519485809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1801561049357615f86575b50615d97565b80610487615f93926124ae565b5f615f80565b90505f615d7b565b91909160018211615fb3575050505f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615fe1920192839161538b565b509056fea164736f6c6343000816000ac8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8ff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54ff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000096e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0600000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Deployed Bytecode
0x6080604052600436101561001d575b366135435761001b61353a565b005b5f3560e01c80631a01c5321461023757806342f3b24114610232578063523819fb1461022d57806355c0bff0146102285780635c8b5f44146102235780635c975abb1461021e5780635e94e28d1461021957806366c31b841461021457806367d817401461020f5780636a6f511a1461020a5780636afdd850146102055780637f45767514610200578063838bf44d146101fb578063876a02f6146101f65780638940192a146101f157806390a0c0ea146101ce5780639facd044146101ec578063a76f4eb6146101e7578063aad8a491146101e2578063ad5c4648146101dd578063b613cc8a146101d8578063bc163846146101d3578063c8e416dd146101ce578063d6ed22e6146101c9578063d85ca173146101c4578063da35bb0d146101bf578063e37ed256146101ba578063e3ead59e146101b5578063e65dc2f2146101b0578063e8bb3b6c146101ab578063ed386afa146101a6578063f25f4b56146101a1578063fa461e331461019c5763fe12941f0361000e5761236c565b612015565b611fc4565b611f8b565b611d3d565b611cec565b611be5565b611958565b6118b2565b611787565b61163a565b610f3b565b611528565b6114d0565b611462565b61140a565b610faf565b610f75565b610ecd565b610c20565b610be6565b610a15565b6108f2565b61089a565b610861565b610809565b610796565b6106e4565b610679565b6105ed565b6105b4565b61057a565b61026e565b9181601f8401121561026a5782359167ffffffffffffffff831161026a576020838186019501011161026a57565b5f80fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601610160811261026a576101201361026a576101443567ffffffffffffffff811161026a576102c46004913690830161023c565b9060ff60025460a01c16610547576102da6123be565b926102e36123cd565b906084359360a435946102f46123fb565b94833590871561051e5773ffffffffffffffffffffffffffffffffffffffff9889881615610516575b916002949391610385938b8316809260018560a01c169060038660a11c169861034688866135f5565b6104db57876101018210156104c95750806104b8575b505061036a8630338661386a565b505b6104a8575b50505b8460036024359360a31c169161390c565b0361049857847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2166103bf6103ba3083613acb565b612442565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af180156104935761047a575b5047935b84106104525761044e61043160c435866101243586888b16613bb9565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761048d926124ae565b80610570565b5f610410565b6124e3565b6104a23083613acb565b93610414565b6104b191613660565b5f81610371565b6104c291856137b8565b5f8061035c565b90916104d692309161371f565b61036c565b50509150501561037457610511818c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216613660565b610374565b33975061031d565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b5f91031261026a57565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516121348152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160648152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a06168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026a57565b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5773ffffffffffffffffffffffffffffffffffffffff6004356106c98161065b565b165f525f602052602060ff60405f2054166040519015158152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060ff60025460a01c166040519015158152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9160608383011261026a5760043567ffffffffffffffff9384821161026a5761010090828503011261026a57600401926024359260443591821161026a576107929160040161023c565b9091565b61079f36610727565b9060ff60029493945460a01c166107df5761044e936107bd9361256b565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160c88152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f00000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3168152f35b359061096b8261065b565b565b906101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261026a576004356109a58161065b565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026a57602491610104359167ffffffffffffffff916101243583811161026a57826109fa9160040161023c565b939093926101443591821161026a576107929160040161023c565b610a1e3661096d565b909192959360ff60025460a01c166107df57610a3c602087016123f1565b610a45876123f1565b604088013593606089013595610a5d60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610bbc578c8b1615610bb4575b8815610b8a57610a983086613acb565b99610aa389876135f5565b610b6f579282610ada9592858b80966101018f99105f14610b5f57505080610b4e575b5050610ad48482338a61386a565b50614865565b610b02610ae73084613acb565b96610af23084613acb565b906001811115610b465790612474565b948610610b1c5761044e9860806107bd99013597166144dd565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b505f90612474565b610b5891896137b8565b5f80610ac6565b9091610b6a9361371f565b614865565b505097610ada928892610b8489933490612474565b9a614865565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a50610a88565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516127108152f35b610c2936610727565b60ff60025460a01c166107df57610c3f846123f1565b92610c4c602086016123f1565b60409485870135606088013594610c6560c08a016123f1565b96610c7360e08b018b6124ee565b8895919515610ea45773ffffffffffffffffffffffffffffffffffffffff95868b1615610e9c575b3392610ca787826135f5565b610e1157610cc896610cc3916101018810610df1575b50613ef9565b61489b565b938410610dc85773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610d20575b90610d0494939291608061044e9801359416614b83565b9251918252602082015260408101919091529081906060820190565b93929190847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21694853b1561026a575f875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610d8c8a600483019190602083019252565b03925af180156104935761044e98610d0497608092610db5575b50985050909192939450610ced565b80610487610dc2926124ae565b5f610da6565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610e00575b5050610cbd565b610e09926137b8565b5f8787610df9565b507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc287169250823b1561026a575f869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561049357610cc896610e89575b50610cc33093613ef9565b80610487610e96926124ae565b5f610e7e565b339a50610c9b565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516113888152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516109c48152f35b610fb836610727565b9260ff60025460a01c166107df57610fcf816123f1565b93610fdc602083016123f1565b936040948584013592606085013597610ff760c087016123f1565b9861100560e08801886124ee565b9190938115610ea45773ffffffffffffffffffffffffffffffffffffffff93848d1615611402575b61103789826135f5565b1580159b906113aa57505050827f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216936110713086613acb565b94803b1561026a578b517fd0e30db00000000000000000000000000000000000000000000000000000000081525f816004818d865af1801561049357611397575b50905b848716938583169185831461136e57906110d0918486614ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361135d57847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216809114611334576111203082613acb565b90838211611324575b803b1561026a578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611311575b5047995b6111833083613acb565b928b106112e857156112bc57506111a6904794600181115f14610b465790612474565b60018111611206575b50916111e29795939160806111d761044e9c999795934790600181115f14610b465790612474565b965b013597166144dd565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161123b817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169b612442565b9a803b1561026a578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c525f908c90602490829084905af19a8b15610493576111d76111e29a60809261044e9e6112a9575b50939597999c505050919395976111af565b806104876112b6926124ae565b5f611297565b91509160806112e26111e29a98969461044e9d9a9896600181115f14610b465790612474565b966111d9565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761131e926124ae565b5f611175565b9061132e90612a14565b90611129565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506113683087613acb565b99611179565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b806104876113a4926124ae565b5f6110b2565b6113b8969192963084613acb565b968a6101018210156113f05750806113df575b50506113d98930338561386a565b506110b5565b6113e991846137b8565b5f806113cb565b90916113fd92309161371f565b6110b5565b339c5061102d565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b548152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8168152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261026a57600490565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8101610100811261026a5760a01361026a5760049160a4359167ffffffffffffffff9160c43583811161026a57826116209160040161023c565b9390939260e43591821161026a576107929160040161023c565b611643366115c5565b949360ff60025460a01c166107df5784359060208601359261166981608089013561518d565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610bbc578915610b8a578d161561177f575b6116a93086613acb565b996116b489876135f5565b611766576116eb949392919089610101821015611754575080611743575b50506116e08830338861386a565b505b611712576152c2565b6116f8610ae73084613acb565b948610610b1c5761044e9860406107bd99013597166144dd565b61173e8c7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81685613660565b6152c2565b61174d91876137b8565b5f806116d2565b909161176192309161371f565b6116e2565b50505096906117796116eb923490612474565b976152c2565b339a5061169f565b611790366115c5565b90949360ff60025460a01c166107df578435926020860135926117b788608089013561518d565b909a9291948b988815610b8a5773ffffffffffffffffffffffffffffffffffffffff809d16156118aa575b611804969798999a6117f482866135f5565b15611828575b50505050506152c2565b61180e3082613acb565b928310610b1c5761044e9560406104319601359416613bb9565b61010183101561189a578261184693611889575b505030338561386a565b505b611856575b808080806117fa565b611883908a7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81690613660565b5f61184d565b61189391866137b8565b5f8061183c565b6118a592309161371f565b611848565b3399506117e2565b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576118e536611596565b67ffffffffffffffff60a43581811161026a573660238201121561026a57806004013582811161026a573660248260051b8401011161026a5760c43592831161026a576119469361193c602494369060040161023c565b9490930190612a3f565b60408051928352602083019190915290f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36016101a0811261026a576101601361026a576101843567ffffffffffffffff811161026a576119ae6004913690830161023c565b60ff60025460a01c16610547576119c36123d9565b926119cc6123e5565b9060c4359260e435946119dd612408565b946119e66123cd565b9184358815611bbc5773ffffffffffffffffffffffffffffffffffffffff998a891615611bb4575b918160029695938c611a7b969416809360018460a01c169060038560a11c1699611a3888866135f5565b611b795787610101821015611b67575080611b56575b5050611a5c8630338661386a565b505b611b46575b50505b604435918660036024359360a31c1691615431565b03611b3657847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216611ab06103ba3083613acb565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af1801561049357611b23575b5047935b84106104525761044e61043161010435866101643586888b16613bb9565b80610487611b30926124ae565b5f611b01565b611b403083613acb565b93611b05565b611b4f91613660565b5f82611a63565b611b6091856137b8565b5f80611a4e565b9091611b7492309161371f565b611a5e565b505091505015611a6657611baf828d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216613660565b611a66565b339850611a0e565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b611bee3661096d565b959260ff60029593955460a01c166107df57611c0c602087016123f1565b91611c16876123f1565b97604088013591606089013596611c2f60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611ce4575b8915610b8a578281611c7698611c668980956135f5565b15611c9a575b5050505050615616565b611c803082613acb565b928310610b1c5761044e9560806104319601359416613bb9565b610101851015611cd45784611cb795611cc3575b5050339061386a565b505b5f84828280611c6c565b611ccd91836137b8565b5f80611cae565b611cdf94915061371f565b611cb9565b339a50611c4f565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611d4636610727565b9160ff60025460a01c166107df57611d5d846123f1565b93611d6a602082016123f1565b906040948582013590606083013595611d8560c085016123f1565b98611d9360e08601866124ee565b9033928a15611f625773ffffffffffffffffffffffffffffffffffffffff9796959493929190888e1615611f5a575b611dcc87826135f5565b611ed257611de3966101018710611ec1575b61563c565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611eb157817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216611e306103ba3083613acb565b90803b1561026a5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611e9e575b5047945b8510610dc8579161044e9693916080610d04969401359416613bb9565b80610487611eab926124ae565b5f611e7d565b611ebb3084613acb565b94611e81565b8615611dde57611dde8787846137b8565b507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28816959250853b1561026a578b51957fd0e30db00000000000000000000000000000000000000000000000000000000087525f8760048187855af196871561049357611de397611f47575b50309361563c565b80610487611f54926124ae565b5f611f3f565b339d50611dc2565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576020604051600b8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026a5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576004602435813560443567ffffffffffffffff811161026a5761206a903690850161023c565b91909260ff60025460a01c16612343576040517fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008152610200841460158201605f6041880160168501376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460358301526055822080925273ffffffffffffffffffffffffffffffffffffffff809216331861231c5760a0851180612314575b15612148575061001b9550505f821315612138575061212d9061253f565b915b60a43592613f31565b612142915061253f565b9161212f565b935093905f915f93604051965f8213612302575b50505f82136122f2575b505060a435923084146001146122a8571561221257507f30f28b7a0000000000000000000000000000000000000000000000000000000083525f928392610164926101606101248885013733608484015260a483015260c4820152827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af1156121ec57005b7f6b836e6b000000000000000000000000000000000000000000000000000000005f525ffd5b92805f926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081612286575b501561226057005b7f1bbb4abe000000000000000000000000000000000000000000000000000000005f525ffd5b90503d156122a0575060015f5114601f3d11165b5f612258565b3b151561229a565b509260209250805f927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af1908161228657501561226057005b9092506060915001355f80612166565b90945060408201351692505f8061215c565b50801561210f565b867f48f5c3ed000000000000000000000000000000000000000000000000000000005f525ffd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356123ca8161065b565b90565b6064356123ca8161065b565b6084356123ca8161065b565b60a4356123ca8161065b565b356123ca8161065b565b610104356123ca8161065b565b610144356123ca8161065b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161246f57565b612415565b9190820391821161246f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116124c257604052565b612481565b6060810190811067ffffffffffffffff8211176124c257604052565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026a570180359067ffffffffffffffff821161026a5760200191813603831361026a57565b7f8000000000000000000000000000000000000000000000000000000000000000811461246f575f0390565b91612575836123f1565b91612582602085016123f1565b9061258f60c086016123f1565b9161259d60e08701876124ee565b97606088013515610b8a5773ffffffffffffffffffffffffffffffffffffffff851615612a0c575b336125d460408a0135896135f5565b1515975f97895f146129a0575050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216946126263087613acb565b98863b1561026a5760405f8a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af180156104935761298d575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610bbc5787826126c5926126c06126bb60608f0135613ef9565b61253f565b613f31565b8099919360608c01358210610b1c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146128cc575b73ffffffffffffffffffffffffffffffffffffffff1630036128755750501561284257505050479361277573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21698610af2308b613acb565b94600186116127b4575b506127ac97505b73ffffffffffffffffffffffffffffffffffffffff6040608089013598013594166144dd565b929391929091565b90946127bf90612442565b97803b1561026a576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099525f908990602490829084905af1908115610493576127ac986128289261282f575b5047906001811115610b465790612474565b935f61277f565b8061048761283c926124ae565b5f612816565b6127ac9992965060601015612866575061286090610af23087613acb565b93612786565b61286091506040880135612474565b9399509750506127ac99506060106128af575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614144565b6128c59196506128bf3388613acb565b90612474565b945f612888565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168073ffffffffffffffffffffffffffffffffffffffff891614610bbc57803b1561026a575f60405180927f2e1a7d4d0000000000000000000000000000000000000000000000000000000082528183816129658a600483019190602083019252565b03925af180156104935761297a575b50612708565b80610487612987926124ae565b5f612974565b8061048761299a926124ae565b5f61266d565b6129b29b9298939b9491943086613acb565b9b6101018110156129f157806129e0575b505060608211156126725791506129da3384613acb565b91612672565b6129ea91866137b8565b5f806129c3565b90612a0592995060408c013591309161371f565b3096612672565b3394506125c5565b801561246f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c166107df5761079294612db2565b3560ff8116810361026a5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9015612acd578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b612a67565b9190811015612acd5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026a57016020813591019167ffffffffffffffff821161026a57813603831361026a57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481945f925b848410612bfc575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026a5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026a5784612d82612d5b8695612d188f612cf18e612da1998b60019e0152612cca6040612cae818c01610960565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b612cd5818a01610960565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b612cfc818801610960565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612d2860a0612cfc818801610960565b60c0808601359085015260e080860135908501526101009080612d4d83880188612b12565b929093870152850191612b62565b6101208085013590840152610140612d7581860186612b12565b9185840390860152612b62565b91612d936101609182810190612b12565b929091818503910152612b62565b990193019401929195949390612bc9565b9493929192612dc3608087016123f1565b91863594602088013594612dda6040809a01612a59565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613532575b8815611f62578415613509575f5b8581106134ed575050612e45612e2c6060612e268789612a94565b016123f1565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612e57612e2c8d612e26888a612a94565b936003811693600185149283613437573461340e5760019291908d6101018210156133fc5750806133eb575b5050612e918c30338761386a565b505b818160021c166133ba575b60031c166130fb5750506002036130165750827f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a061691823b1561026a57612f1a92875f80948c51968795869485937f1c64b820000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613003575b50807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21692612f583085613acb565b908110612fda57612f6890612a14565b95833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052925f908490602490829084905af191821561049357612fc2938793612fc7575b5016615350565b509190565b80610487612fd4926124ae565b5f612fbb565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b80610487613010926124ae565b5f612f29565b9190838599969916916130298385613acb565b947f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0616803b1561026a575f92838a936130908b519a8b96879586947f1c64b82000000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af1918215610493576130b3946130ae936130e8575b50613acb565b612474565b9384106130bf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b806104876130f5926124ae565b5f6130a8565b9793925099979593906002145f1461332757847f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0616803b1561026a57613175935f80948b51968795869485937f01fb36ba000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613314575b50817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906131bb6131b63084613acb565b612a14565b91803b1561026a575f875180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161320089600483019190602083019252565b03925af180156104935761321d9284928692612fc7575016615350565b50955b61322a3082613acb565b9360018511613245575b5050506132419250612474565b9190565b939493156132f3575061327a907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21693612a14565b91833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052925f908490602490829084905af192831561049357613241936132e0575b506132d68233615350565b505b5f8080613234565b806104876132ed926124ae565b5f6132cb565b92505061330e61330561324194612a14565b8093339061538b565b506132d8565b80610487613321926124ae565b5f613184565b84999392997f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0616803b1561026a575f92838c936133928c51978896879586947f01fb36ba00000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af18015610493576133a7575b50613220565b806104876133b4926124ae565b5f6133a1565b6133e6897f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a061685613660565b612e9e565b6133f591866137b8565b5f80612e83565b909161340992309161371f565b612e93565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036134c457877f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690813b1561026a575f8c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610493576001926134b1575b50612e93565b806104876134be926124ae565b5f6134ab565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806135036134fd8493898b612ad2565b35615302565b01612e0b565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612dfd565b333b1561026a57565b7fffffffff000000000000000000000000000000000000000000000000000000005f35165f527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60405f20541680156135cb575f8091368280378136915af43d5f803e156135c7573d5ff35b3d5ffd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b91905f9273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9182821461364e575b501861361f57565b3461362657565b7f8b6ebb4d000000000000000000000000000000000000000000000000000000005f5260045ffd5b9093503418613626576001925f613617565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90816034526f095ea7b300000000000000000000000090815f5260205f6044601082855af13d1560015f51141716156136c2575b5050505f603452565b60105f60449260209582958360345283528238868683865af1506034525af13d1560015f51141716156136f7575f80806136b9565b7f8164f842000000000000000000000000000000000000000000000000000000005f5260045ffd5b906004905f94859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af11561379057565b7f6b836e6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b918060e01461382e57610100146137f1577fb78cb0dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101045f9182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e45f918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015260205f60648382875af192836138e7575b5082156138bf5750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d15613903575060015f5114601f3d1116915b5f6138b5565b3b1515916138fd565b919293906040519360018214613a57575b915f95608494928796946001146139fb5760031460011461399b577f3df02124000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b3d5f803e3d5ffd5b6fffffffffffffffffffffffffffffffff907f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af161096b573d5f803e3d5ffd5b507fa6417ed6000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b929093917fd0e30db00000000000000000000000000000000000000000000000000000000083525f806004853473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2165af115613993579193909261391d565b5f9291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613b2f576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613b2a575b50565b519150565b31925050565b90600b820180921161246f57565b9190820180921161246f57565b908160640291606483040361246f57565b906113889182810292818404149015171561246f57565b906109c49182810292818404149015171561246f57565b906121349182810292818404149015171561246f57565b8181029291811591840414171561246f57565b9091949392613bda5f96906bffffffffffffffffffffffff8260601c921690565b9490613be582613b35565b8311613eb3575b613bf68884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216613cb5575b505086159050613c9b57613c4e613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b5460ff1690565b613c9b57508482613c6785613c8f94613c959796615968565b613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b9061538b565b50612442565b91905f90565b9250613cae93945082906103ba92615968565b905f905f90565b6b2000000000000000000000008199949395979699161594851594613cfa613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680613eac575b613e93576b0800000000000000000000008316151597613d2084615995565b938415613da1575050948a94613d8c94613d6e613d66613d949b9686613d849f9e9c979b613d51816103ba9e613b43565b871115613d9957613d6191613b43565b613ba6565b612710900490565b90613d7b613d6683613b8f565b9c8d8093612474565b9d8e93615ad2565b928391615968565b929190565b505084613ba6565b80929c99969794506b400000000000000000000000919a989593501615155f14613e0f575089613dd857505050505b5f8080613c13565b8698975091613d949693918a6103ba96613d8c95613e07613dff613d669f613d6690613b61565b9e8f94613b78565b9c8d92615ad2565b939291906b8000000000000000000000008516613e31575b5050505050613dd0565b8a15613e27579092948a92949998506b040000000000000000000000613e5f613d66613e689d9a999a613b61565b9b8c8095612474565b9a16613e87575b926103ba95928a889693613d949a99613d8c97615ad2565b5f995060019550613e6f565b505050509390506103ba9250839150613cae9495615968565b5086613d01565b9650613ebf8183612474565b966b100000000000000000000000861615613bec5796613ee1613d6683613b50565b9081811115613ef257505b96613bec565b9050613eec565b7f8000000000000000000000000000000000000000000000000000000000000000811015613f245790565b6335278d125f526004601cfd5b939192908094845f92606082066140ee575b50505f6040949596855197889586947fff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000865260158601605f6001860160168901376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54603587015273ffffffffffffffffffffffffffffffffffffffff6055872016968030916140e6575b50843560ff1c861461405d577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca60208301519251925b5f03615d3d565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca6020835193015192614056565b90505f613fd3565b60a0821115613f435760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff1691505f6040613f43565b919093979594979692965f9761417561415d8884612474565b91906bffffffffffffffffffffffff8260601c921690565b909286116144b3578a9561418889613b35565b811161446d575b73ffffffffffffffffffffffffffffffffffffffff8416614249575b50505050916141bc9187949361538b565b50816141d4575b506141cd91613b43565b9291905f90565b9050614200613c478273ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b61423f576141cd91614238858093614230612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b90339061386a565b50916141c3565b509291505f908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591614292613c478d73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9380614466575b614448576b08000000000000000000000082161515946142b883615995565b92831561431d575050906142d6918088105f14613d61575086613ba6565b6127109004996142e58b613b8f565b61271090046142f581809d612474565b9c8d923361430298615d47565b9061430c91613b43565b926143169261538b565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156143995750614363575050505050916141bc918794935b91939481935f6141ab565b909192939998949695979a61437781613b61565b61271090049b6143878d92613b78565b61271090049b8c913361430298615d47565b946b800000000000000000000000869b9a999897929b166143c6575b505050505050906141bc9291614358565b909192939495969798996143df578c99989796956143b5565b988c929394959b9a96996b04000000000000000000000061440661440f9f613d6690613b61565b9e8f8096612474565b9d1661443c575b928c6143169a99989693614431969361443699963390615d47565b613b43565b9361538b565b5f9c5060019650614416565b5050505050509450919061445d93955061538b565b5091905f905f90565b5083614299565b9950614479888b612474565b996b10000000000000000000000082161561418f579961449b613d668c613b50565b90818111156144ac57505b9961418f565b90506144a6565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315155f1461485c576144ff6144f985612442565b87612474565b5f98606081901c906bffffffffffffffffffffffff1690928881116144b35761452783613b35565b8111614816575b73ffffffffffffffffffffffffffffffffffffffff84166145f8575b505050509061455b61456392612a14565b97889161538b565b5084156145df57614594613c478373ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b6145df576141cd92916145d7866128bf936145d1826145cb612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8661538b565b50612474565b903390615fa1565b6145f09394506128bf913390615fa1565b91905f905f90565b6b20000000000000000000000082161591821591614636613c478b73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b938061480f575b6147e7576b080000000000000000000000821615159461465c83615995565b9182614798575050506b4000000000000000000000008116156146f757508a61469557505050509061455b614563925b91925f8061454a565b8a6146a78996989c9b9a99959b613b61565b61271090049b6146b78d92613b78565b61271090049b6146c8968d92615ad2565b336146d292615fa1565b916146dc90612a14565b80976146e79261538b565b506146f191612474565b93929190565b9291936b8000000000000000000000008416614720575b50505050509061455b6145639261468c565b8b1561470e578b919293999695976b04000000000000000000000061474e613d666147579f9e9c989e613b61565b9d8e8095612474565b9b1661478c575b938a9b61477e9487946146f19c9d6145d7956145d19b9a6147849a615ad2565b94612a14565b98899161538b565b5f9a506001945061475e565b8b989a9d9c9b979e506147b593508082105f146147e05750613ba6565b6127109004996147c48b613b8f565b61271090046147d481809d612474565b9c6146c8968e93615ad2565b9050613ba6565b50505050505093926145f095965061477e6145d193614807923390615fa1565b96879161538b565b508361463d565b9950614822828b612474565b996b10000000000000000000000082161561452e5799614844613d668c613b50565b908181111561485557505b9961452e565b905061484f565b6144ff846144f9565b91805f958695606493607c95608037608083015260a08201523360c0820152019134905af11561489157565b3d5f607c3e3d607cfd5b94919392935f925f945f93604051975f975b6060860489106148ca57505050505050505050506123ca90615d3d565b909192939495969798809b9a8915614afe575b6060880460018c0110614a71575b8a614a69575b8a60a061016492896001610100821114614a5b575b506060830288013560ff1c156149be579460608086945f946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146149b1575b5af1156139935760208a0151945b600187965f0399019796959a98999a9493929190946148ad565b8989610164850137614989565b9460608086945f946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614a4e575b5af11561399357895194614997565b8989610164850137614a3f565b809192940193019089614906565b3093506148f1565b97505096507fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916976148eb565b985050507fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008a5260158a01605f6001840160168d01376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916916148dd565b9091949392614ba45f96906bffffffffffffffffffffffff8260601c921690565b9490614baf82613b35565b8311614e2b575b614bc08884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216614c67575b505086159050614c5057614c11613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b614c50575081614c26848793614c499561538b565b50613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b5091905f90565b939450614c5f9250839161538b565b50905f905f90565b6b2000000000000000000000008199949395979699161594851594614cac613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680614e24575b614e0e576b0800000000000000000000008316151597614cd284615995565b938415614d2c57505093614d1699989793614d00613d66614d269995858f9a9699613d5181614d1e9c613b43565b90614d0d613d6683613b8f565b9b8c8093612474565b9c8d93615ad2565b92839161538b565b50929190565b80929c99969794506b400000000000000000000000919a989593501615155f14614d97575089614d6357505050505b5f8080614bdd565b869897509089614d26969795614d1e959493614d8f614d87613d66613d669f613b61565b9d8e94613b78565b9b8c92615ad2565b939291906b8000000000000000000000008516614db9575b5050505050614d5b565b8a15614daf579092948a92949998506b040000000000000000000000613e5f613d66614de79d9a999a613b61565b9a16614e02575b918987969492614d2698614d1e9795615ad2565b5f995060019550614dee565b5050505094959050849250614c5f93915061538b565b5086614cb3565b9650614e378183612474565b966b100000000000000000000000861615614bb65796614e59613d6683613b50565b9081811115614e6a57505b96614bb6565b9050614e64565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f00000000000000000000007f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f91604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16935f97858501525f5b8360061c8110615169575050508060061c805b61507957505f935f965f955b8360061c8710614f5557505050505050505050565b8015615027575b308460061c600189011061500f575b60a46040925f928a6001810160051b8a01518b8b826020899560061b8d010135600116615004575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561399357600188960195614f40565b935087925081614f93565b508486016001880160051b0160200151985088614f6b565b508385016020818101517fa9059cbb0000000000000000000000000000000000000000000000000000000092880180830193845260248101829052604490810185905290925f9190828c5af150614f5c565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156139935760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c010135851661515e575b50906126f28161271093030292020204018098838301901b8501520180614f34565b9092506126f261513c565b80615187838560019460061b8b0160208560051b8c8c010101614e71565b01614f21565b9190918235807f52bbbe2900000000000000000000000000000000000000000000000000000000146152b0577f945bcec90000000000000000000000000000000000000000000000000000000014615207577f7352d91c000000000000000000000000000000000000000000000000000000005f5260045ffd5b60448301359283810193600485013594600183600401351460011461529d5760059590951b01016004013592602401355b8015615282575b8315615266575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350615246565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61523f565b602401359460051b010160040135615238565b50610144830135926101240135615238565b905f80918060405194853783347f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c85af1156152fa5750565b3d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff16806153205750565b331861532857565b7f02a43f8b000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8080938193612710f190811561536357565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee841460011461541d5760446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af191826153f8575b505b811561536357565b9091503d15615414575060015f5114601f3d1116905b5f6153ee565b3b15159061540e565b5f809394508092918192612710f1906153f0565b929490936040519460019384821461559c575b905f988998979695949392806001146155515760021461550157506003146001146154b15790869392916084967f5b41b90800000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b60a4957f394747c5000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152806064840152608483015234905af161096b573d5f803e3d5ffd5b889594939291509660a4977f64a14558000000000000000000000000000000000000000000000000000000008852600488015260248701526044860152606485015260848401525af11561399357565b50505090869392916084967f65b2489b00000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b969594939291907fd0e30db00000000000000000000000000000000000000000000000000000000086525f806004883473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2165af1156139935790919293949596615444565b906044835f958695607c9460803760808201523360a0820152019134905af11561489157565b9095929193955f955f945f985f965b8660061c8810615662575050505050505050505050565b89156157e3575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156139935783519084015190818a6157da575b506126f291612710838502910201920202049687905f906157d1575b30918a60061c60018d0110615744575b5f9360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156139935760018a97019661564b565b92939d509b50507fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f00000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f60358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d919093926156e8565b50505f876156d8565b9150905f6156bc565b506040517fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000081529850601589016028808a8337812090527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f60358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a1690308314600114615929576101008411156158e4577f30f28b7a0000000000000000000000000000000000000000000000000000000081525f806004928688858301378460848201528960a48201528560c482015283870190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af1156121ec5750615669565b5f6064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615669565b5f6044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615669565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613b2793019161538b565b613fff1660c881116159a45790565b5060c890565b6040519061096b826124c7565b604051906159c4826124c7565b600282526040366020840137565b805115612acd5760200190565b805160011015612acd5760400190565b9081518082526020808093019301915f5b828110615a0e575050505090565b835185529381019392810192600101615a00565b6020808252825160608284015280516080840181905293949360a084019392918201905f5b818110615aa8575050508473ffffffffffffffffffffffffffffffffffffffff6040926123ca96970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526159ef565b825173ffffffffffffffffffffffffffffffffffffffff1686529483019491830191600101615a47565b96909591939294615ae38487613b43565b9283615af457505050505050505090565b9697959681615d35575b5015615b745750505082828111615b4a576123ca9481615b21575b505050612474565b73ffffffffffffffffffffffffffffffffffffffff615b4193169061538b565b505f8080615b19565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b84829693979211615b4a5715615bcf57856123ca96615b9c575b5081615b2157505050612474565b615bc890615bc2612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8361538b565b505f615b8e565b615cc09073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc1694615c1b88878561538b565b50615c56615c276159b7565b99615c306159b7565b9616615c3b8b6159d2565b9073ffffffffffffffffffffffffffffffffffffffff169052565b615c5f856159d2565b52615c8e615c85612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b8a6159df565b615c97846159df565b52615ca06159aa565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026a57615d095f949185926040519687809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1928315610493576123ca93615d225750612474565b80610487615d2f926124ae565b5f6145d1565b90505f615afe565b5f8112613f245790565b969790929594919394615d5a8387613b43565b9889615d6d575b50505050505050505090565b819994959697989991615f99575b5015615dcf5750505082948311615b4a5782615da5575b505050505b5f8080808080808080615d61565b73ffffffffffffffffffffffffffffffffffffffff615dc594169161386a565b505f808080615d92565b9091968711615b4a5715615e515780615e1d575b5082615df3575b50505050615d97565b73ffffffffffffffffffffffffffffffffffffffff615e1394169161386a565b505f808080615dea565b615e4a90615e43612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b838561386a565b505f615de3565b91615f289193949273ffffffffffffffffffffffffffffffffffffffff95615e9e88887f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc1680988661386a565b50615ebe615eaa6159b7565b97615eb36159b7565b9616615c3b896159d2565b615ec7856159d2565b52615ef6615eed612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b886159df565b615eff846159df565b52615f086159aa565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026a57615f715f929183926040519485809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1801561049357615f86575b50615d97565b80610487615f93926124ae565b5f615f80565b90505f615d7b565b91909160018211615fb3575050505f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615fe1920192839161538b565b509056fea164736f6c6343000816000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8ff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54ff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000096e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0600000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
-----Decoded View---------------
Arg [0] : _owner (address): 0xD7e24A49944F7972cEb826C7557580658F9C3303
Arg [1] : _diamondCutFacet (address): 0xfa39c1c670b48956eeF9fd0BbD0E81A290326330
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _balancerVault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
Arg [4] : _uniV3FactoryAndFF (uint256): 115395599522508061429428307120883023012361415493104242916976774162621891870720
Arg [5] : _uniswapV3PoolInitCodeHash (uint256): 102814774271675688723325049954498779091328469440286648861889194717372678376276
Arg [6] : _uniswapV2FactoryAndFF (uint256): 115503056148776755782671941100287934110443767449021598599426467662402456387584
Arg [7] : _uniswapV2PoolInitCodeHash (uint256): 68258024698789349894765071221733254692395760896603232917352833510768193864799
Arg [8] : _rfq (address): 0xe92b586627ccA7a83dC919cc7127196d70f55a06
Arg [9] : _feeVault (address): 0x00700052c0608F670705380a4900e0a8080010CC
Arg [10] : _permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303
Arg [1] : 000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Arg [4] : ff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000
Arg [5] : e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54
Arg [6] : ff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f0000000000000000000000
Arg [7] : 96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f
Arg [8] : 000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a06
Arg [9] : 00000000000000000000000000700052c0608f670705380a4900e0a8080010cc
Arg [10] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 46.61% | $3.89 | 1 | $3.89 | |
| ETH | 6.83% | $0.570416 | 1 | $0.5704 | |
| ETH | <0.01% | $3,039.34 | 0.000000000000000001 | <$0.000001 | |
| GNO | 46.56% | $3.89 | 1 | $3.89 | |
| GNO | <0.01% | $0.999908 | 0.000000000000000001 | <$0.000001 | |
| OP | <0.01% | $3,043.15 | 0.000000000000000001 | <$0.000001 | |
| BASE | <0.01% | $3,042.88 | 0.000000000000000001 | <$0.000001 | |
| ARB | <0.01% | $3,041.54 | 0.000000000000000001 | <$0.000001 | |
| UNI | <0.01% | $3,040.82 | 0.000000000000000001 | <$0.000001 | |
| BSC | <0.01% | $910.94 | 0.000000000000000001 | <$0.000001 | |
| AVAX | <0.01% | $14.33 | 0.000000000000000001 | <$0.000001 | |
| POL | <0.01% | $0.146777 | 0.000000000000000001 | <$0.000001 | |
| SONIC | <0.01% | $0.121732 | 0.000000000000000001 | <$0.000001 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.