Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 141 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Add Liquidity ET... | 19695577 | 162 days ago | IN | 1.64007972 ETH | 0.00212688 | ||||
Add Liquidity ET... | 19695512 | 162 days ago | IN | 0.993411 ETH | 0.001981 | ||||
Swap Exact Token... | 19695109 | 163 days ago | IN | 0 ETH | 0.00144529 | ||||
Add Liquidity | 19694958 | 163 days ago | IN | 0 ETH | 0.00189574 | ||||
Swap Exact Token... | 19694928 | 163 days ago | IN | 0 ETH | 0.00124913 | ||||
Swap Exact Token... | 19694888 | 163 days ago | IN | 0 ETH | 0.00144406 | ||||
Remove Liquidity | 19589563 | 177 days ago | IN | 0 ETH | 0.00206768 | ||||
Remove Liquidity... | 19582139 | 178 days ago | IN | 0 ETH | 0.00327846 | ||||
Remove Liquidity | 19497469 | 190 days ago | IN | 0 ETH | 0.00239572 | ||||
Remove Liquidity | 19480652 | 193 days ago | IN | 0 ETH | 0.00371459 | ||||
Swap Exact Token... | 19477669 | 193 days ago | IN | 0 ETH | 0.00703059 | ||||
Remove Liquidity | 19439944 | 198 days ago | IN | 0 ETH | 0.00435252 | ||||
Add Liquidity Wi... | 19424415 | 201 days ago | IN | 0 ETH | 0.00720679 | ||||
Add Liquidity Wi... | 19402587 | 204 days ago | IN | 0 ETH | 0.00828289 | ||||
Add Liquidity | 19376150 | 207 days ago | IN | 0 ETH | 0.01882286 | ||||
Remove Liquidity... | 19349764 | 211 days ago | IN | 0 ETH | 0.01128554 | ||||
Add Liquidity ET... | 19333761 | 213 days ago | IN | 0.66279172 ETH | 0.01815094 | ||||
Swap Exact Token... | 19315867 | 216 days ago | IN | 0 ETH | 0.00784855 | ||||
Swap Exact Token... | 19266375 | 223 days ago | IN | 0 ETH | 0.00463739 | ||||
Remove Liquidity | 19266368 | 223 days ago | IN | 0 ETH | 0.00287527 | ||||
Remove Liquidity... | 19204023 | 231 days ago | IN | 0 ETH | 0.0049746 | ||||
Add Liquidity ET... | 19198318 | 232 days ago | IN | 0.017 ETH | 0.00479889 | ||||
Remove Liquidity... | 19149528 | 239 days ago | IN | 0 ETH | 0.00405299 | ||||
Add Liquidity | 19120730 | 243 days ago | IN | 0 ETH | 0.00807876 | ||||
Swap Exact Token... | 19120722 | 243 days ago | IN | 0 ETH | 0.00809287 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
19695577 | 162 days ago | 1.64007972 ETH | ||||
19695512 | 162 days ago | 0.993411 ETH | ||||
19582139 | 178 days ago | 0.52362051 ETH | ||||
19582139 | 178 days ago | 0.52362051 ETH | ||||
19349764 | 211 days ago | 1.00183833 ETH | ||||
19349764 | 211 days ago | 1.00183833 ETH | ||||
19333761 | 213 days ago | 0.66279172 ETH | ||||
19204023 | 231 days ago | 0.00849985 ETH | ||||
19204023 | 231 days ago | 0.00849985 ETH | ||||
19198318 | 232 days ago | 0.017 ETH | ||||
19149528 | 239 days ago | 0.09974175 ETH | ||||
19149528 | 239 days ago | 0.09974175 ETH | ||||
19110095 | 245 days ago | 0.49816151 ETH | ||||
19058102 | 252 days ago | 1.99384078 ETH | ||||
19058102 | 252 days ago | 1.99384078 ETH | ||||
19057869 | 252 days ago | 1.99384078 ETH | ||||
19019492 | 257 days ago | 0.09974175 ETH | ||||
19000566 | 260 days ago | 0.200877 ETH | ||||
19000566 | 260 days ago | 0.200877 ETH | ||||
18879700 | 277 days ago | 0.99936628 ETH | ||||
18851476 | 281 days ago | 2.00009329 ETH | ||||
18851476 | 281 days ago | 2.00009329 ETH | ||||
18851104 | 281 days ago | 2.00009329 ETH | ||||
18839597 | 283 days ago | 0.003 ETH | ||||
18767321 | 293 days ago | 0.016 ETH |
Loading...
Loading
Contract Name:
ButtonswapRouter
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IButtonswapRouter} from "./interfaces/IButtonswapRouter/IButtonswapRouter.sol"; import {IButtonswapFactory} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol"; import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol"; import {TransferHelper} from "./libraries/TransferHelper.sol"; import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol"; import {IERC20} from "./interfaces/IERC20.sol"; import {IWETH} from "./interfaces/IWETH.sol"; import {ETHButtonswapRouter} from "./ETHButtonswapRouter.sol"; contract ButtonswapRouter is ETHButtonswapRouter, IButtonswapRouter { constructor(address _factory, address _WETH) ETHButtonswapRouter(_factory, _WETH) {} /** * @inheritdoc IButtonswapRouter */ function getPair(address tokenA, address tokenB) external view returns (address pair) { return IButtonswapFactory(factory).getPair(tokenA, tokenB); } /** * @inheritdoc IButtonswapRouter */ function isCreationRestricted() external view returns (bool _isCreationRestricted) { _isCreationRestricted = IButtonswapFactory(factory).isCreationRestricted(); } // **** LIBRARY FUNCTIONS **** /** * @inheritdoc IButtonswapRouter */ function quote(uint256 amountA, uint256 poolA, uint256 poolB) external pure virtual override returns (uint256 amountB) { return ButtonswapLibrary.quote(amountA, poolA, poolB); } /** * @inheritdoc IButtonswapRouter */ function getAmountOut(uint256 amountIn, uint256 poolIn, uint256 poolOut) external pure virtual override returns (uint256 amountOut) { return ButtonswapLibrary.getAmountOut(amountIn, poolIn, poolOut); } /** * @inheritdoc IButtonswapRouter */ function getAmountIn(uint256 amountOut, uint256 poolIn, uint256 poolOut) external pure virtual override returns (uint256 amountIn) { return ButtonswapLibrary.getAmountIn(amountOut, poolIn, poolOut); } /** * @inheritdoc IButtonswapRouter */ function getAmountsOut(uint256 amountIn, address[] memory path) external view virtual override returns (uint256[] memory amounts) { return ButtonswapLibrary.getAmountsOut(factory, amountIn, path); } /** * @inheritdoc IButtonswapRouter */ function getAmountsIn(uint256 amountOut, address[] memory path) external view virtual override returns (uint256[] memory amounts) { return ButtonswapLibrary.getAmountsIn(factory, amountOut, path); } /** * @inheritdoc IButtonswapRouter */ function getMintSwappedAmounts(address tokenA, address tokenB, uint256 mintAmountA) external view virtual override returns (uint256 tokenAToSwap, uint256 swappedReservoirAmountB) { return ButtonswapLibrary.getMintSwappedAmounts(factory, tokenA, tokenB, mintAmountA); } /** * @inheritdoc IButtonswapRouter */ function getBurnSwappedAmounts(address tokenA, address tokenB, uint256 liquidity) external view virtual override returns (uint256 tokenOutA, uint256 swappedReservoirAmountA) { return ButtonswapLibrary.getBurnSwappedAmounts(factory, tokenA, tokenB, liquidity); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IETHButtonswapRouter} from "./IETHButtonswapRouter.sol"; interface IButtonswapRouter is IETHButtonswapRouter { /** * @notice Returns the Pair contract for given tokens. Returns the zero address if no pair exists * @param tokenA First token address * @param tokenB Second token address * @return pair The pair address */ function getPair(address tokenA, address tokenB) external view returns (address pair); /** * @notice Returns the factory state of `isCreationRestricted` * @return _isCreationRestricted The `isCreationRestricted` state of the factory. */ function isCreationRestricted() external view returns (bool _isCreationRestricted); /** * @notice Given some amount of an asset and pair pools, returns an equivalent amount of the other asset * @param amountA The amount of token A * @param poolA The balance of token A in the pool * @param poolB The balance of token B in the pool * @return amountB The amount of token B */ function quote(uint256 amountA, uint256 poolA, uint256 poolB) external pure returns (uint256 amountB); /** * @notice Given an input amount of an asset and pair pools, returns the maximum output amount of the other asset * Factors in the fee on the input amount. * @param amountIn The input amount of the asset * @param poolIn The balance of the input asset in the pool * @param poolOut The balance of the output asset in the pool * @return amountOut The output amount of the other asset */ function getAmountOut(uint256 amountIn, uint256 poolIn, uint256 poolOut) external pure returns (uint256 amountOut); /** * @notice Given an output amount of an asset and pair pools, returns a required input amount of the other asset * @param amountOut The output amount of the asset * @param poolIn The balance of the input asset in the pool * @param poolOut The balance of the output asset in the pool * @return amountIn The required input amount of the other asset */ function getAmountIn(uint256 amountOut, uint256 poolIn, uint256 poolOut) external pure returns (uint256 amountIn); /** * @notice Given an ordered array of tokens and an input amount of the first asset, performs chained getAmountOut calculations to calculate the output amount of the final asset * @param amountIn The input amount of the first asset * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @return amounts The output amounts of each asset in the path */ function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); /** * @notice Given an ordered array of tokens and an output amount of the final asset, performs chained getAmountIn calculations to calculate the input amount of the first asset * @param amountOut The output amount of the final asset * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @return amounts The input amounts of each asset in the path */ function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); /** * @notice Returns how much of the much of mintAmountA will be swapped for tokenB and for how much during a mintWithReservoir operation. * @param tokenA First token address * @param tokenB Second token address * @param mintAmountA The amount of tokenA to be minted * @return tokenAToSwap The amount of tokenA to be exchanged for tokenB from the reservoir * @return swappedReservoirAmountB The amount of tokenB returned from the reservoir */ function getMintSwappedAmounts(address tokenA, address tokenB, uint256 mintAmountA) external view returns (uint256 tokenAToSwap, uint256 swappedReservoirAmountB); /** * @notice Returns how much of tokenA will be withdrawn from the pair and how much of it came from the reservoir during a burnFromReservoir operation. * @param tokenA First token address * @param tokenB Second token address * @param liquidity The amount of liquidity to be burned * @return tokenOutA The amount of tokenA to be withdrawn from the pair * @return swappedReservoirAmountA The amount of tokenA returned from the reservoir */ function getBurnSwappedAmounts(address tokenA, address tokenB, uint256 liquidity) external view returns (uint256 tokenOutA, uint256 swappedReservoirAmountA); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapFactoryErrors} from "./IButtonswapFactoryErrors.sol"; import {IButtonswapFactoryEvents} from "./IButtonswapFactoryEvents.sol"; interface IButtonswapFactory is IButtonswapFactoryErrors, IButtonswapFactoryEvents { /** * @notice Returns the current address for `feeTo`. * The owner of this address receives the protocol fee as it is collected over time. * @return _feeTo The `feeTo` address */ function feeTo() external view returns (address _feeTo); /** * @notice Returns the current address for `feeToSetter`. * The owner of this address has the power to update both `feeToSetter` and `feeTo`. * @return _feeToSetter The `feeToSetter` address */ function feeToSetter() external view returns (address _feeToSetter); /** * @notice The name of the ERC20 liquidity token. * @return _tokenName The `tokenName` */ function tokenName() external view returns (string memory _tokenName); /** * @notice The symbol of the ERC20 liquidity token. * @return _tokenSymbol The `tokenSymbol` */ function tokenSymbol() external view returns (string memory _tokenSymbol); /** * @notice Returns the current state of restricted creation. * If true, then no new pairs, only feeToSetter can create new pairs * @return _isCreationRestricted The `isCreationRestricted` state */ function isCreationRestricted() external view returns (bool _isCreationRestricted); /** * @notice Returns the current address for `isCreationRestrictedSetter`. * The owner of this address has the power to update both `isCreationRestrictedSetter` and `isCreationRestricted`. * @return _isCreationRestrictedSetter The `isCreationRestrictedSetter` address */ function isCreationRestrictedSetter() external view returns (address _isCreationRestrictedSetter); /** * @notice Get the (unique) Pair address created for the given combination of `tokenA` and `tokenB`. * If the Pair does not exist then zero address is returned. * @param tokenA The first unsorted token * @param tokenB The second unsorted token * @return pair The address of the Pair instance */ function getPair(address tokenA, address tokenB) external view returns (address pair); /** * @notice Get the Pair address at the given `index`, ordered chronologically. * @param index The index to query * @return pair The address of the Pair created at the given `index` */ function allPairs(uint256 index) external view returns (address pair); /** * @notice Get the current total number of Pairs created * @return count The total number of Pairs created */ function allPairsLength() external view returns (uint256 count); /** * @notice Creates a new {ButtonswapPair} instance for the given unsorted tokens `tokenA` and `tokenB`. * @dev The tokens are sorted later, but can be provided to this method in either order. * @param tokenA The first unsorted token address * @param tokenB The second unsorted token address * @return pair The address of the new {ButtonswapPair} instance */ function createPair(address tokenA, address tokenB) external returns (address pair); /** * @notice Updates the address that receives the protocol fee. * This can only be called by the `feeToSetter` address. * @param _feeTo The new address */ function setFeeTo(address _feeTo) external; /** * @notice Updates the address that has the power to set the `feeToSetter` and `feeTo` addresses. * This can only be called by the `feeToSetter` address. * @param _feeToSetter The new address */ function setFeeToSetter(address _feeToSetter) external; /** * @notice Updates the state of restricted creation. * This can only be called by the `feeToSetter` address. * @param _isCreationRestricted The new state */ function setIsCreationRestricted(bool _isCreationRestricted) external; /** * @notice Updates the address that has the power to set the `isCreationRestrictedSetter` and `isCreationRestricted`. * This can only be called by the `isCreationRestrictedSetter` address. * @param _isCreationRestrictedSetter The new address */ function setIsCreationRestrictedSetter(address _isCreationRestrictedSetter) external; /** * @notice Returns the current address for `isPausedSetter`. * The owner of this address has the power to update both `isPausedSetter` and call `setIsPaused`. * @return _isPausedSetter The `isPausedSetter` address */ function isPausedSetter() external view returns (address _isPausedSetter); /** * @notice Updates the address that has the power to set the `isPausedSetter` and call `setIsPaused`. * This can only be called by the `isPausedSetter` address. * @param _isPausedSetter The new address */ function setIsPausedSetter(address _isPausedSetter) external; /** * @notice Updates the pause state of given Pairs. * This can only be called by the `feeToSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param isPausedNew The new pause state */ function setIsPaused(address[] calldata pairs, bool isPausedNew) external; /** * @notice Returns the current address for `paramSetter`. * The owner of this address has the power to update `paramSetter`, default parameters, and current parameters on existing pairs * @return _paramSetter The `paramSetter` address */ function paramSetter() external view returns (address _paramSetter); /** * @notice Updates the address that has the power to set the `paramSetter` and update the default params. * This can only be called by the `paramSetter` address. * @param _paramSetter The new address */ function setParamSetter(address _paramSetter) external; /** * @notice Returns the default value of `movingAverageWindow` used for new pairs. * @return _defaultMovingAverageWindow The `defaultMovingAverageWindow` value */ function defaultMovingAverageWindow() external view returns (uint32 _defaultMovingAverageWindow); /** * @notice Returns the default value of `maxVolatilityBps` used for new pairs. * @return _defaultMaxVolatilityBps The `defaultMaxVolatilityBps` value */ function defaultMaxVolatilityBps() external view returns (uint16 _defaultMaxVolatilityBps); /** * @notice Returns the default value of `minTimelockDuration` used for new pairs. * @return _defaultMinTimelockDuration The `defaultMinTimelockDuration` value */ function defaultMinTimelockDuration() external view returns (uint32 _defaultMinTimelockDuration); /** * @notice Returns the default value of `maxTimelockDuration` used for new pairs. * @return _defaultMaxTimelockDuration The `defaultMaxTimelockDuration` value */ function defaultMaxTimelockDuration() external view returns (uint32 _defaultMaxTimelockDuration); /** * @notice Returns the default value of `maxSwappableReservoirLimitBps` used for new pairs. * @return _defaultMaxSwappableReservoirLimitBps The `defaultMaxSwappableReservoirLimitBps` value */ function defaultMaxSwappableReservoirLimitBps() external view returns (uint16 _defaultMaxSwappableReservoirLimitBps); /** * @notice Returns the default value of `swappableReservoirGrowthWindow` used for new pairs. * @return _defaultSwappableReservoirGrowthWindow The `defaultSwappableReservoirGrowthWindow` value */ function defaultSwappableReservoirGrowthWindow() external view returns (uint32 _defaultSwappableReservoirGrowthWindow); /** * @notice Updates the default parameters used for new pairs. * This can only be called by the `paramSetter` address. * @param newDefaultMovingAverageWindow The new defaultMovingAverageWindow * @param newDefaultMaxVolatilityBps The new defaultMaxVolatilityBps * @param newDefaultMinTimelockDuration The new defaultMinTimelockDuration * @param newDefaultMaxTimelockDuration The new defaultMaxTimelockDuration * @param newDefaultMaxSwappableReservoirLimitBps The new defaultMaxSwappableReservoirLimitBps * @param newDefaultSwappableReservoirGrowthWindow The new defaultSwappableReservoirGrowthWindow */ function setDefaultParameters( uint32 newDefaultMovingAverageWindow, uint16 newDefaultMaxVolatilityBps, uint32 newDefaultMinTimelockDuration, uint32 newDefaultMaxTimelockDuration, uint16 newDefaultMaxSwappableReservoirLimitBps, uint32 newDefaultSwappableReservoirGrowthWindow ) external; /** * @notice Updates the `movingAverageWindow` value of given Pairs. * This can only be called by the `paramSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param newMovingAverageWindow The new `movingAverageWindow` value */ function setMovingAverageWindow(address[] calldata pairs, uint32 newMovingAverageWindow) external; /** * @notice Updates the `maxVolatilityBps` value of given Pairs. * This can only be called by the `paramSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param newMaxVolatilityBps The new `maxVolatilityBps` value */ function setMaxVolatilityBps(address[] calldata pairs, uint16 newMaxVolatilityBps) external; /** * @notice Updates the `minTimelockDuration` value of given Pairs. * This can only be called by the `paramSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param newMinTimelockDuration The new `minTimelockDuration` value */ function setMinTimelockDuration(address[] calldata pairs, uint32 newMinTimelockDuration) external; /** * @notice Updates the `maxTimelockDuration` value of given Pairs. * This can only be called by the `paramSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param newMaxTimelockDuration The new `maxTimelockDuration` value */ function setMaxTimelockDuration(address[] calldata pairs, uint32 newMaxTimelockDuration) external; /** * @notice Updates the `maxSwappableReservoirLimitBps` value of given Pairs. * This can only be called by the `paramSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param newMaxSwappableReservoirLimitBps The new `maxSwappableReservoirLimitBps` value */ function setMaxSwappableReservoirLimitBps(address[] calldata pairs, uint16 newMaxSwappableReservoirLimitBps) external; /** * @notice Updates the `swappableReservoirGrowthWindow` value of given Pairs. * This can only be called by the `paramSetter` address. * @param pairs A list of addresses for the pairs that should be updated * @param newSwappableReservoirGrowthWindow The new `swappableReservoirGrowthWindow` value */ function setSwappableReservoirGrowthWindow(address[] calldata pairs, uint32 newSwappableReservoirGrowthWindow) external; /** * @notice Returns the last token pair created and the parameters used. * @return token0 The first token address * @return token1 The second token address * @return movingAverageWindow The moving average window * @return maxVolatilityBps The max volatility bps * @return minTimelockDuration The minimum time lock duration * @return maxTimelockDuration The maximum time lock duration * @return maxSwappableReservoirLimitBps The max swappable reservoir limit bps * @return swappableReservoirGrowthWindow The swappable reservoir growth window */ function lastCreatedTokensAndParameters() external returns ( address token0, address token1, uint32 movingAverageWindow, uint16 maxVolatilityBps, uint32 minTimelockDuration, uint32 maxTimelockDuration, uint16 maxSwappableReservoirLimitBps, uint32 swappableReservoirGrowthWindow ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapPairErrors} from "./IButtonswapPairErrors.sol"; import {IButtonswapPairEvents} from "./IButtonswapPairEvents.sol"; import {IButtonswapERC20} from "../IButtonswapERC20/IButtonswapERC20.sol"; interface IButtonswapPair is IButtonswapPairErrors, IButtonswapPairEvents, IButtonswapERC20 { /** * @notice The smallest value that {IButtonswapERC20-totalSupply} can be. * @dev After the first mint the total liquidity (represented by the liquidity token total supply) can never drop below this value. * * This is to protect against an attack where the attacker mints a very small amount of liquidity, and then donates pool tokens to skew the ratio. * This results in future minters receiving no liquidity tokens when they deposit. * By enforcing a minimum liquidity value this attack becomes prohibitively expensive to execute. * @return MINIMUM_LIQUIDITY The MINIMUM_LIQUIDITY value */ function MINIMUM_LIQUIDITY() external pure returns (uint256 MINIMUM_LIQUIDITY); /** * @notice The duration for which the moving average is calculated for. * @return _movingAverageWindow The value of movingAverageWindow */ function movingAverageWindow() external view returns (uint32 _movingAverageWindow); /** * @notice Updates the movingAverageWindow parameter of the pair. * This can only be called by the Factory address. * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#movingaveragewindow) for more detail. * @param newMovingAverageWindow The new value for movingAverageWindow */ function setMovingAverageWindow(uint32 newMovingAverageWindow) external; /** * @notice Numerator (over 10_000) of the threshold when price volatility triggers maximum single-sided timelock duration. * @return _maxVolatilityBps The value of maxVolatilityBps */ function maxVolatilityBps() external view returns (uint16 _maxVolatilityBps); /** * @notice Updates the maxVolatilityBps parameter of the pair. * This can only be called by the Factory address. * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxvolatilitybps) for more detail. * @param newMaxVolatilityBps The new value for maxVolatilityBps */ function setMaxVolatilityBps(uint16 newMaxVolatilityBps) external; /** * @notice How long the minimum singled-sided timelock lasts for. * @return _minTimelockDuration The value of minTimelockDuration */ function minTimelockDuration() external view returns (uint32 _minTimelockDuration); /** * @notice Updates the minTimelockDuration parameter of the pair. * This can only be called by the Factory address. * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#mintimelockduration) for more detail. * @param newMinTimelockDuration The new value for minTimelockDuration */ function setMinTimelockDuration(uint32 newMinTimelockDuration) external; /** * @notice How long the maximum singled-sided timelock lasts for. * @return _maxTimelockDuration The value of maxTimelockDuration */ function maxTimelockDuration() external view returns (uint32 _maxTimelockDuration); /** * @notice Updates the maxTimelockDuration parameter of the pair. * This can only be called by the Factory address. * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxtimelockduration) for more detail. * @param newMaxTimelockDuration The new value for maxTimelockDuration */ function setMaxTimelockDuration(uint32 newMaxTimelockDuration) external; /** * @notice Numerator (over 10_000) of the fraction of the pool balance that acts as the maximum limit on how much of the reservoir * can be swapped in a given timeframe. * @return _maxSwappableReservoirLimitBps The value of maxSwappableReservoirLimitBps */ function maxSwappableReservoirLimitBps() external view returns (uint16 _maxSwappableReservoirLimitBps); /** * @notice Updates the maxSwappableReservoirLimitBps parameter of the pair. * This can only be called by the Factory address. * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxswappablereservoirlimitbps) for more detail. * @param newMaxSwappableReservoirLimitBps The new value for maxSwappableReservoirLimitBps */ function setMaxSwappableReservoirLimitBps(uint16 newMaxSwappableReservoirLimitBps) external; /** * @notice How much time it takes for the swappable reservoir value to grow from nothing to its maximum value. * @return _swappableReservoirGrowthWindow The value of swappableReservoirGrowthWindow */ function swappableReservoirGrowthWindow() external view returns (uint32 _swappableReservoirGrowthWindow); /** * @notice Updates the swappableReservoirGrowthWindow parameter of the pair. * This can only be called by the Factory address. * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#swappablereservoirgrowthwindow) for more detail. * @param newSwappableReservoirGrowthWindow The new value for swappableReservoirGrowthWindow */ function setSwappableReservoirGrowthWindow(uint32 newSwappableReservoirGrowthWindow) external; /** * @notice The address of the {ButtonswapFactory} instance used to create this Pair. * @dev Set to `msg.sender` in the Pair constructor. * @return factory The factory address */ function factory() external view returns (address factory); /** * @notice The address of the first sorted token. * @return token0 The token address */ function token0() external view returns (address token0); /** * @notice The address of the second sorted token. * @return token1 The token address */ function token1() external view returns (address token1); /** * @notice The time-weighted average price of the Pair. * The price is of `token0` in terms of `token1`. * @dev The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision. * Consequently this value must be divided by `2^112` to get the actual price. * * Because of the time weighting, `price0CumulativeLast` must also be divided by the total Pair lifetime to get the average price over that time period. * @return price0CumulativeLast The current cumulative `token0` price */ function price0CumulativeLast() external view returns (uint256 price0CumulativeLast); /** * @notice The time-weighted average price of the Pair. * The price is of `token1` in terms of `token0`. * @dev The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision. * Consequently this value must be divided by `2^112` to get the actual price. * * Because of the time weighting, `price1CumulativeLast` must also be divided by the total Pair lifetime to get the average price over that time period. * @return price1CumulativeLast The current cumulative `token1` price */ function price1CumulativeLast() external view returns (uint256 price1CumulativeLast); /** * @notice The timestamp for when the single-sided timelock concludes. * The timelock is initiated based on price volatility of swaps over the last `movingAverageWindow`, and can be * extended by new swaps if they are sufficiently volatile. * The timelock protects against attempts to manipulate the price that is used to valuate the reservoir tokens during * single-sided operations. * It also guards against general legitimate volatility, as it is preferable to defer single-sided operations until * it is clearer what the market considers the price to be. * @return singleSidedTimelockDeadline The current deadline timestamp */ function singleSidedTimelockDeadline() external view returns (uint120 singleSidedTimelockDeadline); /** * @notice The timestamp by which the amount of reservoir tokens that can be exchanged during a single-sided operation * reaches its maximum value. * This maximum value is not necessarily the entirety of the reservoir, instead being calculated as a fraction of the * corresponding token's active liquidity. * @return swappableReservoirLimitReachesMaxDeadline The current deadline timestamp */ function swappableReservoirLimitReachesMaxDeadline() external view returns (uint120 swappableReservoirLimitReachesMaxDeadline); /** * @notice Returns the current limit on the number of reservoir tokens that can be exchanged during a single-sided mint/burn operation. * @return swappableReservoirLimit The amount of reservoir token that can be exchanged */ function getSwappableReservoirLimit() external view returns (uint256 swappableReservoirLimit); /** * @notice Whether the Pair is currently paused * @return _isPaused The paused state */ function getIsPaused() external view returns (bool _isPaused); /** * @notice Updates the pause state. * This can only be called by the Factory address. * @param isPausedNew The new value for isPaused */ function setIsPaused(bool isPausedNew) external; /** * @notice Get the current liquidity values. * @return _pool0 The active `token0` liquidity * @return _pool1 The active `token1` liquidity * @return _reservoir0 The inactive `token0` liquidity * @return _reservoir1 The inactive `token1` liquidity * @return _blockTimestampLast The timestamp of when the price was last updated */ function getLiquidityBalances() external view returns (uint112 _pool0, uint112 _pool1, uint112 _reservoir0, uint112 _reservoir1, uint32 _blockTimestampLast); /** * @notice The current `movingAveragePrice0` value, based on the current block timestamp. * @dev This is the `token0` price, time weighted to prevent manipulation. * Refer to [reservoir-valuation.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/reservoir-valuation.md#price-stability) for more detail. * * The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision. * It is used to valuate the reservoir tokens that are exchanged during single-sided operations. * @return _movingAveragePrice0 The current `movingAveragePrice0` value */ function movingAveragePrice0() external view returns (uint256 _movingAveragePrice0); /** * @notice Mints new liquidity tokens to `to` based on `amountIn0` of `token0` and `amountIn1 of`token1` deposited. * Expects both tokens to be deposited in a ratio that matches the current Pair price. * @dev The token deposits are deduced to be the delta between token balance before and after the transfers in order to account for unusual tokens. * Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#dual-sided-mint) for more detail. * @param amountIn0 The amount of `token0` that should be transferred in from the user * @param amountIn1 The amount of `token1` that should be transferred in from the user * @param to The account that receives the newly minted liquidity tokens * @return liquidityOut THe amount of liquidity tokens minted */ function mint(uint256 amountIn0, uint256 amountIn1, address to) external returns (uint256 liquidityOut); /** * @notice Mints new liquidity tokens to `to` based on how much `token0` or `token1` has been deposited. * The token transferred is the one that the Pair does not have a non-zero inactive liquidity balance for. * Expects only one token to be deposited, so that it can be paired with the other token's inactive liquidity. * @dev The token deposits are deduced to be the delta between token balance before and after the transfers in order to account for unusual tokens. * Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail. * @param amountIn The amount of tokens that should be transferred in from the user * @param to The account that receives the newly minted liquidity tokens * @return liquidityOut THe amount of liquidity tokens minted */ function mintWithReservoir(uint256 amountIn, address to) external returns (uint256 liquidityOut); /** * @notice Burns `liquidityIn` liquidity tokens to redeem to `to` the corresponding `amountOut0` of `token0` and `amountOut1` of `token1`. * @dev Refer to [burn-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/burn-math.md#dual-sided-burn) for more detail. * @param liquidityIn The amount of liquidity tokens to burn * @param to The account that receives the redeemed tokens * @return amountOut0 The amount of `token0` that the liquidity tokens are redeemed for * @return amountOut1 The amount of `token1` that the liquidity tokens are redeemed for */ function burn(uint256 liquidityIn, address to) external returns (uint256 amountOut0, uint256 amountOut1); /** * @notice Burns `liquidityIn` liquidity tokens to redeem to `to` the corresponding `amountOut0` of `token0` and `amountOut1` of `token1`. * Only returns tokens from the non-zero inactive liquidity balance, meaning one of `amountOut0` and `amountOut1` will be zero. * @dev Refer to [burn-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/burn-math.md#single-sided-burn) for more detail. * @param liquidityIn The amount of liquidity tokens to burn * @param to The account that receives the redeemed tokens * @return amountOut0 The amount of `token0` that the liquidity tokens are redeemed for * @return amountOut1 The amount of `token1` that the liquidity tokens are redeemed for */ function burnFromReservoir(uint256 liquidityIn, address to) external returns (uint256 amountOut0, uint256 amountOut1); /** * @notice Swaps one token for the other, taking `amountIn0` of `token0` and `amountIn1` of `token1` from the sender and sending `amountOut0` of `token0` and `amountOut1` of `token1` to `to`. * The price of the swap is determined by maintaining the "K Invariant". * A 0.3% fee is collected to distribute between liquidity providers and the protocol. * @dev The token deposits are deduced to be the delta between the current Pair contract token balances and the last stored balances. * Optional calldata can be passed to `data`, which will be used to confirm the output token transfer with `to` if `to` is a contract that implements the {IButtonswapCallee} interface. * Refer to [swap-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/swap-math.md) for more detail. * @param amountIn0 The amount of `token0` that the sender sends * @param amountIn1 The amount of `token1` that the sender sends * @param amountOut0 The amount of `token0` that the recipient receives * @param amountOut1 The amount of `token1` that the recipient receives * @param to The account that receives the swap output */ function swap(uint256 amountIn0, uint256 amountIn1, uint256 amountOut0, uint256 amountOut1, address to) external; }
// SPDX-License-Identifier: GPL-3.0-or-later // ToDo: Replace with solmate/SafeTransferLib pragma solidity >=0.6.0; // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false library TransferHelper { function safeApprove(address token, address to, uint256 value) internal { // bytes4(keccak256(bytes('approve(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED"); } function safeTransfer(address token, address to, uint256 value) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED"); } function safeTransferFrom(address token, address from, address to, uint256 value) internal { // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED"); } function safeTransferETH(address to, uint256 value) internal { (bool success,) = to.call{value: value}(new bytes(0)); require(success, "TransferHelper: ETH_TRANSFER_FAILED"); } }
pragma solidity ^0.8.13; import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol"; import {Math} from "buttonswap-periphery_buttonswap-core/libraries/Math.sol"; import {IERC20} from "../interfaces/IERC20.sol"; library ButtonswapLibrary { /// @notice Identical addresses provided error IdenticalAddresses(); /// @notice Zero address provided error ZeroAddress(); /// @notice Insufficient amount provided error InsufficientAmount(); /// @notice Insufficient liquidity provided error InsufficientLiquidity(); /// @notice Insufficient input amount provided error InsufficientInputAmount(); /// @notice Insufficient output amount provided error InsufficientOutputAmount(); /// @notice Invalid path provided error InvalidPath(); /** * @dev Returns sorted token addresses, used to handle return values from pairs sorted in this order * @param tokenA First token address * @param tokenB Second token address * @return token0 First sorted token address * @return token1 Second sorted token address */ function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { if (tokenA == tokenB) { revert IdenticalAddresses(); } (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); // If the tokens are different and sorted, only token0 can be the zero address if (token0 == address(0)) { revert ZeroAddress(); } } /** * @dev Predicts the address that the Pair contract for given tokens would have been deployed to * @dev Specifically, this calculates the CREATE2 address for a Pair contract. * @dev It's done this way to avoid making any external calls, and thus saving on gas versus other approaches. * @param factory The address of the ButtonswapFactory used to create the pair * @param tokenA First token address * @param tokenB Second token address * @return pair The pair address */ function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) { (address token0, address token1) = sortTokens(tokenA, tokenB); // Init Hash Code is generated by the following command: // bytes32 initHashCode = keccak256(abi.encodePacked(type(ButtonswapPair).creationCode)); pair = address( uint160( uint256( keccak256( abi.encodePacked( hex"ff", factory, keccak256(abi.encodePacked(token0, token1)), hex"912fa011211d18178fef8f22392edc90ca8f101645ab8347e1359b5ce2f890df" // init code hash ) ) ) ) ); } /** * @dev Fetches and sorts the pools for a pair. Pools are the current token balances in the pair contract serving as liquidity. * @param factory The address of the ButtonswapFactory * @param tokenA First token address * @param tokenB Second token address * @return poolA Pool corresponding to tokenA * @return poolB Pool corresponding to tokenB */ function getPools(address factory, address tokenA, address tokenB) internal view returns (uint256 poolA, uint256 poolB) { (address token0,) = sortTokens(tokenA, tokenB); (uint256 pool0, uint256 pool1,,,) = IButtonswapPair(pairFor(factory, tokenA, tokenB)).getLiquidityBalances(); (poolA, poolB) = tokenA == token0 ? (pool0, pool1) : (pool1, pool0); } /** * @dev Fetches and sorts the reservoirs for a pair. Reservoirs are the current token balances in the pair contract not actively serving as liquidity. * @param factory The address of the ButtonswapFactory * @param tokenA First token address * @param tokenB Second token address * @return reservoirA Reservoir corresponding to tokenA * @return reservoirB Reservoir corresponding to tokenB */ function getReservoirs(address factory, address tokenA, address tokenB) internal view returns (uint256 reservoirA, uint256 reservoirB) { (address token0,) = sortTokens(tokenA, tokenB); (,, uint256 reservoir0, uint256 reservoir1,) = IButtonswapPair(pairFor(factory, tokenA, tokenB)).getLiquidityBalances(); (reservoirA, reservoirB) = tokenA == token0 ? (reservoir0, reservoir1) : (reservoir1, reservoir0); } /** * @dev Fetches and sorts the pools and reservoirs for a pair. * - Pools are the current token balances in the pair contract serving as liquidity. * - Reservoirs are the current token balances in the pair contract not actively serving as liquidity. * @param factory The address of the ButtonswapFactory * @param tokenA First token address * @param tokenB Second token address * @return poolA Pool corresponding to tokenA * @return poolB Pool corresponding to tokenB * @return reservoirA Reservoir corresponding to tokenA * @return reservoirB Reservoir corresponding to tokenB */ function getLiquidityBalances(address factory, address tokenA, address tokenB) internal view returns (uint256 poolA, uint256 poolB, uint256 reservoirA, uint256 reservoirB) { (address token0,) = sortTokens(tokenA, tokenB); (uint256 pool0, uint256 pool1, uint256 reservoir0, uint256 reservoir1,) = IButtonswapPair(pairFor(factory, tokenA, tokenB)).getLiquidityBalances(); (poolA, poolB, reservoirA, reservoirB) = tokenA == token0 ? (pool0, pool1, reservoir0, reservoir1) : (pool1, pool0, reservoir1, reservoir0); } /** * @dev Given some amount of an asset and pair pools, returns an equivalent amount of the other asset * @param amountA The amount of token A * @param poolA The balance of token A in the pool * @param poolB The balance of token B in the pool * @return amountB The amount of token B */ function quote(uint256 amountA, uint256 poolA, uint256 poolB) internal pure returns (uint256 amountB) { if (amountA == 0) { revert InsufficientAmount(); } if (poolA == 0 || poolB == 0) { revert InsufficientLiquidity(); } amountB = (amountA * poolB) / poolA; } /** * @dev Given a factory, two tokens, and a mintAmount of the first, returns how much of the much of the mintAmount will be swapped for the other token and for how much during a mintWithReservoir operation. * @dev The logic is a condensed version of PairMath.getSingleSidedMintLiquidityOutAmountA and PairMath.getSingleSidedMintLiquidityOutAmountB * @param factory The address of the ButtonswapFactory that created the pairs * @param tokenA First token address * @param tokenB Second token address * @param mintAmountA The amount of tokenA to be minted * @return tokenAToSwap The amount of tokenA to be exchanged for tokenB from the reservoir * @return swappedReservoirAmountB The amount of tokenB returned from the reservoir */ function getMintSwappedAmounts(address factory, address tokenA, address tokenB, uint256 mintAmountA) internal view returns (uint256 tokenAToSwap, uint256 swappedReservoirAmountB) { IButtonswapPair pair = IButtonswapPair(pairFor(factory, tokenA, tokenB)); uint256 totalA = IERC20(tokenA).balanceOf(address(pair)); uint256 totalB = IERC20(tokenB).balanceOf(address(pair)); uint256 movingAveragePrice0 = pair.movingAveragePrice0(); // tokenA == token0 if (tokenA < tokenB) { tokenAToSwap = (mintAmountA * totalB) / (Math.mulDiv(movingAveragePrice0, (totalA + mintAmountA), 2 ** 112) + totalB); swappedReservoirAmountB = (tokenAToSwap * movingAveragePrice0) / 2 ** 112; } else { tokenAToSwap = (mintAmountA * totalB) / (((2 ** 112 * (totalA + mintAmountA)) / movingAveragePrice0) + totalB); // Inverse price so again we can use it without overflow risk swappedReservoirAmountB = (tokenAToSwap * (2 ** 112)) / movingAveragePrice0; } } /** * @dev Given a factory, two tokens, and a liquidity amount, returns how much of the first token will be withdrawn from the pair and how much of it came from the reservoir during a burnFromReservoir operation. * @dev The logic is a condensed version of PairMath.getSingleSidedBurnOutputAmountA and PairMath.getSingleSidedBurnOutputAmountB * @param factory The address of the ButtonswapFactory that created the pairs * @param tokenA First token address * @param tokenB Second token address * @param liquidity The amount of liquidity to be burned * @return tokenOutA The amount of tokenA to be withdrawn from the pair * @return swappedReservoirAmountA The amount of tokenA returned from the reservoir */ function getBurnSwappedAmounts(address factory, address tokenA, address tokenB, uint256 liquidity) internal view returns (uint256 tokenOutA, uint256 swappedReservoirAmountA) { IButtonswapPair pair = IButtonswapPair(pairFor(factory, tokenA, tokenB)); uint256 totalLiquidity = pair.totalSupply(); uint256 totalA = IERC20(tokenA).balanceOf(address(pair)); uint256 totalB = IERC20(tokenB).balanceOf(address(pair)); uint256 movingAveragePrice0 = pair.movingAveragePrice0(); uint256 tokenBToSwap = (totalB * liquidity) / totalLiquidity; tokenOutA = (totalA * liquidity) / totalLiquidity; // tokenA == token0 if (tokenA < tokenB) { swappedReservoirAmountA = (tokenBToSwap * (2 ** 112)) / movingAveragePrice0; } else { swappedReservoirAmountA = (tokenBToSwap * movingAveragePrice0) / 2 ** 112; } tokenOutA += swappedReservoirAmountA; } /** * @dev Given an input amount of an asset and pair pools, returns the maximum output amount of the other asset * Factors in the fee on the input amount. * @param amountIn The input amount of the asset * @param poolIn The balance of the input asset in the pool * @param poolOut The balance of the output asset in the pool * @return amountOut The output amount of the other asset */ function getAmountOut(uint256 amountIn, uint256 poolIn, uint256 poolOut) internal pure returns (uint256 amountOut) { if (amountIn == 0) { revert InsufficientInputAmount(); } if (poolIn == 0 || poolOut == 0) { revert InsufficientLiquidity(); } uint256 amountInWithFee = amountIn * 997; uint256 numerator = amountInWithFee * poolOut; uint256 denominator = (poolIn * 1000) + amountInWithFee; amountOut = numerator / denominator; } /** * @dev Given an output amount of an asset and pair pools, returns a required input amount of the other asset * @param amountOut The output amount of the asset * @param poolIn The balance of the input asset in the pool * @param poolOut The balance of the output asset in the pool * @return amountIn The required input amount of the other asset */ function getAmountIn(uint256 amountOut, uint256 poolIn, uint256 poolOut) internal pure returns (uint256 amountIn) { if (amountOut == 0) { revert InsufficientOutputAmount(); } if (poolIn == 0 || poolOut == 0) { revert InsufficientLiquidity(); } uint256 numerator = poolIn * amountOut * 1000; uint256 denominator = (poolOut - amountOut) * 997; amountIn = (numerator / denominator) + 1; } /** * @dev Given an ordered array of tokens and an input amount of the first asset, performs chained getAmountOut calculations to calculate the output amount of the final asset * @param factory The address of the ButtonswapFactory that created the pairs * @param amountIn The input amount of the first asset * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @return amounts The output amounts of each asset in the path */ function getAmountsOut(address factory, uint256 amountIn, address[] memory path) internal view returns (uint256[] memory amounts) { if (path.length < 2) { revert InvalidPath(); } amounts = new uint256[](path.length); amounts[0] = amountIn; for (uint256 i; i < path.length - 1; i++) { (uint256 poolIn, uint256 poolOut,,) = getLiquidityBalances(factory, path[i], path[i + 1]); amounts[i + 1] = getAmountOut(amounts[i], poolIn, poolOut); } } /** * @dev Given an ordered array of tokens and an output amount of the final asset, performs chained getAmountIn calculations to calculate the input amount of the first asset * @param factory The address of the ButtonswapFactory that created the pairs * @param amountOut The output amount of the final asset * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @return amounts The input amounts of each asset in the path */ function getAmountsIn(address factory, uint256 amountOut, address[] memory path) internal view returns (uint256[] memory amounts) { if (path.length < 2) { revert InvalidPath(); } amounts = new uint256[](path.length); amounts[amounts.length - 1] = amountOut; for (uint256 i = path.length - 1; i > 0; i--) { (uint256 poolIn, uint256 poolOut,,) = getLiquidityBalances(factory, path[i - 1], path[i]); amounts[i - 1] = getAmountIn(amounts[i], poolIn, poolOut); } } }
pragma solidity >=0.5.0; interface IERC20 { event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); }
pragma solidity >=0.5.0; interface IWETH { function deposit() external payable; function transfer(address to, uint256 value) external returns (bool); function withdraw(uint256) external; function approve(address guy, uint256 wad) external returns (bool); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IButtonswapFactory} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol"; import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol"; import {TransferHelper} from "./libraries/TransferHelper.sol"; import {IETHButtonswapRouter} from "./interfaces/IButtonswapRouter/IETHButtonswapRouter.sol"; import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol"; import {IERC20} from "./interfaces/IERC20.sol"; import {IWETH} from "./interfaces/IWETH.sol"; import {BasicButtonswapRouter} from "./BasicButtonswapRouter.sol"; contract ETHButtonswapRouter is BasicButtonswapRouter, IETHButtonswapRouter { /** * @inheritdoc IETHButtonswapRouter */ address public immutable override WETH; constructor(address _factory, address _WETH) BasicButtonswapRouter(_factory) { WETH = _WETH; } /** * @dev Only accepts ETH via fallback from the WETH contract */ receive() external payable { if (msg.sender != WETH) { revert NonWETHSender(); } } /** * @inheritdoc IETHButtonswapRouter */ function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, uint16 movingAveragePrice0ThresholdBps, address to, uint256 deadline ) external payable virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH, uint256 liquidity) { (amountToken, amountETH) = _addLiquidity( token, WETH, amountTokenDesired, msg.value, amountTokenMin, amountETHMin, movingAveragePrice0ThresholdBps ); address pair = ButtonswapLibrary.pairFor(factory, token, WETH); TransferHelper.safeTransferFrom(token, msg.sender, address(this), amountToken); TransferHelper.safeApprove(token, pair, amountToken); IWETH(WETH).deposit{value: amountETH}(); TransferHelper.safeApprove(WETH, pair, amountETH); (address token0,) = ButtonswapLibrary.sortTokens(token, WETH); liquidity = (token == token0) ? IButtonswapPair(pair).mint(amountToken, amountETH, to) : IButtonswapPair(pair).mint(amountETH, amountToken, to); // refund dust eth, if any if (msg.value > amountETH) { TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); } } /** * @inheritdoc IETHButtonswapRouter */ function addLiquidityETHWithReservoir( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH, uint256 liquidity) { (amountToken, amountETH) = _addLiquidityWithReservoir(token, WETH, amountTokenDesired, msg.value, amountTokenMin, amountETHMin); address pair = ButtonswapLibrary.pairFor(factory, token, WETH); if (amountToken > 0) { TransferHelper.safeTransferFrom(token, msg.sender, address(this), amountToken); TransferHelper.safeApprove(token, pair, amountToken); liquidity = IButtonswapPair(pair).mintWithReservoir(amountToken, to); } else if (amountETH > 0) { IWETH(WETH).deposit{value: amountETH}(); TransferHelper.safeApprove(WETH, pair, amountETH); liquidity = IButtonswapPair(pair).mintWithReservoir(amountETH, to); } // refund dust eth, if any if (msg.value > amountETH) { TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); } } /** * @inheritdoc IETHButtonswapRouter */ function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH) { (amountToken, amountETH) = removeLiquidity(token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline); TransferHelper.safeTransfer(token, to, amountToken); IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } /** * @inheritdoc IETHButtonswapRouter */ function removeLiquidityETHFromReservoir( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH) { (amountToken, amountETH) = removeLiquidityFromReservoir(token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline); if (amountToken > 0) { TransferHelper.safeTransfer(token, to, amountToken); } else if (amountETH > 0) { IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } } function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint256 amountToken, uint256 amountETH) { address pair = ButtonswapLibrary.pairFor(factory, token, WETH); uint256 value = approveMax ? type(uint256).max : liquidity; IButtonswapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline); } /** * @inheritdoc IETHButtonswapRouter */ function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) external payable virtual override ensure(deadline) returns (uint256[] memory amounts) { if (path[0] != WETH) { revert InvalidPath(); } amounts = ButtonswapLibrary.getAmountsOut(factory, msg.value, path); if (amounts[amounts.length - 1] < amountOutMin) { revert InsufficientOutputAmount(); } IWETH(WETH).deposit{value: amounts[0]}(); if (!IWETH(WETH).transfer(address(this), amounts[0])) { revert FailedWETHTransfer(); } _swap(amounts, path, to); } /** * @inheritdoc IETHButtonswapRouter */ function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { if (path[path.length - 1] != WETH) { revert InvalidPath(); } amounts = ButtonswapLibrary.getAmountsIn(factory, amountOut, path); if (amounts[0] > amountInMax) { revert ExcessiveInputAmount(); } TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]); _swap(amounts, path, address(this)); // Convert final token to ETH and send to `to` IWETH(WETH).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); } /** * @inheritdoc IETHButtonswapRouter */ function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { if (path[path.length - 1] != WETH) { revert InvalidPath(); } amounts = ButtonswapLibrary.getAmountsOut(factory, amountIn, path); if (amounts[amounts.length - 1] < amountOutMin) { revert InsufficientOutputAmount(); } TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); } /** * @inheritdoc IETHButtonswapRouter */ function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to, uint256 deadline) external payable virtual override ensure(deadline) returns (uint256[] memory amounts) { if (path[0] != WETH) { revert InvalidPath(); } amounts = ButtonswapLibrary.getAmountsIn(factory, amountOut, path); if (amounts[0] > msg.value) { revert ExcessiveInputAmount(); } IWETH(WETH).deposit{value: amounts[0]}(); if (!IWETH(WETH).transfer(address(this), amounts[0])) { revert FailedWETHTransfer(); } _swap(amounts, path, to); // refund dust eth, if any if (msg.value > amounts[0]) { TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IBasicButtonswapRouter} from "./IBasicButtonswapRouter.sol"; import {IETHButtonswapRouterErrors} from "./IETHButtonswapRouterErrors.sol"; interface IETHButtonswapRouter is IBasicButtonswapRouter, IETHButtonswapRouterErrors { /** * @notice Returns the address of the WETH token * @return WETH The address of the WETH token */ function WETH() external view returns (address WETH); /** * @notice Similar to `addLiquidity` but one of the tokens is ETH wrapped into WETH. * Adds liquidity to a pair, creating it if it doesn't exist yet, and transfers the liquidity tokens to the recipient. * @dev If the pair is empty, amountTokenMin and amountETHMin are ignored. * If the pair is nonempty, it deposits as much of token and WETH as possible while maintaining 3 conditions: * 1. The ratio of token to WETH in the pair remains approximately the same * 2. The amount of token in the pair is at least amountTokenMin but less than or equal to amountTokenDesired * 3. The amount of WETH in the pair is at least amountETHMin but less than or equal to ETH sent * @param token The address of the non-WETH token in the pair. * @param amountTokenDesired The maximum amount of the non-ETH token to add to the pair. * @param amountTokenMin The minimum amount of the non-ETH token to add to the pair. * @param amountETHMin The minimum amount of ETH/WETH to add to the pair. * @param movingAveragePrice0ThresholdBps The percentage threshold that movingAveragePrice0 can deviate from the current price. * @param to The address to send the liquidity tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountToken The amount of token actually added to the pair. * @return amountETH The amount of ETH/WETH actually added to the pair. * @return liquidity The amount of liquidity tokens minted. */ function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, uint16 movingAveragePrice0ThresholdBps, address to, uint256 deadline ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); /** * @notice Similar to `addLiquidityWithReservoir` but one of the tokens is ETH wrapped into WETH. * Adds liquidity to a pair, opposite to the existing reservoir, and transfers the liquidity tokens to the recipient * @dev Since there at most one reservoir at a given time, some conditions are checked: * 1. If there is no reservoir, it rejects * 2. If the non-WETH token has the reservoir, amountTokenDesired parameter ignored. * 3. The token/WETH with the reservoir has its amount deducted from the reservoir (checked against corresponding amountMin parameter) * @param token The address of the non-WETH token in the pair. * @param amountTokenDesired The maximum amount of the non-WETH token to add to the pair. * @param amountTokenMin The minimum amount of the non-WETH token to add to the pair. * @param amountETHMin The minimum amount of WETH to add to the pair. * @param to The address to send the liquidity tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountToken The amount of the non-ETH token actually added to the pair. * @return amountETH The amount of WETH actually added to the pair. * @return liquidity The amount of liquidity tokens minted. */ function addLiquidityETHWithReservoir( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); /** * @notice Similar to `removeLiquidity()` but one of the tokens is ETH wrapped into WETH. * Removes liquidity from a pair, and transfers the tokens to the recipient. * @param token The address of the non-WETH token in the pair. * @param liquidity The amount of liquidity tokens to burn. * @param amountTokenMin The minimum amount of the non-WETH token to withdraw from the pair. * @param amountETHMin The minimum amount of ETH/WETH to withdraw from the pair. * @param to The address to send the tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountToken The amount of the non-WETH token actually withdrawn from the pair. * @return amountETH The amount of ETH/WETH actually withdrawn from the pair. */ function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); /** * @notice Similar to `removeLiquidityFromReservoir()` but one of the tokens is ETH wrapped into WETH. * Removes liquidity from the reservoir of a pair and transfers the tokens to the recipient. * @param token The address of the non-WETH token in the pair. * @param liquidity The amount of liquidity tokens to burn. * @param amountTokenMin The minimum amount of the non-WETH token to withdraw from the pair. * @param amountETHMin The minimum amount of ETH/WETH to withdraw from the pair. * @param to The address to send the tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountToken The amount of the non-WETH token actually withdrawn from the pair. * @return amountETH The amount of ETH/WETH actually withdrawn from the pair. */ function removeLiquidityETHFromReservoir( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); /** * @notice Similar to `removeLiquidityWETH()` but utilizes the Permit signatures to reduce gas consumption. * Removes liquidity from a pair where one of the tokens is ETH wrapped into WETH, and transfers the tokens to the recipient. * @param token The address of the non-WETH token in the pair. * @param liquidity The amount of liquidity tokens to burn. * @param amountTokenMin The minimum amount of the non-WETH token to withdraw from the pair. * @param amountETHMin The minimum amount of ETH/WETH to withdraw from the pair. * @param to The address to send the tokens to. * @param deadline The time after which this transaction can no longer be executed. * @param approveMax Whether the signature is for the max uint256 or liquidity value * @param v Part of the signature * @param r Part of the signature * @param s Part of the signature * @return amountToken The amount of the non-WETH token actually withdrawn from the pair. * @return amountETH The amount of ETH/WETH actually withdrawn from the pair. */ function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountETH); /** * @notice Similar to `swapExactTokensForTokens()` the first token is ETH wrapped into WETH. * Given an ordered array of tokens, performs consecutive swaps from a specific amount of the first token to the last token in the array. * @param amountOutMin The minimum amount of the last token to receive from the swap. * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @param to The address to send the output token to. * @param deadline The time after which this transaction can no longer be executed. */ function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) external payable returns (uint256[] memory amounts); /** * @notice Similar to `swapTokensForExactTokens()` the last token is ETH wrapped into WETH. * Given an ordered array of tokens, performs consecutive swaps from the first token to a specific amount of the last token in the array. * @param amountOut The amount of ETH to receive from the swap. * @param amountInMax The maximum amount of the first token to swap. * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @param to The address to send the output token to. * @param deadline The time after which this transaction can no longer be executed. */ function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); /** * @notice Similar to `swapExactTokensForTokens()` but the last token is ETH wrapped into WETH. * Given an ordered array of tokens, performs consecutive swaps from a specific amount of the first token to the last token in the array. * @param amountIn The amount of the first token to swap. * @param amountOutMin The minimum amount of the last token to receive from the swap. * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @param to The address to send the output token to. * @param deadline The time after which this transaction can no longer be executed. */ function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); /** * @notice Similar to `swapTokensForExactTokens()` but the first token is ETH wrapped into WETH. * Given an ordered array of tokens, performs consecutive swaps from the first token to a specific amount of the last token in the array. * @param amountOut The amount of the last token to receive from the swap. * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @param to The address to send the output token to. * @param deadline The time after which this transaction can no longer be executed. */ function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to, uint256 deadline) external payable returns (uint256[] memory amounts); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; interface IButtonswapFactoryErrors { /** * @notice The given token addresses are the same */ error TokenIdenticalAddress(); /** * @notice The given token address is the zero address */ error TokenZeroAddress(); /** * @notice The given tokens already have a {ButtonswapPair} instance */ error PairExists(); /** * @notice User does not have permission for the attempted operation */ error Forbidden(); /** * @notice There was an attempt to update a parameter to an invalid value */ error InvalidParameter(); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; interface IButtonswapFactoryEvents { /** * @notice Emitted when a new Pair is created. * @param token0 The first sorted token * @param token1 The second sorted token * @param pair The address of the new {ButtonswapPair} contract * @param count The new total number of Pairs created */ event PairCreated(address indexed token0, address indexed token1, address pair, uint256 count); /** * @notice Emitted when the default parameters for a new pair have been updated. * @param paramSetter The address that changed the parameters * @param newDefaultMovingAverageWindow The new movingAverageWindow default value * @param newDefaultMaxVolatilityBps The new maxVolatilityBps default value * @param newDefaultMinTimelockDuration The new minTimelockDuration default value * @param newDefaultMaxTimelockDuration The new maxTimelockDuration default value * @param newDefaultMaxSwappableReservoirLimitBps The new maxSwappableReservoirLimitBps default value * @param newDefaultSwappableReservoirGrowthWindow The new swappableReservoirGrowthWindow default value */ event DefaultParametersUpdated( address indexed paramSetter, uint32 newDefaultMovingAverageWindow, uint16 newDefaultMaxVolatilityBps, uint32 newDefaultMinTimelockDuration, uint32 newDefaultMaxTimelockDuration, uint16 newDefaultMaxSwappableReservoirLimitBps, uint32 newDefaultSwappableReservoirGrowthWindow ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapERC20Errors} from "../IButtonswapERC20/IButtonswapERC20Errors.sol"; interface IButtonswapPairErrors is IButtonswapERC20Errors { /** * @notice Re-entrancy guard prevented method call */ error Locked(); /** * @notice User does not have permission for the attempted operation */ error Forbidden(); /** * @notice Integer maximums exceeded */ error Overflow(); /** * @notice Initial deposit not yet made */ error Uninitialized(); /** * @notice There was not enough liquidity in the reservoir */ error InsufficientReservoir(); /** * @notice Not enough liquidity was created during mint */ error InsufficientLiquidityMinted(); /** * @notice Not enough funds added to mint new liquidity */ error InsufficientLiquidityAdded(); /** * @notice More liquidity must be burned to be redeemed for non-zero amounts */ error InsufficientLiquidityBurned(); /** * @notice Swap was attempted with zero input */ error InsufficientInputAmount(); /** * @notice Swap was attempted with zero output */ error InsufficientOutputAmount(); /** * @notice Pool doesn't have the liquidity to service the swap */ error InsufficientLiquidity(); /** * @notice The specified "to" address is invalid */ error InvalidRecipient(); /** * @notice The product of pool balances must not change during a swap (save for accounting for fees) */ error KInvariant(); /** * @notice The new price ratio after a swap is invalid (one or more of the price terms are zero) */ error InvalidFinalPrice(); /** * @notice Single sided operations are not executable at this point in time */ error SingleSidedTimelock(); /** * @notice The attempted operation would have swapped reservoir tokens above the current limit */ error SwappableReservoirExceeded(); /** * @notice All operations on the pair other than dual-sided burning are currently paused */ error Paused(); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapERC20Events} from "../IButtonswapERC20/IButtonswapERC20Events.sol"; interface IButtonswapPairEvents is IButtonswapERC20Events { /** * @notice Emitted when a {IButtonswapPair-mint} is performed. * Some `token0` and `token1` are deposited in exchange for liquidity tokens representing a claim on them. * @param from The account that supplied the tokens for the mint * @param amount0 The amount of `token0` that was deposited * @param amount1 The amount of `token1` that was deposited * @param amountOut The amount of liquidity tokens that were minted * @param to The account that received the tokens from the mint */ event Mint(address indexed from, uint256 amount0, uint256 amount1, uint256 amountOut, address indexed to); /** * @notice Emitted when a {IButtonswapPair-burn} is performed. * Liquidity tokens are redeemed for underlying `token0` and `token1`. * @param from The account that supplied the tokens for the burn * @param amountIn The amount of liquidity tokens that were burned * @param amount0 The amount of `token0` that was received * @param amount1 The amount of `token1` that was received * @param to The account that received the tokens from the burn */ event Burn(address indexed from, uint256 amountIn, uint256 amount0, uint256 amount1, address indexed to); /** * @notice Emitted when a {IButtonswapPair-swap} is performed. * @param from The account that supplied the tokens for the swap * @param amount0In The amount of `token0` that went into the swap * @param amount1In The amount of `token1` that went into the swap * @param amount0Out The amount of `token0` that came out of the swap * @param amount1Out The amount of `token1` that came out of the swap * @param to The account that received the tokens from the swap */ event Swap( address indexed from, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); /** * @notice Emitted when the movingAverageWindow parameter for the pair has been updated. * @param newMovingAverageWindow The new movingAverageWindow value */ event MovingAverageWindowUpdated(uint32 newMovingAverageWindow); /** * @notice Emitted when the maxVolatilityBps parameter for the pair has been updated. * @param newMaxVolatilityBps The new maxVolatilityBps value */ event MaxVolatilityBpsUpdated(uint16 newMaxVolatilityBps); /** * @notice Emitted when the minTimelockDuration parameter for the pair has been updated. * @param newMinTimelockDuration The new minTimelockDuration value */ event MinTimelockDurationUpdated(uint32 newMinTimelockDuration); /** * @notice Emitted when the maxTimelockDuration parameter for the pair has been updated. * @param newMaxTimelockDuration The new maxTimelockDuration value */ event MaxTimelockDurationUpdated(uint32 newMaxTimelockDuration); /** * @notice Emitted when the maxSwappableReservoirLimitBps parameter for the pair has been updated. * @param newMaxSwappableReservoirLimitBps The new maxSwappableReservoirLimitBps value */ event MaxSwappableReservoirLimitBpsUpdated(uint16 newMaxSwappableReservoirLimitBps); /** * @notice Emitted when the swappableReservoirGrowthWindow parameter for the pair has been updated. * @param newSwappableReservoirGrowthWindow The new swappableReservoirGrowthWindow value */ event SwappableReservoirGrowthWindowUpdated(uint32 newSwappableReservoirGrowthWindow); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapERC20Errors} from "./IButtonswapERC20Errors.sol"; import {IButtonswapERC20Events} from "./IButtonswapERC20Events.sol"; interface IButtonswapERC20 is IButtonswapERC20Errors, IButtonswapERC20Events { /** * @notice Returns the name of the token. * @return _name The token name */ function name() external view returns (string memory _name); /** * @notice Returns the symbol of the token, usually a shorter version of the name. * @return _symbol The token symbol */ function symbol() external view returns (string memory _symbol); /** * @notice Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5.05` (`505 / 10 ** 2`). * @dev This information is only used for _display_ purposes: it in no way affects any of the arithmetic of the contract. * @return decimals The number of decimals */ function decimals() external pure returns (uint8 decimals); /** * @notice Returns the amount of tokens in existence. * @return totalSupply The amount of tokens in existence */ function totalSupply() external view returns (uint256 totalSupply); /** * @notice Returns the amount of tokens owned by `account`. * @param owner The account the balance is being checked for * @return balance The amount of tokens owned by `owner` */ function balanceOf(address owner) external view returns (uint256 balance); /** * @notice 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. * @param owner The account that owns the tokens * @param spender The account that can spend the tokens * @return allowance The amount of tokens owned by `owner` that the `spender` can transfer */ function allowance(address owner, address spender) external view returns (uint256 allowance); /** * @notice Sets `value` as the allowance of `spender` over the caller's tokens. * @dev 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 {IButtonswapERC20Events-Approval} event. * @param spender The account that is granted permission to spend the tokens * @param value The amount of tokens that can be spent * @return success Whether the operation succeeded */ function approve(address spender, uint256 value) external returns (bool success); /** * @notice Moves `value` tokens from the caller's account to `to`. * @dev Emits a {IButtonswapERC20Events-Transfer} event. * @param to The account that is receiving the tokens * @param value The amount of tokens being sent * @return success Whether the operation succeeded */ function transfer(address to, uint256 value) external returns (bool success); /** * @notice Moves `value` tokens from `from` to `to` using the allowance mechanism. * `value` is then deducted from the caller's allowance. * @dev Emits a {IButtonswapERC20Events-Transfer} event. * @param from The account that is sending the tokens * @param to The account that is receiving the tokens * @param value The amount of tokens being sent * @return success Whether the operation succeeded */ function transferFrom(address from, address to, uint256 value) external returns (bool); /** * @notice Returns the domain separator used in the encoding of the signature for {permit}, as defined by [EIP712](https://eips.ethereum.org/EIPS/eip-712). * @return DOMAIN_SEPARATOR The `DOMAIN_SEPARATOR` value */ function DOMAIN_SEPARATOR() external view returns (bytes32 DOMAIN_SEPARATOR); /** * @notice Returns the typehash used in the encoding of the signature for {permit}, as defined by [EIP712](https://eips.ethereum.org/EIPS/eip-712). * @return PERMIT_TYPEHASH The `PERMIT_TYPEHASH` value */ function PERMIT_TYPEHASH() external pure returns (bytes32 PERMIT_TYPEHASH); /** * @notice Returns the current nonce for `owner`. * This value must be included whenever a signature is generated for {permit}. * @dev Every successful call to {permit} increases `owner`'s nonce by one. * This prevents a signature from being used multiple times. * @param owner The account to get the nonce for * @return nonce The current nonce for the given `owner` */ function nonces(address owner) external view returns (uint256 nonce); /** * @notice Sets `value` as the allowance of `spender` over `owner`'s tokens, given `owner`'s signed approval. * @dev IMPORTANT: The same issues {approve} has related to transaction ordering also apply here. * * Emits an {IButtonswapERC20Events-Approval} event. * * Requirements: * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` over the EIP712-formatted function arguments. * - the signature must use `owner`'s current nonce (see {nonces}). * * For more information on the signature format, see the [relevant EIP section](https://eips.ethereum.org/EIPS/eip-2612#specification). * @param owner The account that owns the tokens * @param spender The account that can spend the tokens * @param value The amount of `owner`'s tokens that `spender` can transfer * @param deadline The future time after which the permit is no longer valid * @param v Part of the signature * @param r Part of the signature * @param s Part of the signature */ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.13; // a library for performing various math operations library Math { function min(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x < y ? x : y; } function max(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x > y ? x : y; } // Borrowed implementation from solmate // https://github.com/transmissions11/solmate/blob/2001af43aedb46fdc2335d2a7714fb2dae7cfcd1/src/utils/FixedPointMathLib.sol#L164 function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IButtonswapFactory} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol"; import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol"; import {TransferHelper} from "./libraries/TransferHelper.sol"; import {IBasicButtonswapRouter} from "./interfaces/IButtonswapRouter/IBasicButtonswapRouter.sol"; import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol"; import {IERC20} from "./interfaces/IERC20.sol"; import {IWETH} from "./interfaces/IWETH.sol"; import {RootButtonswapRouter} from "./RootButtonswapRouter.sol"; contract BasicButtonswapRouter is RootButtonswapRouter, IBasicButtonswapRouter { modifier ensure(uint256 deadline) { if (block.timestamp > deadline) { revert Expired(); } _; } constructor(address _factory) RootButtonswapRouter(_factory) {} /** * @inheritdoc IBasicButtonswapRouter */ function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, uint16 movingAveragePrice0ThresholdBps, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) { (amountA, amountB) = _addLiquidity( tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin, movingAveragePrice0ThresholdBps ); address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, address(this), amountA); TransferHelper.safeApprove(tokenA, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, address(this), amountB); TransferHelper.safeApprove(tokenB, pair, amountB); if (tokenA < tokenB) { liquidity = IButtonswapPair(pair).mint(amountA, amountB, to); } else { liquidity = IButtonswapPair(pair).mint(amountB, amountA, to); } } /** * @inheritdoc IBasicButtonswapRouter */ function addLiquidityWithReservoir( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) { (amountA, amountB) = _addLiquidityWithReservoir(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB); if (amountA > 0) { TransferHelper.safeTransferFrom(tokenA, msg.sender, address(this), amountA); TransferHelper.safeApprove(tokenA, pair, amountA); liquidity = IButtonswapPair(pair).mintWithReservoir(amountA, to); } else if (amountB > 0) { TransferHelper.safeTransferFrom(tokenB, msg.sender, address(this), amountB); TransferHelper.safeApprove(tokenB, pair, amountB); liquidity = IButtonswapPair(pair).mintWithReservoir(amountB, to); } } // **** REMOVE LIQUIDITY **** /** * @inheritdoc IBasicButtonswapRouter */ function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB) { address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB); IButtonswapPair(pair).transferFrom(msg.sender, address(this), liquidity); // send liquidity to router (uint256 amount0, uint256 amount1) = IButtonswapPair(pair).burn(liquidity, to); (address token0,) = ButtonswapLibrary.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); if (amountA < amountAMin) { revert InsufficientAAmount(); } if (amountB < amountBMin) { revert InsufficientBAmount(); } } /** * @inheritdoc IBasicButtonswapRouter */ function removeLiquidityFromReservoir( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB) { address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB); IButtonswapPair(pair).transferFrom(msg.sender, address(this), liquidity); // send liquidity to router (uint256 amount0, uint256 amount1) = IButtonswapPair(pair).burnFromReservoir(liquidity, to); (address token0,) = ButtonswapLibrary.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); if (amountA < amountAMin) { revert InsufficientAAmount(); } if (amountB < amountBMin) { revert InsufficientBAmount(); } } /** * @inheritdoc IBasicButtonswapRouter */ function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint256 amountA, uint256 amountB) { address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB); uint256 value = approveMax ? type(uint256).max : liquidity; IButtonswapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline); } // **** SWAP **** /** * @inheritdoc IBasicButtonswapRouter */ function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { amounts = ButtonswapLibrary.getAmountsOut(factory, amountIn, path); if (amounts[amounts.length - 1] < amountOutMin) { revert InsufficientOutputAmount(); } IButtonswapPair(ButtonswapLibrary.pairFor(factory, path[0], path[1])); TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]); _swap(amounts, path, to); } /** * @inheritdoc IBasicButtonswapRouter */ function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { amounts = ButtonswapLibrary.getAmountsIn(factory, amountOut, path); if (amounts[0] > amountInMax) { revert ExcessiveInputAmount(); } // IButtonswapPair pair = IButtonswapPair(ButtonswapLibrary.pairFor(factory, path[0], path[1])); TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]); _swap(amounts, path, to); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IRootButtonswapRouter} from "./IRootButtonswapRouter.sol"; interface IBasicButtonswapRouter is IRootButtonswapRouter { /** * @notice Adds liquidity to a pair, creating it if it doesn't exist yet, and transfers the liquidity tokens to the recipient. * @dev If the pair is empty, amountAMin and amountBMin are ignored. * If the pair is nonempty, it deposits as much of tokenA and tokenB as possible while maintaining 3 conditions: * 1. The ratio of tokenA to tokenB in the pair remains approximately the same * 2. The amount of tokenA in the pair is at least amountAMin but less than or equal to amountADesired * 3. The amount of tokenB in the pair is at least amountBMin but less than or equal to amountBDesired * @param tokenA The address of the first token in the pair. * @param tokenB The address of the second token in the pair. * @param amountADesired The maximum amount of the first token to add to the pair. * @param amountBDesired The maximum amount of the second token to add to the pair. * @param amountAMin The minimum amount of the first token to add to the pair. * @param amountBMin The minimum amount of the second token to add to the pair. * @param movingAveragePrice0ThresholdBps The percentage threshold that movingAveragePrice0 can deviate from the current price. * @param to The address to send the liquidity tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountA The amount of tokenA actually added to the pair. * @return amountB The amount of tokenB actually added to the pair. * @return liquidity The amount of liquidity tokens minted. */ function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, uint16 movingAveragePrice0ThresholdBps, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); /** * @notice Adds liquidity to a pair, opposite to the existing reservoir, and transfers the liquidity tokens to the recipient * @dev Since there at most one reservoir at a given time, some conditions are checked: * 1. If there is no reservoir, it rejects * 2. The token with the reservoir has its amountDesired parameter ignored * 3. The token with the reservoir has its amount deducted from the reservoir (checked against corresponding amountMin parameter) * @param tokenA The address of the first token in the pair. * @param tokenB The address of the second token in the pair. * @param amountADesired The maximum amount of the first token to add to the pair. * @param amountBDesired The maximum amount of the second token to add to the pair. * @param amountAMin The minimum amount of the first token to add to the pair. * @param amountBMin The minimum amount of the second token to add to the pair. * @param to The address to send the liquidity tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountA The amount of tokenA actually added to the pair. * @return amountB The amount of tokenB actually added to the pair. * @return liquidity The amount of liquidity tokens minted. */ function addLiquidityWithReservoir( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); /** * @notice Removes liquidity from a pair, and transfers the tokens to the recipient. * @param tokenA The address of the first token in the pair. * @param tokenB The address of the second token in the pair. * @param liquidity The amount of liquidity tokens to burn. * @param amountAMin The minimum amount of the first token to withdraw from the pair. * @param amountBMin The minimum amount of the second token to withdraw from the pair. * @param to The address to send the tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountA The amount of tokenA actually withdrawn from the pair. * @return amountB The amount of tokenB actually withdrawn from the pair. */ function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); /** * @notice Removes liquidity from the reservoir of a pair and transfers the tokens to the recipient. * @param tokenA The address of the first token in the pair. * @param tokenB The address of the second token in the pair. * @param liquidity The amount of liquidity tokens to burn. * @param amountAMin The minimum amount of the first token to withdraw from the pair. * @param amountBMin The minimum amount of the second token to withdraw from the pair. * @param to The address to send the tokens to. * @param deadline The time after which this transaction can no longer be executed. * @return amountA The amount of tokenA actually withdrawn from the pair. * @return amountB The amount of tokenB actually withdrawn from the pair. */ function removeLiquidityFromReservoir( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); /** * @notice Similar to `removeLiquidity()` but utilizes the Permit signatures to reduce gas consumption. * Removes liquidity from a pair, and transfers the tokens to the recipient. * @param tokenA The address of the first token in the pair. * @param tokenB The address of the second token in the pair. * @param liquidity The amount of liquidity tokens to burn. * @param amountAMin The minimum amount of the first token to withdraw from the pair. * @param amountBMin The minimum amount of the second token to withdraw from the pair. * @param to The address to send the tokens to. * @param deadline The time after which this transaction can no longer be executed. * @param approveMax Whether the signature is for the max uint256 or liquidity value * @param v Part of the signature * @param r Part of the signature * @param s Part of the signature * @return amountA The amount of tokenA actually withdrawn from the pair. * @return amountB The amount of tokenB actually withdrawn from the pair. */ function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); /** * @notice Given an ordered array of tokens, performs consecutive swaps from a specific amount of the first token to the last token in the array. * @param amountIn The amount of the first token to swap. * @param amountOutMin The minimum amount of the last token to receive from the swap. * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @param to The address to send the output token to. * @param deadline The time after which this transaction can no longer be executed. */ function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); /** * @notice Given an ordered array of tokens, performs consecutive swaps from the first token to a specific amount of the last token in the array. * @param amountOut The amount of the last token to receive from the swap. * @param amountInMax The maximum amount of the first token to swap. * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token * @param to The address to send the output token to. * @param deadline The time after which this transaction can no longer be executed. */ function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; interface IETHButtonswapRouterErrors { /// @notice Only WETH contract can send ETH to contract error NonWETHSender(); /// @notice WETH transfer failed error FailedWETHTransfer(); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; interface IButtonswapERC20Errors { /** * @notice Permit deadline was exceeded */ error PermitExpired(); /** * @notice Permit signature invalid */ error PermitInvalidSignature(); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; interface IButtonswapERC20Events { /** * @notice Emitted when the allowance of a `spender` for an `owner` is set by a call to {IButtonswapERC20-approve}. * `value` is the new allowance. * @param owner The account that has granted approval * @param spender The account that has been given approval * @param value The amount the spender can transfer */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @notice Emitted when `value` tokens are moved from one account (`from`) to another (`to`). * @param from The account that sent the tokens * @param to The account that received the tokens * @param value The amount of tokens transferred */ event Transfer(address indexed from, address indexed to, uint256 value); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IButtonswapFactory} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol"; import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol"; import {TransferHelper} from "./libraries/TransferHelper.sol"; import {IRootButtonswapRouter} from "./interfaces/IButtonswapRouter/IRootButtonswapRouter.sol"; import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol"; import {Math} from "./libraries/Math.sol"; import {IERC20} from "./interfaces/IERC20.sol"; contract RootButtonswapRouter is IRootButtonswapRouter { uint256 private constant BPS = 10_000; /** * @inheritdoc IRootButtonswapRouter */ address public immutable override factory; constructor(address _factory) { factory = _factory; } // **** ADD LIQUIDITY **** // @dev Refer to [movingAveragePriceThreshold.md](https://github.com/buttonwood-protocol/buttonswap-periphery/blob/main/notes/movingAveragePriceThreshold.md) for more detail. function _addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, uint16 movingAveragePrice0ThresholdBps ) internal virtual returns (uint256 amountA, uint256 amountB) { // create the pair if it doesn't exist yet address pair = IButtonswapFactory(factory).getPair(tokenA, tokenB); if (pair == address(0)) { pair = IButtonswapFactory(factory).createPair(tokenA, tokenB); } (uint256 poolA, uint256 poolB, uint256 reservoirA, uint256 reservoirB) = ButtonswapLibrary.getLiquidityBalances(factory, tokenA, tokenB); if ((poolA + reservoirA) == 0 && (poolB + reservoirB) == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { uint256 amountBOptimal = ButtonswapLibrary.quote(amountADesired, poolA + reservoirA, poolB + reservoirB); if (amountBOptimal <= amountBDesired) { if (amountBOptimal < amountBMin) { revert InsufficientBAmount(); } (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint256 amountAOptimal = ButtonswapLibrary.quote(amountBDesired, poolB + reservoirB, poolA + reservoirA); assert(amountAOptimal <= amountADesired); if (amountAOptimal < amountAMin) { revert InsufficientAAmount(); } (amountA, amountB) = (amountAOptimal, amountBDesired); } } // Validate that the moving average price is within the threshold for pairs that exist if (poolA > 0 && poolB > 0) { uint256 movingAveragePrice0 = IButtonswapPair(pair).movingAveragePrice0(); if (tokenA < tokenB) { // tokenA is token0 uint256 cachedTerm = Math.mulDiv(movingAveragePrice0, poolA * BPS, 2 ** 112); if ( poolB * (BPS - movingAveragePrice0ThresholdBps) > cachedTerm || poolB * (BPS + movingAveragePrice0ThresholdBps) < cachedTerm ) { revert MovingAveragePriceOutOfBounds(); } } else { // tokenB is token0 uint256 cachedTerm = Math.mulDiv(movingAveragePrice0, poolB * BPS, 2 ** 112); if ( poolA * (BPS - movingAveragePrice0ThresholdBps) > cachedTerm || poolA * (BPS + movingAveragePrice0ThresholdBps) < cachedTerm ) { revert MovingAveragePriceOutOfBounds(); } } } } function _addLiquidityWithReservoir( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin ) internal virtual returns (uint256 amountA, uint256 amountB) { // If the pair doesn't exist yet, there isn't any reservoir if (IButtonswapFactory(factory).getPair(tokenA, tokenB) == address(0)) { revert NoReservoir(); } (uint256 poolA, uint256 poolB, uint256 reservoirA, uint256 reservoirB) = ButtonswapLibrary.getLiquidityBalances(factory, tokenA, tokenB); // the first liquidity addition should happen through _addLiquidity // can't initialize by matching with a reservoir if (poolA == 0 || poolB == 0) { revert NotInitialized(); } if (reservoirA == 0 && reservoirB == 0) { revert NoReservoir(); } if (reservoirA > 0) { // we take from reservoirA and the user-provided amountBDesired // But modify so that you don't do liquidityOut logic since you don't need it (, uint256 amountAOptimal) = ButtonswapLibrary.getMintSwappedAmounts(factory, tokenB, tokenA, amountBDesired); // User wants to drain to the res by amountAMin or more // Slippage-check if (amountAOptimal < amountAMin) { revert InsufficientAAmount(); } (amountA, amountB) = (0, amountBDesired); } else { // we take from reservoirB and the user-provided amountADesired (, uint256 amountBOptimal) = ButtonswapLibrary.getMintSwappedAmounts(factory, tokenA, tokenB, amountADesired); if (amountBOptimal < amountBMin) { revert InsufficientBAmount(); } (amountA, amountB) = (amountADesired, 0); } } // **** SWAP **** function _swap(uint256[] memory amounts, address[] memory path, address _to) internal virtual { for (uint256 i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); (address token0,) = ButtonswapLibrary.sortTokens(input, output); uint256 amountIn = amounts[i]; uint256 amountOut = amounts[i + 1]; (uint256 amount0In, uint256 amount1In) = input == token0 ? (amountIn, uint256(0)) : (uint256(0), amountIn); (uint256 amount0Out, uint256 amount1Out) = input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0)); address to = i < path.length - 2 ? address(this) : _to; IButtonswapPair pair = IButtonswapPair(ButtonswapLibrary.pairFor(factory, input, output)); TransferHelper.safeApprove(input, address(pair), amountIn); pair.swap(amount0In, amount1In, amount0Out, amount1Out, to); } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IButtonswapRouterErrors} from "./IButtonswapRouterErrors.sol"; interface IRootButtonswapRouter is IButtonswapRouterErrors { /** * @notice Returns the address of the Buttonswap Factory * @return factory The address of the Buttonswap Factory */ function factory() external view returns (address factory); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; // a library for performing various math operations library Math { /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; interface IButtonswapRouterErrors { /// @notice Deadline was exceeded error Expired(); /// @notice Insufficient amount of token A available error InsufficientAAmount(); /// @notice Insufficient amount of token B available error InsufficientBAmount(); /// @notice Neither token in the pool has the required reservoir error NoReservoir(); /// @notice Pools are not initialized error NotInitialized(); /// @notice Insufficient amount of token A in the reservoir error InsufficientAReservoir(); /// @notice Insufficient amount of token B in the reservoir error InsufficientBReservoir(); /// @notice Insufficient tokens returned from operation error InsufficientOutputAmount(); /// @notice Required input amount exceeds specified maximum error ExcessiveInputAmount(); /// @notice Invalid path provided error InvalidPath(); /// @notice movingAveragePrice0 is out of specified bounds error MovingAveragePriceOutOfBounds(); }
{ "remappings": [ "@openzeppelin-contracts/=null/", "buttonswap-core/=null/", "buttonswap-core_@openzeppelin-contracts/=lib/buttonswap-core/lib/openzeppelin-contracts/contracts/", "buttonswap-core_forge-std/=lib/buttonswap-core/lib/forge-std/src/", "buttonswap-core_mock-contracts/=lib/buttonswap-core/lib/mock-contracts/src/", "buttonswap-periphery_@openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "buttonswap-periphery_buttonswap-core/=lib/buttonswap-core/src/", "buttonswap-periphery_forge-std/=lib/forge-std/src/", "buttonswap-periphery_mock-contracts/=lib/mock-contracts/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/buttonswap-core/lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=null/", "mock-contracts/=null/", "mock-contracts_@openzeppelin-contracts/=lib/mock-contracts/lib/openzeppelin-contracts/contracts/", "mock-contracts_forge-std/=lib/mock-contracts/lib/forge-std/src/", "openzeppelin-contracts/=null/", "openzeppelin/=null/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "none", "appendCBOR": false }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ExcessiveInputAmount","type":"error"},{"inputs":[],"name":"Expired","type":"error"},{"inputs":[],"name":"FailedWETHTransfer","type":"error"},{"inputs":[],"name":"IdenticalAddresses","type":"error"},{"inputs":[],"name":"InsufficientAAmount","type":"error"},{"inputs":[],"name":"InsufficientAReservoir","type":"error"},{"inputs":[],"name":"InsufficientAmount","type":"error"},{"inputs":[],"name":"InsufficientBAmount","type":"error"},{"inputs":[],"name":"InsufficientBReservoir","type":"error"},{"inputs":[],"name":"InsufficientInputAmount","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"MovingAveragePriceOutOfBounds","type":"error"},{"inputs":[],"name":"NoReservoir","type":"error"},{"inputs":[],"name":"NonWETHSender","type":"error"},{"inputs":[],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"uint16","name":"movingAveragePrice0ThresholdBps","type":"uint16"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"uint16","name":"movingAveragePrice0ThresholdBps","type":"uint16"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETHWithReservoir","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityWithReservoir","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"poolIn","type":"uint256"},{"internalType":"uint256","name":"poolOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"poolIn","type":"uint256"},{"internalType":"uint256","name":"poolOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"getBurnSwappedAmounts","outputs":[{"internalType":"uint256","name":"tokenOutA","type":"uint256"},{"internalType":"uint256","name":"swappedReservoirAmountA","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"mintAmountA","type":"uint256"}],"name":"getMintSwappedAmounts","outputs":[{"internalType":"uint256","name":"tokenAToSwap","type":"uint256"},{"internalType":"uint256","name":"swappedReservoirAmountB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCreationRestricted","outputs":[{"internalType":"bool","name":"_isCreationRestricted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"poolA","type":"uint256"},{"internalType":"uint256","name":"poolB","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHFromReservoir","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityFromReservoir","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60c06040523480156200001157600080fd5b50604051620046f9380380620046f9833981016040819052620000349162000069565b6001600160a01b039182166080521660a052620000a1565b80516001600160a01b03811681146200006457600080fd5b919050565b600080604083850312156200007d57600080fd5b62000088836200004c565b915062000098602084016200004c565b90509250929050565b60805160a0516144bf6200023a600039600081816101b001528181610435015281816105e101528181610630015281816108c301528181610a7001528181610e6001528181610eb701528181610ef601528181610f6d01528181610f9b0152818161112f0152818161126f015281816112c50152818161138a0152818161140101528181611570015281816116900152818161171e01528181611c5201528181611cb201528181611d42015281816120690152818161217d015261220b015260008181610501015281816106ee0152818161094001528181610b5601528181610b8f01528181610c6601528181610cc201528181610d6901528181610e95015281816111ac015281816112a3015281816114c5015281816115ec015281816118770152818161192b0152818161198b01528181611aa801528181611cf001528181611d2001528181611e3301528181611ee1015281816120e501528181612b1f01528181612fa70152818161304b015281816130c6015281816134a00152818161353f015281816135cf015261362e01526144bf6000f3fe6080604052600436106101a05760003560e01c806385f8c259116100ec578063c2de19331161008a578063ded9382a11610064578063ded9382a14610543578063e6a4390514610563578063e78e168414610583578063fb3bdb41146105a357600080fd5b8063c2de1933146104cf578063c45a0155146104ef578063d06ca61f1461052357600080fd5b8063ad5c4648116100c6578063ad5c464814610423578063ad615dec1461046f578063baa2abde1461048f578063c2772c24146104af57600080fd5b806385f8c259146103c35780638803dbee146103e35780639adf412b1461040357600080fd5b806329c89c48116101595780634a25d94a116101335780634a25d94a146103585780635a0f2c2d146103785780637561d3751461038b5780637ff36ab5146103b057600080fd5b806329c89c48146102ea57806338ed17391461030a57806344794dbf1461032a57600080fd5b806302751cec146101f5578063054d50d41461022f5780630917e98b1461025d57806318cbafe51461027d5780631f00ca74146102aa5780632195995c146102ca57600080fd5b366101f057336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101ee576040516305bbb6bf60e31b815260040160405180910390fd5b005b600080fd5b34801561020157600080fd5b50610215610210366004613bb8565b6105b6565b604080519283526020830191909152015b60405180910390f35b34801561023b57600080fd5b5061024f61024a366004613c16565b6106ab565b604051908152602001610226565b34801561026957600080fd5b50610215610278366004613c42565b6106c2565b34801561028957600080fd5b5061029d610298366004613cf9565b610895565b6040516102269190613d6c565b3480156102b657600080fd5b5061029d6102c5366004613dc6565b610b4f565b3480156102d657600080fd5b506102156102e5366004613eb6565b610b85565b3480156102f657600080fd5b50610215610305366004613f60565b610c5e565b34801561031657600080fd5b5061029d610325366004613cf9565b610c99565b61033d610338366004613fb3565b610e33565b60408051938452602084019290925290820152606001610226565b34801561036457600080fd5b5061029d610373366004613cf9565b611101565b61033d610386366004613bb8565b611242565b34801561039757600080fd5b506103a06114c1565b6040519015158152602001610226565b61029d6103be36600461400c565b61154a565b3480156103cf57600080fd5b5061024f6103de366004613c16565b611841565b3480156103ef57600080fd5b5061029d6103fe366004613cf9565b61184e565b34801561040f57600080fd5b5061021561041e366004613f60565b611923565b34801561042f57600080fd5b506104577f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610226565b34801561047b57600080fd5b5061024f61048a366004613c16565b611952565b34801561049b57600080fd5b506102156104aa366004613c42565b61195f565b3480156104bb57600080fd5b5061033d6104ca366004614073565b611a66565b3480156104db57600080fd5b506102156104ea366004613bb8565b611c27565b3480156104fb57600080fd5b506104577f000000000000000000000000000000000000000000000000000000000000000081565b34801561052f57600080fd5b5061029d61053e366004613dc6565b611ce9565b34801561054f57600080fd5b5061021561055e366004614100565b611d16565b34801561056f57600080fd5b5061045761057e366004614196565b611e09565b34801561058f57600080fd5b5061033d61059e3660046141cf565b611ea0565b61029d6105b136600461400c565b612043565b60008082804211156105db57604051630407b05b60e31b815260040160405180910390fd5b61060a897f00000000000000000000000000000000000000000000000000000000000000008a8a8a308a61195f565b909350915061061a898685612370565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d906024015b600060405180830381600087803b15801561067d57600080fd5b505af1158015610691573d6000803e3d6000fd5b5050505061069f8583612490565b50965096945050505050565b60006106b884848461255e565b90505b9392505050565b60008082804211156106e757604051630407b05b60e31b815260040160405180910390fd5b60006107147f00000000000000000000000000000000000000000000000000000000000000008c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e919061424b565b5060405163bd3a5c2d60e01b8152600481018a90526001600160a01b038781166024830152600091829184169063bd3a5c2d906044015b60408051808303816000875af11580156107e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108079190614268565b9150915060006108178e8e6126d1565b509050806001600160a01b03168e6001600160a01b03161461083a57818361083d565b82825b90975095508a87101561086357604051638dc525d160e01b815260040160405180910390fd5b898610156108845760405163ef71d09160e01b815260040160405180910390fd5b505050505097509795505050505050565b606081804211156108b957604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001686866108f06001826142a2565b8181106108ff576108ff6142b5565b905060200201602081019061091491906142cb565b6001600160a01b03161461093b576040516320db826760e01b815260040160405180910390fd5b6109997f00000000000000000000000000000000000000000000000000000000000000008988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b91508682600184516109ab91906142a2565b815181106109bb576109bb6142b5565b602002602001015110156109e2576040516342301c2360e01b815260040160405180910390fd5b610a2f868660008181106109f8576109f86142b5565b9050602002016020810190610a0d91906142cb565b333085600081518110610a2257610a226142b5565b60200260200101516128ba565b610a6e828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503092506129ea915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d8360018551610aac91906142a2565b81518110610abc57610abc6142b5565b60200260200101516040518263ffffffff1660e01b8152600401610ae291815260200190565b600060405180830381600087803b158015610afc57600080fd5b505af1158015610b10573d6000803e3d6000fd5b50505050610b44848360018551610b2791906142a2565b81518110610b3757610b376142b5565b6020026020010151612490565b509695505050505050565b6060610b7c7f00000000000000000000000000000000000000000000000000000000000000008484612bed565b90505b92915050565b6000806000610bb57f00000000000000000000000000000000000000000000000000000000000000008f8f6125f8565b9050600087610bc4578c610bc8565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610c03903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015610c1d57600080fd5b505af1158015610c31573d6000803e3d6000fd5b50505050610c448f8f8f8f8f8f8f61195f565b809450819550505050509b509b9950505050505050505050565b600080610c8d7f0000000000000000000000000000000000000000000000000000000000000000868686612d41565b91509150935093915050565b60608180421115610cbd57604051630407b05b60e31b815260040160405180910390fd5b610d1b7f00000000000000000000000000000000000000000000000000000000000000008988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b9150868260018451610d2d91906142a2565b81518110610d3d57610d3d6142b5565b60200260200101511015610d64576040516342301c2360e01b815260040160405180910390fd5b610ddd7f000000000000000000000000000000000000000000000000000000000000000087876000818110610d9b57610d9b6142b5565b9050602002016020810190610db091906142cb565b88886001818110610dc357610dc36142b5565b9050602002016020810190610dd891906142cb565b6125f8565b50610df4868660008181106109f8576109f86142b5565b610b44828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b60008060008380421115610e5a57604051630407b05b60e31b815260040160405180910390fd5b610e898b7f00000000000000000000000000000000000000000000000000000000000000008c348d8d8d612f79565b90945092506000610edb7f00000000000000000000000000000000000000000000000000000000000000008d7f00000000000000000000000000000000000000000000000000000000000000006125f8565b9050610ee98c3330886128ba565b610ef48c828761335e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015610f4f57600080fd5b505af1158015610f63573d6000803e3d6000fd5b5050505050610f937f0000000000000000000000000000000000000000000000000000000000000000828661335e565b6000610fbf8d7f00000000000000000000000000000000000000000000000000000000000000006126d1565b509050806001600160a01b03168d6001600160a01b03161461105a5760405163e7d3fe6b60e01b815260048101869052602481018790526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af1158015611031573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110559190614329565b6110d4565b60405163e7d3fe6b60e01b815260048101879052602481018690526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af11580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d49190614329565b9350843411156110f1576110f1336110ec87346142a2565b612490565b5050509750975097945050505050565b6060818042111561112557604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016868661115c6001826142a2565b81811061116b5761116b6142b5565b905060200201602081019061118091906142cb565b6001600160a01b0316146111a7576040516320db826760e01b815260040160405180910390fd5b6112057f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b9150868260008151811061121b5761121b6142b5565b602002602001015111156109e25760405163e1b0da4f60e01b815260040160405180910390fd5b6000806000838042111561126957604051630407b05b60e31b815260040160405180910390fd5b6112978a7f00000000000000000000000000000000000000000000000000000000000000008b348c8c613472565b909450925060006112e97f00000000000000000000000000000000000000000000000000000000000000008c7f00000000000000000000000000000000000000000000000000000000000000006125f8565b90508415611382576112fd8b3330886128ba565b6113088b828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611357573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137b9190614329565b925061149d565b831561149d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b1580156113e357600080fd5b505af11580156113f7573d6000803e3d6000fd5b50505050506114277f0000000000000000000000000000000000000000000000000000000000000000828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611476573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149a9190614329565b92505b833411156114b3576114b3336110ec86346142a2565b505096509650969350505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637561d3756040518163ffffffff1660e01b8152600401602060405180830381865afa158015611521573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611545919061424b565b905090565b6060818042111561156e57604051630407b05b60e31b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106115ab576115ab6142b5565b90506020020160208101906115c091906142cb565b6001600160a01b0316146115e7576040516320db826760e01b815260040160405180910390fd5b6116457f00000000000000000000000000000000000000000000000000000000000000003488888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b915086826001845161165791906142a2565b81518110611667576116676142b5565b6020026020010151101561168e576040516342301c2360e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106116d0576116d06142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb308460008151811061175f5761175f6142b5565b60200260200101516040518363ffffffff1660e01b81526004016117989291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156117b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117db919061424b565b6117f857604051631f0236bb60e01b815260040160405180910390fd5b611837828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b5095945050505050565b60006106b8848484613691565b6060818042111561187257604051630407b05b60e31b815260040160405180910390fd5b6118d07f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b915086826000815181106118e6576118e66142b5565b6020026020010151111561190d5760405163e1b0da4f60e01b815260040160405180910390fd5b610df4868660008181106109f8576109f86142b5565b600080610c8d7f0000000000000000000000000000000000000000000000000000000000000000868686613730565b60006106b884848461398f565b600080828042111561198457604051630407b05b60e31b815260040160405180910390fd5b60006119b17f00000000000000000000000000000000000000000000000000000000000000008c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af1158015611a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2b919061424b565b50604051633f34d4cf60e21b8152600481018a90526001600160a01b038781166024830152600091829184169063fcd3533c906044016107c5565b60008060008380421115611a8d57604051630407b05b60e31b815260040160405180910390fd5b611a9c8d8d8d8d8d8d8d612f79565b90945092506000611ace7f00000000000000000000000000000000000000000000000000000000000000008f8f6125f8565b9050611adc8e3330886128ba565b611ae78e828761335e565b611af38d3330876128ba565b611afe8d828661335e565b8c6001600160a01b03168e6001600160a01b03161015611b995760405163e7d3fe6b60e01b815260048101869052602481018590526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b929190614329565b9250611c16565b60405163e7d3fe6b60e01b815260048101859052602481018690526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c139190614329565b92505b505099509950999650505050505050565b6000808280421115611c4c57604051630407b05b60e31b815260040160405180910390fd5b611c7b897f00000000000000000000000000000000000000000000000000000000000000008a8a8a308a6106c2565b90935091508215611c9657611c91898685612370565b61069f565b811561069f57604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401610663565b6060610b7c7f0000000000000000000000000000000000000000000000000000000000000000848461275c565b6000806000611d667f00000000000000000000000000000000000000000000000000000000000000008e7f00000000000000000000000000000000000000000000000000000000000000006125f8565b9050600087611d75578c611d79565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90611db4903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015611dce57600080fd5b505af1158015611de2573d6000803e3d6000fd5b50505050611df48e8e8e8e8e8e6105b6565b909f909e509c50505050505050505050505050565b60405163e6a4390560e01b81526001600160a01b03838116600483015282811660248301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063e6a4390590604401602060405180830381865afa158015611e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7c9190614342565b60008060008380421115611ec757604051630407b05b60e31b815260040160405180910390fd5b611ed58c8c8c8c8c8c613472565b90945092506000611f077f00000000000000000000000000000000000000000000000000000000000000008e8e6125f8565b90508415611fa057611f1b8d3330886128ba565b611f268d828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f999190614329565b9250612033565b831561203357611fb28c3330876128ba565b611fbd8c828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af115801561200c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120309190614329565b92505b5050985098509895505050505050565b6060818042111561206757604051630407b05b60e31b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106120a4576120a46142b5565b90506020020160208101906120b991906142cb565b6001600160a01b0316146120e0576040516320db826760e01b815260040160405180910390fd5b61213e7f000000000000000000000000000000000000000000000000000000000000000088888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b91503482600081518110612154576121546142b5565b6020026020010151111561217b5760405163e1b0da4f60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106121bd576121bd6142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156121f057600080fd5b505af1158015612204573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb308460008151811061224c5761224c6142b5565b60200260200101516040518363ffffffff1660e01b81526004016122859291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156122a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c8919061424b565b6122e557604051631f0236bb60e01b815260040160405180910390fd5b612324828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b81600081518110612337576123376142b5565b602002602001015134111561183757611837338360008151811061235d5761235d6142b5565b6020026020010151346110ec91906142a2565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916123cc919061435f565b6000604051808303816000865af19150503d8060008114612409576040519150601f19603f3d011682016040523d82523d6000602084013e61240e565b606091505b5091509150818015612438575080511580612438575080806020019051810190612438919061424b565b6124895760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064015b60405180910390fd5b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040516124ba919061435f565b60006040518083038185875af1925050503d80600081146124f7576040519150601f19603f3d011682016040523d82523d6000602084013e6124fc565b606091505b50509050806125595760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b6064820152608401612480565b505050565b6000836000036125815760405163098fb56160e01b815260040160405180910390fd5b82158061258c575081155b156125aa5760405163bb55fd2760e01b815260040160405180910390fd5b60006125b8856103e561438e565b905060006125c6848361438e565b90506000826125d7876103e861438e565b6125e191906143a5565b90506125ed81836143ce565b979650505050505050565b600080600061260785856126d1565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015291935091508690604801604051602081830303815290604052805190602001206040516020016126af9291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527f912fa011211d18178fef8f22392edc90ca8f101645ab8347e1359b5ce2f890df603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b600080826001600160a01b0316846001600160a01b03160361270657604051630bd969eb60e41b815260040160405180910390fd5b826001600160a01b0316846001600160a01b031610612726578284612729565b83835b90925090506001600160a01b0382166127555760405163d92e233d60e01b815260040160405180910390fd5b9250929050565b6060600282511015612781576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff81111561279b5761279b613db0565b6040519080825280602002602001820160405280156127c4578160200160208202803683370190505b50905082816000815181106127db576127db6142b5565b60200260200101818152505060005b600183516127f891906142a2565b8110156128b25760008061284b87868581518110612818576128186142b5565b60200260200101518786600161282e91906143a5565b8151811061283e5761283e6142b5565b60200260200101516139f0565b505091509150612875848481518110612866576128666142b5565b6020026020010151838361255e565b846128818560016143a5565b81518110612891576128916142b5565b602002602001018181525050505080806128aa906143f0565b9150506127ea565b509392505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161291e919061435f565b6000604051808303816000865af19150503d806000811461295b576040519150601f19603f3d011682016040523d82523d6000602084013e612960565b606091505b509150915081801561298a57508051158061298a57508080602001905181019061298a919061424b565b6129e25760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401612480565b505050505050565b60005b600183516129fb91906142a2565b811015612be757600080848381518110612a1757612a176142b5565b602002602001015185846001612a2d91906143a5565b81518110612a3d57612a3d6142b5565b6020026020010151915091506000612a5583836126d1565b5090506000878581518110612a6c57612a6c6142b5565b60200260200101519050600088866001612a8691906143a5565b81518110612a9657612a966142b5565b60200260200101519050600080846001600160a01b0316876001600160a01b031614612ac457600084612ac8565b8360005b91509150600080866001600160a01b0316896001600160a01b031614612af057846000612af4565b6000855b91509150600060028d51612b0891906142a2565b8b10612b14578b612b16565b305b90506000612b457f00000000000000000000000000000000000000000000000000000000000000008c8c6125f8565b9050612b528b828a61335e565b60405163562e19df60e01b8152600481018790526024810186905260448101859052606481018490526001600160a01b03838116608483015282169063562e19df9060a401600060405180830381600087803b158015612bb157600080fd5b505af1158015612bc5573d6000803e3d6000fd5b5050505050505050505050505050508080612bdf906143f0565b9150506129ed565b50505050565b6060600282511015612c12576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff811115612c2c57612c2c613db0565b604051908082528060200260200182016040528015612c55578160200160208202803683370190505b509050828160018351612c6891906142a2565b81518110612c7857612c786142b5565b602002602001018181525050600060018351612c9491906142a2565b90505b80156128b257600080612cda8786612cb06001876142a2565b81518110612cc057612cc06142b5565b602002602001015187868151811061283e5761283e6142b5565b505091509150612d04848481518110612cf557612cf56142b5565b60200260200101518383613691565b84612d106001866142a2565b81518110612d2057612d206142b5565b60200260200101818152505050508080612d3990614409565b915050612c97565b6000806000612d518787876125f8565b6040516370a0823160e01b81526001600160a01b0380831660048301529192506000918816906370a0823190602401602060405180830381865afa158015612d9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc19190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918816906370a0823190602401602060405180830381865afa158015612e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e319190614329565b90506000836001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e979190614329565b9050876001600160a01b0316896001600160a01b03161015612f0c5781612ecc82612ec28a876143a5565b600160701b613ae1565b612ed691906143a5565b612ee0838961438e565b612eea91906143ce565b9550600160701b612efb828861438e565b612f0591906143ce565b9450612f6c565b8181612f1889866143a5565b612f2690600160701b61438e565b612f3091906143ce565b612f3a91906143a5565b612f44838961438e565b612f4e91906143ce565b955080612f5f87600160701b61438e565b612f6991906143ce565b94505b5050505094509492505050565b60405163e6a4390560e01b81526001600160a01b0388811660048301528781166024830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063e6a4390590604401602060405180830381865afa158015612ff0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130149190614342565b90506001600160a01b0381166130bb576040516364e329cb60e11b81526001600160a01b038b811660048301528a811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063c9c65396906044016020604051808303816000875af1158015613094573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130b89190614342565b90505b6000806000806130ec7f00000000000000000000000000000000000000000000000000000000000000008f8f6139f0565b9350935093509350818461310091906143a5565b158015613114575061311281846143a5565b155b15613124578b96508a95506131cd565b60006131438d61313485886143a5565b61313e85886143a5565b61398f565b90508b8111613178578981101561316d5760405163ef71d09160e01b815260040160405180910390fd5b8c97509550856131cb565b60006131928d61318885886143a5565b61313e878a6143a5565b90508d8111156131a4576131a4614420565b8b8110156131c557604051638dc525d160e01b815260040160405180910390fd5b97508b96505b505b6000841180156131dd5750600083115b15610884576000856001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613222573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132469190614329565b90508d6001600160a01b03168f6001600160a01b031610156132d957600061327482612ec26127108961438e565b90508061328761ffff8c166127106142a2565b613291908761438e565b11806132b55750806132a961ffff8c166127106143a5565b6132b3908761438e565b105b156132d357604051630d683ed760e21b815260040160405180910390fd5b5061334c565b60006132eb82612ec26127108861438e565b9050806132fe61ffff8c166127106142a2565b613308908861438e565b118061332c57508061332061ffff8c166127106143a5565b61332a908861438e565b105b1561334a57604051630d683ed760e21b815260040160405180910390fd5b505b50505050505097509795505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b17905291516000928392908716916133ba919061435f565b6000604051808303816000865af19150503d80600081146133f7576040519150601f19603f3d011682016040523d82523d6000602084013e6133fc565b606091505b5091509150818015613426575080511580613426575080806020019051810190613426919061424b565b6124895760405162461bcd60e51b815260206004820152601e60248201527f5472616e7366657248656c7065723a20415050524f56455f4641494c454400006044820152606401612480565b60405163e6a4390560e01b81526001600160a01b0387811660048301528681166024830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063e6a4390590604401602060405180830381865afa1580156134e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350d9190614342565b6001600160a01b031603613534576040516391bc00d960e01b815260040160405180910390fd5b6000806000806135657f00000000000000000000000000000000000000000000000000000000000000008d8d6139f0565b9350935093509350836000148061357a575082155b15613598576040516321c4e35760e21b815260040160405180910390fd5b811580156135a4575080155b156135c2576040516391bc00d960e01b815260040160405180910390fd5b81156136275760006135f67f00000000000000000000000000000000000000000000000000000000000000008d8f8d612d41565b9150508881101561361a57604051638dc525d160e01b815260040160405180910390fd5b5060009550889450613682565b60006136557f00000000000000000000000000000000000000000000000000000000000000008e8e8e612d41565b915050878110156136795760405163ef71d09160e01b815260040160405180910390fd5b50899550600094505b50505050965096945050505050565b6000836000036136b4576040516342301c2360e01b815260040160405180910390fd5b8215806136bf575081155b156136dd5760405163bb55fd2760e01b815260040160405180910390fd5b60006136e9858561438e565b6136f5906103e861438e565b9050600061370386856142a2565b61370f906103e561438e565b905061371b81836143ce565b6137269060016143a5565b9695505050505050565b60008060006137408787876125f8565b90506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a69190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918916906370a0823190602401602060405180830381865afa1580156137f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138169190614329565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918916906370a0823190602401602060405180830381865afa158015613862573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138869190614329565b90506000846001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ec9190614329565b90506000846138fb8a8561438e565b61390591906143ce565b9050846139128a8661438e565b61391c91906143ce565b9750896001600160a01b03168b6001600160a01b03161015613958578161394782600160701b61438e565b61395191906143ce565b9650613974565b600160701b613967838361438e565b61397191906143ce565b96505b61397e87896143a5565b975050505050505094509492505050565b6000836000036139b257604051632ca2f52b60e11b815260040160405180910390fd5b8215806139bd575081155b156139db5760405163bb55fd2760e01b815260040160405180910390fd5b826139e6838661438e565b6106b891906143ce565b6000806000806000613a0287876126d1565b509050600080600080613a168c8c8c6125f8565b6001600160a01b031663976bf4166040518163ffffffff1660e01b815260040160a060405180830381865afa158015613a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a77919061444d565b506001600160701b031693506001600160701b031693506001600160701b031693506001600160701b03169350846001600160a01b03168b6001600160a01b031614613ac65782848284613acb565b838383835b929f919e509c50909a5098505050505050505050565b6000808060001985870985870292508281108382030391505080600003613b1b57838281613b1157613b116143b8565b04925050506106bb565b808411613b2757600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6001600160a01b0381168114613ba557600080fd5b50565b8035613bb381613b90565b919050565b60008060008060008060c08789031215613bd157600080fd5b8635613bdc81613b90565b95506020870135945060408701359350606087013592506080870135613c0181613b90565b8092505060a087013590509295509295509295565b600080600060608486031215613c2b57600080fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a031215613c5d57600080fd5b8735613c6881613b90565b96506020880135613c7881613b90565b955060408801359450606088013593506080880135925060a0880135613c9d81613b90565b8092505060c0880135905092959891949750929550565b60008083601f840112613cc657600080fd5b50813567ffffffffffffffff811115613cde57600080fd5b6020830191508360208260051b850101111561275557600080fd5b60008060008060008060a08789031215613d1257600080fd5b8635955060208701359450604087013567ffffffffffffffff811115613d3757600080fd5b613d4389828a01613cb4565b9095509350506060870135613d5781613b90565b80925050608087013590509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015613da457835183529284019291840191600101613d88565b50909695505050505050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613dd957600080fd5b8235915060208084013567ffffffffffffffff80821115613df957600080fd5b818601915086601f830112613e0d57600080fd5b813581811115613e1f57613e1f613db0565b8060051b604051601f19603f83011681018181108582111715613e4457613e44613db0565b604052918252848201925083810185019189831115613e6257600080fd5b938501935b82851015613e8757613e7885613ba8565b84529385019392850192613e67565b8096505050505050509250929050565b8015158114613ba557600080fd5b803560ff81168114613bb357600080fd5b60008060008060008060008060008060006101608c8e031215613ed857600080fd5b8b35613ee381613b90565b9a5060208c0135613ef381613b90565b995060408c0135985060608c0135975060808c0135965060a08c0135613f1881613b90565b955060c08c0135945060e08c0135613f2f81613e97565b9350613f3e6101008d01613ea5565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215613f7557600080fd5b8335613f8081613b90565b92506020840135613f9081613b90565b929592945050506040919091013590565b803561ffff81168114613bb357600080fd5b600080600080600080600060e0888a031215613fce57600080fd5b8735613fd981613b90565b9650602088013595506040880135945060608801359350613ffc60808901613fa1565b925060a0880135613c9d81613b90565b60008060008060006080868803121561402457600080fd5b85359450602086013567ffffffffffffffff81111561404257600080fd5b61404e88828901613cb4565b909550935050604086013561406281613b90565b949793965091946060013592915050565b60008060008060008060008060006101208a8c03121561409257600080fd5b893561409d81613b90565b985060208a01356140ad81613b90565b975060408a0135965060608a0135955060808a0135945060a08a013593506140d760c08b01613fa1565b925060e08a01356140e781613b90565b809250506101008a013590509295985092959850929598565b6000806000806000806000806000806101408b8d03121561412057600080fd5b8a3561412b81613b90565b995060208b0135985060408b0135975060608b0135965060808b013561415081613b90565b955060a08b0135945060c08b013561416781613e97565b935061417560e08c01613ea5565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600080604083850312156141a957600080fd5b82356141b481613b90565b915060208301356141c481613b90565b809150509250929050565b600080600080600080600080610100898b0312156141ec57600080fd5b88356141f781613b90565b9750602089013561420781613b90565b965060408901359550606089013594506080890135935060a0890135925060c089013561423381613b90565b8092505060e089013590509295985092959890939650565b60006020828403121561425d57600080fd5b81516106bb81613e97565b6000806040838503121561427b57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b7f57610b7f61428c565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156142dd57600080fd5b81356106bb81613b90565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b60006020828403121561433b57600080fd5b5051919050565b60006020828403121561435457600080fd5b81516106bb81613b90565b6000825160005b818110156143805760208186018101518583015201614366565b506000920191825250919050565b8082028115828204841417610b7f57610b7f61428c565b80820180821115610b7f57610b7f61428c565b634e487b7160e01b600052601260045260246000fd5b6000826143eb57634e487b7160e01b600052601260045260246000fd5b500490565b6000600182016144025761440261428c565b5060010190565b6000816144185761441861428c565b506000190190565b634e487b7160e01b600052600160045260246000fd5b80516001600160701b0381168114613bb357600080fd5b600080600080600060a0868803121561446557600080fd5b61446e86614436565b945061447c60208701614436565b935061448a60408701614436565b925061449860608701614436565b9150608086015163ffffffff811681146144b157600080fd5b80915050929550929590935056000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Deployed Bytecode
0x6080604052600436106101a05760003560e01c806385f8c259116100ec578063c2de19331161008a578063ded9382a11610064578063ded9382a14610543578063e6a4390514610563578063e78e168414610583578063fb3bdb41146105a357600080fd5b8063c2de1933146104cf578063c45a0155146104ef578063d06ca61f1461052357600080fd5b8063ad5c4648116100c6578063ad5c464814610423578063ad615dec1461046f578063baa2abde1461048f578063c2772c24146104af57600080fd5b806385f8c259146103c35780638803dbee146103e35780639adf412b1461040357600080fd5b806329c89c48116101595780634a25d94a116101335780634a25d94a146103585780635a0f2c2d146103785780637561d3751461038b5780637ff36ab5146103b057600080fd5b806329c89c48146102ea57806338ed17391461030a57806344794dbf1461032a57600080fd5b806302751cec146101f5578063054d50d41461022f5780630917e98b1461025d57806318cbafe51461027d5780631f00ca74146102aa5780632195995c146102ca57600080fd5b366101f057336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216146101ee576040516305bbb6bf60e31b815260040160405180910390fd5b005b600080fd5b34801561020157600080fd5b50610215610210366004613bb8565b6105b6565b604080519283526020830191909152015b60405180910390f35b34801561023b57600080fd5b5061024f61024a366004613c16565b6106ab565b604051908152602001610226565b34801561026957600080fd5b50610215610278366004613c42565b6106c2565b34801561028957600080fd5b5061029d610298366004613cf9565b610895565b6040516102269190613d6c565b3480156102b657600080fd5b5061029d6102c5366004613dc6565b610b4f565b3480156102d657600080fd5b506102156102e5366004613eb6565b610b85565b3480156102f657600080fd5b50610215610305366004613f60565b610c5e565b34801561031657600080fd5b5061029d610325366004613cf9565b610c99565b61033d610338366004613fb3565b610e33565b60408051938452602084019290925290820152606001610226565b34801561036457600080fd5b5061029d610373366004613cf9565b611101565b61033d610386366004613bb8565b611242565b34801561039757600080fd5b506103a06114c1565b6040519015158152602001610226565b61029d6103be36600461400c565b61154a565b3480156103cf57600080fd5b5061024f6103de366004613c16565b611841565b3480156103ef57600080fd5b5061029d6103fe366004613cf9565b61184e565b34801561040f57600080fd5b5061021561041e366004613f60565b611923565b34801561042f57600080fd5b506104577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b039091168152602001610226565b34801561047b57600080fd5b5061024f61048a366004613c16565b611952565b34801561049b57600080fd5b506102156104aa366004613c42565b61195f565b3480156104bb57600080fd5b5061033d6104ca366004614073565b611a66565b3480156104db57600080fd5b506102156104ea366004613bb8565b611c27565b3480156104fb57600080fd5b506104577f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5081565b34801561052f57600080fd5b5061029d61053e366004613dc6565b611ce9565b34801561054f57600080fd5b5061021561055e366004614100565b611d16565b34801561056f57600080fd5b5061045761057e366004614196565b611e09565b34801561058f57600080fd5b5061033d61059e3660046141cf565b611ea0565b61029d6105b136600461400c565b612043565b60008082804211156105db57604051630407b05b60e31b815260040160405180910390fd5b61060a897f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28a8a8a308a61195f565b909350915061061a898685612370565b604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d906024015b600060405180830381600087803b15801561067d57600080fd5b505af1158015610691573d6000803e3d6000fd5b5050505061069f8583612490565b50965096945050505050565b60006106b884848461255e565b90505b9392505050565b60008082804211156106e757604051630407b05b60e31b815260040160405180910390fd5b60006107147f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e919061424b565b5060405163bd3a5c2d60e01b8152600481018a90526001600160a01b038781166024830152600091829184169063bd3a5c2d906044015b60408051808303816000875af11580156107e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108079190614268565b9150915060006108178e8e6126d1565b509050806001600160a01b03168e6001600160a01b03161461083a57818361083d565b82825b90975095508a87101561086357604051638dc525d160e01b815260040160405180910390fd5b898610156108845760405163ef71d09160e01b815260040160405180910390fd5b505050505097509795505050505050565b606081804211156108b957604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21686866108f06001826142a2565b8181106108ff576108ff6142b5565b905060200201602081019061091491906142cb565b6001600160a01b03161461093b576040516320db826760e01b815260040160405180910390fd5b6109997f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b91508682600184516109ab91906142a2565b815181106109bb576109bb6142b5565b602002602001015110156109e2576040516342301c2360e01b815260040160405180910390fd5b610a2f868660008181106109f8576109f86142b5565b9050602002016020810190610a0d91906142cb565b333085600081518110610a2257610a226142b5565b60200260200101516128ba565b610a6e828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503092506129ea915050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316632e1a7d4d8360018551610aac91906142a2565b81518110610abc57610abc6142b5565b60200260200101516040518263ffffffff1660e01b8152600401610ae291815260200190565b600060405180830381600087803b158015610afc57600080fd5b505af1158015610b10573d6000803e3d6000fd5b50505050610b44848360018551610b2791906142a2565b81518110610b3757610b376142b5565b6020026020010151612490565b509695505050505050565b6060610b7c7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508484612bed565b90505b92915050565b6000806000610bb57f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508f8f6125f8565b9050600087610bc4578c610bc8565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610c03903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015610c1d57600080fd5b505af1158015610c31573d6000803e3d6000fd5b50505050610c448f8f8f8f8f8f8f61195f565b809450819550505050509b509b9950505050505050505050565b600080610c8d7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50868686612d41565b91509150935093915050565b60608180421115610cbd57604051630407b05b60e31b815260040160405180910390fd5b610d1b7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b9150868260018451610d2d91906142a2565b81518110610d3d57610d3d6142b5565b60200260200101511015610d64576040516342301c2360e01b815260040160405180910390fd5b610ddd7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5087876000818110610d9b57610d9b6142b5565b9050602002016020810190610db091906142cb565b88886001818110610dc357610dc36142b5565b9050602002016020810190610dd891906142cb565b6125f8565b50610df4868660008181106109f8576109f86142b5565b610b44828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b60008060008380421115610e5a57604051630407b05b60e31b815260040160405180910390fd5b610e898b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28c348d8d8d612f79565b90945092506000610edb7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26125f8565b9050610ee98c3330886128ba565b610ef48c828761335e565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015610f4f57600080fd5b505af1158015610f63573d6000803e3d6000fd5b5050505050610f937f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2828661335e565b6000610fbf8d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26126d1565b509050806001600160a01b03168d6001600160a01b03161461105a5760405163e7d3fe6b60e01b815260048101869052602481018790526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af1158015611031573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110559190614329565b6110d4565b60405163e7d3fe6b60e01b815260048101879052602481018690526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af11580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d49190614329565b9350843411156110f1576110f1336110ec87346142a2565b612490565b5050509750975097945050505050565b6060818042111561112557604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216868661115c6001826142a2565b81811061116b5761116b6142b5565b905060200201602081019061118091906142cb565b6001600160a01b0316146111a7576040516320db826760e01b815260040160405180910390fd5b6112057f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b9150868260008151811061121b5761121b6142b5565b602002602001015111156109e25760405163e1b0da4f60e01b815260040160405180910390fd5b6000806000838042111561126957604051630407b05b60e31b815260040160405180910390fd5b6112978a7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28b348c8c613472565b909450925060006112e97f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26125f8565b90508415611382576112fd8b3330886128ba565b6113088b828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611357573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137b9190614329565b925061149d565b831561149d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b1580156113e357600080fd5b505af11580156113f7573d6000803e3d6000fd5b50505050506114277f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611476573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149a9190614329565b92505b833411156114b3576114b3336110ec86346142a2565b505096509650969350505050565b60007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b0316637561d3756040518163ffffffff1660e01b8152600401602060405180830381865afa158015611521573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611545919061424b565b905090565b6060818042111561156e57604051630407b05b60e31b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316868660008181106115ab576115ab6142b5565b90506020020160208101906115c091906142cb565b6001600160a01b0316146115e7576040516320db826760e01b815260040160405180910390fd5b6116457f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce503488888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b915086826001845161165791906142a2565b81518110611667576116676142b5565b6020026020010151101561168e576040516342301c2360e01b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836000815181106116d0576116d06142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663a9059cbb308460008151811061175f5761175f6142b5565b60200260200101516040518363ffffffff1660e01b81526004016117989291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156117b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117db919061424b565b6117f857604051631f0236bb60e01b815260040160405180910390fd5b611837828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b5095945050505050565b60006106b8848484613691565b6060818042111561187257604051630407b05b60e31b815260040160405180910390fd5b6118d07f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b915086826000815181106118e6576118e66142b5565b6020026020010151111561190d5760405163e1b0da4f60e01b815260040160405180910390fd5b610df4868660008181106109f8576109f86142b5565b600080610c8d7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50868686613730565b60006106b884848461398f565b600080828042111561198457604051630407b05b60e31b815260040160405180910390fd5b60006119b17f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af1158015611a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2b919061424b565b50604051633f34d4cf60e21b8152600481018a90526001600160a01b038781166024830152600091829184169063fcd3533c906044016107c5565b60008060008380421115611a8d57604051630407b05b60e31b815260040160405180910390fd5b611a9c8d8d8d8d8d8d8d612f79565b90945092506000611ace7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508f8f6125f8565b9050611adc8e3330886128ba565b611ae78e828761335e565b611af38d3330876128ba565b611afe8d828661335e565b8c6001600160a01b03168e6001600160a01b03161015611b995760405163e7d3fe6b60e01b815260048101869052602481018590526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b929190614329565b9250611c16565b60405163e7d3fe6b60e01b815260048101859052602481018690526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c139190614329565b92505b505099509950999650505050505050565b6000808280421115611c4c57604051630407b05b60e31b815260040160405180910390fd5b611c7b897f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28a8a8a308a6106c2565b90935091508215611c9657611c91898685612370565b61069f565b811561069f57604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401610663565b6060610b7c7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50848461275c565b6000806000611d667f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508e7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26125f8565b9050600087611d75578c611d79565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90611db4903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015611dce57600080fd5b505af1158015611de2573d6000803e3d6000fd5b50505050611df48e8e8e8e8e8e6105b6565b909f909e509c50505050505050505050505050565b60405163e6a4390560e01b81526001600160a01b03838116600483015282811660248301526000917f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce509091169063e6a4390590604401602060405180830381865afa158015611e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7c9190614342565b60008060008380421115611ec757604051630407b05b60e31b815260040160405180910390fd5b611ed58c8c8c8c8c8c613472565b90945092506000611f077f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508e8e6125f8565b90508415611fa057611f1b8d3330886128ba565b611f268d828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f999190614329565b9250612033565b831561203357611fb28c3330876128ba565b611fbd8c828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af115801561200c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120309190614329565b92505b5050985098509895505050505050565b6060818042111561206757604051630407b05b60e31b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316868660008181106120a4576120a46142b5565b90506020020160208101906120b991906142cb565b6001600160a01b0316146120e0576040516320db826760e01b815260040160405180910390fd5b61213e7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5088888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b91503482600081518110612154576121546142b5565b6020026020010151111561217b5760405163e1b0da4f60e01b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836000815181106121bd576121bd6142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156121f057600080fd5b505af1158015612204573d6000803e3d6000fd5b50505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663a9059cbb308460008151811061224c5761224c6142b5565b60200260200101516040518363ffffffff1660e01b81526004016122859291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156122a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c8919061424b565b6122e557604051631f0236bb60e01b815260040160405180910390fd5b612324828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b81600081518110612337576123376142b5565b602002602001015134111561183757611837338360008151811061235d5761235d6142b5565b6020026020010151346110ec91906142a2565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916123cc919061435f565b6000604051808303816000865af19150503d8060008114612409576040519150601f19603f3d011682016040523d82523d6000602084013e61240e565b606091505b5091509150818015612438575080511580612438575080806020019051810190612438919061424b565b6124895760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064015b60405180910390fd5b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040516124ba919061435f565b60006040518083038185875af1925050503d80600081146124f7576040519150601f19603f3d011682016040523d82523d6000602084013e6124fc565b606091505b50509050806125595760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b6064820152608401612480565b505050565b6000836000036125815760405163098fb56160e01b815260040160405180910390fd5b82158061258c575081155b156125aa5760405163bb55fd2760e01b815260040160405180910390fd5b60006125b8856103e561438e565b905060006125c6848361438e565b90506000826125d7876103e861438e565b6125e191906143a5565b90506125ed81836143ce565b979650505050505050565b600080600061260785856126d1565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015291935091508690604801604051602081830303815290604052805190602001206040516020016126af9291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527f912fa011211d18178fef8f22392edc90ca8f101645ab8347e1359b5ce2f890df603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b600080826001600160a01b0316846001600160a01b03160361270657604051630bd969eb60e41b815260040160405180910390fd5b826001600160a01b0316846001600160a01b031610612726578284612729565b83835b90925090506001600160a01b0382166127555760405163d92e233d60e01b815260040160405180910390fd5b9250929050565b6060600282511015612781576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff81111561279b5761279b613db0565b6040519080825280602002602001820160405280156127c4578160200160208202803683370190505b50905082816000815181106127db576127db6142b5565b60200260200101818152505060005b600183516127f891906142a2565b8110156128b25760008061284b87868581518110612818576128186142b5565b60200260200101518786600161282e91906143a5565b8151811061283e5761283e6142b5565b60200260200101516139f0565b505091509150612875848481518110612866576128666142b5565b6020026020010151838361255e565b846128818560016143a5565b81518110612891576128916142b5565b602002602001018181525050505080806128aa906143f0565b9150506127ea565b509392505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161291e919061435f565b6000604051808303816000865af19150503d806000811461295b576040519150601f19603f3d011682016040523d82523d6000602084013e612960565b606091505b509150915081801561298a57508051158061298a57508080602001905181019061298a919061424b565b6129e25760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401612480565b505050505050565b60005b600183516129fb91906142a2565b811015612be757600080848381518110612a1757612a176142b5565b602002602001015185846001612a2d91906143a5565b81518110612a3d57612a3d6142b5565b6020026020010151915091506000612a5583836126d1565b5090506000878581518110612a6c57612a6c6142b5565b60200260200101519050600088866001612a8691906143a5565b81518110612a9657612a966142b5565b60200260200101519050600080846001600160a01b0316876001600160a01b031614612ac457600084612ac8565b8360005b91509150600080866001600160a01b0316896001600160a01b031614612af057846000612af4565b6000855b91509150600060028d51612b0891906142a2565b8b10612b14578b612b16565b305b90506000612b457f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c8c6125f8565b9050612b528b828a61335e565b60405163562e19df60e01b8152600481018790526024810186905260448101859052606481018490526001600160a01b03838116608483015282169063562e19df9060a401600060405180830381600087803b158015612bb157600080fd5b505af1158015612bc5573d6000803e3d6000fd5b5050505050505050505050505050508080612bdf906143f0565b9150506129ed565b50505050565b6060600282511015612c12576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff811115612c2c57612c2c613db0565b604051908082528060200260200182016040528015612c55578160200160208202803683370190505b509050828160018351612c6891906142a2565b81518110612c7857612c786142b5565b602002602001018181525050600060018351612c9491906142a2565b90505b80156128b257600080612cda8786612cb06001876142a2565b81518110612cc057612cc06142b5565b602002602001015187868151811061283e5761283e6142b5565b505091509150612d04848481518110612cf557612cf56142b5565b60200260200101518383613691565b84612d106001866142a2565b81518110612d2057612d206142b5565b60200260200101818152505050508080612d3990614409565b915050612c97565b6000806000612d518787876125f8565b6040516370a0823160e01b81526001600160a01b0380831660048301529192506000918816906370a0823190602401602060405180830381865afa158015612d9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc19190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918816906370a0823190602401602060405180830381865afa158015612e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e319190614329565b90506000836001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e979190614329565b9050876001600160a01b0316896001600160a01b03161015612f0c5781612ecc82612ec28a876143a5565b600160701b613ae1565b612ed691906143a5565b612ee0838961438e565b612eea91906143ce565b9550600160701b612efb828861438e565b612f0591906143ce565b9450612f6c565b8181612f1889866143a5565b612f2690600160701b61438e565b612f3091906143ce565b612f3a91906143a5565b612f44838961438e565b612f4e91906143ce565b955080612f5f87600160701b61438e565b612f6991906143ce565b94505b5050505094509492505050565b60405163e6a4390560e01b81526001600160a01b0388811660048301528781166024830152600091829182917f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce509091169063e6a4390590604401602060405180830381865afa158015612ff0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130149190614342565b90506001600160a01b0381166130bb576040516364e329cb60e11b81526001600160a01b038b811660048301528a811660248301527f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50169063c9c65396906044016020604051808303816000875af1158015613094573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130b89190614342565b90505b6000806000806130ec7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508f8f6139f0565b9350935093509350818461310091906143a5565b158015613114575061311281846143a5565b155b15613124578b96508a95506131cd565b60006131438d61313485886143a5565b61313e85886143a5565b61398f565b90508b8111613178578981101561316d5760405163ef71d09160e01b815260040160405180910390fd5b8c97509550856131cb565b60006131928d61318885886143a5565b61313e878a6143a5565b90508d8111156131a4576131a4614420565b8b8110156131c557604051638dc525d160e01b815260040160405180910390fd5b97508b96505b505b6000841180156131dd5750600083115b15610884576000856001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613222573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132469190614329565b90508d6001600160a01b03168f6001600160a01b031610156132d957600061327482612ec26127108961438e565b90508061328761ffff8c166127106142a2565b613291908761438e565b11806132b55750806132a961ffff8c166127106143a5565b6132b3908761438e565b105b156132d357604051630d683ed760e21b815260040160405180910390fd5b5061334c565b60006132eb82612ec26127108861438e565b9050806132fe61ffff8c166127106142a2565b613308908861438e565b118061332c57508061332061ffff8c166127106143a5565b61332a908861438e565b105b1561334a57604051630d683ed760e21b815260040160405180910390fd5b505b50505050505097509795505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b17905291516000928392908716916133ba919061435f565b6000604051808303816000865af19150503d80600081146133f7576040519150601f19603f3d011682016040523d82523d6000602084013e6133fc565b606091505b5091509150818015613426575080511580613426575080806020019051810190613426919061424b565b6124895760405162461bcd60e51b815260206004820152601e60248201527f5472616e7366657248656c7065723a20415050524f56455f4641494c454400006044820152606401612480565b60405163e6a4390560e01b81526001600160a01b0387811660048301528681166024830152600091829182917f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce509091169063e6a4390590604401602060405180830381865afa1580156134e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350d9190614342565b6001600160a01b031603613534576040516391bc00d960e01b815260040160405180910390fd5b6000806000806135657f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508d8d6139f0565b9350935093509350836000148061357a575082155b15613598576040516321c4e35760e21b815260040160405180910390fd5b811580156135a4575080155b156135c2576040516391bc00d960e01b815260040160405180910390fd5b81156136275760006135f67f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508d8f8d612d41565b9150508881101561361a57604051638dc525d160e01b815260040160405180910390fd5b5060009550889450613682565b60006136557f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508e8e8e612d41565b915050878110156136795760405163ef71d09160e01b815260040160405180910390fd5b50899550600094505b50505050965096945050505050565b6000836000036136b4576040516342301c2360e01b815260040160405180910390fd5b8215806136bf575081155b156136dd5760405163bb55fd2760e01b815260040160405180910390fd5b60006136e9858561438e565b6136f5906103e861438e565b9050600061370386856142a2565b61370f906103e561438e565b905061371b81836143ce565b6137269060016143a5565b9695505050505050565b60008060006137408787876125f8565b90506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a69190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918916906370a0823190602401602060405180830381865afa1580156137f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138169190614329565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918916906370a0823190602401602060405180830381865afa158015613862573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138869190614329565b90506000846001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ec9190614329565b90506000846138fb8a8561438e565b61390591906143ce565b9050846139128a8661438e565b61391c91906143ce565b9750896001600160a01b03168b6001600160a01b03161015613958578161394782600160701b61438e565b61395191906143ce565b9650613974565b600160701b613967838361438e565b61397191906143ce565b96505b61397e87896143a5565b975050505050505094509492505050565b6000836000036139b257604051632ca2f52b60e11b815260040160405180910390fd5b8215806139bd575081155b156139db5760405163bb55fd2760e01b815260040160405180910390fd5b826139e6838661438e565b6106b891906143ce565b6000806000806000613a0287876126d1565b509050600080600080613a168c8c8c6125f8565b6001600160a01b031663976bf4166040518163ffffffff1660e01b815260040160a060405180830381865afa158015613a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a77919061444d565b506001600160701b031693506001600160701b031693506001600160701b031693506001600160701b03169350846001600160a01b03168b6001600160a01b031614613ac65782848284613acb565b838383835b929f919e509c50909a5098505050505050505050565b6000808060001985870985870292508281108382030391505080600003613b1b57838281613b1157613b116143b8565b04925050506106bb565b808411613b2757600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6001600160a01b0381168114613ba557600080fd5b50565b8035613bb381613b90565b919050565b60008060008060008060c08789031215613bd157600080fd5b8635613bdc81613b90565b95506020870135945060408701359350606087013592506080870135613c0181613b90565b8092505060a087013590509295509295509295565b600080600060608486031215613c2b57600080fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a031215613c5d57600080fd5b8735613c6881613b90565b96506020880135613c7881613b90565b955060408801359450606088013593506080880135925060a0880135613c9d81613b90565b8092505060c0880135905092959891949750929550565b60008083601f840112613cc657600080fd5b50813567ffffffffffffffff811115613cde57600080fd5b6020830191508360208260051b850101111561275557600080fd5b60008060008060008060a08789031215613d1257600080fd5b8635955060208701359450604087013567ffffffffffffffff811115613d3757600080fd5b613d4389828a01613cb4565b9095509350506060870135613d5781613b90565b80925050608087013590509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015613da457835183529284019291840191600101613d88565b50909695505050505050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613dd957600080fd5b8235915060208084013567ffffffffffffffff80821115613df957600080fd5b818601915086601f830112613e0d57600080fd5b813581811115613e1f57613e1f613db0565b8060051b604051601f19603f83011681018181108582111715613e4457613e44613db0565b604052918252848201925083810185019189831115613e6257600080fd5b938501935b82851015613e8757613e7885613ba8565b84529385019392850192613e67565b8096505050505050509250929050565b8015158114613ba557600080fd5b803560ff81168114613bb357600080fd5b60008060008060008060008060008060006101608c8e031215613ed857600080fd5b8b35613ee381613b90565b9a5060208c0135613ef381613b90565b995060408c0135985060608c0135975060808c0135965060a08c0135613f1881613b90565b955060c08c0135945060e08c0135613f2f81613e97565b9350613f3e6101008d01613ea5565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215613f7557600080fd5b8335613f8081613b90565b92506020840135613f9081613b90565b929592945050506040919091013590565b803561ffff81168114613bb357600080fd5b600080600080600080600060e0888a031215613fce57600080fd5b8735613fd981613b90565b9650602088013595506040880135945060608801359350613ffc60808901613fa1565b925060a0880135613c9d81613b90565b60008060008060006080868803121561402457600080fd5b85359450602086013567ffffffffffffffff81111561404257600080fd5b61404e88828901613cb4565b909550935050604086013561406281613b90565b949793965091946060013592915050565b60008060008060008060008060006101208a8c03121561409257600080fd5b893561409d81613b90565b985060208a01356140ad81613b90565b975060408a0135965060608a0135955060808a0135945060a08a013593506140d760c08b01613fa1565b925060e08a01356140e781613b90565b809250506101008a013590509295985092959850929598565b6000806000806000806000806000806101408b8d03121561412057600080fd5b8a3561412b81613b90565b995060208b0135985060408b0135975060608b0135965060808b013561415081613b90565b955060a08b0135945060c08b013561416781613e97565b935061417560e08c01613ea5565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600080604083850312156141a957600080fd5b82356141b481613b90565b915060208301356141c481613b90565b809150509250929050565b600080600080600080600080610100898b0312156141ec57600080fd5b88356141f781613b90565b9750602089013561420781613b90565b965060408901359550606089013594506080890135935060a0890135925060c089013561423381613b90565b8092505060e089013590509295985092959890939650565b60006020828403121561425d57600080fd5b81516106bb81613e97565b6000806040838503121561427b57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b7f57610b7f61428c565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156142dd57600080fd5b81356106bb81613b90565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b60006020828403121561433b57600080fd5b5051919050565b60006020828403121561435457600080fd5b81516106bb81613b90565b6000825160005b818110156143805760208186018101518583015201614366565b506000920191825250919050565b8082028115828204841417610b7f57610b7f61428c565b80820180821115610b7f57610b7f61428c565b634e487b7160e01b600052601260045260246000fd5b6000826143eb57634e487b7160e01b600052601260045260246000fd5b500490565b6000600182016144025761440261428c565b5060010190565b6000816144185761441861428c565b506000190190565b634e487b7160e01b600052600160045260246000fd5b80516001600160701b0381168114613bb357600080fd5b600080600080600060a0868803121561446557600080fd5b61446e86614436565b945061447c60208701614436565b935061448a60408701614436565b925061449860608701614436565b9150608086015163ffffffff811681146144b157600080fd5b80915050929550929590935056
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
-----Decoded View---------------
Arg [0] : _factory (address): 0xB8DE4aB6C65e274630F5279f74eB69b66327CE50
Arg [1] : _WETH (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.