More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 60 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
Value | ||||
---|---|---|---|---|---|---|---|---|---|
Approve | 20095179 | 14 days ago | IN | 0 ETH | 0.00009965 | ||||
Approve | 20095176 | 14 days ago | IN | 0 ETH | 0.00009409 | ||||
Approve | 19963838 | 32 days ago | IN | 0 ETH | 0.00090266 | ||||
Approve | 19934462 | 36 days ago | IN | 0 ETH | 0.00057984 | ||||
Approve | 19934335 | 36 days ago | IN | 0 ETH | 0.00068829 | ||||
Approve | 19915649 | 39 days ago | IN | 0 ETH | 0.0004655 | ||||
Approve | 19899216 | 41 days ago | IN | 0 ETH | 0.00013883 | ||||
Approve | 19805410 | 54 days ago | IN | 0 ETH | 0.00041592 | ||||
Approve | 19775088 | 58 days ago | IN | 0 ETH | 0.00032821 | ||||
Approve | 19698959 | 69 days ago | IN | 0 ETH | 0.00033751 | ||||
Approve | 19695008 | 70 days ago | IN | 0 ETH | 0.00043991 | ||||
Approve | 19589561 | 84 days ago | IN | 0 ETH | 0.00084669 | ||||
Approve | 19497465 | 97 days ago | IN | 0 ETH | 0.00077815 | ||||
Approve | 19480649 | 100 days ago | IN | 0 ETH | 0.00115898 | ||||
Approve | 19439935 | 105 days ago | IN | 0 ETH | 0.0018068 | ||||
Transfer | 19424429 | 108 days ago | IN | 0 ETH | 0.00204894 | ||||
Transfer | 19403787 | 111 days ago | IN | 0 ETH | 0.00553932 | ||||
Approve | 19379643 | 114 days ago | IN | 0 ETH | 0.00343215 | ||||
Approve | 19376379 | 114 days ago | IN | 0 ETH | 0.00324773 | ||||
Transfer | 19376344 | 114 days ago | IN | 0 ETH | 0.00349 | ||||
Approve | 19266364 | 130 days ago | IN | 0 ETH | 0.00117743 | ||||
Approve | 19120740 | 150 days ago | IN | 0 ETH | 0.00169344 | ||||
Approve | 19119751 | 150 days ago | IN | 0 ETH | 0.00114236 | ||||
Approve | 19036426 | 162 days ago | IN | 0 ETH | 0.00166433 | ||||
Approve | 19015810 | 165 days ago | IN | 0 ETH | 0.00114217 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | Value | ||
---|---|---|---|---|---|---|
17877930 | 324 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
ButtonswapPair
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapPair} from "./interfaces/IButtonswapPair/IButtonswapPair.sol"; import {IButtonswapERC20} from "./interfaces/IButtonswapERC20/IButtonswapERC20.sol"; import {ButtonswapERC20} from "./ButtonswapERC20.sol"; import {Math} from "./libraries/Math.sol"; import {PairMath} from "./libraries/PairMath.sol"; import {UQ112x112} from "./libraries/UQ112x112.sol"; import {IERC20} from "buttonswap-core_@openzeppelin-contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "buttonswap-core_@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {IButtonswapFactory} from "./interfaces/IButtonswapFactory/IButtonswapFactory.sol"; contract ButtonswapPair is IButtonswapPair, ButtonswapERC20 { using UQ112x112 for uint224; /** * @dev A set of liquidity values. * @param pool0 The active `token0` liquidity * @param pool1 The active `token1` liquidity * @param reservoir0 The inactive `token0` liquidity * @param reservoir1 The inactive `token1` liquidity */ struct LiquidityBalances { uint256 pool0; uint256 pool1; uint256 reservoir0; uint256 reservoir1; } /** * @inheritdoc IButtonswapPair */ uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3; /** * @dev Denominator for basis points. */ uint256 private constant BPS = 10_000; /** * @inheritdoc IButtonswapPair */ uint32 public movingAverageWindow; /** * @inheritdoc IButtonswapPair */ uint16 public maxVolatilityBps; /** * @inheritdoc IButtonswapPair */ uint32 public minTimelockDuration; /** * @inheritdoc IButtonswapPair */ uint32 public maxTimelockDuration; /** * @inheritdoc IButtonswapPair */ uint16 public maxSwappableReservoirLimitBps; /** * @inheritdoc IButtonswapPair */ uint32 public swappableReservoirGrowthWindow; /** * @inheritdoc IButtonswapPair */ address public immutable factory; /** * @inheritdoc IButtonswapPair */ address public immutable token0; /** * @inheritdoc IButtonswapPair */ address public immutable token1; /** * @dev The active `token0` liquidity amount following the last swap. * This value is used to determine active liquidity balances after potential rebases until the next future swap. */ uint112 internal pool0Last; /** * @dev The active `token1` liquidity amount following the last swap. * This value is used to determine active liquidity balances after potential rebases until the next future swap. */ uint112 internal pool1Last; /** * @dev The timestamp of the block that the last swap occurred in. */ uint32 internal blockTimestampLast; /** * @inheritdoc IButtonswapPair */ uint256 public price0CumulativeLast; /** * @inheritdoc IButtonswapPair */ uint256 public price1CumulativeLast; /** * @dev The value of `movingAveragePrice0` at the time of the last swap. */ uint256 internal movingAveragePrice0Last; /** * @inheritdoc IButtonswapPair */ uint120 public singleSidedTimelockDeadline; /** * @inheritdoc IButtonswapPair */ uint120 public swappableReservoirLimitReachesMaxDeadline; /** * @dev Whether or not the pair is isPaused (paused = 1, unPaused = 0). * When paused, all operations other than dual-sided burning LP tokens are disabled. */ uint8 internal isPaused; /** * @dev Value to track the state of the re-entrancy guard. */ uint8 private unlocked = 1; /** * @dev Guards against re-entrancy. */ modifier lock() { if (unlocked == 0) { revert Locked(); } unlocked = 0; _; unlocked = 1; } /** * @dev Prevents certain operations from being executed if the price volatility induced timelock has yet to conclude. */ modifier singleSidedTimelock() { if (block.timestamp < singleSidedTimelockDeadline) { revert SingleSidedTimelock(); } _; } /** * @dev Prevents operations from being executed if the Pair is currently paused. */ modifier checkPaused() { if (isPaused == 1) { revert Paused(); } _; } /** * @dev Called whenever an LP wants to burn their LP tokens to make sure they get their fair share of fees. * If `feeTo` is defined, `balanceOf(address(this))` gets transferred to `feeTo`. * If `feeTo` is not defined, `balanceOf(address(this))` gets burned and the LP tokens all grow in value. */ modifier sendOrRefundFee() { if (balanceOf[address(this)] > 0) { address feeTo = IButtonswapFactory(factory).feeTo(); if (feeTo != address(0)) { _transfer(address(this), feeTo, balanceOf[address(this)]); } else { _burn(address(this), balanceOf[address(this)]); } } _; } /** * @dev Prevents operations from being executed if the caller is not the factory. */ modifier onlyFactory() { if (msg.sender != factory) { revert Forbidden(); } _; } constructor() { factory = msg.sender; ( token0, token1, movingAverageWindow, maxVolatilityBps, minTimelockDuration, maxTimelockDuration, maxSwappableReservoirLimitBps, swappableReservoirGrowthWindow ) = IButtonswapFactory(factory).lastCreatedTokensAndParameters(); } /** * @inheritdoc IButtonswapERC20 */ function name() external view override(ButtonswapERC20, IButtonswapERC20) returns (string memory _name) { _name = IButtonswapFactory(factory).tokenName(); } /** * @inheritdoc IButtonswapERC20 */ function symbol() external view override(ButtonswapERC20, IButtonswapERC20) returns (string memory _symbol) { _symbol = IButtonswapFactory(factory).tokenSymbol(); } /** * @dev Always mints liquidity equivalent to 1/6th of the growth in sqrt(k) and allocates to address(this) * If there isn't a `feeTo` address defined, these LP tokens will get burned this 1/6th gets reallocated to LPs * @param pool0 The `token0` active liquidity balance at the start of the ongoing swap * @param pool1 The `token1` active liquidity balance at the start of the ongoing swap * @param pool0New The `token0` active liquidity balance at the end of the ongoing swap * @param pool1New The `token1` active liquidity balance at the end of the ongoing swap */ function _mintFee(uint256 pool0, uint256 pool1, uint256 pool0New, uint256 pool1New) internal { uint256 liquidityOut = PairMath.getProtocolFeeLiquidityMinted(totalSupply, pool0 * pool1, pool0New * pool1New); if (liquidityOut > 0) { _mint(address(this), liquidityOut); } } /** * @dev Updates `price0CumulativeLast` and `price1CumulativeLast` based on the current timestamp. * @param pool0 The `token0` active liquidity balance at the start of the ongoing swap * @param pool1 The `token1` active liquidity balance at the start of the ongoing swap */ function _updatePriceCumulative(uint256 pool0, uint256 pool1) internal { uint112 _pool0 = uint112(pool0); uint112 _pool1 = uint112(pool1); uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32); uint32 timeElapsed; unchecked { // underflow is desired timeElapsed = blockTimestamp - blockTimestampLast; } if (timeElapsed > 0 && pool0 != 0 && pool1 != 0) { // * never overflows, and + overflow is desired unchecked { price0CumulativeLast += ((pool1 << 112) * timeElapsed) / _pool0; price1CumulativeLast += ((pool0 << 112) * timeElapsed) / _pool1; } blockTimestampLast = blockTimestamp; } } /** * @dev Refer to [closest-bound-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/closest-bound-math.md) for more detail. * @param poolALower The lower bound for the active liquidity balance of the non-fixed token * @param poolB The active liquidity balance of the fixed token * @param _poolALast The active liquidity balance at the end of the last swap for the non-fixed token * @param _poolBLast The active liquidity balance at the end of the last swap for the fixed token * @return closestBound The bound for the active liquidity balance of the non-fixed token that produces a price ratio closest to last swap price */ function _closestBound(uint256 poolALower, uint256 poolB, uint256 _poolALast, uint256 _poolBLast) internal pure returns (uint256 closestBound) { if ((2 * poolALower * _poolBLast) + _poolBLast < 2 * _poolALast * poolB) { closestBound = poolALower + 1; } else { closestBound = poolALower; } } /** * @dev Refer to [liquidity-balances-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/liquidity-balances-math.md) for more detail. * @param total0 The total amount of `token0` held by the Pair * @param total1 The total amount of `token1` held by the Pair * @return lb The current active and inactive liquidity balances */ function _getLiquidityBalances(uint256 total0, uint256 total1) internal view returns (LiquidityBalances memory lb) { uint256 _pool0Last = uint256(pool0Last); uint256 _pool1Last = uint256(pool1Last); if (_pool0Last == 0 || _pool1Last == 0) { // Before Pair is initialized by first dual mint just return zeroes } else if (total0 == 0 || total1 == 0) { // Save the extra calculations and just return zeroes } else { if (total0 * _pool1Last < total1 * _pool0Last) { lb.pool0 = total0; // pool0Last/pool1Last == pool0/pool1 => pool1 == (pool0*pool1Last)/pool0Last // pool1Last/pool0Last == pool1/pool0 => pool1 == (pool0*pool1Last)/pool0Last lb.pool1 = (lb.pool0 * _pool1Last) / _pool0Last; lb.pool1 = _closestBound(lb.pool1, lb.pool0, _pool1Last, _pool0Last); // reservoir0 is zero, so no need to set it lb.reservoir1 = total1 - lb.pool1; } else { lb.pool1 = total1; // pool0Last/pool1Last == pool0/pool1 => pool0 == (pool1*pool0Last)/pool1Last // pool1Last/pool0Last == pool1/pool0 => pool0 == (pool1*pool0Last)/pool1Last lb.pool0 = (lb.pool1 * _pool0Last) / _pool1Last; lb.pool0 = _closestBound(lb.pool0, lb.pool1, _pool0Last, _pool1Last); // reservoir1 is zero, so no need to set it lb.reservoir0 = total0 - lb.pool0; } if (lb.pool0 > type(uint112).max || lb.pool1 > type(uint112).max) { revert Overflow(); } } } /** * @dev Calculates current price volatility and initiates a timelock scaled to the volatility size. * This timelock prohibits single-sided operations from being executed until enough time has passed for the timelock * to conclude. * This protects against attempts to manipulate the price that the reservoir is valued at during single-sided operations. * @param _movingAveragePrice0 The current `movingAveragePrice0` value * @param pool0New The `token0` active liquidity balance at the end of the ongoing swap * @param pool1New The `token1` active liquidity balance at the end of the ongoing swap */ function _updateSingleSidedTimelock(uint256 _movingAveragePrice0, uint112 pool0New, uint112 pool1New) internal { uint256 newPrice0 = uint256(UQ112x112.encode(pool1New).uqdiv(pool0New)); uint256 priceDifference; if (newPrice0 > _movingAveragePrice0) { priceDifference = newPrice0 - _movingAveragePrice0; } else { priceDifference = _movingAveragePrice0 - newPrice0; } // priceDifference / ((_movingAveragePrice0 * maxVolatilityBps)/BPS) uint32 _minTimelockDuration = minTimelockDuration; uint32 _maxTimelockDuration = maxTimelockDuration; uint256 timelock = Math.min( _minTimelockDuration + ( (priceDifference * BPS * (_maxTimelockDuration - _minTimelockDuration)) / (_movingAveragePrice0 * maxVolatilityBps) ), _maxTimelockDuration ); uint120 timelockDeadline = uint120(block.timestamp + timelock); if (timelockDeadline > singleSidedTimelockDeadline) { singleSidedTimelockDeadline = timelockDeadline; } } /** * @dev Calculates the current limit on the number of reservoir tokens that can be exchanged during a single-sided * operation. * This is based on corresponding active liquidity size and time since and size of the last single-sided operation. * @param poolA The active liquidity balance for the non-zero reservoir token * @return swappableReservoir The amount of non-zero reservoir token that can be exchanged as part of a single-sided operation */ function _getSwappableReservoirLimit(uint256 poolA) internal view returns (uint256 swappableReservoir) { // Calculate the maximum the limit can be as a fraction of the corresponding active liquidity uint256 maxSwappableReservoirLimit = (poolA * maxSwappableReservoirLimitBps) / BPS; uint256 _swappableReservoirLimitReachesMaxDeadline = swappableReservoirLimitReachesMaxDeadline; if (_swappableReservoirLimitReachesMaxDeadline > block.timestamp) { // If the current deadline is still active then calculate the progress towards reaching it uint32 _swappableReservoirGrowthWindow = swappableReservoirGrowthWindow; uint256 progress = _swappableReservoirGrowthWindow - (_swappableReservoirLimitReachesMaxDeadline - block.timestamp); // The greater the progress, the closer to the max limit we get swappableReservoir = (maxSwappableReservoirLimit * progress) / _swappableReservoirGrowthWindow; } else { // If the current deadline has expired then the full limit is available swappableReservoir = maxSwappableReservoirLimit; } } /** * @inheritdoc IButtonswapPair */ function getSwappableReservoirLimit() external view returns (uint256 swappableReservoirLimit) { uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); LiquidityBalances memory lb = _getLiquidityBalances(total0, total1); if (lb.reservoir0 > 0) { swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0); } else { swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1); } } /** * @dev Updates the value of `swappableReservoirLimitReachesMaxDeadline` which is the time at which the maximum * amount of inactive liquidity tokens can be exchanged during a single-sided operation. * @dev Assumes `swappedAmountA` is less than or equal to `maxSwappableReservoirLimit` * @param poolA The active liquidity balance for the non-zero reservoir token * @param swappedAmountA The amount of non-zero reservoir tokens that were exchanged during the ongoing single-sided * operation */ function _updateSwappableReservoirDeadline(uint256 poolA, uint256 swappedAmountA) internal { // Calculate the maximum the limit can be as a fraction of the corresponding active liquidity uint256 maxSwappableReservoirLimit = (poolA * maxSwappableReservoirLimitBps) / BPS; // Calculate how much time delay the swap instigates uint256 delay; // Check non-zero to avoid div by zero error if (maxSwappableReservoirLimit > 0) { // Since `swappedAmountA/maxSwappableReservoirLimit <= 1`, `delay <= swappableReservoirGrowthWindow` delay = (swappableReservoirGrowthWindow * swappedAmountA) / maxSwappableReservoirLimit; } else { // If it is zero then it's in an extreme condition and a delay is most appropriate way to handle it delay = swappableReservoirGrowthWindow; } // Apply the delay uint256 _swappableReservoirLimitReachesMaxDeadline = swappableReservoirLimitReachesMaxDeadline; if (_swappableReservoirLimitReachesMaxDeadline > block.timestamp) { // If the current deadline hasn't expired yet then add the delay to it swappableReservoirLimitReachesMaxDeadline = uint120(_swappableReservoirLimitReachesMaxDeadline + delay); } else { // If the current deadline has expired already then add the delay to the current time, so that the full // delay is still applied swappableReservoirLimitReachesMaxDeadline = uint120(block.timestamp + delay); } } /** * @inheritdoc IButtonswapPair */ function getIsPaused() external view returns (bool _isPaused) { _isPaused = isPaused == 1; } /** * @inheritdoc IButtonswapPair */ function setIsPaused(bool isPausedNew) external onlyFactory { if (isPausedNew) { isPaused = 1; } else { singleSidedTimelockDeadline = uint120(block.timestamp + maxTimelockDuration); isPaused = 0; } } /** * @inheritdoc IButtonswapPair */ function getLiquidityBalances() external view returns (uint112 _pool0, uint112 _pool1, uint112 _reservoir0, uint112 _reservoir1, uint32 _blockTimestampLast) { uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); LiquidityBalances memory lb = _getLiquidityBalances(total0, total1); _pool0 = uint112(lb.pool0); _pool1 = uint112(lb.pool1); _reservoir0 = uint112(lb.reservoir0); _reservoir1 = uint112(lb.reservoir1); _blockTimestampLast = blockTimestampLast; } /** * @inheritdoc IButtonswapPair */ function movingAveragePrice0() public view returns (uint256 _movingAveragePrice0) { uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32); uint32 timeElapsed; unchecked { // overflow is desired timeElapsed = blockTimestamp - blockTimestampLast; } uint256 currentPrice0 = uint256(UQ112x112.encode(pool1Last).uqdiv(pool0Last)); if (timeElapsed == 0) { _movingAveragePrice0 = movingAveragePrice0Last; } else if (timeElapsed >= movingAverageWindow) { _movingAveragePrice0 = currentPrice0; } else { uint32 _movingAverageWindow = movingAverageWindow; _movingAveragePrice0 = ( (movingAveragePrice0Last * (_movingAverageWindow - timeElapsed)) + (currentPrice0 * timeElapsed) ) / _movingAverageWindow; } } /** * @inheritdoc IButtonswapPair */ function mint(uint256 amountIn0, uint256 amountIn1, address to) external lock checkPaused sendOrRefundFee returns (uint256 liquidityOut) { uint256 _totalSupply = totalSupply; uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn0); SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn1); // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math amountIn0 = IERC20(token0).balanceOf(address(this)) - total0; amountIn1 = IERC20(token1).balanceOf(address(this)) - total1; if (_totalSupply == 0) { liquidityOut = Math.sqrt(amountIn0 * amountIn1) - MINIMUM_LIQUIDITY; // permanently lock the first MINIMUM_LIQUIDITY tokens _mint(address(0), MINIMUM_LIQUIDITY); // Initialize Pair last swap price pool0Last = uint112(amountIn0); pool1Last = uint112(amountIn1); // Initialize timestamp so first price update is accurate blockTimestampLast = uint32(block.timestamp % 2 ** 32); // Initialize moving average to price from initial amounts movingAveragePrice0Last = uint256(UQ112x112.encode(uint112(amountIn1)).uqdiv(uint112(amountIn0))); } else { // Don't need to check that amountIn{0,1} are in the right ratio because the least generous ratio is used // to determine the liquidityOut value, meaning any tokens that exceed that ratio are donated. // If total0 or total1 are zero (eg. due to negative rebases) then the function call reverts with div by zero liquidityOut = PairMath.getDualSidedMintLiquidityOutAmount(_totalSupply, amountIn0, amountIn1, total0, total1); } if (liquidityOut == 0) { revert InsufficientLiquidityMinted(); } _mint(to, liquidityOut); emit Mint(msg.sender, amountIn0, amountIn1, liquidityOut, to); } /** * @inheritdoc IButtonswapPair */ function mintWithReservoir(uint256 amountIn, address to) external lock checkPaused singleSidedTimelock sendOrRefundFee returns (uint256 liquidityOut) { if (amountIn == 0) { revert InsufficientLiquidityAdded(); } uint256 _totalSupply = totalSupply; if (_totalSupply == 0) { revert Uninitialized(); } uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); // Determine current pool liquidity LiquidityBalances memory lb = _getLiquidityBalances(total0, total1); if (lb.pool0 == 0 || lb.pool1 == 0) { revert InsufficientLiquidity(); } if (lb.reservoir0 == 0) { // If reservoir0 is empty then we're adding token0 to pair with token1 reservoir liquidity SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn); // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math amountIn = IERC20(token0).balanceOf(address(this)) - total0; // Ensure there's enough reservoir1 liquidity to do this without growing reservoir0 LiquidityBalances memory lbNew = _getLiquidityBalances(total0 + amountIn, total1); if (lbNew.reservoir0 > 0) { revert InsufficientReservoir(); } uint256 swappedReservoirAmount1; (liquidityOut, swappedReservoirAmount1) = PairMath.getSingleSidedMintLiquidityOutAmountA( _totalSupply, amountIn, total0, total1, movingAveragePrice0() ); uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1); if (swappedReservoirAmount1 > swappableReservoirLimit) { revert SwappableReservoirExceeded(); } _updateSwappableReservoirDeadline(lb.pool1, swappedReservoirAmount1); } else { // If reservoir1 is empty then we're adding token1 to pair with token0 reservoir liquidity SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn); // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math amountIn = IERC20(token1).balanceOf(address(this)) - total1; // Ensure there's enough reservoir0 liquidity to do this without growing reservoir1 LiquidityBalances memory lbNew = _getLiquidityBalances(total0, total1 + amountIn); if (lbNew.reservoir1 > 0) { revert InsufficientReservoir(); } uint256 swappedReservoirAmount0; (liquidityOut, swappedReservoirAmount0) = PairMath.getSingleSidedMintLiquidityOutAmountB( _totalSupply, amountIn, total0, total1, movingAveragePrice0() ); uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0); if (swappedReservoirAmount0 > swappableReservoirLimit) { revert SwappableReservoirExceeded(); } _updateSwappableReservoirDeadline(lb.pool0, swappedReservoirAmount0); } if (liquidityOut == 0) { revert InsufficientLiquidityMinted(); } _mint(to, liquidityOut); if (lb.reservoir0 == 0) { emit Mint(msg.sender, amountIn, 0, liquidityOut, to); } else { emit Mint(msg.sender, 0, amountIn, liquidityOut, to); } } /** * @inheritdoc IButtonswapPair */ function burn(uint256 liquidityIn, address to) external lock sendOrRefundFee returns (uint256 amountOut0, uint256 amountOut1) { if (liquidityIn == 0) { revert InsufficientLiquidityBurned(); } uint256 _totalSupply = totalSupply; uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); (amountOut0, amountOut1) = PairMath.getDualSidedBurnOutputAmounts(_totalSupply, liquidityIn, total0, total1); _burn(msg.sender, liquidityIn); SafeERC20.safeTransfer(IERC20(token0), to, amountOut0); SafeERC20.safeTransfer(IERC20(token1), to, amountOut1); emit Burn(msg.sender, liquidityIn, amountOut0, amountOut1, to); } /** * @inheritdoc IButtonswapPair */ function burnFromReservoir(uint256 liquidityIn, address to) external lock checkPaused singleSidedTimelock sendOrRefundFee returns (uint256 amountOut0, uint256 amountOut1) { uint256 _totalSupply = totalSupply; uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); // Determine current pool liquidity LiquidityBalances memory lb = _getLiquidityBalances(total0, total1); if (lb.pool0 == 0 || lb.pool1 == 0) { revert InsufficientLiquidity(); } if (lb.reservoir0 == 0) { // If reservoir0 is empty then we're swapping amountOut0 for token1 from reservoir1 uint256 swappedReservoirAmount1; (amountOut1, swappedReservoirAmount1) = PairMath.getSingleSidedBurnOutputAmountB( _totalSupply, liquidityIn, total0, total1, movingAveragePrice0() ); // Check there's enough reservoir liquidity to withdraw from // If `amountOut1` exceeds reservoir1 then it will result in reservoir0 growing from excess token0 if (amountOut1 > lb.reservoir1) { revert InsufficientReservoir(); } uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1); if (swappedReservoirAmount1 > swappableReservoirLimit) { revert SwappableReservoirExceeded(); } _updateSwappableReservoirDeadline(lb.pool1, swappedReservoirAmount1); } else { // If reservoir0 isn't empty then we're swapping amountOut1 for token0 from reservoir0 uint256 swappedReservoirAmount0; (amountOut0, swappedReservoirAmount0) = PairMath.getSingleSidedBurnOutputAmountA( _totalSupply, liquidityIn, total0, total1, movingAveragePrice0() ); // Check there's enough reservoir liquidity to withdraw from // If `amountOut0` exceeds reservoir0 then it will result in reservoir1 growing from excess token1 if (amountOut0 > lb.reservoir0) { revert InsufficientReservoir(); } uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0); if (swappedReservoirAmount0 > swappableReservoirLimit) { revert SwappableReservoirExceeded(); } _updateSwappableReservoirDeadline(lb.pool0, swappedReservoirAmount0); } _burn(msg.sender, liquidityIn); if (amountOut0 > 0) { SafeERC20.safeTransfer(IERC20(token0), to, amountOut0); } else if (amountOut1 > 0) { SafeERC20.safeTransfer(IERC20(token1), to, amountOut1); } else { revert InsufficientLiquidityBurned(); } emit Burn(msg.sender, liquidityIn, amountOut0, amountOut1, to); } /** * @inheritdoc IButtonswapPair */ function swap(uint256 amountIn0, uint256 amountIn1, uint256 amountOut0, uint256 amountOut1, address to) external lock checkPaused { { if (amountOut0 == 0 && amountOut1 == 0) { revert InsufficientOutputAmount(); } if (to == token0 || to == token1) { revert InvalidRecipient(); } uint256 total0 = IERC20(token0).balanceOf(address(this)); uint256 total1 = IERC20(token1).balanceOf(address(this)); // Determine current pool liquidity LiquidityBalances memory lb = _getLiquidityBalances(total0, total1); if (amountOut0 >= lb.pool0 || amountOut1 >= lb.pool1) { revert InsufficientLiquidity(); } // Transfer in the specified input if (amountIn0 > 0) { SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn0); } if (amountIn1 > 0) { SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn1); } // Optimistically transfer output if (amountOut0 > 0) { SafeERC20.safeTransfer(IERC20(token0), to, amountOut0); } if (amountOut1 > 0) { SafeERC20.safeTransfer(IERC20(token1), to, amountOut1); } // Refresh balances total0 = IERC20(token0).balanceOf(address(this)); total1 = IERC20(token1).balanceOf(address(this)); // The reservoir balances must remain unchanged during a swap, so all balance changes impact the pool balances uint256 pool0New = total0 - lb.reservoir0; uint256 pool1New = total1 - lb.reservoir1; if (pool0New == 0 || pool1New == 0) { revert InvalidFinalPrice(); } // Update to the actual amount of tokens the user sent in based on the delta between old and new pool balances if (pool0New > lb.pool0) { amountIn0 = pool0New - lb.pool0; amountOut0 = 0; } else { amountIn0 = 0; amountOut0 = lb.pool0 - pool0New; } if (pool1New > lb.pool1) { amountIn1 = pool1New - lb.pool1; amountOut1 = 0; } else { amountIn1 = 0; amountOut1 = lb.pool1 - pool1New; } // If after accounting for input and output cancelling one another out, fee on transfer, etc there is no // input tokens in real terms then revert. if (amountIn0 == 0 && amountIn1 == 0) { revert InsufficientInputAmount(); } uint256 pool0NewAdjusted = (pool0New * 1000) - (amountIn0 * 3); uint256 pool1NewAdjusted = (pool1New * 1000) - (amountIn1 * 3); // After account for 0.3% fees, the new K must not be less than the old K if (pool0NewAdjusted * pool1NewAdjusted < (lb.pool0 * lb.pool1 * 1000 ** 2)) { revert KInvariant(); } // Update moving average before `_updatePriceCumulative` updates `blockTimestampLast` and the new `poolXLast` values are set uint256 _movingAveragePrice0 = movingAveragePrice0(); movingAveragePrice0Last = _movingAveragePrice0; _mintFee(lb.pool0, lb.pool1, pool0New, pool1New); _updatePriceCumulative(lb.pool0, lb.pool1); _updateSingleSidedTimelock(_movingAveragePrice0, uint112(pool0New), uint112(pool1New)); // Update Pair last swap price pool0Last = uint112(pool0New); pool1Last = uint112(pool1New); } emit Swap(msg.sender, amountIn0, amountIn1, amountOut0, amountOut1, to); } /** * @inheritdoc IButtonswapPair */ function setMovingAverageWindow(uint32 newMovingAverageWindow) external onlyFactory { movingAverageWindow = newMovingAverageWindow; emit MovingAverageWindowUpdated(newMovingAverageWindow); } /** * @inheritdoc IButtonswapPair */ function setMaxVolatilityBps(uint16 newMaxVolatilityBps) external onlyFactory { maxVolatilityBps = newMaxVolatilityBps; emit MaxVolatilityBpsUpdated(newMaxVolatilityBps); } /** * @inheritdoc IButtonswapPair */ function setMinTimelockDuration(uint32 newMinTimelockDuration) external onlyFactory { singleSidedTimelockDeadline = uint120(Math.max(block.timestamp + uint256(minTimelockDuration), uint256(singleSidedTimelockDeadline))); minTimelockDuration = newMinTimelockDuration; emit MinTimelockDurationUpdated(newMinTimelockDuration); } /** * @inheritdoc IButtonswapPair */ function setMaxTimelockDuration(uint32 newMaxTimelockDuration) external onlyFactory { singleSidedTimelockDeadline = uint120(Math.min(block.timestamp + uint256(newMaxTimelockDuration), uint256(singleSidedTimelockDeadline))); maxTimelockDuration = newMaxTimelockDuration; emit MaxTimelockDurationUpdated(newMaxTimelockDuration); } /** * @inheritdoc IButtonswapPair */ function setMaxSwappableReservoirLimitBps(uint16 newMaxSwappableReservoirLimitBps) external onlyFactory { maxSwappableReservoirLimitBps = newMaxSwappableReservoirLimitBps; emit MaxSwappableReservoirLimitBpsUpdated(newMaxSwappableReservoirLimitBps); } /** * @inheritdoc IButtonswapPair */ function setSwappableReservoirGrowthWindow(uint32 newSwappableReservoirGrowthWindow) external onlyFactory { uint256 oldSwappableReservoirLimitReachesMaxDeadline = uint256(swappableReservoirLimitReachesMaxDeadline); if (oldSwappableReservoirLimitReachesMaxDeadline > block.timestamp) { swappableReservoirLimitReachesMaxDeadline = uint120( block.timestamp + ( uint256(newSwappableReservoirGrowthWindow) * ((oldSwappableReservoirLimitReachesMaxDeadline - block.timestamp)) / uint256(swappableReservoirGrowthWindow) ) ); } swappableReservoirGrowthWindow = newSwappableReservoirGrowthWindow; emit SwappableReservoirGrowthWindowUpdated(newSwappableReservoirGrowthWindow); } }
// 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-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: GPL-3.0-only pragma solidity ^0.8.13; import {IButtonswapERC20} from "./interfaces/IButtonswapERC20/IButtonswapERC20.sol"; contract ButtonswapERC20 is IButtonswapERC20 { /** * @inheritdoc IButtonswapERC20 */ uint8 public constant decimals = 18; /** * @inheritdoc IButtonswapERC20 */ uint256 public totalSupply; /** * @inheritdoc IButtonswapERC20 */ mapping(address => uint256) public balanceOf; /** * @inheritdoc IButtonswapERC20 */ mapping(address => mapping(address => uint256)) public allowance; /** * @inheritdoc IButtonswapERC20 */ bytes32 public immutable DOMAIN_SEPARATOR; /** * @inheritdoc IButtonswapERC20 * @dev Value should equal `0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9` but it * is recommended to verify this by checking the public method on-chain. */ bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); /** * @inheritdoc IButtonswapERC20 */ mapping(address => uint256) public nonces; constructor() { uint256 chainId; assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes("Buttonswap")), keccak256(bytes("1")), chainId, address(this) ) ); } /** * @dev Mints `value` tokens to `to`. * * Emits a {IButtonswapERC20Events-Transfer} event. * @param to The account that is receiving the tokens * @param value The amount of tokens being created */ function _mint(address to, uint256 value) internal { totalSupply = totalSupply + value; balanceOf[to] = balanceOf[to] + value; emit Transfer(address(0), to, value); } /** * @dev Burns `value` tokens from `from`. * * Emits a {IButtonswapERC20Events-Transfer} event. * @param from The account that is sending the tokens * @param value The amount of tokens being destroyed */ function _burn(address from, uint256 value) internal { balanceOf[from] = balanceOf[from] - value; totalSupply = totalSupply - value; emit Transfer(from, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the caller's tokens. * * Emits a {IButtonswapERC20Events-Approval} event. * @param owner The account whose tokens are being approved * @param spender The account that is granted permission to spend the tokens * @param value The amount of tokens that can be spent */ function _approve(address owner, address spender, uint256 value) private { allowance[owner][spender] = value; emit Approval(owner, spender, value); } /** * @dev Moves `value` tokens from `from` to `to`. * * 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 */ function _transfer(address from, address to, uint256 value) internal { balanceOf[from] = balanceOf[from] - value; balanceOf[to] = balanceOf[to] + value; emit Transfer(from, to, value); } /** * @inheritdoc IButtonswapERC20 */ function name() external view virtual override returns (string memory _name) { _name = "Buttonswap"; } /** * @inheritdoc IButtonswapERC20 */ function symbol() external view virtual override returns (string memory _symbol) { _symbol = "BTNSWP"; } /** * @inheritdoc IButtonswapERC20 */ function approve(address spender, uint256 value) external returns (bool success) { _approve(msg.sender, spender, value); success = true; } /** * @inheritdoc IButtonswapERC20 */ function transfer(address to, uint256 value) external returns (bool success) { _transfer(msg.sender, to, value); success = true; } /** * @inheritdoc IButtonswapERC20 */ function transferFrom(address from, address to, uint256 value) external returns (bool success) { uint256 allowanceFromSender = allowance[from][msg.sender]; if (allowanceFromSender != type(uint256).max) { _approve(from, msg.sender, allowanceFromSender - value); } _transfer(from, to, value); success = true; } /** * @inheritdoc IButtonswapERC20 */ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { if (block.timestamp > deadline) { revert PermitExpired(); } bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); if (recoveredAddress == address(0) || recoveredAddress != owner) { revert PermitInvalidSignature(); } _approve(owner, spender, value); } }
// 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: GPL-3.0-only pragma solidity ^0.8.13; import {Math} from "./Math.sol"; library PairMath { /// @dev Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#dual-sided-mint) for more detail. function getDualSidedMintLiquidityOutAmount( uint256 totalLiquidity, uint256 amountInA, uint256 amountInB, uint256 totalA, uint256 totalB ) internal pure returns (uint256 liquidityOut) { liquidityOut = Math.min((totalLiquidity * amountInA) / totalA, (totalLiquidity * amountInB) / totalB); } /// @dev Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail. function getSingleSidedMintLiquidityOutAmountA( uint256 totalLiquidity, uint256 mintAmountA, uint256 totalA, uint256 totalB, uint256 movingAveragePriceA ) internal pure returns (uint256 liquidityOut, uint256 swappedReservoirAmountB) { // movingAveragePriceA is a UQ112x112 and so is a uint224 that needs to be divided by 2^112 after being multiplied. // Here we risk `movingAveragePriceA * (totalA + mintAmountA)` overflowing since we multiple a uint224 by the sum // of two uint112s, however: // - `totalA + mintAmountA` don't exceed 2^112 without violating max pool size. // - 2^256/2^112 = 144 bits spare for movingAveragePriceA // - 2^144/2^112 = 2^32 is the maximum price ratio that can be expressed without overflowing // Is 2^32 sufficient? Consider a pair with 1 WBTC (8 decimals) and 30,000 USDX (18 decimals) // log2((30000*1e18)/1e8) = 48 and as such a greater price ratio that can be handled. // Consequently we require a mulDiv that can handle phantom overflow. uint256 tokenAToSwap = (mintAmountA * totalB) / (Math.mulDiv(movingAveragePriceA, (totalA + mintAmountA), 2 ** 112) + totalB); // Here we don't risk undesired overflow because if `tokenAToSwap * movingAveragePriceA` exceeded 2^256 then it // would necessarily mean `swappedReservoirAmountB` exceeded 2^112, which would result in breaking the poolX uint112 limits. swappedReservoirAmountB = (tokenAToSwap * movingAveragePriceA) / 2 ** 112; // Update totals to account for the fixed price swap totalA += tokenAToSwap; totalB -= swappedReservoirAmountB; uint256 tokenARemaining = mintAmountA - tokenAToSwap; liquidityOut = getDualSidedMintLiquidityOutAmount(totalLiquidity, tokenARemaining, swappedReservoirAmountB, totalA, totalB); } /// @dev Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail. function getSingleSidedMintLiquidityOutAmountB( uint256 totalLiquidity, uint256 mintAmountB, uint256 totalA, uint256 totalB, uint256 movingAveragePriceA ) internal pure returns (uint256 liquidityOut, uint256 swappedReservoirAmountA) { // `movingAveragePriceA` is a UQ112x112 and so is a uint224 that needs to be divided by 2^112 after being multiplied. // Here we need to use the inverse price however, which means we multiply the numerator by 2^112 and then divide that // by movingAveragePriceA to get the result, all without risk of overflow. uint256 tokenBToSwap = (mintAmountB * totalA) / (((2 ** 112 * (totalB + mintAmountB)) / movingAveragePriceA) + totalA); // Inverse price so again we can use it without overflow risk swappedReservoirAmountA = (tokenBToSwap * (2 ** 112)) / movingAveragePriceA; // Update totals to account for the fixed price swap totalA -= swappedReservoirAmountA; totalB += tokenBToSwap; uint256 tokenBRemaining = mintAmountB - tokenBToSwap; liquidityOut = getDualSidedMintLiquidityOutAmount(totalLiquidity, swappedReservoirAmountA, tokenBRemaining, totalA, totalB); } /// @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. function getDualSidedBurnOutputAmounts(uint256 totalLiquidity, uint256 liquidityIn, uint256 totalA, uint256 totalB) internal pure returns (uint256 amountOutA, uint256 amountOutB) { amountOutA = (totalA * liquidityIn) / totalLiquidity; amountOutB = (totalB * liquidityIn) / totalLiquidity; } /// @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. function getSingleSidedBurnOutputAmountA( uint256 totalLiquidity, uint256 liquidityIn, uint256 totalA, uint256 totalB, uint256 movingAveragePriceA ) internal pure returns (uint256 amountOutA, uint256 swappedReservoirAmountA) { // Calculate what the liquidity is worth in terms of both tokens uint256 amountOutB; (amountOutA, amountOutB) = getDualSidedBurnOutputAmounts(totalLiquidity, liquidityIn, totalA, totalB); // Here we need to use the inverse price however, which means we multiply the numerator by 2^112 and then divide that // by movingAveragePriceA to get the result, all without risk of overflow (because amountOutB must be less than 2*2^112) swappedReservoirAmountA = (amountOutB * (2 ** 112)) / movingAveragePriceA; amountOutA = amountOutA + swappedReservoirAmountA; } /// @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. function getSingleSidedBurnOutputAmountB( uint256 totalLiquidity, uint256 liquidityIn, uint256 totalA, uint256 totalB, uint256 movingAveragePriceA ) internal pure returns (uint256 amountOutB, uint256 swappedReservoirAmountB) { // Calculate what the liquidity is worth in terms of both tokens uint256 amountOutA; (amountOutA, amountOutB) = getDualSidedBurnOutputAmounts(totalLiquidity, liquidityIn, totalA, totalB); // Whilst we appear to risk overflow here, the final `swappedReservoirAmountB` needs to be smaller than the reservoir // which soft-caps it at 2^112. // As such, any combination of amountOutA and movingAveragePriceA that would overflow would violate the next // check anyway, and we can therefore safely ignore the overflow potential. swappedReservoirAmountB = (amountOutA * movingAveragePriceA) / 2 ** 112; amountOutB = amountOutB + swappedReservoirAmountB; } /// @dev Refer to [fee-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/fee-math.md) for more detail. function getProtocolFeeLiquidityMinted(uint256 totalLiquidity, uint256 kLast, uint256 k) internal pure returns (uint256 liquidityOut) { uint256 rootKLast = Math.sqrt(kLast); uint256 rootK = Math.sqrt(k); liquidityOut = (totalLiquidity * (rootK - rootKLast)) / ((5 * rootK) + rootKLast); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.13; // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) // range: [0, 2**112 - 1] // resolution: 1 / 2**112 library UQ112x112 { uint224 constant Q112 = 2 ** 112; // encode a uint112 as a UQ112x112 function encode(uint112 y) internal pure returns (uint224 z) { z = uint224(y) * Q112; // never overflows } // divide a UQ112x112 by a uint112, returning a UQ112x112 function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) { z = x / uint224(y); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// 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 {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; 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: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {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 * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// 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 ); }
{ "remappings": [ "forge-std/=null/", "openzeppelin-contracts/=null/", "@openzeppelin-contracts/=null/", "openzeppelin/=null/", "mock-contracts/=null/", "buttonswap-core_@openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "buttonswap-core_forge-std/=lib/forge-std/src/", "buttonswap-core_mock-contracts/=lib/mock-contracts/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "mock-contracts_@openzeppelin-contracts/=lib/mock-contracts/lib/openzeppelin-contracts/contracts/", "mock-contracts_forge-std/=lib/mock-contracts/lib/forge-std/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "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":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"InsufficientInputAmount","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientLiquidityAdded","type":"error"},{"inputs":[],"name":"InsufficientLiquidityBurned","type":"error"},{"inputs":[],"name":"InsufficientLiquidityMinted","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InsufficientReservoir","type":"error"},{"inputs":[],"name":"InvalidFinalPrice","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"KInvariant","type":"error"},{"inputs":[],"name":"Locked","type":"error"},{"inputs":[],"name":"Overflow","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"PermitInvalidSignature","type":"error"},{"inputs":[],"name":"SingleSidedTimelock","type":"error"},{"inputs":[],"name":"SwappableReservoirExceeded","type":"error"},{"inputs":[],"name":"Uninitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newMaxSwappableReservoirLimitBps","type":"uint16"}],"name":"MaxSwappableReservoirLimitBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newMaxTimelockDuration","type":"uint32"}],"name":"MaxTimelockDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newMaxVolatilityBps","type":"uint16"}],"name":"MaxVolatilityBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newMinTimelockDuration","type":"uint32"}],"name":"MinTimelockDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newMovingAverageWindow","type":"uint32"}],"name":"MovingAverageWindowUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1Out","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newSwappableReservoirGrowthWindow","type":"uint32"}],"name":"SwappableReservoirGrowthWindowUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidityIn","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amountOut0","type":"uint256"},{"internalType":"uint256","name":"amountOut1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidityIn","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"burnFromReservoir","outputs":[{"internalType":"uint256","name":"amountOut0","type":"uint256"},{"internalType":"uint256","name":"amountOut1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getIsPaused","outputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityBalances","outputs":[{"internalType":"uint112","name":"_pool0","type":"uint112"},{"internalType":"uint112","name":"_pool1","type":"uint112"},{"internalType":"uint112","name":"_reservoir0","type":"uint112"},{"internalType":"uint112","name":"_reservoir1","type":"uint112"},{"internalType":"uint32","name":"_blockTimestampLast","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSwappableReservoirLimit","outputs":[{"internalType":"uint256","name":"swappableReservoirLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSwappableReservoirLimitBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTimelockDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxVolatilityBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTimelockDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidityOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"mintWithReservoir","outputs":[{"internalType":"uint256","name":"liquidityOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"movingAveragePrice0","outputs":[{"internalType":"uint256","name":"_movingAveragePrice0","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"movingAverageWindow","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"_name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"price0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isPausedNew","type":"bool"}],"name":"setIsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newMaxSwappableReservoirLimitBps","type":"uint16"}],"name":"setMaxSwappableReservoirLimitBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newMaxTimelockDuration","type":"uint32"}],"name":"setMaxTimelockDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newMaxVolatilityBps","type":"uint16"}],"name":"setMaxVolatilityBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newMinTimelockDuration","type":"uint32"}],"name":"setMinTimelockDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newMovingAverageWindow","type":"uint32"}],"name":"setMovingAverageWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newSwappableReservoirGrowthWindow","type":"uint32"}],"name":"setSwappableReservoirGrowthWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"singleSidedTimelockDeadline","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"uint256","name":"amountOut0","type":"uint256"},{"internalType":"uint256","name":"amountOut1","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swappableReservoirGrowthWindow","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swappableReservoirLimitReachesMaxDeadline","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
610100604052600980546001600160f81b0316600160f81b1790553480156200002757600080fd5b50604080518082018252600a8152690427574746f6e737761760b41b6020918201528151808301835260018152603160f81b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f3410c89869cea2edd8d127839826a19c8dfa48f3496e84bc0e7070ad9bdb521d818401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152466080808301919091523060a08084019190915284518084038201815260c0840180875281519190950120909152339081905263340b2ca160e01b9092529151909163340b2ca19160c48083019261010092919082900301816000875af11580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000165919062000261565b6004805465ffffffffffff60701b1916600160801b63ffffffff9384160261ffff60701b191617600160701b61ffff9485160217600160301b600160701b0319166a01000000000000000000009483169490940263ffffffff60301b1916939093176601000000000000948216949094029390931765ffffffffffff1916640100000000949091169390930263ffffffff19169290921792169190911790556001600160a01b0390811660e0521660c05262000309565b80516001600160a01b03811681146200023457600080fd5b919050565b805163ffffffff811681146200023457600080fd5b805161ffff811681146200023457600080fd5b600080600080600080600080610100898b0312156200027f57600080fd5b6200028a896200021c565b97506200029a60208a016200021c565b9650620002aa60408a0162000239565b9550620002ba60608a016200024e565b9450620002ca60808a0162000239565b9350620002da60a08a0162000239565b9250620002ea60c08a016200024e565b9150620002fa60e08a0162000239565b90509295985092959890939650565b60805160a05160c05160e0516141ab620004936000396000818161061d01528181610e91015281816110ae015281816110ec015281816113cb015281816114cb015281816115b601528181611619015281816116df01528181611ad001528181611f47015281816121500152818161266f0152818161270d015281816127ea01528181612a6401528181612d0b0152612dc30152600081816102b401528181610dfb01528181610f4c01528181610f8a015281816113900152818161143501528181611584015281816115e80152818161165401528181611a4201528181611eb10152818161211a015281816125d9015281816126e10152818161274b015281816129d601528181612c750152612d980152600081816105e3015281816106db015281816107850152818161096b01528181610a2601528181610b0701528181610ccf015281816119bc01528181611b9701528181611c3201528181611dc70152818161220a015281816124ef0152612b6a0152600081816103dd01526122cd01526141ab6000f3fe608060405234801561001057600080fd5b50600436106102695760003560e01c806370a0823111610151578063bd3a5c2d116100c3578063dd62ed3e11610087578063dd62ed3e14610652578063e7d3fe6b1461067d578063f2a6f21514610690578063f69bd428146106a5578063f75bc6b5146106bc578063fcd3533c146106c457600080fd5b8063bd3a5c2d146105b6578063c45a0155146105de578063cbbcd1cc14610605578063d21220a714610618578063d505accf1461063f57600080fd5b806395d89b411161011557806395d89b4114610524578063976bf4161461052c578063a9059cbb14610574578063b0c94e9914610587578063b25200ce1461059a578063ba9a7a56146105ad57600080fd5b806370a08231146104a5578063751f66ba146104c55780637d5eea90146104da5780637ecebe00146104f1578063914e6dfb1461051157600080fd5b806326d18b5a116101ea57806343b6b289116101ae57806343b6b289146104285780634ac93bd51461045a578063501ca4881461046d578063562e19df146104805780635909c0d5146104935780635a3d54931461049c57600080fd5b806326d18b5a1461038057806330adf81f14610397578063313ce567146103be5780633644e515146103d857806340a0b2bc146103ff57600080fd5b8063187fba5511610231578063187fba551461031a5780632025070a1461033f57806323b872dd14610347578063240976bf1461035a578063249d13a11461036d57600080fd5b806306fdde031461026e578063095ea7b31461028c5780630dfe1681146102af578063134e8577146102ee57806318160ddd14610303575b600080fd5b6102766106d7565b6040516102839190613cbc565b60405180910390f35b61029f61029a366004613d04565b610764565b6040519015158152602001610283565b6102d67f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610283565b6103016102fc366004613d30565b61077a565b005b61030c60005481565b604051908152602001610283565b60045461032a9063ffffffff1681565b60405163ffffffff9091168152602001610283565b61030c61081a565b61029f610355366004613d54565b610909565b610301610368366004613da3565b610960565b61030161037b366004613dc0565b610a1b565b60045461032a90600160801b900463ffffffff1681565b61030c7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6103c6601281565b60405160ff9091168152602001610283565b61030c7f000000000000000000000000000000000000000000000000000000000000000081565b60045461041590640100000000900461ffff1681565b60405161ffff9091168152602001610283565b60095461044290600160781b90046001600160781b031681565b6040516001600160781b039091168152602001610283565b610301610468366004613dc0565b610afc565b61030c61047b366004613de6565b610c20565b61030161048e366004613e16565b6112fc565b61030c60065481565b61030c60075481565b61030c6104b3366004613e63565b60016020526000908152604090205481565b60045461041590600160701b900461ffff1681565b60045461032a90600160301b900463ffffffff1681565b61030c6104ff366004613e63565b60036020526000908152604090205481565b600954610442906001600160781b031681565b6102766119b8565b610534611a18565b604080516001600160701b039687168152948616602086015292851692840192909252909216606082015263ffffffff909116608082015260a001610283565b61029f610582366004613d04565b611b7f565b610301610595366004613d30565b611b8c565b6103016105a8366004613dc0565b611c27565b61030c6103e881565b6105c96105c4366004613de6565b611d16565b60408051928352602083019190915201610283565b6102d67f000000000000000000000000000000000000000000000000000000000000000081565b610301610613366004613dc0565b6121ff565b6102d67f000000000000000000000000000000000000000000000000000000000000000081565b61030161064d366004613e80565b612290565b61030c610660366004613ef7565b600260209081526000928352604080842090915290825290205481565b61030c61068b366004613f25565b61246c565b600954600160f01b900460ff1660011461029f565b60045461032a90600160501b900463ffffffff1681565b61030c6129b4565b6105c96106d2366004613de6565b612b11565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636c02a9316040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261075f9190810190613f74565b905090565b6000610771338484612e58565b50600192915050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107c357604051631dd2188d60e31b815260040160405180910390fd5b6004805461ffff60701b1916600160701b61ffff8416908102919091179091556040519081527f5132865a5f3289cd175a05a50735aafb1a7e1bbbfb0c269dae3985c1eb93c893906020015b60405180910390a150565b60008061082c6401000000004261402c565b60055490915063ffffffff600160e01b820416820390600090610876906001600160701b038082169161086791600160701b90910416612eba565b6001600160e01b031690612ed9565b6001600160e01b031690508163ffffffff16600003610899576008549350610903565b60045463ffffffff908116908316106108b457809350610903565b60045463ffffffff9081169081906108ce90851684614056565b6108d8858461406d565b63ffffffff166008546108eb9190614056565b6108f59190614091565b6108ff91906140a4565b9450505b50505090565b6001600160a01b0383166000908152600260209081526040808320338452909152812054600019811461094a5761094a853361094586856140b8565b612e58565b610955858585612ef5565b506001949350505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109a957604051631dd2188d60e31b815260040160405180910390fd5b80156109c5576009805460ff60f01b1916600160f01b17905550565b6004546109df90600160501b900463ffffffff1642614091565b600980547fff00ffffffffffffffffffffffffffffff000000000000000000000000000000166001600160781b03929092169190911790555b50565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a6457604051631dd2188d60e31b815260040160405180910390fd5b610a88610a7763ffffffff831642614091565b6009546001600160781b0316612f9d565b600980546001600160781b0319166001600160781b03929092169190911790556004805463ffffffff60501b1916600160501b63ffffffff8416908102919091179091556040519081527fb40c7a0f3fe456f123e9206acea9eb8b229f25ef0b7c28dcde5683f75a1f70659060200161080f565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b4557604051631dd2188d60e31b815260040160405180910390fd5b600954600160781b90046001600160781b031642811115610bc557600454600160801b900463ffffffff16610b7a42836140b8565b610b8a9063ffffffff8516614056565b610b9491906140a4565b610b9e9042614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b6004805463ffffffff60801b1916600160801b63ffffffff8516908102919091179091556040519081527f35c743ab7de62a674476813485ff91b6930daf72ea8c37e41fe2f747d36298cb9060200160405180910390a15050565b600954600090600160f81b900460ff168103610c4f576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103610c8a576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015610cb6576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415610d9d5760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f91906140cb565b90506001600160a01b03811615610d815730600081815260016020526040902054610d7c91908390612ef5565b610d9b565b30600081815260016020526040902054610d9b9190612fb3565b505b82600003610dbe576040516347c3655560e01b815260040160405180910390fd5b6000805490819003610de35760405163071cbeb560e21b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6e91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610ed8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efc91906140e8565b90506000610f0a8383613047565b80519091501580610f1d57506020810151155b15610f3b5760405163bb55fd2760e01b815260040160405180910390fd5b80604001516000036110a957610f737f000000000000000000000000000000000000000000000000000000000000000033308a613192565b6040516370a0823160e01b815230600482015283907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610fd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffd91906140e8565b61100791906140b8565b9650600061101e6110188986614091565b84613047565b60408101519091501561104457604051630e7a760760e41b815260040160405180910390fd5b600061105a868a878761105561081a565b613203565b602085015191985091506000906110709061329c565b9050808211156110935760405163bacea60f60e01b815260040160405180910390fd5b6110a1846020015183613342565b505050611202565b6110d57f000000000000000000000000000000000000000000000000000000000000000033308a613192565b6040516370a0823160e01b815230600482015282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561113b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115f91906140e8565b61116991906140b8565b965060006111808461117b8a86614091565b613047565b6060810151909150156111a657604051630e7a760760e41b815260040160405180910390fd5b60006111bc868a87876111b761081a565b61343d565b845191985091506000906111cf9061329c565b9050808211156111f25760405163bacea60f60e01b815260040160405180910390fd5b83516111fe9083613342565b5050505b8460000361122357604051633489be7560e21b815260040160405180910390fd5b61122d86866134d3565b806040015160000361128c5760408051888152600060208201529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a36112dc565b6040805160008152602081018990529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35b5050600980546001600160f81b0316600160f81b17905550909392505050565b600954600160f81b900460ff16600003611329576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611364576040516313d0ff5960e31b815260040160405180910390fd5b82158015611370575081155b1561138e576040516342301c2360e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614806113ff57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b1561141d57604051634e46966960e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611484573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611512573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153691906140e8565b905060006115448383613047565b80519091508610158061155b575080602001518510155b156115795760405163bb55fd2760e01b815260040160405180910390fd5b87156115ab576115ab7f000000000000000000000000000000000000000000000000000000000000000033308b613192565b86156115dd576115dd7f000000000000000000000000000000000000000000000000000000000000000033308a613192565b851561160e5761160e7f00000000000000000000000000000000000000000000000000000000000000008588613558565b841561163f5761163f7f00000000000000000000000000000000000000000000000000000000000000008587613558565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156116a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c791906140e8565b6040516370a0823160e01b81523060048201529093507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561172e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175291906140e8565b9150600081604001518461176691906140b8565b9050600082606001518461177a91906140b8565b9050811580611787575080155b156117a5576040516323da237d60e01b815260040160405180910390fd5b82518211156117c55782516117ba90836140b8565b9950600097506117d9565b825160009a506117d69083906140b8565b97505b82602001518111156117ff5760208301516117f490826140b8565b985060009650611816565b6000985080836020015161181391906140b8565b96505b89158015611822575088155b156118405760405163098fb56160e01b815260040160405180910390fd5b600061184d8b6003614056565b611859846103e8614056565b61186391906140b8565b905060006118728b6003614056565b61187e846103e8614056565b61188891906140b8565b6020860151865191925061189b91614056565b6118a890620f4240614056565b6118b28284614056565b10156118d15760405163bf6056e760e01b815260040160405180910390fd5b60006118db61081a565b6008819055865160208801519192506118f591878761358d565b611907866000015187602001516135c0565b611912818686613692565b5050600580546001600160701b039485166001600160e01b031990911617600160701b939094169290920292909217905550506040805188815260208101889052908101869052606081018590526001600160a01b03841692503391507fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a35050600980546001600160f81b0316600160f81b179055505050565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637b61c3206040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b6040516370a0823160e01b8152306004820152600090819081908190819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aad91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3b91906140e8565b90506000611b498383613047565b805160208201516040830151606090930151600554929c919b50929950919750600160e01b900463ffffffff1695509350505050565b6000610771338484612ef5565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bd557604051631dd2188d60e31b815260040160405180910390fd5b6004805465ffff00000000191664010000000061ffff8416908102919091179091556040519081527fc9e0eeef32bc2c2d7bf9a6830def15f44953dd73a3229bd87a87b12bed187b8d9060200161080f565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611c7057604051631dd2188d60e31b815260040160405180910390fd5b600454611c9f90611c8e90600160301b900463ffffffff1642614091565b6009546001600160781b03166137ab565b600980546001600160781b0319166001600160781b03929092169190911790556004805469ffffffff0000000000001916600160301b63ffffffff8416908102919091179091556040519081527ffdc6d94bf7b4eaa8656351b49ed4d2aa06e1d903bf053c9a8e17b8c8cff415469060200161080f565b6009546000908190600160f81b900460ff168103611d47576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611d82576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015611dae576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415611e955760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4791906140cb565b90506001600160a01b03811615611e795730600081815260016020526040902054611e7491908390612ef5565b611e93565b30600081815260016020526040902054611e939190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611f00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2491906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611f8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb291906140e8565b90506000611fc08383613047565b80519091501580611fd357506020810151155b15611ff15760405163bb55fd2760e01b815260040160405180910390fd5b8060400151600003612084576000612013858a868661200e61081a565b6137ba565b6060840151919750915086111561203d57604051630e7a760760e41b815260040160405180910390fd5b600061204c836020015161329c565b90508082111561206f5760405163bacea60f60e01b815260040160405180910390fd5b61207d836020015183613342565b5050612105565b600061209a858a868661209561081a565b613801565b604084015191985091508711156120c457604051630e7a760760e41b815260040160405180910390fd5b60006120d3836000015161329c565b9050808211156120f65760405163bacea60f60e01b815260040160405180910390fd5b82516121029083613342565b50505b61210f3389612fb3565b8515612145576121407f00000000000000000000000000000000000000000000000000000000000000008888613558565b61218f565b8415612176576121407f00000000000000000000000000000000000000000000000000000000000000008887613558565b60405163749383ad60e01b815260040160405180910390fd5b60408051898152602081018890529081018690526001600160a01b0388169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b179055509194909350915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461224857604051631dd2188d60e31b815260040160405180910390fd5b6004805463ffffffff191663ffffffff83169081179091556040519081527ff905287cbb43751efa1a33cb1f6b357b3b5c574bc270934153d2c96ff5dfb7ef9060200161080f565b834211156122b15760405163068568f360e21b815260040160405180910390fd5b6001600160a01b038716600090815260036020526040812080547f0000000000000000000000000000000000000000000000000000000000000000917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b918761231f83614101565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016040516020818303038152906040528051906020012060405160200161239892919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612403573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615806124385750886001600160a01b0316816001600160a01b031614155b156124565760405163b6ea5e6160e01b815260040160405180910390fd5b612461898989612e58565b505050505050505050565b600954600090600160f81b900460ff16810361249b576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff166001036124d6576040516313d0ff5960e31b815260040160405180910390fd5b30600090815260016020526040902054156125bd5760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa15801561254b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256f91906140cb565b90506001600160a01b038116156125a1573060008181526001602052604090205461259c91908390612ef5565b6125bb565b306000818152600160205260409020546125bb9190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612628573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264c91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156126b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126da91906140e8565b90506127087f000000000000000000000000000000000000000000000000000000000000000033308a613192565b6127347f0000000000000000000000000000000000000000000000000000000000000000333089613192565b6040516370a0823160e01b815230600482015282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561279a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127be91906140e8565b6127c891906140b8565b6040516370a0823160e01b815230600482015290975081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285591906140e8565b61285f91906140b8565b95508260000361290c576103e861287e612879888a614056565b613826565b61288891906140b8565b935061289760006103e86134d3565b600580546001600160701b03888116600160701b026001600160e01b0319909216908a16171790556128ce6401000000004261402c565b6005601c6101000a81548163ffffffff021916908363ffffffff1602179055506128fb8761086788612eba565b6001600160e01b031660085561291c565b61291983888885856138ca565b93505b8360000361293d57604051633489be7560e21b815260040160405180910390fd5b61294785856134d3565b60408051888152602081018890529081018590526001600160a01b0386169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35050600980546001600160f81b0316600160f81b179055509392505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a4191906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acf91906140e8565b90506000612add8383613047565b604081015190915015612afc578051612af59061329c565b9350610903565b612b09816020015161329c565b935050505090565b6009546000908190600160f81b900460ff168103612b42576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690553060009081526001602052604090205415612c385760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bea91906140cb565b90506001600160a01b03811615612c1c5730600081815260016020526040902054612c1791908390612ef5565b612c36565b30600081815260016020526040902054612c369190612fb3565b505b83600003612c595760405163749383ad60e01b815260040160405180910390fd5b600080546040516370a0823160e01b81523060048201529091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612cc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7691906140e8565b9050612d8483888484613908565b9095509350612d933388612fb3565b612dbe7f00000000000000000000000000000000000000000000000000000000000000008787613558565b612de97f00000000000000000000000000000000000000000000000000000000000000008786613558565b60408051888152602081018790529081018590526001600160a01b0387169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b1790555090939092509050565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000612ed3600160701b6001600160701b03841661411a565b92915050565b6000612eee6001600160701b0383168461414c565b9392505050565b6001600160a01b038316600090815260016020526040902054612f199082906140b8565b6001600160a01b038085166000908152600160205260408082209390935590841681522054612f49908290614091565b6001600160a01b0380841660008181526001602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612ead9085815260200190565b6000818310612fac5781612eee565b5090919050565b6001600160a01b038216600090815260016020526040902054612fd79082906140b8565b6001600160a01b03831660009081526001602052604081209190915554612fff9082906140b8565b60009081556040518281526001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6130726040518060800160405280600081526020016000815260200160008152602001600081525090565b6005546001600160701b0380821691600160701b900416811580613094575080155b61318a578415806130a3575083155b61318a576130b18285614056565b6130bb8287614056565b101561310957848352816130cf8287614056565b6130d991906140a4565b6020840181905283516130ee91908385613942565b602084018190526130ff90856140b8565b606084015261314c565b602083018490528061311b8386614056565b61312591906140a4565b808452602084015161313991908484613942565b80845261314690866140b8565b60408401525b82516001600160701b03108061316c575060208301516001600160701b03105b1561318a57604051631a93c68960e11b815260040160405180910390fd5b505092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526131fd9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261399e565b50505050565b6000808084613220856132168a8a614091565b600160701b613a78565b61322a9190614091565b6132348689614056565b61323e91906140a4565b9050600160701b61324f8583614056565b61325991906140a4565b91506132658187614091565b955061327182866140b8565b9450600061327f82896140b8565b905061328e8982858a8a6138ca565b935050509550959350505050565b6004546000908190612710906132bd90600160701b900461ffff1685614056565b6132c791906140a4565b600954909150600160781b90046001600160781b03164281111561333757600454600160801b900463ffffffff16600061330142846140b8565b6133119063ffffffff84166140b8565b905063ffffffff82166133248286614056565b61332e91906140a4565b9450505061333b565b8192505b5050919050565b6004546000906127109061336190600160701b900461ffff1685614056565b61336b91906140a4565b9050600081156133a3576004548290613392908590600160801b900463ffffffff16614056565b61339c91906140a4565b90506133b5565b50600454600160801b900463ffffffff165b600954600160781b90046001600160781b031642811115613405576133da8282614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b03160217905550613436565b61340f8242614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b5050505050565b60008080858461344d8988614091565b61345b90600160701b614056565b61346591906140a4565b61346f9190614091565b6134798789614056565b61348391906140a4565b90508361349482600160701b614056565b61349e91906140a4565b91506134aa82876140b8565b95506134b68186614091565b945060006134c482896140b8565b905061328e8984838a8a6138ca565b806000546134e19190614091565b60009081556001600160a01b038316815260016020526040902054613507908290614091565b6001600160a01b0383166000818152600160205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061303b9085815260200190565b6040516001600160a01b03831660248201526044810182905261358890849063a9059cbb60e01b906064016131c6565b505050565b600080546135ae9061359f8688614056565b6135a98587614056565b613b26565b905080156134365761343630826134d3565b818160006135d36401000000004261402c565b60055490915063ffffffff600160e01b90910481168203908116158015906135fa57508515155b801561360557508415155b1561368a57836001600160701b03168163ffffffff16607087901b028161362e5761362e614016565b600680549290910490910190556001600160701b03831663ffffffff8216607088901b028161365f5761365f614016565b60078054929091049091019055600580546001600160e01b0316600160e01b63ffffffff8516021790555b505050505050565b60006136a18361086784612eba565b6001600160e01b031690506000848211156136c7576136c085836140b8565b90506136d4565b6136d182866140b8565b90505b60045463ffffffff600160301b8204811691600160501b81049091169060009061375b9061370f9061ffff640100000000909104168a614056565b613719858561406d565b63ffffffff1661372b61271088614056565b6137359190614056565b61373f91906140a4565b61374f9063ffffffff8616614091565b8363ffffffff16612f9d565b905060006137698242614091565b6009549091506001600160781b03908116908216111561246157600980546001600160781b0383166001600160781b0319909116179055505050505050505050565b6000818311612fac5781612eee565b60008060006137cb88888888613908565b93509050600160701b6137de8583614056565b6137e891906140a4565b91506137f48284614091565b9250509550959350505050565b600080600061381288888888613908565b9093509050836137de82600160701b614056565b60b581600160881b811061383f5760409190911b9060801c5b6901000000000000000000811061385b5760209190911b9060401c5b6501000000000081106138735760109190911b9060201c5b630100000081106138895760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b60006138fe836138da8789614056565b6138e491906140a4565b836138ef878a614056565b6138f991906140a4565b612f9d565b9695505050505050565b600080856139168686614056565b61392091906140a4565b91508561392d8685614056565b61393791906140a4565b905094509492505050565b600083613950846002614056565b61395a9190614056565b8280613967886002614056565b6139719190614056565b61397b9190614091565b10156139935761398c856001614091565b9050613996565b50835b949350505050565b60006139f3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b759092919063ffffffff16565b9050805160001480613a14575080806020019051810190613a149190614172565b6135885760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b6000808060001985870985870292508281108382030391505080600003613ab257838281613aa857613aa8614016565b0492505050612eee565b808411613abe57600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b600080613b3284613826565b90506000613b3f84613826565b905081613b4d826005614056565b613b579190614091565b613b6183836140b8565b613b6b9088614056565b6138fe91906140a4565b6060613996848460008585600080866001600160a01b03168587604051613b9c919061418f565b60006040518083038185875af1925050503d8060008114613bd9576040519150601f19603f3d011682016040523d82523d6000602084013e613bde565b606091505b5091509150613bef87838387613bfa565b979650505050505050565b60608315613c69578251600003613c62576001600160a01b0385163b613c625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401613a6f565b5081613996565b6139968383815115613c7e5781518083602001fd5b8060405162461bcd60e51b8152600401613a6f9190613cbc565b60005b83811015613cb3578181015183820152602001613c9b565b50506000910152565b6020815260008251806020840152613cdb816040850160208701613c98565b601f01601f19169190910160400192915050565b6001600160a01b0381168114610a1857600080fd5b60008060408385031215613d1757600080fd5b8235613d2281613cef565b946020939093013593505050565b600060208284031215613d4257600080fd5b813561ffff81168114612eee57600080fd5b600080600060608486031215613d6957600080fd5b8335613d7481613cef565b92506020840135613d8481613cef565b929592945050506040919091013590565b8015158114610a1857600080fd5b600060208284031215613db557600080fd5b8135612eee81613d95565b600060208284031215613dd257600080fd5b813563ffffffff81168114612eee57600080fd5b60008060408385031215613df957600080fd5b823591506020830135613e0b81613cef565b809150509250929050565b600080600080600060a08688031215613e2e57600080fd5b853594506020860135935060408601359250606086013591506080860135613e5581613cef565b809150509295509295909350565b600060208284031215613e7557600080fd5b8135612eee81613cef565b600080600080600080600060e0888a031215613e9b57600080fd5b8735613ea681613cef565b96506020880135613eb681613cef565b95506040880135945060608801359350608088013560ff81168114613eda57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215613f0a57600080fd5b8235613f1581613cef565b91506020830135613e0b81613cef565b600080600060608486031215613f3a57600080fd5b83359250602084013591506040840135613f5381613cef565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600060208284031215613f8657600080fd5b815167ffffffffffffffff80821115613f9e57600080fd5b818401915084601f830112613fb257600080fd5b815181811115613fc457613fc4613f5e565b604051601f8201601f19908116603f01168101908382118183101715613fec57613fec613f5e565b8160405282815287602084870101111561400557600080fd5b613bef836020830160208801613c98565b634e487b7160e01b600052601260045260246000fd5b60008261403b5761403b614016565b500690565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417612ed357612ed3614040565b63ffffffff82811682821603908082111561408a5761408a614040565b5092915050565b80820180821115612ed357612ed3614040565b6000826140b3576140b3614016565b500490565b81810381811115612ed357612ed3614040565b6000602082840312156140dd57600080fd5b8151612eee81613cef565b6000602082840312156140fa57600080fd5b5051919050565b60006001820161411357614113614040565b5060010190565b6001600160e01b0382811682821681810283169291811582850482141761414357614143614040565b50505092915050565b60006001600160e01b038381168061416657614166614016565b92169190910492915050565b60006020828403121561418457600080fd5b8151612eee81613d95565b600082516141a1818460208701613c98565b919091019291505056
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102695760003560e01c806370a0823111610151578063bd3a5c2d116100c3578063dd62ed3e11610087578063dd62ed3e14610652578063e7d3fe6b1461067d578063f2a6f21514610690578063f69bd428146106a5578063f75bc6b5146106bc578063fcd3533c146106c457600080fd5b8063bd3a5c2d146105b6578063c45a0155146105de578063cbbcd1cc14610605578063d21220a714610618578063d505accf1461063f57600080fd5b806395d89b411161011557806395d89b4114610524578063976bf4161461052c578063a9059cbb14610574578063b0c94e9914610587578063b25200ce1461059a578063ba9a7a56146105ad57600080fd5b806370a08231146104a5578063751f66ba146104c55780637d5eea90146104da5780637ecebe00146104f1578063914e6dfb1461051157600080fd5b806326d18b5a116101ea57806343b6b289116101ae57806343b6b289146104285780634ac93bd51461045a578063501ca4881461046d578063562e19df146104805780635909c0d5146104935780635a3d54931461049c57600080fd5b806326d18b5a1461038057806330adf81f14610397578063313ce567146103be5780633644e515146103d857806340a0b2bc146103ff57600080fd5b8063187fba5511610231578063187fba551461031a5780632025070a1461033f57806323b872dd14610347578063240976bf1461035a578063249d13a11461036d57600080fd5b806306fdde031461026e578063095ea7b31461028c5780630dfe1681146102af578063134e8577146102ee57806318160ddd14610303575b600080fd5b6102766106d7565b6040516102839190613cbc565b60405180910390f35b61029f61029a366004613d04565b610764565b6040519015158152602001610283565b6102d67f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe81565b6040516001600160a01b039091168152602001610283565b6103016102fc366004613d30565b61077a565b005b61030c60005481565b604051908152602001610283565b60045461032a9063ffffffff1681565b60405163ffffffff9091168152602001610283565b61030c61081a565b61029f610355366004613d54565b610909565b610301610368366004613da3565b610960565b61030161037b366004613dc0565b610a1b565b60045461032a90600160801b900463ffffffff1681565b61030c7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6103c6601281565b60405160ff9091168152602001610283565b61030c7f62732c8b657c622f8f8c0e3993169125026a519229c7f6bdeb367cb58083ab1e81565b60045461041590640100000000900461ffff1681565b60405161ffff9091168152602001610283565b60095461044290600160781b90046001600160781b031681565b6040516001600160781b039091168152602001610283565b610301610468366004613dc0565b610afc565b61030c61047b366004613de6565b610c20565b61030161048e366004613e16565b6112fc565b61030c60065481565b61030c60075481565b61030c6104b3366004613e63565b60016020526000908152604090205481565b60045461041590600160701b900461ffff1681565b60045461032a90600160301b900463ffffffff1681565b61030c6104ff366004613e63565b60036020526000908152604090205481565b600954610442906001600160781b031681565b6102766119b8565b610534611a18565b604080516001600160701b039687168152948616602086015292851692840192909252909216606082015263ffffffff909116608082015260a001610283565b61029f610582366004613d04565b611b7f565b610301610595366004613d30565b611b8c565b6103016105a8366004613dc0565b611c27565b61030c6103e881565b6105c96105c4366004613de6565b611d16565b60408051928352602083019190915201610283565b6102d67f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5081565b610301610613366004613dc0565b6121ff565b6102d67f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16181565b61030161064d366004613e80565b612290565b61030c610660366004613ef7565b600260209081526000928352604080842090915290825290205481565b61030c61068b366004613f25565b61246c565b600954600160f01b900460ff1660011461029f565b60045461032a90600160501b900463ffffffff1681565b61030c6129b4565b6105c96106d2366004613de6565b612b11565b60607f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b0316636c02a9316040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261075f9190810190613f74565b905090565b6000610771338484612e58565b50600192915050565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5016146107c357604051631dd2188d60e31b815260040160405180910390fd5b6004805461ffff60701b1916600160701b61ffff8416908102919091179091556040519081527f5132865a5f3289cd175a05a50735aafb1a7e1bbbfb0c269dae3985c1eb93c893906020015b60405180910390a150565b60008061082c6401000000004261402c565b60055490915063ffffffff600160e01b820416820390600090610876906001600160701b038082169161086791600160701b90910416612eba565b6001600160e01b031690612ed9565b6001600160e01b031690508163ffffffff16600003610899576008549350610903565b60045463ffffffff908116908316106108b457809350610903565b60045463ffffffff9081169081906108ce90851684614056565b6108d8858461406d565b63ffffffff166008546108eb9190614056565b6108f59190614091565b6108ff91906140a4565b9450505b50505090565b6001600160a01b0383166000908152600260209081526040808320338452909152812054600019811461094a5761094a853361094586856140b8565b612e58565b610955858585612ef5565b506001949350505050565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5016146109a957604051631dd2188d60e31b815260040160405180910390fd5b80156109c5576009805460ff60f01b1916600160f01b17905550565b6004546109df90600160501b900463ffffffff1642614091565b600980547fff00ffffffffffffffffffffffffffffff000000000000000000000000000000166001600160781b03929092169190911790555b50565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614610a6457604051631dd2188d60e31b815260040160405180910390fd5b610a88610a7763ffffffff831642614091565b6009546001600160781b0316612f9d565b600980546001600160781b0319166001600160781b03929092169190911790556004805463ffffffff60501b1916600160501b63ffffffff8416908102919091179091556040519081527fb40c7a0f3fe456f123e9206acea9eb8b229f25ef0b7c28dcde5683f75a1f70659060200161080f565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614610b4557604051631dd2188d60e31b815260040160405180910390fd5b600954600160781b90046001600160781b031642811115610bc557600454600160801b900463ffffffff16610b7a42836140b8565b610b8a9063ffffffff8516614056565b610b9491906140a4565b610b9e9042614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b6004805463ffffffff60801b1916600160801b63ffffffff8516908102919091179091556040519081527f35c743ab7de62a674476813485ff91b6930daf72ea8c37e41fe2f747d36298cb9060200160405180910390a15050565b600954600090600160f81b900460ff168103610c4f576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103610c8a576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015610cb6576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415610d9d5760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f91906140cb565b90506001600160a01b03811615610d815730600081815260016020526040902054610d7c91908390612ef5565b610d9b565b30600081815260016020526040902054610d9b9190612fb3565b505b82600003610dbe576040516347c3655560e01b815260040160405180910390fd5b6000805490819003610de35760405163071cbeb560e21b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6e91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015610ed8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efc91906140e8565b90506000610f0a8383613047565b80519091501580610f1d57506020810151155b15610f3b5760405163bb55fd2760e01b815260040160405180910390fd5b80604001516000036110a957610f737f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe33308a613192565b6040516370a0823160e01b815230600482015283907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015610fd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffd91906140e8565b61100791906140b8565b9650600061101e6110188986614091565b84613047565b60408101519091501561104457604051630e7a760760e41b815260040160405180910390fd5b600061105a868a878761105561081a565b613203565b602085015191985091506000906110709061329c565b9050808211156110935760405163bacea60f60e01b815260040160405180910390fd5b6110a1846020015183613342565b505050611202565b6110d57f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16133308a613192565b6040516370a0823160e01b815230600482015282907f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1616001600160a01b0316906370a0823190602401602060405180830381865afa15801561113b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115f91906140e8565b61116991906140b8565b965060006111808461117b8a86614091565b613047565b6060810151909150156111a657604051630e7a760760e41b815260040160405180910390fd5b60006111bc868a87876111b761081a565b61343d565b845191985091506000906111cf9061329c565b9050808211156111f25760405163bacea60f60e01b815260040160405180910390fd5b83516111fe9083613342565b5050505b8460000361122357604051633489be7560e21b815260040160405180910390fd5b61122d86866134d3565b806040015160000361128c5760408051888152600060208201529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a36112dc565b6040805160008152602081018990529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35b5050600980546001600160f81b0316600160f81b17905550909392505050565b600954600160f81b900460ff16600003611329576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611364576040516313d0ff5960e31b815260040160405180910390fd5b82158015611370575081155b1561138e576040516342301c2360e01b815260040160405180910390fd5b7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316816001600160a01b031614806113ff57507f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1616001600160a01b0316816001600160a01b0316145b1561141d57604051634e46966960e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015611484573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015611512573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153691906140e8565b905060006115448383613047565b80519091508610158061155b575080602001518510155b156115795760405163bb55fd2760e01b815260040160405180910390fd5b87156115ab576115ab7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe33308b613192565b86156115dd576115dd7f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16133308a613192565b851561160e5761160e7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe8588613558565b841561163f5761163f7f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1618587613558565b6040516370a0823160e01b81523060048201527f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa1580156116a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c791906140e8565b6040516370a0823160e01b81523060048201529093507f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1616001600160a01b0316906370a0823190602401602060405180830381865afa15801561172e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175291906140e8565b9150600081604001518461176691906140b8565b9050600082606001518461177a91906140b8565b9050811580611787575080155b156117a5576040516323da237d60e01b815260040160405180910390fd5b82518211156117c55782516117ba90836140b8565b9950600097506117d9565b825160009a506117d69083906140b8565b97505b82602001518111156117ff5760208301516117f490826140b8565b985060009650611816565b6000985080836020015161181391906140b8565b96505b89158015611822575088155b156118405760405163098fb56160e01b815260040160405180910390fd5b600061184d8b6003614056565b611859846103e8614056565b61186391906140b8565b905060006118728b6003614056565b61187e846103e8614056565b61188891906140b8565b6020860151865191925061189b91614056565b6118a890620f4240614056565b6118b28284614056565b10156118d15760405163bf6056e760e01b815260040160405180910390fd5b60006118db61081a565b6008819055865160208801519192506118f591878761358d565b611907866000015187602001516135c0565b611912818686613692565b5050600580546001600160701b039485166001600160e01b031990911617600160701b939094169290920292909217905550506040805188815260208101889052908101869052606081018590526001600160a01b03841692503391507fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a35050600980546001600160f81b0316600160f81b179055505050565b60607f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b0316637b61c3206040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b6040516370a0823160e01b8152306004820152600090819081908190819081906001600160a01b037f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe16906370a0823190602401602060405180830381865afa158015611a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aad91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015611b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3b91906140e8565b90506000611b498383613047565b805160208201516040830151606090930151600554929c919b50929950919750600160e01b900463ffffffff1695509350505050565b6000610771338484612ef5565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614611bd557604051631dd2188d60e31b815260040160405180910390fd5b6004805465ffff00000000191664010000000061ffff8416908102919091179091556040519081527fc9e0eeef32bc2c2d7bf9a6830def15f44953dd73a3229bd87a87b12bed187b8d9060200161080f565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614611c7057604051631dd2188d60e31b815260040160405180910390fd5b600454611c9f90611c8e90600160301b900463ffffffff1642614091565b6009546001600160781b03166137ab565b600980546001600160781b0319166001600160781b03929092169190911790556004805469ffffffff0000000000001916600160301b63ffffffff8416908102919091179091556040519081527ffdc6d94bf7b4eaa8656351b49ed4d2aa06e1d903bf053c9a8e17b8c8cff415469060200161080f565b6009546000908190600160f81b900460ff168103611d47576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611d82576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015611dae576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415611e955760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4791906140cb565b90506001600160a01b03811615611e795730600081815260016020526040902054611e7491908390612ef5565b611e93565b30600081815260016020526040902054611e939190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015611f00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2491906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015611f8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb291906140e8565b90506000611fc08383613047565b80519091501580611fd357506020810151155b15611ff15760405163bb55fd2760e01b815260040160405180910390fd5b8060400151600003612084576000612013858a868661200e61081a565b6137ba565b6060840151919750915086111561203d57604051630e7a760760e41b815260040160405180910390fd5b600061204c836020015161329c565b90508082111561206f5760405163bacea60f60e01b815260040160405180910390fd5b61207d836020015183613342565b5050612105565b600061209a858a868661209561081a565b613801565b604084015191985091508711156120c457604051630e7a760760e41b815260040160405180910390fd5b60006120d3836000015161329c565b9050808211156120f65760405163bacea60f60e01b815260040160405180910390fd5b82516121029083613342565b50505b61210f3389612fb3565b8515612145576121407f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe8888613558565b61218f565b8415612176576121407f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1618887613558565b60405163749383ad60e01b815260040160405180910390fd5b60408051898152602081018890529081018690526001600160a01b0388169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b179055509194909350915050565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50161461224857604051631dd2188d60e31b815260040160405180910390fd5b6004805463ffffffff191663ffffffff83169081179091556040519081527ff905287cbb43751efa1a33cb1f6b357b3b5c574bc270934153d2c96ff5dfb7ef9060200161080f565b834211156122b15760405163068568f360e21b815260040160405180910390fd5b6001600160a01b038716600090815260036020526040812080547f62732c8b657c622f8f8c0e3993169125026a519229c7f6bdeb367cb58083ab1e917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b918761231f83614101565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016040516020818303038152906040528051906020012060405160200161239892919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612403573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615806124385750886001600160a01b0316816001600160a01b031614155b156124565760405163b6ea5e6160e01b815260040160405180910390fd5b612461898989612e58565b505050505050505050565b600954600090600160f81b900460ff16810361249b576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff166001036124d6576040516313d0ff5960e31b815260040160405180910390fd5b30600090815260016020526040902054156125bd5760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa15801561254b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256f91906140cb565b90506001600160a01b038116156125a1573060008181526001602052604090205461259c91908390612ef5565b6125bb565b306000818152600160205260409020546125bb9190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015612628573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264c91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa1580156126b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126da91906140e8565b90506127087f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe33308a613192565b6127347f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a161333089613192565b6040516370a0823160e01b815230600482015282907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa15801561279a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127be91906140e8565b6127c891906140b8565b6040516370a0823160e01b815230600482015290975081906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015612831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285591906140e8565b61285f91906140b8565b95508260000361290c576103e861287e612879888a614056565b613826565b61288891906140b8565b935061289760006103e86134d3565b600580546001600160701b03888116600160701b026001600160e01b0319909216908a16171790556128ce6401000000004261402c565b6005601c6101000a81548163ffffffff021916908363ffffffff1602179055506128fb8761086788612eba565b6001600160e01b031660085561291c565b61291983888885856138ca565b93505b8360000361293d57604051633489be7560e21b815260040160405180910390fd5b61294785856134d3565b60408051888152602081018890529081018590526001600160a01b0386169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35050600980546001600160f81b0316600160f81b179055509392505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe16906370a0823190602401602060405180830381865afa158015612a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a4191906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015612aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acf91906140e8565b90506000612add8383613047565b604081015190915015612afc578051612af59061329c565b9350610903565b612b09816020015161329c565b935050505090565b6009546000908190600160f81b900460ff168103612b42576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690553060009081526001602052604090205415612c385760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bea91906140cb565b90506001600160a01b03811615612c1c5730600081815260016020526040902054612c1791908390612ef5565b612c36565b30600081815260016020526040902054612c369190612fb3565b505b83600003612c595760405163749383ad60e01b815260040160405180910390fd5b600080546040516370a0823160e01b81523060048201529091907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015612cc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015612d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7691906140e8565b9050612d8483888484613908565b9095509350612d933388612fb3565b612dbe7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe8787613558565b612de97f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1618786613558565b60408051888152602081018790529081018590526001600160a01b0387169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b1790555090939092509050565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000612ed3600160701b6001600160701b03841661411a565b92915050565b6000612eee6001600160701b0383168461414c565b9392505050565b6001600160a01b038316600090815260016020526040902054612f199082906140b8565b6001600160a01b038085166000908152600160205260408082209390935590841681522054612f49908290614091565b6001600160a01b0380841660008181526001602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612ead9085815260200190565b6000818310612fac5781612eee565b5090919050565b6001600160a01b038216600090815260016020526040902054612fd79082906140b8565b6001600160a01b03831660009081526001602052604081209190915554612fff9082906140b8565b60009081556040518281526001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6130726040518060800160405280600081526020016000815260200160008152602001600081525090565b6005546001600160701b0380821691600160701b900416811580613094575080155b61318a578415806130a3575083155b61318a576130b18285614056565b6130bb8287614056565b101561310957848352816130cf8287614056565b6130d991906140a4565b6020840181905283516130ee91908385613942565b602084018190526130ff90856140b8565b606084015261314c565b602083018490528061311b8386614056565b61312591906140a4565b808452602084015161313991908484613942565b80845261314690866140b8565b60408401525b82516001600160701b03108061316c575060208301516001600160701b03105b1561318a57604051631a93c68960e11b815260040160405180910390fd5b505092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526131fd9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261399e565b50505050565b6000808084613220856132168a8a614091565b600160701b613a78565b61322a9190614091565b6132348689614056565b61323e91906140a4565b9050600160701b61324f8583614056565b61325991906140a4565b91506132658187614091565b955061327182866140b8565b9450600061327f82896140b8565b905061328e8982858a8a6138ca565b935050509550959350505050565b6004546000908190612710906132bd90600160701b900461ffff1685614056565b6132c791906140a4565b600954909150600160781b90046001600160781b03164281111561333757600454600160801b900463ffffffff16600061330142846140b8565b6133119063ffffffff84166140b8565b905063ffffffff82166133248286614056565b61332e91906140a4565b9450505061333b565b8192505b5050919050565b6004546000906127109061336190600160701b900461ffff1685614056565b61336b91906140a4565b9050600081156133a3576004548290613392908590600160801b900463ffffffff16614056565b61339c91906140a4565b90506133b5565b50600454600160801b900463ffffffff165b600954600160781b90046001600160781b031642811115613405576133da8282614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b03160217905550613436565b61340f8242614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b5050505050565b60008080858461344d8988614091565b61345b90600160701b614056565b61346591906140a4565b61346f9190614091565b6134798789614056565b61348391906140a4565b90508361349482600160701b614056565b61349e91906140a4565b91506134aa82876140b8565b95506134b68186614091565b945060006134c482896140b8565b905061328e8984838a8a6138ca565b806000546134e19190614091565b60009081556001600160a01b038316815260016020526040902054613507908290614091565b6001600160a01b0383166000818152600160205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061303b9085815260200190565b6040516001600160a01b03831660248201526044810182905261358890849063a9059cbb60e01b906064016131c6565b505050565b600080546135ae9061359f8688614056565b6135a98587614056565b613b26565b905080156134365761343630826134d3565b818160006135d36401000000004261402c565b60055490915063ffffffff600160e01b90910481168203908116158015906135fa57508515155b801561360557508415155b1561368a57836001600160701b03168163ffffffff16607087901b028161362e5761362e614016565b600680549290910490910190556001600160701b03831663ffffffff8216607088901b028161365f5761365f614016565b60078054929091049091019055600580546001600160e01b0316600160e01b63ffffffff8516021790555b505050505050565b60006136a18361086784612eba565b6001600160e01b031690506000848211156136c7576136c085836140b8565b90506136d4565b6136d182866140b8565b90505b60045463ffffffff600160301b8204811691600160501b81049091169060009061375b9061370f9061ffff640100000000909104168a614056565b613719858561406d565b63ffffffff1661372b61271088614056565b6137359190614056565b61373f91906140a4565b61374f9063ffffffff8616614091565b8363ffffffff16612f9d565b905060006137698242614091565b6009549091506001600160781b03908116908216111561246157600980546001600160781b0383166001600160781b0319909116179055505050505050505050565b6000818311612fac5781612eee565b60008060006137cb88888888613908565b93509050600160701b6137de8583614056565b6137e891906140a4565b91506137f48284614091565b9250509550959350505050565b600080600061381288888888613908565b9093509050836137de82600160701b614056565b60b581600160881b811061383f5760409190911b9060801c5b6901000000000000000000811061385b5760209190911b9060401c5b6501000000000081106138735760109190911b9060201c5b630100000081106138895760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b60006138fe836138da8789614056565b6138e491906140a4565b836138ef878a614056565b6138f991906140a4565b612f9d565b9695505050505050565b600080856139168686614056565b61392091906140a4565b91508561392d8685614056565b61393791906140a4565b905094509492505050565b600083613950846002614056565b61395a9190614056565b8280613967886002614056565b6139719190614056565b61397b9190614091565b10156139935761398c856001614091565b9050613996565b50835b949350505050565b60006139f3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b759092919063ffffffff16565b9050805160001480613a14575080806020019051810190613a149190614172565b6135885760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b6000808060001985870985870292508281108382030391505080600003613ab257838281613aa857613aa8614016565b0492505050612eee565b808411613abe57600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b600080613b3284613826565b90506000613b3f84613826565b905081613b4d826005614056565b613b579190614091565b613b6183836140b8565b613b6b9088614056565b6138fe91906140a4565b6060613996848460008585600080866001600160a01b03168587604051613b9c919061418f565b60006040518083038185875af1925050503d8060008114613bd9576040519150601f19603f3d011682016040523d82523d6000602084013e613bde565b606091505b5091509150613bef87838387613bfa565b979650505050505050565b60608315613c69578251600003613c62576001600160a01b0385163b613c625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401613a6f565b5081613996565b6139968383815115613c7e5781518083602001fd5b8060405162461bcd60e51b8152600401613a6f9190613cbc565b60005b83811015613cb3578181015183820152602001613c9b565b50506000910152565b6020815260008251806020840152613cdb816040850160208701613c98565b601f01601f19169190910160400192915050565b6001600160a01b0381168114610a1857600080fd5b60008060408385031215613d1757600080fd5b8235613d2281613cef565b946020939093013593505050565b600060208284031215613d4257600080fd5b813561ffff81168114612eee57600080fd5b600080600060608486031215613d6957600080fd5b8335613d7481613cef565b92506020840135613d8481613cef565b929592945050506040919091013590565b8015158114610a1857600080fd5b600060208284031215613db557600080fd5b8135612eee81613d95565b600060208284031215613dd257600080fd5b813563ffffffff81168114612eee57600080fd5b60008060408385031215613df957600080fd5b823591506020830135613e0b81613cef565b809150509250929050565b600080600080600060a08688031215613e2e57600080fd5b853594506020860135935060408601359250606086013591506080860135613e5581613cef565b809150509295509295909350565b600060208284031215613e7557600080fd5b8135612eee81613cef565b600080600080600080600060e0888a031215613e9b57600080fd5b8735613ea681613cef565b96506020880135613eb681613cef565b95506040880135945060608801359350608088013560ff81168114613eda57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215613f0a57600080fd5b8235613f1581613cef565b91506020830135613e0b81613cef565b600080600060608486031215613f3a57600080fd5b83359250602084013591506040840135613f5381613cef565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600060208284031215613f8657600080fd5b815167ffffffffffffffff80821115613f9e57600080fd5b818401915084601f830112613fb257600080fd5b815181811115613fc457613fc4613f5e565b604051601f8201601f19908116603f01168101908382118183101715613fec57613fec613f5e565b8160405282815287602084870101111561400557600080fd5b613bef836020830160208801613c98565b634e487b7160e01b600052601260045260246000fd5b60008261403b5761403b614016565b500690565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417612ed357612ed3614040565b63ffffffff82811682821603908082111561408a5761408a614040565b5092915050565b80820180821115612ed357612ed3614040565b6000826140b3576140b3614016565b500490565b81810381811115612ed357612ed3614040565b6000602082840312156140dd57600080fd5b8151612eee81613cef565b6000602082840312156140fa57600080fd5b5051919050565b60006001820161411357614113614040565b5060010190565b6001600160e01b0382811682821681810283169291811582850482141761414357614143614040565b50505092915050565b60006001600160e01b038381168061416657614166614016565b92169190910492915050565b60006020828403121561418457600080fd5b8151612eee81613d95565b600082516141a1818460208701613c98565b919091019291505056
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.