From a3229a1d40405bb5106d7a5a2fb87cf39ad93a45 Mon Sep 17 00:00:00 2001 From: Ahmed Ihsan Tawfeeq Date: Tue, 12 Dec 2023 13:14:03 -0700 Subject: [PATCH] Add support for n-hop swaps in FlashUniswapV3 (#105) * feat(flash-swap): add "Path.sol" library feat(flash-swap): add "BytesLib.sol" library refactor(flash-swap): move "NoDelegateCall.sol" to "libraries/" dir * feat(flash-swap): add support for n-hop swaps in "flashLiquidate" function feat(flash-swap): add "swapExactOutputInternal" function feat(flash-swap): replace "poolFee" param in "FlashLiquidateParams" struct with "path" feat(flash-swap): add "FlashUniswapV3__InsufficientSwapOutputAmount" error refactor(flash-swap): use specific imports refactor(flash-swap): rename "FlashSwapAndLiquidateBorrow" event to "FlashLiquidate" refactor(flash-swap): rename "UniswapV3SwapCallback" structs to "FlashLiquidateCallback" test(flash-swap): update "getSwapCallbackData" function to mirror contract changes chore(flash-swap): re-generate types * fix(flash-swap): bug in "uniswapV3SwapCallback" function regarding "repayAmount" recalculation refactor(flash-swap): merge "getPoolKey" and "poolFor" functions into a single "getPool" function refactor(flash-swap): rename "FlashLiquidateCallbackParams" struct to "UniswapV3SwapCallbackParams" refactor(flash-swap): rename "benificiary" param in "swapExactOutputInternal" function to "to" chore(flash-swap): regenerate types * chore(flash-swap): skip test coverage for "uniswap-v3/libraries" dir chore(flash-swap): re-generate types * chore(flash-swap): disable solhint for Uniswap V3 libraries * test(flash-swap): update integration tests to mirror FlashUniswapV3 contract changes test(flash-swap): remove "uniswapV3Pool" contract type to "Contracts" interface test(flash-swap): add "dai" contract type to "Contracts" interface feat(constants): add "DAI_{DECIMALS,NAME,SYMBOL}" constants to tokens.ts feat(constants): add "DEFAULT_FEE" constant to oracles.ts * chore(flash-swap): fix lint issues * docs(flash-swap): add dev NatSpec comments to libraries --- packages/constants/src/oracles.ts | 1 + packages/constants/src/tokens.ts | 4 + packages/flash-swap/.solcover.js | 2 +- .../contracts/uniswap-v3/FlashUniswapV3.sol | 224 +++++++++++------- .../contracts/uniswap-v3/IFlashUniswapV3.sol | 18 +- .../contracts/uniswap-v3/UniswapV3Pool.sol | 2 +- .../uniswap-v3/libraries/BytesLib.sol | 103 ++++++++ .../{ => libraries}/NoDelegateCall.sol | 0 .../contracts/uniswap-v3/libraries/Path.sol | 71 ++++++ .../contracts/uniswap-v3/FlashUniswapV3.ts | 31 ++- .../contracts/uniswap-v3/IFlashUniswapV3.ts | 31 ++- .../uniswap-v3/FlashUniswapV3__factory.ts | 26 +- .../uniswap-v3/IFlashUniswapV3__factory.ts | 24 +- .../uniswap-v3/UniswapV3Pool__factory.ts | 2 +- .../flashUniswapV3/FlashUniswapV3.ts | 4 +- .../uniswapV3SwapCallback/flashLiquidate.ts | 62 +++-- .../effects/uniswapV3SwapCallback/index.ts | 48 ++-- packages/flash-swap/test/shared/fixtures.ts | 25 +- packages/flash-swap/test/shared/helpers.ts | 65 +++-- packages/flash-swap/test/shared/types.ts | 3 +- 20 files changed, 532 insertions(+), 214 deletions(-) create mode 100644 packages/flash-swap/contracts/uniswap-v3/libraries/BytesLib.sol rename packages/flash-swap/contracts/uniswap-v3/{ => libraries}/NoDelegateCall.sol (100%) create mode 100644 packages/flash-swap/contracts/uniswap-v3/libraries/Path.sol diff --git a/packages/constants/src/oracles.ts b/packages/constants/src/oracles.ts index e0bdf63d..06dd5634 100644 --- a/packages/constants/src/oracles.ts +++ b/packages/constants/src/oracles.ts @@ -1,6 +1,7 @@ import { BigNumber } from "@ethersproject/bignumber"; export const DEFAULT_CARDINALITY: number = 144; +export const DEFAULT_FEE: number = 500; export const DEFAULT_TWAP_INTERVAL: number = 1800; export const Q192: BigNumber = BigNumber.from("6277101735386680763835789423207666416102355444464034512896"); export const TICKS = { diff --git a/packages/constants/src/tokens.ts b/packages/constants/src/tokens.ts index b31b9eec..73180989 100644 --- a/packages/constants/src/tokens.ts +++ b/packages/constants/src/tokens.ts @@ -1,5 +1,9 @@ import { BigNumber } from "@ethersproject/bignumber"; +export const DAI_DECIMALS: BigNumber = BigNumber.from(18); +export const DAI_NAME: string = "Dai Stablecoin"; +export const DAI_SYMBOL: string = "DAI"; + export const USDC_DECIMALS: BigNumber = BigNumber.from(6); export const USDC_NAME: string = "USD Coin"; export const USDC_PRICE_PRECISION_SCALAR: BigNumber = BigNumber.from(1_000_000_000_000); diff --git a/packages/flash-swap/.solcover.js b/packages/flash-swap/.solcover.js index c8142087..15753588 100644 --- a/packages/flash-swap/.solcover.js +++ b/packages/flash-swap/.solcover.js @@ -7,7 +7,7 @@ module.exports = { "uniswap-v2/IUniswapV2Pair.sol", "uniswap-v2/UniswapV2Pair.sol", "uniswap-v2/test", - "uniswap-v3/NoDelegateCall.sol", + "uniswap-v3/libraries", "uniswap-v3/UniswapV3Pool", "uniswap-v3/test", ], diff --git a/packages/flash-swap/contracts/uniswap-v3/FlashUniswapV3.sol b/packages/flash-swap/contracts/uniswap-v3/FlashUniswapV3.sol index 7dbf0745..b188f34f 100644 --- a/packages/flash-swap/contracts/uniswap-v3/FlashUniswapV3.sol +++ b/packages/flash-swap/contracts/uniswap-v3/FlashUniswapV3.sol @@ -1,16 +1,19 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.4; -import "@prb/contracts/token/erc20/IErc20.sol"; -import "@prb/contracts/token/erc20/SafeErc20.sol"; -import "@hifi/protocol/contracts/core/balance-sheet/IBalanceSheetV2.sol"; -import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; - -import "./IFlashUniswapV3.sol"; +import { IErc20 } from "@prb/contracts/token/erc20/IErc20.sol"; +import { SafeErc20 } from "@prb/contracts/token/erc20/SafeErc20.sol"; +import { IBalanceSheetV2 } from "@hifi/protocol/contracts/core/balance-sheet/IBalanceSheetV2.sol"; +import { IHToken } from "@hifi/protocol/contracts/core/h-token/IHToken.sol"; +import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; +import { IUniswapV3SwapCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol"; +import { Path } from "./libraries/Path.sol"; +import { IFlashUniswapV3 } from "./IFlashUniswapV3.sol"; /// @title FlashUniswapV3 /// @author Hifi contract FlashUniswapV3 is IFlashUniswapV3 { + using Path for bytes; using SafeErc20 for IErc20; /// PUBLIC STORAGE /// @@ -35,16 +38,14 @@ contract FlashUniswapV3 is IFlashUniswapV3 { } struct FlashLiquidateLocalVars { - PoolKey poolKey; IErc20 underlying; - bool zeroForOne; } struct UniswapV3SwapCallbackParams { IHToken bond; address borrower; IErc20 collateral; - PoolKey poolKey; + bytes path; address sender; int256 turnout; uint256 underlyingAmount; @@ -65,32 +66,18 @@ contract FlashUniswapV3 is IFlashUniswapV3 { }); } - // Compute the flash pool key and address. - vars.poolKey = getPoolKey({ - tokenA: address(params.collateral), - tokenB: address(vars.underlying), - fee: params.poolFee - }); - - // The direction of the swap, true for token0 to token1, false for token1 to token0. - vars.zeroForOne = address(vars.underlying) == vars.poolKey.token1; - - IUniswapV3Pool(poolFor(vars.poolKey)).swap({ - recipient: address(this), - zeroForOne: vars.zeroForOne, - amountSpecified: int256(params.underlyingAmount) * -1, - sqrtPriceLimitX96: vars.zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1, - data: abi.encode( - UniswapV3SwapCallbackParams({ - bond: params.bond, - borrower: params.borrower, - collateral: params.collateral, - poolKey: vars.poolKey, - sender: msg.sender, - turnout: params.turnout, - underlyingAmount: params.underlyingAmount - }) - ) + swapExactOutputInternal({ + amountOut: params.underlyingAmount, + to: address(this), + params: UniswapV3SwapCallbackParams({ + bond: params.bond, + borrower: params.borrower, + collateral: params.collateral, + path: params.path, + sender: msg.sender, + turnout: params.turnout, + underlyingAmount: params.underlyingAmount + }) }); } @@ -100,6 +87,9 @@ contract FlashUniswapV3 is IFlashUniswapV3 { uint256 repayAmount; uint256 seizeAmount; uint256 subsidyAmount; + address tokenOut; + uint24 fee; + address tokenIn; } /// @inheritdoc IUniswapV3SwapCallback @@ -113,79 +103,87 @@ contract FlashUniswapV3 is IFlashUniswapV3 { // Unpack the ABI encoded data passed by the UniswapV3Pool contract. UniswapV3SwapCallbackParams memory params = abi.decode(data, (UniswapV3SwapCallbackParams)); + (vars.tokenOut, vars.tokenIn, vars.fee) = params.path.decodeFirstPool(); + // Check that the caller is the Uniswap V3 flash pool contract. - if (msg.sender != poolFor(params.poolKey)) { + if (msg.sender != getPool({ tokenA: vars.tokenIn, tokenB: vars.tokenOut, fee: vars.fee })) { revert FlashUniswapV3__CallNotAuthorized(msg.sender); } - // Mint hTokens and liquidate the borrower. - vars.mintedHTokenAmount = mintHTokens({ bond: params.bond, underlyingAmount: params.underlyingAmount }); - vars.seizeAmount = liquidateBorrow({ - borrower: params.borrower, - bond: params.bond, - collateral: params.collateral, - mintedHTokenAmount: vars.mintedHTokenAmount - }); - - // Calculate the amount of collateral required to repay. - vars.repayAmount = uint256(amount0Delta > 0 ? amount0Delta : amount1Delta); + // Calculate the amount of input tokens required to receive the exact output amount. + vars.repayAmount = amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta); - // Note that "turnout" is a signed int. When it is negative, it acts as a maximum subsidy amount. - // When its value is positive, it acts as a minimum profit. - if (int256(vars.seizeAmount) < int256(vars.repayAmount) + params.turnout) { - revert FlashUniswapV3__TurnoutNotSatisfied({ - seizeAmount: vars.seizeAmount, - repayAmount: vars.repayAmount, - turnout: params.turnout - }); + // Initiate the next swap. + if (params.path.hasMultiplePools()) { + params.path = params.path.skipToken(); + swapExactOutputInternal({ amountOut: vars.repayAmount, to: msg.sender, params: params }); } + // Or liquidate the underwater vault. + else { + // Mint hTokens and liquidate the borrower. + vars.mintedHTokenAmount = mintHTokens({ bond: params.bond, underlyingAmount: params.underlyingAmount }); + vars.seizeAmount = liquidateBorrow({ + borrower: params.borrower, + bond: params.bond, + collateral: params.collateral, + mintedHTokenAmount: vars.mintedHTokenAmount + }); - // Transfer the subsidy amount. - if (vars.repayAmount > vars.seizeAmount) { - unchecked { - vars.subsidyAmount = vars.repayAmount - vars.seizeAmount; + // Note that "turnout" is a signed int. When it is negative, it acts as a maximum subsidy amount. + // When its value is positive, it acts as a minimum profit. + if (int256(vars.seizeAmount) < int256(vars.repayAmount) + params.turnout) { + revert FlashUniswapV3__TurnoutNotSatisfied({ + seizeAmount: vars.seizeAmount, + repayAmount: vars.repayAmount, + turnout: params.turnout + }); } - params.collateral.safeTransferFrom(params.sender, address(this), vars.subsidyAmount); - } - // Or reap the profit. - else if (vars.seizeAmount > vars.repayAmount) { - unchecked { - vars.profitAmount = vars.seizeAmount - vars.repayAmount; + + // Transfer the subsidy amount. + if (vars.repayAmount > vars.seizeAmount) { + unchecked { + vars.subsidyAmount = vars.repayAmount - vars.seizeAmount; + } + params.collateral.safeTransferFrom(params.sender, address(this), vars.subsidyAmount); + } + // Or reap the profit. + else if (vars.seizeAmount > vars.repayAmount) { + unchecked { + vars.profitAmount = vars.seizeAmount - vars.repayAmount; + } + params.collateral.safeTransfer(params.sender, vars.profitAmount); } - params.collateral.safeTransfer(params.sender, vars.profitAmount); - } - // Pay back the loan. - params.collateral.safeTransfer(msg.sender, vars.repayAmount); - - // Emit an event. - emit FlashSwapAndLiquidateBorrow({ - liquidator: params.sender, - borrower: params.borrower, - bond: address(params.bond), - collateral: address(params.collateral), - underlyingAmount: params.underlyingAmount, - seizeAmount: vars.seizeAmount, - repayAmount: vars.repayAmount, - subsidyAmount: vars.subsidyAmount, - profitAmount: vars.profitAmount - }); + // Pay back the loan. + params.collateral.safeTransfer(msg.sender, vars.repayAmount); + + // Emit an event. + emit FlashLiquidate({ + liquidator: params.sender, + borrower: params.borrower, + bond: address(params.bond), + collateral: address(params.collateral), + underlyingAmount: params.underlyingAmount, + seizeAmount: vars.seizeAmount, + repayAmount: vars.repayAmount, + subsidyAmount: vars.subsidyAmount, + profitAmount: vars.profitAmount + }); + } } /// INTERNAL CONSTANT FUNCTIONS /// - /// @dev Returns the Uniswap V3 pool key for a given token pair and fee level. - function getPoolKey( + /// @dev Calculates the CREATE2 address for a Uniswap V3 pool for a given token pair and fee level without + /// making any external calls. + function getPool( address tokenA, address tokenB, uint24 fee - ) internal pure returns (PoolKey memory) { + ) internal view returns (address pool) { if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA); - return PoolKey({ token0: tokenA, token1: tokenB, fee: fee }); - } + PoolKey memory key = PoolKey({ token0: tokenA, token1: tokenB, fee: fee }); - /// @dev Calculates the CREATE2 address for a Uniswap V3 pool without making any external calls. - function poolFor(PoolKey memory key) internal view returns (address pool) { // solhint-disable-next-line reason-string require(key.token0 < key.token1); pool = address( @@ -253,4 +251,52 @@ contract FlashUniswapV3 is IFlashUniswapV3 { mintedHTokenAmount = newHTokenBalance - oldHTokenBalance; } } + + struct SwapExactOutputLocalVars { + uint256 amountOutReceived; + uint24 fee; + address tokenIn; + address tokenOut; + bool zeroForOne; + } + + /// @dev Performs a Uniswap V3 swap, receiving an exact amount of output. + function swapExactOutputInternal( + uint256 amountOut, + address to, + UniswapV3SwapCallbackParams memory params + ) private returns (uint256 amountIn) { + SwapExactOutputLocalVars memory vars; + + // Decode the first pool from the path. + (vars.tokenOut, vars.tokenIn, vars.fee) = params.path.decodeFirstPool(); + + // Compute the direction of the swap. + vars.zeroForOne = vars.tokenIn < vars.tokenOut; + + // Swap the exact output amount. + (int256 amount0Delta, int256 amount1Delta) = IUniswapV3Pool( + getPool({ tokenA: vars.tokenIn, tokenB: vars.tokenOut, fee: vars.fee }) + ).swap({ + recipient: to, + zeroForOne: vars.zeroForOne, + amountSpecified: -int256(amountOut), + sqrtPriceLimitX96: vars.zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1, + data: abi.encode(params) + }); + + // Compute the amount of input required to receive the exact output amount and the actual amount + // of output received. + (amountIn, vars.amountOutReceived) = vars.zeroForOne + ? (uint256(amount0Delta), uint256(-amount1Delta)) + : (uint256(amount1Delta), uint256(-amount0Delta)); + + // It's technically possible to not receive the full output amount when no price limit has been specified. + if (vars.amountOutReceived != amountOut) { + revert FlashUniswapV3__InsufficientSwapOutputAmount({ + amountOutExpected: amountOut, + amountOutReceived: vars.amountOutReceived + }); + } + } } diff --git a/packages/flash-swap/contracts/uniswap-v3/IFlashUniswapV3.sol b/packages/flash-swap/contracts/uniswap-v3/IFlashUniswapV3.sol index 4886edef..6f91d387 100644 --- a/packages/flash-swap/contracts/uniswap-v3/IFlashUniswapV3.sol +++ b/packages/flash-swap/contracts/uniswap-v3/IFlashUniswapV3.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity >=0.8.4; -import "@hifi/protocol/contracts/core/balance-sheet/IBalanceSheetV2.sol"; -import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol"; +import { IErc20 } from "@prb/contracts/token/erc20/IErc20.sol"; +import { IBalanceSheetV2 } from "@hifi/protocol/contracts/core/balance-sheet/IBalanceSheetV2.sol"; +import { IHToken } from "@hifi/protocol/contracts/core/h-token/IHToken.sol"; +import { IUniswapV3SwapCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol"; /// @title IFlashUniswapV3 /// @author Hifi @@ -13,6 +15,9 @@ interface IFlashUniswapV3 is IUniswapV3SwapCallback { /// @notice Emitted when the caller is not the Uniswap V3 pool contract. error FlashUniswapV3__CallNotAuthorized(address caller); + /// @notice Emitted when the amount of tokens received from the swap is less than the amount expected. + error FlashUniswapV3__InsufficientSwapOutputAmount(uint256 amountOutExpected, uint256 amountOutReceived); + /// @notice Emitted when liquidating a vault backed by underlying. error FlashUniswapV3__LiquidateUnderlyingBackedVault(address borrower, address underlying); @@ -32,7 +37,7 @@ interface IFlashUniswapV3 is IUniswapV3SwapCallback { /// @param repayAmount The amount of collateral that had to be repaid by the liquidator. /// @param subsidyAmount The amount of collateral subsidized by the liquidator. /// @param profitAmount The amount of collateral pocketed as profit by the liquidator. - event FlashSwapAndLiquidateBorrow( + event FlashLiquidate( address indexed liquidator, address indexed borrower, address indexed bond, @@ -46,12 +51,12 @@ interface IFlashUniswapV3 is IUniswapV3SwapCallback { /// STRUCTS /// - /// @dev The parameters for the flash liquidation. + /// @dev The parameters for the n-hop flash liquidation. struct FlashLiquidateParams { address borrower; IHToken bond; IErc20 collateral; - uint24 poolFee; + bytes path; int256 turnout; uint256 underlyingAmount; } @@ -73,7 +78,8 @@ interface IFlashUniswapV3 is IUniswapV3SwapCallback { /// NON-CONSTANT FUNCTIONS /// - /// @notice Flash borrows underlying from Uniswap V3, liquidates the underwater account, and repays the flash loan. + /// @notice Flash borrows underlying from Uniswap V3 via an n-hop swap, liquidates the underwater account, and + /// repays the flash loan. /// @param params The parameters for the liquidation. function flashLiquidate(FlashLiquidateParams memory params) external; } diff --git a/packages/flash-swap/contracts/uniswap-v3/UniswapV3Pool.sol b/packages/flash-swap/contracts/uniswap-v3/UniswapV3Pool.sol index addd33ed..f77691a4 100644 --- a/packages/flash-swap/contracts/uniswap-v3/UniswapV3Pool.sol +++ b/packages/flash-swap/contracts/uniswap-v3/UniswapV3Pool.sol @@ -4,7 +4,7 @@ pragma solidity =0.7.6; import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; -import "./NoDelegateCall.sol"; +import "./libraries/NoDelegateCall.sol"; import "@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol"; import "@uniswap/v3-core/contracts/libraries/SafeCast.sol"; diff --git a/packages/flash-swap/contracts/uniswap-v3/libraries/BytesLib.sol b/packages/flash-swap/contracts/uniswap-v3/libraries/BytesLib.sol new file mode 100644 index 00000000..7adce82f --- /dev/null +++ b/packages/flash-swap/contracts/uniswap-v3/libraries/BytesLib.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// solhint-disable +/* + * @title Solidity Bytes Arrays Utils + * @author Gonçalo Sá + * + * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. + * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. + */ +pragma solidity >=0.8.4 <0.9.0; + +/// @dev https://raw.githubusercontent.com/Uniswap/v3-periphery/v1.3.0/contracts/libraries/BytesLib.sol +library BytesLib { + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { + require(_length + 31 >= _length, "slice_overflow"); + require(_start + _length >= _start, "slice_overflow"); + require(_bytes.length >= _start + _length, "slice_outOfBounds"); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + + function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + require(_start + 20 >= _start, "toAddress_overflow"); + require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); + address tempAddress; + + assembly { + tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + } + + return tempAddress; + } + + function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { + require(_start + 3 >= _start, "toUint24_overflow"); + require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); + uint24 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x3), _start)) + } + + return tempUint; + } +} diff --git a/packages/flash-swap/contracts/uniswap-v3/NoDelegateCall.sol b/packages/flash-swap/contracts/uniswap-v3/libraries/NoDelegateCall.sol similarity index 100% rename from packages/flash-swap/contracts/uniswap-v3/NoDelegateCall.sol rename to packages/flash-swap/contracts/uniswap-v3/libraries/NoDelegateCall.sol diff --git a/packages/flash-swap/contracts/uniswap-v3/libraries/Path.sol b/packages/flash-swap/contracts/uniswap-v3/libraries/Path.sol new file mode 100644 index 00000000..ad9294bf --- /dev/null +++ b/packages/flash-swap/contracts/uniswap-v3/libraries/Path.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// solhint-disable +pragma solidity >=0.8.4; + +import "./BytesLib.sol"; + +/// @title Functions for manipulating path data for multihop swaps +/// @dev https://raw.githubusercontent.com/Uniswap/v3-periphery/v1.3.0/contracts/libraries/Path.sol +library Path { + using BytesLib for bytes; + + /// @dev The length of the bytes encoded address + uint256 private constant ADDR_SIZE = 20; + /// @dev The length of the bytes encoded fee + uint256 private constant FEE_SIZE = 3; + + /// @dev The offset of a single token address and pool fee + uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; + /// @dev The offset of an encoded pool key + uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE; + /// @dev The minimum length of an encoding that contains 2 or more pools + uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET; + + /// @notice Returns true iff the path contains two or more pools + /// @param path The encoded swap path + /// @return True if path contains two or more pools, otherwise false + function hasMultiplePools(bytes memory path) internal pure returns (bool) { + return path.length >= MULTIPLE_POOLS_MIN_LENGTH; + } + + /// @notice Returns the number of pools in the path + /// @param path The encoded swap path + /// @return The number of pools in the path + function numPools(bytes memory path) internal pure returns (uint256) { + // Ignore the first token address. From then on every fee and token offset indicates a pool. + return ((path.length - ADDR_SIZE) / NEXT_OFFSET); + } + + /// @notice Decodes the first pool in path + /// @param path The bytes encoded swap path + /// @return tokenA The first token of the given pool + /// @return tokenB The second token of the given pool + /// @return fee The fee level of the pool + function decodeFirstPool(bytes memory path) + internal + pure + returns ( + address tokenA, + address tokenB, + uint24 fee + ) + { + tokenA = path.toAddress(0); + fee = path.toUint24(ADDR_SIZE); + tokenB = path.toAddress(NEXT_OFFSET); + } + + /// @notice Gets the segment corresponding to the first pool in the path + /// @param path The bytes encoded swap path + /// @return The segment containing all data necessary to target the first pool in the path + function getFirstPool(bytes memory path) internal pure returns (bytes memory) { + return path.slice(0, POP_OFFSET); + } + + /// @notice Skips a token + fee element from the buffer and returns the remainder + /// @param path The swap path + /// @return The remaining token + fee elements in the path + function skipToken(bytes memory path) internal pure returns (bytes memory) { + return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); + } +} diff --git a/packages/flash-swap/src/types/contracts/uniswap-v3/FlashUniswapV3.ts b/packages/flash-swap/src/types/contracts/uniswap-v3/FlashUniswapV3.ts index 02c91f1f..35991887 100644 --- a/packages/flash-swap/src/types/contracts/uniswap-v3/FlashUniswapV3.ts +++ b/packages/flash-swap/src/types/contracts/uniswap-v3/FlashUniswapV3.ts @@ -32,7 +32,7 @@ export declare namespace IFlashUniswapV3 { borrower: PromiseOrValue; bond: PromiseOrValue; collateral: PromiseOrValue; - poolFee: PromiseOrValue; + path: PromiseOrValue; turnout: PromiseOrValue; underlyingAmount: PromiseOrValue; }; @@ -41,14 +41,14 @@ export declare namespace IFlashUniswapV3 { string, string, string, - number, + string, BigNumber, BigNumber ] & { borrower: string; bond: string; collateral: string; - poolFee: number; + path: string; turnout: BigNumber; underlyingAmount: BigNumber; }; @@ -57,7 +57,7 @@ export declare namespace IFlashUniswapV3 { export interface FlashUniswapV3Interface extends utils.Interface { functions: { "balanceSheet()": FunctionFragment; - "flashLiquidate((address,address,address,uint24,int256,uint256))": FunctionFragment; + "flashLiquidate((address,address,address,bytes,int256,uint256))": FunctionFragment; "uniV3Factory()": FunctionFragment; "uniswapV3SwapCallback(int256,int256,bytes)": FunctionFragment; }; @@ -109,15 +109,13 @@ export interface FlashUniswapV3Interface extends utils.Interface { ): Result; events: { - "FlashSwapAndLiquidateBorrow(address,address,address,address,uint256,uint256,uint256,uint256,uint256)": EventFragment; + "FlashLiquidate(address,address,address,address,uint256,uint256,uint256,uint256,uint256)": EventFragment; }; - getEvent( - nameOrSignatureOrTopic: "FlashSwapAndLiquidateBorrow" - ): EventFragment; + getEvent(nameOrSignatureOrTopic: "FlashLiquidate"): EventFragment; } -export interface FlashSwapAndLiquidateBorrowEventObject { +export interface FlashLiquidateEventObject { liquidator: string; borrower: string; bond: string; @@ -128,7 +126,7 @@ export interface FlashSwapAndLiquidateBorrowEventObject { subsidyAmount: BigNumber; profitAmount: BigNumber; } -export type FlashSwapAndLiquidateBorrowEvent = TypedEvent< +export type FlashLiquidateEvent = TypedEvent< [ string, string, @@ -140,11 +138,10 @@ export type FlashSwapAndLiquidateBorrowEvent = TypedEvent< BigNumber, BigNumber ], - FlashSwapAndLiquidateBorrowEventObject + FlashLiquidateEventObject >; -export type FlashSwapAndLiquidateBorrowEventFilter = - TypedEventFilter; +export type FlashLiquidateEventFilter = TypedEventFilter; export interface FlashUniswapV3 extends BaseContract { connect(signerOrProvider: Signer | Provider | string): this; @@ -225,7 +222,7 @@ export interface FlashUniswapV3 extends BaseContract { }; filters: { - "FlashSwapAndLiquidateBorrow(address,address,address,address,uint256,uint256,uint256,uint256,uint256)"( + "FlashLiquidate(address,address,address,address,uint256,uint256,uint256,uint256,uint256)"( liquidator?: PromiseOrValue | null, borrower?: PromiseOrValue | null, bond?: PromiseOrValue | null, @@ -235,8 +232,8 @@ export interface FlashUniswapV3 extends BaseContract { repayAmount?: null, subsidyAmount?: null, profitAmount?: null - ): FlashSwapAndLiquidateBorrowEventFilter; - FlashSwapAndLiquidateBorrow( + ): FlashLiquidateEventFilter; + FlashLiquidate( liquidator?: PromiseOrValue | null, borrower?: PromiseOrValue | null, bond?: PromiseOrValue | null, @@ -246,7 +243,7 @@ export interface FlashUniswapV3 extends BaseContract { repayAmount?: null, subsidyAmount?: null, profitAmount?: null - ): FlashSwapAndLiquidateBorrowEventFilter; + ): FlashLiquidateEventFilter; }; estimateGas: { diff --git a/packages/flash-swap/src/types/contracts/uniswap-v3/IFlashUniswapV3.ts b/packages/flash-swap/src/types/contracts/uniswap-v3/IFlashUniswapV3.ts index 39ecc880..1cc67e32 100644 --- a/packages/flash-swap/src/types/contracts/uniswap-v3/IFlashUniswapV3.ts +++ b/packages/flash-swap/src/types/contracts/uniswap-v3/IFlashUniswapV3.ts @@ -32,7 +32,7 @@ export declare namespace IFlashUniswapV3 { borrower: PromiseOrValue; bond: PromiseOrValue; collateral: PromiseOrValue; - poolFee: PromiseOrValue; + path: PromiseOrValue; turnout: PromiseOrValue; underlyingAmount: PromiseOrValue; }; @@ -41,14 +41,14 @@ export declare namespace IFlashUniswapV3 { string, string, string, - number, + string, BigNumber, BigNumber ] & { borrower: string; bond: string; collateral: string; - poolFee: number; + path: string; turnout: BigNumber; underlyingAmount: BigNumber; }; @@ -57,7 +57,7 @@ export declare namespace IFlashUniswapV3 { export interface IFlashUniswapV3Interface extends utils.Interface { functions: { "balanceSheet()": FunctionFragment; - "flashLiquidate((address,address,address,uint24,int256,uint256))": FunctionFragment; + "flashLiquidate((address,address,address,bytes,int256,uint256))": FunctionFragment; "uniV3Factory()": FunctionFragment; "uniswapV3SwapCallback(int256,int256,bytes)": FunctionFragment; }; @@ -109,15 +109,13 @@ export interface IFlashUniswapV3Interface extends utils.Interface { ): Result; events: { - "FlashSwapAndLiquidateBorrow(address,address,address,address,uint256,uint256,uint256,uint256,uint256)": EventFragment; + "FlashLiquidate(address,address,address,address,uint256,uint256,uint256,uint256,uint256)": EventFragment; }; - getEvent( - nameOrSignatureOrTopic: "FlashSwapAndLiquidateBorrow" - ): EventFragment; + getEvent(nameOrSignatureOrTopic: "FlashLiquidate"): EventFragment; } -export interface FlashSwapAndLiquidateBorrowEventObject { +export interface FlashLiquidateEventObject { liquidator: string; borrower: string; bond: string; @@ -128,7 +126,7 @@ export interface FlashSwapAndLiquidateBorrowEventObject { subsidyAmount: BigNumber; profitAmount: BigNumber; } -export type FlashSwapAndLiquidateBorrowEvent = TypedEvent< +export type FlashLiquidateEvent = TypedEvent< [ string, string, @@ -140,11 +138,10 @@ export type FlashSwapAndLiquidateBorrowEvent = TypedEvent< BigNumber, BigNumber ], - FlashSwapAndLiquidateBorrowEventObject + FlashLiquidateEventObject >; -export type FlashSwapAndLiquidateBorrowEventFilter = - TypedEventFilter; +export type FlashLiquidateEventFilter = TypedEventFilter; export interface IFlashUniswapV3 extends BaseContract { connect(signerOrProvider: Signer | Provider | string): this; @@ -225,7 +222,7 @@ export interface IFlashUniswapV3 extends BaseContract { }; filters: { - "FlashSwapAndLiquidateBorrow(address,address,address,address,uint256,uint256,uint256,uint256,uint256)"( + "FlashLiquidate(address,address,address,address,uint256,uint256,uint256,uint256,uint256)"( liquidator?: PromiseOrValue | null, borrower?: PromiseOrValue | null, bond?: PromiseOrValue | null, @@ -235,8 +232,8 @@ export interface IFlashUniswapV3 extends BaseContract { repayAmount?: null, subsidyAmount?: null, profitAmount?: null - ): FlashSwapAndLiquidateBorrowEventFilter; - FlashSwapAndLiquidateBorrow( + ): FlashLiquidateEventFilter; + FlashLiquidate( liquidator?: PromiseOrValue | null, borrower?: PromiseOrValue | null, bond?: PromiseOrValue | null, @@ -246,7 +243,7 @@ export interface IFlashUniswapV3 extends BaseContract { repayAmount?: null, subsidyAmount?: null, profitAmount?: null - ): FlashSwapAndLiquidateBorrowEventFilter; + ): FlashLiquidateEventFilter; }; estimateGas: { diff --git a/packages/flash-swap/src/types/factories/contracts/uniswap-v3/FlashUniswapV3__factory.ts b/packages/flash-swap/src/types/factories/contracts/uniswap-v3/FlashUniswapV3__factory.ts index 86d61ca2..b314ef81 100644 --- a/packages/flash-swap/src/types/factories/contracts/uniswap-v3/FlashUniswapV3__factory.ts +++ b/packages/flash-swap/src/types/factories/contracts/uniswap-v3/FlashUniswapV3__factory.ts @@ -37,6 +37,22 @@ const _abi = [ name: "FlashUniswapV3__CallNotAuthorized", type: "error", }, + { + inputs: [ + { + internalType: "uint256", + name: "amountOutExpected", + type: "uint256", + }, + { + internalType: "uint256", + name: "amountOutReceived", + type: "uint256", + }, + ], + name: "FlashUniswapV3__InsufficientSwapOutputAmount", + type: "error", + }, { inputs: [ { @@ -148,7 +164,7 @@ const _abi = [ type: "uint256", }, ], - name: "FlashSwapAndLiquidateBorrow", + name: "FlashLiquidate", type: "event", }, { @@ -184,9 +200,9 @@ const _abi = [ type: "address", }, { - internalType: "uint24", - name: "poolFee", - type: "uint24", + internalType: "bytes", + name: "path", + type: "bytes", }, { internalType: "int256", @@ -248,7 +264,7 @@ const _abi = [ ] as const; const _bytecode = - "0x60c060405234801561001057600080fd5b50604051620016653803806200166583398101604081905261003191610060565b6001600160a01b039182166080521660a05261009a565b6001600160a01b038116811461005d57600080fd5b50565b6000806040838503121561007357600080fd5b825161007e81610048565b602084015190925061008f81610048565b809150509250929050565b60805160a051611584620000e16000396000818160ae01526106e1015260008181605601528181610a2b01528181610aca01528181610b620152610c9a01526115846000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806322285cf6146100515780635fd3acc214610094578063705e474b146100a9578063fa461e33146100d0575b600080fd5b6100787f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b6100a76100a236600461100e565b6100e3565b005b6100787f000000000000000000000000000000000000000000000000000000000000000081565b6100a76100de3660046110b6565b61034b565b6040805160c0810182526000606082018181526080830182905260a083018290528252602082018190529181019190915281602001516001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610156573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061017a9190611136565b6001600160a01b0390811660208301819052604084015190911614156101d257815160208201516040516356c284cb60e11b81526001600160a01b039283166004820152911660248201526044015b60405180910390fd5b6101e98260400151826020015184606001516105cc565b808252602080820151908301516001600160a01b03908116911614604083015261021290610643565b6001600160a01b031663128acb083083604001518560a001516000196102389190611169565b856040015161026557610260600173fffd8963efd1fc6a506488495d951d5263988d266111f0565b610275565b6102756401000276a36001611218565b6040805160e0810182526020808b01516001600160a01b0390811683528b518116828401528b840151168284015289516060830152336080808401919091528b015160a0808401919091528b015160c083015291516102d49201611243565b6040516020818303038152906040526040518663ffffffff1660e01b815260040161030395949392919061132a565b60408051808303816000875af1158015610321573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610345919061136f565b50505050565b61037d6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b600061038b83850185611414565b905061039a8160600151610643565b6001600160a01b0316336001600160a01b0316146103cd57604051639b2d751d60e01b81523360048201526024016101c9565b6103df81600001518260c00151610763565b8083526020820151825160408401516103f7936109ff565b60608301526000861361040a578461040c565b855b6040830181905260a0820151610421916114cd565b8260600151121561046257606082015160408084015160a08401519151636d0db81160e01b81526004810193909352602483015260448201526064016101c9565b8160600151826040015111156104ac576060820151604080840151919091036080808501829052830151918301516104a7926001600160a01b03909116913090610d76565b6104ed565b8160400151826060015111156104ed57604080830151606084015103602084018190526080830151918301516104ed926001600160a01b0390911691610e0e565b61051333836040015183604001516001600160a01b0316610e0e9092919063ffffffff16565b80600001516001600160a01b031681602001516001600160a01b031682608001516001600160a01b03167f612c4a3d511ecc6d0a51d965cc76c7ed6a97215ac56d9a290c17c501ac7c643684604001518560c001518760600151886040015189608001518a602001516040516105bc969594939291906001600160a01b03969096168652602086019490945260408501929092526060840152608083015260a082015260c00190565b60405180910390a4505050505050565b6040805160608101825260008082526020820181905291810191909152826001600160a01b0316846001600160a01b03161115610607579192915b6040518060600160405280856001600160a01b03168152602001846001600160a01b031681526020018362ffffff1681525090505b9392505050565b600081602001516001600160a01b031682600001516001600160a01b03161061066b57600080fd5b815160208084015160408086015181516001600160a01b0395861681860152949092168482015262ffffff90911660608085019190915281518085038201815260808501909252815191909201207fff0000000000000000000000000000000000000000000000000000000000000060a08401527f000000000000000000000000000000000000000000000000000000000000000090911b6bffffffffffffffffffffffff191660a183015260b58201527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460d582015260f50160408051601f19818403018152919052805160209091012092915050565b600080836001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c89190611136565b604051636eb1769f60e11b81523060048201526001600160a01b03868116602483015291925060009183169063dd62ed3e90604401602060405180830381865afa15801561081a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083e919061150d565b9050838110156108be5760405163095ea7b360e01b81526001600160a01b038681166004830152600019602483015283169063095ea7b3906044016020604051808303816000875af1158015610898573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108bc9190611526565b505b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610905573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610929919061150d565b60405163b9f5be4160e01b8152600481018790529091506001600160a01b0387169063b9f5be4190602401600060405180830381600087803b15801561096e57600080fd5b505af1158015610982573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03891691506370a0823190602401602060405180830381865afa1580156109cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f1919061150d565b919091039695505050505050565b604051630f20729d60e11b81526001600160a01b038581166004830152838116602483015260009182917f00000000000000000000000000000000000000000000000000000000000000001690631e40e53a90604401602060405180830381865afa158015610a72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a96919061150d565b60405163f37765c760e01b81526001600160a01b0386811660048301526024820183905287811660448301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063f37765c790606401602060405180830381865afa158015610b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b35919061150d565b604051634d7c892f60e01b81526001600160a01b03898116600483015288811660248301529192506000917f00000000000000000000000000000000000000000000000000000000000000001690634d7c892f90604401602060405180830381865afa158015610ba9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcd919061150d565b90506000818311610bde5782610be0565b815b90506000818711610bf15786610bf3565b815b6040516370a0823160e01b81523060048201529091506000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015610c3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c61919061150d565b604051630c9fae0f60e31b81526001600160a01b038d811660048301528c81166024830152604482018590528b811660648301529192507f0000000000000000000000000000000000000000000000000000000000000000909116906364fd707890608401600060405180830381600087803b158015610ce057600080fd5b505af1158015610cf4573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b038c1691506370a0823190602401602060405180830381865afa158015610d3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d63919061150d565b919091039b9a5050505050505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526103459085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152610e43565b6040516001600160a01b038316602482015260448101829052610e3e90849063a9059cbb60e01b90606401610daa565b505050565b6000610e8583836040518060400160405280601581526020017f5361666545726332304c6f774c6576656c43616c6c0000000000000000000000815250610ec0565b805190915015610e3e5780806020019051810190610ea39190611526565b610e3e576040516364d6fc4d60e01b815260040160405180910390fd5b6060610ed4846001600160a01b0316610f97565b610efc57604051638201cc0560e01b81526001600160a01b03851660048201526024016101c9565b600080856001600160a01b031685604051610f179190611548565b6000604051808303816000865af19150503d8060008114610f54576040519150601f19603f3d011682016040523d82523d6000602084013e610f59565b606091505b50915091508115610f6d57915061063c9050565b805115610f7d5780518082602001fd5b8360405162461bcd60e51b81526004016101c99190611564565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590610fcb57508115155b949350505050565b6001600160a01b0381168114610fe857600080fd5b50565b8035610ff681610fd3565b919050565b803562ffffff81168114610ff657600080fd5b600060c0828403121561102057600080fd5b60405160c0810181811067ffffffffffffffff8211171561105157634e487b7160e01b600052604160045260246000fd5b604052823561105f81610fd3565b8152602083013561106f81610fd3565b6020820152604083013561108281610fd3565b604082015261109360608401610ffb565b60608201526080830135608082015260a083013560a08201528091505092915050565b600080600080606085870312156110cc57600080fd5b8435935060208501359250604085013567ffffffffffffffff808211156110f257600080fd5b818701915087601f83011261110657600080fd5b81358181111561111557600080fd5b88602082850101111561112757600080fd5b95989497505060200194505050565b60006020828403121561114857600080fd5b815161063c81610fd3565b634e487b7160e01b600052601160045260246000fd5b60006001600160ff1b0360008413600084138583048511828216161561119157611191611153565b600160ff1b60008712828116878305891216156111b0576111b0611153565b600087129250878205871284841616156111cc576111cc611153565b878505871281841616156111e2576111e2611153565b505050929093029392505050565b60006001600160a01b038381169083168181101561121057611210611153565b039392505050565b60006001600160a01b0380831681851680830382111561123a5761123a611153565b01949350505050565b6000610120820190506001600160a01b03808451168352806020850151166020840152806040850151166040840152606084015181815116606085015281602082015116608085015262ffffff60408201511660a0850152505060808301516112b760c08401826001600160a01b03169052565b5060a083015160e083015260c0909201516101009091015290565b60005b838110156112ed5781810151838201526020016112d5565b838111156103455750506000910152565b600081518084526113168160208601602086016112d2565b601f01601f19169290920160200192915050565b60006001600160a01b038088168352861515602084015285604084015280851660608401525060a0608083015261136460a08301846112fe565b979650505050505050565b6000806040838503121561138257600080fd5b505080516020909101519092909150565b6000606082840312156113a557600080fd5b6040516060810181811067ffffffffffffffff821117156113d657634e487b7160e01b600052604160045260246000fd5b60405290508082356113e781610fd3565b815260208301356113f781610fd3565b602082015261140860408401610ffb565b60408201525092915050565b6000610120828403121561142757600080fd5b60405160e0810181811067ffffffffffffffff8211171561145857634e487b7160e01b600052604160045260246000fd5b604052823561146681610fd3565b8152602083013561147681610fd3565b6020820152604083013561148981610fd3565b604082015261149b8460608501611393565b60608201526114ac60c08401610feb565b608082015260e083013560a08201526101009092013560c083015250919050565b6000808212826001600160ff1b03038413811516156114ee576114ee611153565b600160ff1b839003841281161561150757611507611153565b50500190565b60006020828403121561151f57600080fd5b5051919050565b60006020828403121561153857600080fd5b8151801515811461063c57600080fd5b6000825161155a8184602087016112d2565b9190910192915050565b60208152600061063c60208301846112fe56fea164736f6c634300080c000a"; + "0x60c06040523480156200001157600080fd5b5060405162001b6438038062001b64833981016040819052620000349162000065565b6001600160a01b039182166080521660a052620000a4565b6001600160a01b03811681146200006257600080fd5b50565b600080604083850312156200007957600080fd5b825162000086816200004c565b602084015190925062000099816200004c565b809150509250929050565b60805160a051611a79620000eb6000396000818160ae0152610828015260008181605601528181610be601528181610c8501528181610d1d0152610e550152611a796000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806322285cf6146100515780633c21077e14610094578063705e474b146100a9578063fa461e33146100d0575b600080fd5b6100787f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b6100a76100a236600461159b565b6100e3565b005b6100787f000000000000000000000000000000000000000000000000000000000000000081565b6100a76100de366004611658565b61022b565b60408051602081019091526000815281602001516001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610134573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061015891906116d8565b6001600160a01b03908116808352604084015190911614156101a957815181516040516356c284cb60e11b81526001600160a01b039283166004820152911660248201526044015b60405180910390fd5b6102268260a00151306040518060e0016040528086602001516001600160a01b0316815260200186600001516001600160a01b0316815260200186604001516001600160a01b0316815260200186606001518152602001336001600160a01b03168152602001866080015181526020018660a0015181525061054c565b505050565b61028a604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600062ffffff16815260200160006001600160a01b031681525090565b6000610298838501856116f5565b90506102a7816060015161071b565b62ffffff1660c085018190526001600160a01b0391821660e086018190529290911660a085018190526102da9291610757565b6001600160a01b0316336001600160a01b03161461030d57604051639b2d751d60e01b81523360048201526024016101a0565b6000861361031b578461031d565b855b60408301526060810151610330906108ad565b1561035d5761034281606001516108e7565b6060820152604082015161035790338361054c565b50610544565b61036f81600001518260c0015161091e565b80835260208201518251604084015161038793610bba565b606083015260a081015160408301516103a091906117d3565b826060015112156103e157606082015160408084015160a08401519151636d0db81160e01b81526004810193909352602483015260448201526064016101a0565b81606001518260400151111561042b57606082015160408084015191909103608080850182905283015191830151610426926001600160a01b03909116913090610f31565b61046c565b81604001518260600151111561046c576040808301516060840151036020840181905260808301519183015161046c926001600160a01b0390911691610fcf565b61049233836040015183604001516001600160a01b0316610fcf9092919063ffffffff16565b80600001516001600160a01b031681602001516001600160a01b031682608001516001600160a01b03167f8e4c31c4deff218c705ee01f5c23123c4864d19b598eabe8c6d3e9768ae983ef84604001518560c001518760600151886040015189608001518a6020015160405161053b969594939291906001600160a01b03969096168652602086019490945260408501929092526060840152608083015260a082015260c00190565b60405180910390a45b505050505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101829052610583836060015161071b565b62ffffff16602084018190526001600160a01b03918216604085018190529290911660608401819052808310608085015260009283926105c39290610757565b6001600160a01b031663128acb088785608001518a6105e19061182c565b876080015161060e57610609600173fffd8963efd1fc6a506488495d951d5263988d26611849565b61061e565b61061e6401000276a36001611871565b8a60405160200161062f91906118f4565b6040516020818303038152906040526040518663ffffffff1660e01b815260040161065e95949392919061196a565b60408051808303816000875af115801561067c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a091906119af565b9150915082608001516106bc57806106b78361182c565b6106c6565b816106c68261182c565b80855290945087146107115782516040517fff454b820000000000000000000000000000000000000000000000000000000081526101a0918991600401918252602082015260400190565b5050509392505050565b600080806107298482610fff565b92506107368460146110cf565b905061074e610747600360146119d3565b8590610fff565b91509193909250565b6000826001600160a01b0316846001600160a01b03161115610777579192915b604080516060810182526001600160a01b038087168083529086166020830181905262ffffff8616938301939093529091116107b257600080fd5b805160208083015160408085015181516001600160a01b0395861681860152949092168482015262ffffff90911660608085019190915281518085038201815260808501909252815191909201207fff0000000000000000000000000000000000000000000000000000000000000060a08401527f000000000000000000000000000000000000000000000000000000000000000090911b6bffffffffffffffffffffffff191660a183015260b58201527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460d582015260f50160408051601f19818403018152919052805160209091012095945050505050565b60006108bb600360146119d3565b60146108c86003826119d3565b6108d291906119d3565b6108dc91906119d3565b825110159050919050565b60606109186108f8600360146119d3565b610904600360146119d3565b845161091091906119eb565b84919061118f565b92915050565b600080836001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561095f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098391906116d8565b604051636eb1769f60e11b81523060048201526001600160a01b03868116602483015291925060009183169063dd62ed3e90604401602060405180830381865afa1580156109d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f99190611a02565b905083811015610a795760405163095ea7b360e01b81526001600160a01b038681166004830152600019602483015283169063095ea7b3906044016020604051808303816000875af1158015610a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a779190611a1b565b505b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610ac0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae49190611a02565b60405163b9f5be4160e01b8152600481018790529091506001600160a01b0387169063b9f5be4190602401600060405180830381600087803b158015610b2957600080fd5b505af1158015610b3d573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03891691506370a0823190602401602060405180830381865afa158015610b88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bac9190611a02565b919091039695505050505050565b604051630f20729d60e11b81526001600160a01b038581166004830152838116602483015260009182917f00000000000000000000000000000000000000000000000000000000000000001690631e40e53a90604401602060405180830381865afa158015610c2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c519190611a02565b60405163f37765c760e01b81526001600160a01b0386811660048301526024820183905287811660448301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063f37765c790606401602060405180830381865afa158015610ccc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf09190611a02565b604051634d7c892f60e01b81526001600160a01b03898116600483015288811660248301529192506000917f00000000000000000000000000000000000000000000000000000000000000001690634d7c892f90604401602060405180830381865afa158015610d64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d889190611a02565b90506000818311610d995782610d9b565b815b90506000818711610dac5786610dae565b815b6040516370a0823160e01b81523060048201529091506000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015610df8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1c9190611a02565b604051630c9fae0f60e31b81526001600160a01b038d811660048301528c81166024830152604482018590528b811660648301529192507f0000000000000000000000000000000000000000000000000000000000000000909116906364fd707890608401600060405180830381600087803b158015610e9b57600080fd5b505af1158015610eaf573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b038c1691506370a0823190602401602060405180830381865afa158015610efa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1e9190611a02565b919091039b9a5050505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052610fc99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526112f4565b50505050565b6040516001600160a01b03831660248201526044810182905261022690849063a9059cbb60e01b90606401610f65565b60008161100d8160146119d3565b101561105b5760405162461bcd60e51b815260206004820152601260248201527f746f416464726573735f6f766572666c6f77000000000000000000000000000060448201526064016101a0565b6110668260146119d3565b835110156110b65760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e6473000000000000000000000060448201526064016101a0565b5001602001516c01000000000000000000000000900490565b6000816110dd8160036119d3565b101561112b5760405162461bcd60e51b815260206004820152601160248201527f746f55696e7432345f6f766572666c6f7700000000000000000000000000000060448201526064016101a0565b6111368260036119d3565b835110156111865760405162461bcd60e51b815260206004820152601460248201527f746f55696e7432345f6f75744f66426f756e647300000000000000000000000060448201526064016101a0565b50016003015190565b60608161119d81601f6119d3565b10156111dc5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016101a0565b826111e783826119d3565b10156112265760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016101a0565b61123082846119d3565b845110156112805760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016101a0565b60608215801561129f57604051915060008252602082016040526112e9565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156112d85780518352602092830192016112c0565b5050858452601f01601f1916604052505b5090505b9392505050565b600061133683836040518060400160405280601581526020017f5361666545726332304c6f774c6576656c43616c6c0000000000000000000000815250611371565b80519091501561022657808060200190518101906113549190611a1b565b610226576040516364d6fc4d60e01b815260040160405180910390fd5b6060611385846001600160a01b0316611448565b6113ad57604051638201cc0560e01b81526001600160a01b03851660048201526024016101a0565b600080856001600160a01b0316856040516113c89190611a3d565b6000604051808303816000865af19150503d8060008114611405576040519150601f19603f3d011682016040523d82523d6000602084013e61140a565b606091505b5091509150811561141e5791506112ed9050565b80511561142e5780518082602001fd5b8360405162461bcd60e51b81526004016101a09190611a59565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061147c57508115155b949350505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156114bd576114bd611484565b60405290565b60405160e0810167ffffffffffffffff811182821017156114bd576114bd611484565b6001600160a01b03811681146114fb57600080fd5b50565b8035611509816114e6565b919050565b600082601f83011261151f57600080fd5b813567ffffffffffffffff8082111561153a5761153a611484565b604051601f8301601f19908116603f0116810190828211818310171561156257611562611484565b8160405283815286602085880101111561157b57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000602082840312156115ad57600080fd5b813567ffffffffffffffff808211156115c557600080fd5b9083019060c082860312156115d957600080fd5b6115e161149a565b82356115ec816114e6565b815260208301356115fc816114e6565b6020820152604083013561160f816114e6565b604082015260608301358281111561162657600080fd5b6116328782860161150e565b6060830152506080830135608082015260a083013560a082015280935050505092915050565b6000806000806060858703121561166e57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561169457600080fd5b818701915087601f8301126116a857600080fd5b8135818111156116b757600080fd5b8860208285010111156116c957600080fd5b95989497505060200194505050565b6000602082840312156116ea57600080fd5b81516112ed816114e6565b60006020828403121561170757600080fd5b813567ffffffffffffffff8082111561171f57600080fd5b9083019060e0828603121561173357600080fd5b61173b6114c3565b611744836114fe565b8152611752602084016114fe565b6020820152611763604084016114fe565b604082015260608301358281111561177a57600080fd5b6117868782860161150e565b606083015250611798608084016114fe565b608082015260a083013560a082015260c083013560c082015280935050505092915050565b634e487b7160e01b600052601160045260246000fd5b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561180d5761180d6117bd565b600160ff1b8390038412811615611826576118266117bd565b50500190565b6000600160ff1b821415611842576118426117bd565b5060000390565b60006001600160a01b0383811690831681811015611869576118696117bd565b039392505050565b60006001600160a01b03808316818516808303821115611893576118936117bd565b01949350505050565b60005b838110156118b757818101518382015260200161189f565b83811115610fc95750506000910152565b600081518084526118e081602086016020860161189c565b601f01601f19169290920160200192915050565b6020815260006001600160a01b03808451166020840152806020850151166040840152806040850151166060840152606084015160e0608085015261193d6101008501826118c8565b90508160808601511660a085015260a085015160c085015260c085015160e0850152809250505092915050565b60006001600160a01b038088168352861515602084015285604084015280851660608401525060a060808301526119a460a08301846118c8565b979650505050505050565b600080604083850312156119c257600080fd5b505080516020909101519092909150565b600082198211156119e6576119e66117bd565b500190565b6000828210156119fd576119fd6117bd565b500390565b600060208284031215611a1457600080fd5b5051919050565b600060208284031215611a2d57600080fd5b815180151581146112ed57600080fd5b60008251611a4f81846020870161189c565b9190910192915050565b6020815260006112ed60208301846118c856fea164736f6c634300080c000a"; type FlashUniswapV3ConstructorParams = | [signer?: Signer] diff --git a/packages/flash-swap/src/types/factories/contracts/uniswap-v3/IFlashUniswapV3__factory.ts b/packages/flash-swap/src/types/factories/contracts/uniswap-v3/IFlashUniswapV3__factory.ts index 43e66467..0f06ce73 100644 --- a/packages/flash-swap/src/types/factories/contracts/uniswap-v3/IFlashUniswapV3__factory.ts +++ b/packages/flash-swap/src/types/factories/contracts/uniswap-v3/IFlashUniswapV3__factory.ts @@ -21,6 +21,22 @@ const _abi = [ name: "FlashUniswapV3__CallNotAuthorized", type: "error", }, + { + inputs: [ + { + internalType: "uint256", + name: "amountOutExpected", + type: "uint256", + }, + { + internalType: "uint256", + name: "amountOutReceived", + type: "uint256", + }, + ], + name: "FlashUniswapV3__InsufficientSwapOutputAmount", + type: "error", + }, { inputs: [ { @@ -116,7 +132,7 @@ const _abi = [ type: "uint256", }, ], - name: "FlashSwapAndLiquidateBorrow", + name: "FlashLiquidate", type: "event", }, { @@ -152,9 +168,9 @@ const _abi = [ type: "address", }, { - internalType: "uint24", - name: "poolFee", - type: "uint24", + internalType: "bytes", + name: "path", + type: "bytes", }, { internalType: "int256", diff --git a/packages/flash-swap/src/types/factories/contracts/uniswap-v3/UniswapV3Pool__factory.ts b/packages/flash-swap/src/types/factories/contracts/uniswap-v3/UniswapV3Pool__factory.ts index f6aefbc3..da07c0a2 100644 --- a/packages/flash-swap/src/types/factories/contracts/uniswap-v3/UniswapV3Pool__factory.ts +++ b/packages/flash-swap/src/types/factories/contracts/uniswap-v3/UniswapV3Pool__factory.ts @@ -999,7 +999,7 @@ const _abi = [ ] as const; const _bytecode = - "0x6101206040523480156200001257600080fd5b503060601b60805260408051630890357360e41b81529051600091339163890357309160048082019260a092909190829003018186803b1580156200005657600080fd5b505afa1580156200006b573d6000803e3d6000fd5b505050506040513d60a08110156200008257600080fd5b508051602080830151604084015160608086015160809096015160e896871b6001600160e81b03191660c052600180546001600160a01b03199081166001600160a01b039586161790915560008054909116939094169290921790925592901b6001600160601b03191660a052600282810b900b90921b60e0529150620001149082906200012c811b62003de517901c565b60801b6001600160801b03191661010052506200019a565b60008082600281900b620d89e719816200014257fe5b05029050600083600281900b620d89e8816200015a57fe5b0502905060008460020b83830360020b816200017257fe5b0560010190508062ffffff166001600160801b038016816200019057fe5b0495945050505050565b60805160601c60a05160601c60c05160e81c60e05160e81c6101005160801c6176b66200021b60003980612c30528061684b5280616882525080610ed15280613a3052806168b652806168e8525080611039528061236052806123975280613a70525080612d4d528061303e5280613a0c525080613e8e52506176b66000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806370cf754a116100ee578063c45a015511610097578063ddca3f4311610071578063ddca3f43146108bd578063f3058399146108dd578063f30dba93146108e5578063f637731d1461097d576101ae565b8063c45a01551461088e578063d0c93a7c14610896578063d21220a7146108b5576101ae565b8063883bdbfd116100c8578063883bdbfd146106da578063a34123a7146107e3578063a38807f214610826576101ae565b806370cf754a146106575780638206a4d11461065f57806385b6672914610687576101ae565b80633850c7bd1161015b578063490e6cbc11610135578063490e6cbc146104d55780634f1eb3d81461056e578063514ea4bf146105d55780635339c29614610637576101ae565b80633850c7bd1461039d5780633c8a7d8d1461040357806346141319146104bb576101ae565b80631ad8b03b1161018c5780631ad8b03b146102cd578063252c09d71461031657806332148f671461037a576101ae565b80630dfe1681146101b3578063128acb08146101e45780631a686502146102a0575b600080fd5b6101bb6109b0565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b610287600480360360a08110156101fa57600080fd5b73ffffffffffffffffffffffffffffffffffffffff82358116926020810135151592604082013592606083013516919081019060a08101608082013564010000000081111561024857600080fd5b82018360208201111561025a57600080fd5b8035906020019184600183028401116401000000008311171561027c57600080fd5b5090925090506109cc565b6040805192835260208301919091528051918290030190f35b6102a8611a6f565b604080516fffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6102d5611a87565b60405180836fffffffffffffffffffffffffffffffff168152602001826fffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b6103336004803603602081101561032c57600080fd5b5035611ab7565b6040805163ffffffff909516855260069390930b602085015273ffffffffffffffffffffffffffffffffffffffff9091168383015215156060830152519081900360800190f35b61039b6004803603602081101561039057600080fd5b503561ffff16611b2e565b005b6103a5611d12565b6040805173ffffffffffffffffffffffffffffffffffffffff909816885260029690960b602088015261ffff9485168787015292841660608701529216608085015260ff90911660a0840152151560c0830152519081900360e00190f35b610287600480360360a081101561041957600080fd5b73ffffffffffffffffffffffffffffffffffffffff823516916020810135600290810b92604083013590910b916fffffffffffffffffffffffffffffffff6060820135169181019060a08101608082013564010000000081111561047c57600080fd5b82018360208201111561048e57600080fd5b803590602001918460018302840111640100000000831117156104b057600080fd5b509092509050611df7565b6104c361220e565b60408051918252519081900360200190f35b61039b600480360360808110156104eb57600080fd5b73ffffffffffffffffffffffffffffffffffffffff823516916020810135916040820135919081019060808101606082013564010000000081111561052f57600080fd5b82018360208201111561054157600080fd5b8035906020019184600183028401116401000000008311171561056357600080fd5b509092509050612214565b6102d5600480360360a081101561058457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813516906020810135600290810b91604081013590910b906fffffffffffffffffffffffffffffffff6060820135811691608001351661287a565b6105f2600480360360208110156105eb57600080fd5b5035612bc9565b604080516fffffffffffffffffffffffffffffffff96871681526020810195909552848101939093529084166060840152909216608082015290519081900360a00190f35b6104c36004803603602081101561064d57600080fd5b503560010b612c1c565b6102a8612c2e565b61039b6004803603604081101561067557600080fd5b5060ff81358116916020013516612c52565b6102d56004803603606081101561069d57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813516906fffffffffffffffffffffffffffffffff60208201358116916040013516612f3e565b61074a600480360360208110156106f057600080fd5b81019060208101813564010000000081111561070b57600080fd5b82018360208201111561071d57600080fd5b8035906020019184602083028401116401000000008311171561073f57600080fd5b5090925090506133cc565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561078e578181015183820152602001610776565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156107cd5781810151838201526020016107b5565b5050505090500194505050505060405180910390f35b610287600480360360608110156107f957600080fd5b508035600290810b91602081013590910b90604001356fffffffffffffffffffffffffffffffff1661349f565b6108506004803603604081101561083c57600080fd5b508035600290810b9160200135900b6136f7565b6040805160069490940b845273ffffffffffffffffffffffffffffffffffffffff909216602084015263ffffffff1682820152519081900360600190f35b6101bb613a0a565b61089e613a2e565b6040805160029290920b8252519081900360200190f35b6101bb613a52565b6108c5613a6e565b6040805162ffffff9092168252519081900360200190f35b6104c3613a92565b610905600480360360208110156108fb57600080fd5b503560020b613a98565b604080516fffffffffffffffffffffffffffffffff9099168952600f9790970b602089015287870195909552606087019390935260069190910b608086015273ffffffffffffffffffffffffffffffffffffffff1660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b61039b6004803603602081101561099357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16613b5d565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6000806109d7613e76565b85610a4357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4153000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6040805160e0810182526002805473ffffffffffffffffffffffffffffffffffffffff81168352740100000000000000000000000000000000000000008104820b820b90910b602083015261ffff7701000000000000000000000000000000000000000000000082048116938301939093527901000000000000000000000000000000000000000000000000008104831660608301527b010000000000000000000000000000000000000000000000000000008104909216608082015260ff7d0100000000000000000000000000000000000000000000000000000000008304811660a08301527e01000000000000000000000000000000000000000000000000000000000000909204909116151560c08201819052610bc457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b87610c3657806000015173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16118015610c31575073fffd8963efd1fc6a506488495d951d5263988d2673ffffffffffffffffffffffffffffffffffffffff8716105b610c8f565b806000015173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16108015610c8f57506401000276a373ffffffffffffffffffffffffffffffffffffffff8716115b610cfa57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f53504c0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556040805160c08101909152600090808a610d475760048460a0015160ff16901c610d5a565b60108460a0015160ff1681610d5857fe5b065b60ff1681526006546fffffffffffffffffffffffffffffffff166020820152604001610d84613eba565b63ffffffff168152602001600060060b8152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020016000151581525090506000808913905060006040518060e001604052808b815260200160008152602001856000015173ffffffffffffffffffffffffffffffffffffffff168152602001856020015160020b81526020018c610e1a57600454610e1e565b6003545b815260200160006fffffffffffffffffffffffffffffffff16815260200184602001516fffffffffffffffffffffffffffffffff1681525090505b805115801590610e9957508873ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1614155b1561136857610ea661761d565b604082015173ffffffffffffffffffffffffffffffffffffffff1681526060820151610ef6906008907f00000000000000000000000000000000000000000000000000000000000000008f613ebe565b15156040830152600290810b810b602083018190527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff27618910b1215610f5f577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff276186020820152610f7e565b6020810151620d89e860029190910b1315610f7e57620d89e860208201525b610f8b816020015161403c565b73ffffffffffffffffffffffffffffffffffffffff166060820152604082015161105d908d610fec578b73ffffffffffffffffffffffffffffffffffffffff16836060015173ffffffffffffffffffffffffffffffffffffffff1611611020565b8b73ffffffffffffffffffffffffffffffffffffffff16836060015173ffffffffffffffffffffffffffffffffffffffff16105b61102e578260600151611030565b8b5b60c085015185517f00000000000000000000000000000000000000000000000000000000000000006143cf565b60c085015260a0840152608083015273ffffffffffffffffffffffffffffffffffffffff16604083015282156110cc576110a08160c001518260800151016145f5565b825103825260a08101516110c2906110b7906145f5565b602084015190614627565b6020830152611107565b6110d98160a001516145f5565b825101825260c08101516080820151611101916110f691016145f5565b602084015190614643565b60208301525b835160ff1615611156576000846000015160ff168260c001518161112757fe5b60c0840180519290910491829003905260a0840180519091016fffffffffffffffffffffffffffffffff169052505b60c08201516fffffffffffffffffffffffffffffffff16156111b4576111a88160c001517001000000000000000000000000000000008460c001516fffffffffffffffffffffffffffffffff16614659565b60808301805190910190525b806060015173ffffffffffffffffffffffffffffffffffffffff16826040015173ffffffffffffffffffffffffffffffffffffffff16141561130d578060400151156112e4578360a0015161126557611236846040015160008760200151886040015188602001518a60600151600a614727909695949392919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff166080860152600690810b900b6060850152600160a08501525b60006112b182602001518e61127c57600354611282565b84608001515b8f611291578560800151611295565b6004545b608089015160608a015160408b015160079594939291906148f8565b90508c156112bd576000035b6112cb8360c0015182614a2d565b6fffffffffffffffffffffffffffffffff1660c0840152505b8b6112f35780602001516112fc565b60018160200151035b600290810b900b6060830152611362565b806000015173ffffffffffffffffffffffffffffffffffffffff16826040015173ffffffffffffffffffffffffffffffffffffffff1614611362576113558260400151614b71565b600290810b900b60608301525b50610e59565b836020015160020b816060015160020b146114e0576000806113b686604001518660400151886020015188602001518a606001518b60800151600a614f3d909695949392919063ffffffff16565b60408501516060860151600280547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1679010000000000000000000000000000000000000000000000000061ffff95861602177fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff16770100000000000000000000000000000000000000000000009590941694909402929092177fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000062ffffff93850b9390931692909202919091177fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161790555061152a9050565b6040810151600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b8060c001516fffffffffffffffffffffffffffffffff1683602001516fffffffffffffffffffffffffffffffff16146115a35760c0810151600680547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9092169190911790555b8a1561161d57608081015160035560a08101516fffffffffffffffffffffffffffffffff16156116185760a0810151600580547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff918216909301169190911790555b611682565b608081015160045560a08101516fffffffffffffffffffffffffffffffff16156116825760a0810151600580546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000092839004821690940116029190911790555b8115158b15151461169b57602081015181518b036116a8565b80600001518a0381602001515b90965094508a1561181c5760008512156116e4576001546116e49073ffffffffffffffffffffffffffffffffffffffff168d6000889003615183565b60006116ee61535f565b90503373ffffffffffffffffffffffffffffffffffffffff1663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561177f57600080fd5b505af1158015611793573d6000803e3d6000fd5b5050505061179f61535f565b6117a982896154d1565b111561181657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4949410000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b50611987565b60008612156118535761185360008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d88600003615183565b600061185d6154e1565b90503373ffffffffffffffffffffffffffffffffffffffff1663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156118ee57600080fd5b505af1158015611902573d6000803e3d6000fd5b5050505061190e6154e1565b61191882886154d1565b111561198557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4949410000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b505b60408082015160c083015160608085015184518b8152602081018b905273ffffffffffffffffffffffffffffffffffffffff948516818701526fffffffffffffffffffffffffffffffff9093169183019190915260020b60808201529151908e169133917fc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca679181900360a00190a35050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550919890975095505050505050565b6006546fffffffffffffffffffffffffffffffff1681565b6005546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b600a8161ffff8110611ac857600080fd5b015463ffffffff81169150640100000000810460060b906b010000000000000000000000810473ffffffffffffffffffffffffffffffffffffffff16907f0100000000000000000000000000000000000000000000000000000000000000900460ff1684565b6002547e01000000000000000000000000000000000000000000000000000000000000900460ff16611bc157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055611bf1613e76565b6002547b01000000000000000000000000000000000000000000000000000000900461ffff166000611c25600a83856155b4565b6002805461ffff8084167b0100000000000000000000000000000000000000000000000000000081027fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff90931692909217909255919250831614611cc4576040805161ffff80851682528316602082015281517fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a929181900390910190a15b5050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550565b6002805473ffffffffffffffffffffffffffffffffffffffff811691740100000000000000000000000000000000000000008204900b9061ffff77010000000000000000000000000000000000000000000000820481169179010000000000000000000000000000000000000000000000000081048216917b010000000000000000000000000000000000000000000000000000008204169060ff7d01000000000000000000000000000000000000000000000000000000000082048116917e0100000000000000000000000000000000000000000000000000000000000090041687565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff16611e8f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556fffffffffffffffffffffffffffffffff8516611ed357600080fd5b600080611f3760405180608001604052808c73ffffffffffffffffffffffffffffffffffffffff1681526020018b60020b81526020018a60020b8152602001611f2d8a6fffffffffffffffffffffffffffffffff166156a8565b600f0b90526156b9565b92509250508193508092506000806000861115611f5957611f5661535f565b91505b8415611f6a57611f676154e1565b90505b3373ffffffffffffffffffffffffffffffffffffffff1663d348799787878b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015611ff957600080fd5b505af115801561200d573d6000803e3d6000fd5b5050505060008611156120995761202261535f565b61202c83886154d1565b111561209957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4d30000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b841561211e576120a76154e1565b6120b182876154d1565b111561211e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4d31000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8960020b8b60020b8d73ffffffffffffffffffffffffffffffffffffffff167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde338d8b8b604051808573ffffffffffffffffffffffffffffffffffffffff168152602001846fffffffffffffffffffffffffffffffff16815260200183815260200182815260200194505050505060405180910390a45050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550919890975095505050505050565b60045481565b6002547e01000000000000000000000000000000000000000000000000000000000000900460ff166122a757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556122d7613e76565b6006546fffffffffffffffffffffffffffffffff168061235857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f4c00000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600061238d867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f4240615a15565b905060006123c4867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f4240615a15565b905060006123d061535f565b905060006123dc6154e1565b90508815612408576000546124089073ffffffffffffffffffffffffffffffffffffffff168b8b615183565b8715612432576001546124329073ffffffffffffffffffffffffffffffffffffffff168b8a615183565b3373ffffffffffffffffffffffffffffffffffffffff1663e9cbafb085858a8a6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156124c157600080fd5b505af11580156124d5573d6000803e3d6000fd5b5050505060006124e361535f565b905060006124ef6154e1565b9050816124fc85886154d1565b111561256957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4630000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8061257484876154d1565b11156125e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4631000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b83820383820381156126ca576002547d0100000000000000000000000000000000000000000000000000000000009004600f166000811561262e578160ff16848161262857fe5b04612631565b60005b90506fffffffffffffffffffffffffffffffff81161561268e57600580546fffffffffffffffffffffffffffffffff8082168401167fffffffffffffffffffffffffffffffff000000000000000000000000000000009091161790555b6126be8185037001000000000000000000000000000000008d6fffffffffffffffffffffffffffffffff16614659565b60038054909101905550505b80156127a4576002547d010000000000000000000000000000000000000000000000000000000000900460041c600f1660008115612714578160ff16838161270e57fe5b04612717565b60005b90506fffffffffffffffffffffffffffffffff81161561276857600580546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008083048216850182160291161790555b6127988184037001000000000000000000000000000000008d6fffffffffffffffffffffffffffffffff16614659565b60048054909101905550505b8d73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a35050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179055505050505050505050505050565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff1661291257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16905560006129496009338989615a6d565b60038101549091506fffffffffffffffffffffffffffffffff90811690861611612973578461298b565b60038101546fffffffffffffffffffffffffffffffff165b60038201549093506fffffffffffffffffffffffffffffffff7001000000000000000000000000000000009091048116908516116129c957836129f5565b600381015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff165b91506fffffffffffffffffffffffffffffffff831615612a7e576003810180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff918216869003821617909155600054612a7e9173ffffffffffffffffffffffffffffffffffffffff909116908a908616615183565b6fffffffffffffffffffffffffffffffff821615612afd576003810180546fffffffffffffffffffffffffffffffff700100000000000000000000000000000000808304821686900382160291811691909117909155600154612afd9173ffffffffffffffffffffffffffffffffffffffff909116908a908516615183565b6040805173ffffffffffffffffffffffffffffffffffffffff8a1681526fffffffffffffffffffffffffffffffff80861660208301528416818301529051600288810b92908a900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a450600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905590969095509350505050565b60096020526000908152604090208054600182015460028301546003909301546fffffffffffffffffffffffffffffffff9283169391928181169170010000000000000000000000000000000090041685565b60086020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6002547e01000000000000000000000000000000000000000000000000000000000000900460ff16612ce557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055604080517f8da5cb5b000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b158015612d9357600080fd5b505afa158015612da7573d6000803e3d6000fd5b505050506040513d6020811015612dbd57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff163314612de057600080fd5b60ff82161580612e03575060048260ff1610158015612e035750600a8260ff1611155b8015612e2d575060ff81161580612e2d575060048160ff1610158015612e2d5750600a8160ff1611155b612e3657600080fd5b60028054610ff0600484901b16840160ff9081167d0100000000000000000000000000000000000000000000000000000000009081027fffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841617909355919004167f973d8d92bb299f4af6ce49b52a8adb85ae46b9f214c4c4fc06ac77401237b1336010826040805160ff9390920683168252600f600486901c16602083015286831682820152918516606082015290519081900360800190a15050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff16612fd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055604080517f8da5cb5b000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b15801561308457600080fd5b505afa158015613098573d6000803e3d6000fd5b505050506040513d60208110156130ae57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1633146130d157600080fd5b6005546fffffffffffffffffffffffffffffffff908116908516116130f6578361310c565b6005546fffffffffffffffffffffffffffffffff165b6005549092506fffffffffffffffffffffffffffffffff7001000000000000000000000000000000009091048116908416116131485782613172565b60055470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff165b90506fffffffffffffffffffffffffffffffff82161561323e576005546fffffffffffffffffffffffffffffffff838116911614156131d1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b600580547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff91821685900382161790915560005461323e9173ffffffffffffffffffffffffffffffffffffffff9091169087908516615183565b6fffffffffffffffffffffffffffffffff811615613311576005546fffffffffffffffffffffffffffffffff8281167001000000000000000000000000000000009092041614156132ac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b600580546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008083048216859003821602918116919091179091556001546133119173ffffffffffffffffffffffffffffffffffffffff9091169087908416615183565b604080516fffffffffffffffffffffffffffffffff808516825283166020820152815173ffffffffffffffffffffffffffffffffffffffff88169233927f596b573906218d3411850b26a6b437d6c4522fdb43d2d2386263f86d50b8b151929081900390910190a3600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790559094909350915050565b6060806133d7613e76565b6134946133e2613eba565b8585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060028054600654600a9695945074010000000000000000000000000000000000000000820490920b925061ffff7701000000000000000000000000000000000000000000000082048116926fffffffffffffffffffffffffffffffff1691790100000000000000000000000000000000000000000000000000900416615ae4565b915091509250929050565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff1661353757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681556040805160808101825233815287830b60208201529186900b90820152600090819081906135b390606081016135a66fffffffffffffffffffffffffffffffff8a166156a8565b600003600f0b90526156b9565b92509250925081600003945080600003935060008511806135d45750600084115b15613641576003830180546fffffffffffffffffffffffffffffffff808216808901821670010000000000000000000000000000000093849004831689019092169092029091177fffffffffffffffffffffffffffffffff00000000000000000000000000000000161790555b604080516fffffffffffffffffffffffffffffffff88168152602081018790528082018690529051600289810b92908b900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a45050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179055509094909350915050565b6000806000613704613e76565b61370e8585615c8e565b600285810b810b60009081526007602052604080822087840b90930b825281206003830154600681900b93670100000000000000820473ffffffffffffffffffffffffffffffffffffffff169284927b01000000000000000000000000000000000000000000000000000000810463ffffffff1692849290917f0100000000000000000000000000000000000000000000000000000000000000900460ff16806137b757600080fd5b6003820154600681900b9850670100000000000000810473ffffffffffffffffffffffffffffffffffffffff1696507b01000000000000000000000000000000000000000000000000000000810463ffffffff1694507f0100000000000000000000000000000000000000000000000000000000000000900460ff168061383d57600080fd5b50506040805160e0810182526002805473ffffffffffffffffffffffffffffffffffffffff81168352740100000000000000000000000000000000000000008104820b820b820b6020840181905261ffff7701000000000000000000000000000000000000000000000083048116958501959095527901000000000000000000000000000000000000000000000000008204851660608501527b010000000000000000000000000000000000000000000000000000008204909416608084015260ff7d0100000000000000000000000000000000000000000000000000000000008204811660a08501527e0100000000000000000000000000000000000000000000000000000000000090910416151560c08301529093508e810b91900b1215905061397757509390940396509003935090039050613a03565b8a60020b816020015160020b12156139f4576000613993613eba565b60208301516040840151600654606086015193945060009384936139d293600a938893879392916fffffffffffffffffffffffffffffffff1690614727565b9a9003989098039b505094909603929092039650909103039250613a03915050565b50949093039650039350900390505b9250925092565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b60035481565b60076020526000908152604090208054600182015460028301546003909301546fffffffffffffffffffffffffffffffff831693700100000000000000000000000000000000909304600f0b9290600681900b90670100000000000000810473ffffffffffffffffffffffffffffffffffffffff16907b01000000000000000000000000000000000000000000000000000000810463ffffffff16907f0100000000000000000000000000000000000000000000000000000000000000900460ff1688565b60025473ffffffffffffffffffffffffffffffffffffffff1615613be257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4149000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6000613bed82614b71565b9050600080613c05613bfd613eba565b600a90615e0f565b6040805160e08101825273ffffffffffffffffffffffffffffffffffffffff8816808252600288810b6020808501829052600085870181905261ffff898116606088018190529089166080880181905260a0880192909252600160c09097019690965283547e010000000000000000000000000000000000000000000000000000000000007fffffffffffffffffffffffff000000000000000000000000000000000000000090911686177fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000062ffffff86880b1602177fffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff16790100000000000000000000000000000000000000000000000000909702969096177fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b01000000000000000000000000000000000000000000000000000000909102177fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169490941790915583519182529181019190915281519395509193507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c9592918290030190a150505050565b60008082600281900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2761881613e1657fe5b05029050600083600281900b620d89e881613e2d57fe5b0502905060008460020b83830360020b81613e4457fe5b0560010190508062ffffff166fffffffffffffffffffffffffffffffff801681613e6a57fe5b0493505050505b919050565b3073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614613eb857600080fd5b565b4290565b60008060008460020b8660020b81613ed257fe5b05905060008660020b128015613ef957508460020b8660020b81613ef257fe5b0760020b15155b15613f21577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b8315613fb457600080613f3383615e92565b600182810b810b600090815260208d9052604090205460ff83169190911b80017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190811680151597509294509092509085613f9657888360ff16860302613fa9565b88613fa082615ea4565b840360ff168603025b965050505050614032565b600080613fc383600101615e92565b91509150600060018260ff166001901b031990506000818b60008660010b60010b815260200190815260200160002054169050806000141595508561401557888360ff0360ff1686600101010261402b565b888361402083615f51565b0360ff168660010101025b9650505050505b5094509492505050565b60008060008360020b12614053578260020b61405b565b8260020b6000035b9050620d89e88111156140cf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f5400000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6000600182166140f057700100000000000000000000000000000000614102565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615614136576ffff97272373d413259a46990580e213a0260801c5b6004821615614155576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614174576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615614193576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156141b2576fff973b41fa98c081472e6896dfb254c00260801c5b60408216156141d1576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156141f0576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615614210576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615614230576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614250576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614270576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614290576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156142b0576fa9f746462d870fdf8a65dc1f90e061e50260801c5b6140008216156142d0576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156142f0576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614311576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614331576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614350576d2216e584f5fa1ea926041bedfe980260801c5b6208000082161561436d576b048a170391f7dc42444e8fa20260801c5b60008460020b13156143a657807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff816143a257fe5b0490505b6401000000008106156143ba5760016143bd565b60005b60ff16602082901c0192505050919050565b600080808073ffffffffffffffffffffffffffffffffffffffff808916908a1610158187128015906144615760006144158989620f42400362ffffff16620f4240614659565b90508261442e576144298c8c8c6001616134565b61443b565b61443b8b8d8c6001616207565b955085811061444c578a965061445b565b6144588c8b838661631f565b96505b506144ab565b81614478576144738b8b8b6000616207565b614485565b6144858a8c8b6000616134565b9350838860000310614499578995506144ab565b6144a88b8a8a60000385616381565b95505b73ffffffffffffffffffffffffffffffffffffffff8a811690871614821561451b578080156144d75750815b6144ed576144e8878d8c6001616207565b6144ef565b855b95508080156144fc575081155b6145125761450d878d8c6000616134565b614514565b845b9450614565565b8080156145255750815b61453b576145368c888c6001616134565b61453d565b855b955080801561454a575081155b6145605761455b8c888c6000616207565b614562565b845b94505b8115801561457557508860000385115b15614581578860000394505b8180156145ba57508a73ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614155b156145c95785890393506145e6565b6145e3868962ffffff168a620f42400362ffffff16615a15565b93505b50505095509550955095915050565b60007f8000000000000000000000000000000000000000000000000000000000000000821061462357600080fd5b5090565b8082038281131560008312151461463d57600080fd5b92915050565b8181018281121560008312151461463d57600080fd5b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858709868602925082811090839003039050806146ad57600084116146a257600080fd5b508290049050614720565b8084116146b957600080fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b60008063ffffffff87166147ff576000898661ffff1661ffff811061474857fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b60208501526b010000000000000000000000830473ffffffffffffffffffffffffffffffffffffffff16948401949094527f010000000000000000000000000000000000000000000000000000000000000090910460ff16151560608301529092508a16146147eb576147e8818a89886163e3565b90505b8060200151816040015192509250506148ec565b8688036000806148148c8c858c8c8c8c6164b2565b91509150816000015163ffffffff168363ffffffff1614156148465781602001518260400151945094505050506148ec565b805163ffffffff8481169116141561486e5780602001518160400151945094505050506148ec565b8151815160208085015190840151918390039286039163ffffffff80841692908516910360060b8161489c57fe5b05028460200151018263ffffffff168263ffffffff16866040015186604001510373ffffffffffffffffffffffffffffffffffffffff1602816148db57fe5b048560400151019650965050505050505b97509795505050505050565b600295860b860b60009081526020979097526040909620600181018054909503909455938301805490920390915560038201805463ffffffff7b0100000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff6701000000000000008085048216909603169094027fffffffffff0000000000000000000000000000000000000000ffffffffffffff90921691909117600681810b90960390950b66ffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000909516949094178281048516909503909316027fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff909316929092179055547001000000000000000000000000000000009004600f0b90565b60008082600f0b1215614ad957826fffffffffffffffffffffffffffffffff168260000384039150816fffffffffffffffffffffffffffffffff1610614ad457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4c53000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b61463d565b826fffffffffffffffffffffffffffffffff168284019150816fffffffffffffffffffffffffffffffff16101561463d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4c41000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60006401000276a373ffffffffffffffffffffffffffffffffffffffff831610801590614bc7575073fffd8963efd1fc6a506488495d951d5263988d2673ffffffffffffffffffffffffffffffffffffffff8316105b614c3257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f5200000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b77ffffffffffffffffffffffffffffffffffffffff00000000602083901b166fffffffffffffffffffffffffffffffff811160071b81811c67ffffffffffffffff811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c97908811961790941790921717909117171760808110614cdc57607f810383901c9150614ce6565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808f0160401b60c09190911c678000000000000000161760c19b909b1c674000000000000000169a909a1760c29990991c672000000000000000169890981760c39790971c671000000000000000169690961760c49590951c670800000000000000169490941760c59390931c670400000000000000169290921760c69190911c670200000000000000161760c79190911c670100000000000000161760c89190911c6680000000000000161760c99190911c6640000000000000161760ca9190911c6620000000000000161760cb9190911c6610000000000000161760cc9190911c6608000000000000161760cd9190911c66040000000000001617693627a301d71055774c8581027ffffffffffffffffffffffffffffffffffd709b7e5480fba5a50fed5e62ffc5568101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b14614f2e578873ffffffffffffffffffffffffffffffffffffffff16614f058261403c565b73ffffffffffffffffffffffffffffffffffffffff161115614f275781614f29565b805b614f30565b815b9998505050505050505050565b6000806000898961ffff1661ffff8110614f5357fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b60208501526b010000000000000000000000830473ffffffffffffffffffffffffffffffffffffffff16948401949094527f010000000000000000000000000000000000000000000000000000000000000090910460ff161515606083015290925089161415614ff457888592509250506148ec565b8461ffff168461ffff1611801561501557506001850361ffff168961ffff16145b1561502257839150615026565b8491505b8161ffff168960010161ffff168161503a57fe5b069250615049818989896163e3565b8a8461ffff1661ffff811061505a57fe5b8251910180546020840151604085015160609095015115157f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff9096166b010000000000000000000000027fff0000000000000000000000000000000000000000ffffffffffffffffffffff60069390930b66ffffffffffffff16640100000000027fffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff63ffffffff9097167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009095169490941795909516929092171692909217929092161790555097509795505050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b6020831061525857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161521b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146152ba576040519150601f19603f3d011682016040523d82523d6000602084013e6152bf565b606091505b50915091508180156152ed5750805115806152ed57508080602001905160208110156152ea57600080fd5b50515b61535857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5446000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b5050505050565b60008054604080513060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000178152915181518594859473ffffffffffffffffffffffffffffffffffffffff9091169392918291908083835b6020831061543157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016153f4565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615491576040519150601f19603f3d011682016040523d82523d6000602084013e615496565b606091505b50915091508180156154aa57506020815110155b6154b357600080fd5b8080602001905160208110156154c857600080fd5b50519250505090565b8082018281101561463d57600080fd5b600154604080513060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000178152915181516000948594859473ffffffffffffffffffffffffffffffffffffffff9092169391928291908083836020831061543157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016153f4565b6000808361ffff161161562857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f4900000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8261ffff168261ffff161161563e575081614720565b825b8261ffff168161ffff16101561569f576001858261ffff1661ffff811061566357fe5b0180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff92909216919091179055600101615640565b50909392505050565b80600f81900b8114613e7157600080fd5b60008060006156c6613e76565b6156d884602001518560400151615c8e565b6040805160e0810182526002805473ffffffffffffffffffffffffffffffffffffffff81168352740100000000000000000000000000000000000000008104820b820b90910b602080840182905261ffff770100000000000000000000000000000000000000000000008404811685870152790100000000000000000000000000000000000000000000000000840481166060808701919091527b010000000000000000000000000000000000000000000000000000008504909116608086015260ff7d0100000000000000000000000000000000000000000000000000000000008504811660a08701527e01000000000000000000000000000000000000000000000000000000000000909404909316151560c0850152885190890151948901519289015193946158109491939092909190616776565b93508460600151600f0b600014615a0d57846020015160020b816020015160020b12156158655761585e615847866020015161403c565b615854876040015161403c565b8760600151616970565b9250615a0d565b846040015160020b816020015160020b12156159e35760065460408201516fffffffffffffffffffffffffffffffff909116906158c0906158a4613eba565b602085015160608601516080870151600a949392918791614f3d565b600280547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1679010000000000000000000000000000000000000000000000000061ffff93841602177fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff167701000000000000000000000000000000000000000000000093909216929092021790558151604087015161596e91906159649061403c565b8860600151616970565b935061598c615980876020015161403c565b835160608901516169b4565b925061599c818760600151614a2d565b600680547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9290921691909117905550615a0d565b615a0a6159f3866020015161403c565b615a00876040015161403c565b87606001516169b4565b91505b509193909250565b6000615a22848484614659565b905060008280615a2e57fe5b8486091115614720577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110615a6357600080fd5b6001019392505050565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080860191909152600293840b60e890811b60348701529290930b90911b60378401528051808403601a018152603a90930181528251928201929092206000908152929052902090565b60608060008361ffff1611615b5a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f4900000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b865167ffffffffffffffff81118015615b7257600080fd5b50604051908082528060200260200182016040528015615b9c578160200160208202803683370190505b509150865167ffffffffffffffff81118015615bb757600080fd5b50604051908082528060200260200182016040528015615be1578160200160208202803683370190505b50905060005b8751811015615c8157615c128a8a8a8481518110615c0157fe5b60200260200101518a8a8a8a614727565b848381518110615c1e57fe5b60200260200101848481518110615c3157fe5b602002602001018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152508260060b60060b81525050508080600101915050615be7565b5097509795505050505050565b8060020b8260020b12615d0257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f544c550000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff27618600283900b1215615d9557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f544c4d0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b620d89e8600282900b1315615e0b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f54554d0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b5050565b6040805160808101825263ffffffff928316808252600060208301819052928201929092526001606090910181905283547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000169091179091167f010000000000000000000000000000000000000000000000000000000000000017909155908190565b60020b600881901d9161010090910790565b6000808211615eb257600080fd5b7001000000000000000000000000000000008210615ed257608091821c91015b680100000000000000008210615eea57604091821c91015b6401000000008210615efe57602091821c91015b620100008210615f1057601091821c91015b6101008210615f2157600891821c91015b60108210615f3157600491821c91015b60048210615f4157600291821c91015b60028210613e7157600101919050565b6000808211615f5f57600080fd5b5060ff6fffffffffffffffffffffffffffffffff821615615fa1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001615fa9565b608082901c91505b67ffffffffffffffff821615615fe0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc001615fe8565b604082901c91505b63ffffffff82161561601b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001616023565b602082901c91505b61ffff821615616054577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00161605c565b601082901c91505b60ff82161561608c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff801616094565b600882901c91505b600f8216156160c4577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc016160cc565b600482901c91505b60038216156160fc577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01616104565b600282901c91505b6001821615613e71577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01919050565b60008373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16111561616e579293925b816161ba576161b5836fffffffffffffffffffffffffffffffff1686860373ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000614659565b6161fc565b6161fc836fffffffffffffffffffffffffffffffff1686860373ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000615a15565b90505b949350505050565b60008373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161115616241579293925b7bffffffffffffffffffffffffffffffff000000000000000000000000606084901b1673ffffffffffffffffffffffffffffffffffffffff868603811690871661628a57600080fd5b836162d4578673ffffffffffffffffffffffffffffffffffffffff166162c783838973ffffffffffffffffffffffffffffffffffffffff16614659565b816162ce57fe5b04616314565b6163146162f883838973ffffffffffffffffffffffffffffffffffffffff16615a15565b8873ffffffffffffffffffffffffffffffffffffffff166169e3565b979650505050505050565b6000808573ffffffffffffffffffffffffffffffffffffffff161161634357600080fd5b6000846fffffffffffffffffffffffffffffffff161161636257600080fd5b81616374576161b585858560016169ee565b6161fc8585856001616b46565b6000808573ffffffffffffffffffffffffffffffffffffffff16116163a557600080fd5b6000846fffffffffffffffffffffffffffffffff16116163c457600080fd5b816163d6576161b58585856000616b46565b6161fc85858560006169ee565b6163eb617659565b600085600001518503905060405180608001604052808663ffffffff1681526020018263ffffffff168660020b0288602001510160060b81526020016000856fffffffffffffffffffffffffffffffff161161644857600161644a565b845b6fffffffffffffffffffffffffffffffff1673ffffffff00000000000000000000000000000000608085901b168161647e57fe5b0488604001510173ffffffffffffffffffffffffffffffffffffffff16815260200160011515815250915050949350505050565b6164ba617659565b6164c2617659565b888561ffff1661ffff81106164d357fe5b60408051608081018252919092015463ffffffff81168083526401000000008204600690810b810b900b60208401526b010000000000000000000000820473ffffffffffffffffffffffffffffffffffffffff16938301939093527f0100000000000000000000000000000000000000000000000000000000000000900460ff1615156060820152925061656990899089616c7c565b156165a1578663ffffffff16826000015163ffffffff16141561658b576148ec565b81616598838989886163e3565b915091506148ec565b888361ffff168660010161ffff16816165b657fe5b0661ffff1661ffff81106165c657fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b602084015273ffffffffffffffffffffffffffffffffffffffff6b0100000000000000000000008204169183019190915260ff7f0100000000000000000000000000000000000000000000000000000000000000909104161515606082018190529092506166df57604080516080810182528a5463ffffffff811682526401000000008104600690810b810b900b60208301526b010000000000000000000000810473ffffffffffffffffffffffffffffffffffffffff16928201929092527f010000000000000000000000000000000000000000000000000000000000000090910460ff161515606082015291505b6166ee88836000015189616c7c565b61675957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4f4c440000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6167668989898887616d3f565b9150915097509795505050505050565b60006167856009878787615a6d565b60035460045491925090600080600f87900b156169105760006167a6613eba565b60028054600654929350600092839261683592600a928792869274010000000000000000000000000000000000000000810490910b9161ffff7701000000000000000000000000000000000000000000000083048116926fffffffffffffffffffffffffffffffff90921691790100000000000000000000000000000000000000000000000000900416614727565b909250905061686f60078d8b8d8b8b87898b60007f0000000000000000000000000000000000000000000000000000000000000000616f41565b94506168a660078c8b8d8b8b87898b60017f0000000000000000000000000000000000000000000000000000000000000000616f41565b935084156168da576168da60088d7f0000000000000000000000000000000000000000000000000000000000000000617246565b831561690c5761690c60088c7f0000000000000000000000000000000000000000000000000000000000000000617246565b5050505b60008061692260078c8c8b8a8a6172ac565b9092509050616933878a8484617358565b600089600f0b12156169615783156169505761695060078c6175ce565b82156169615761696160078b6175ce565b50505050505095945050505050565b60008082600f0b126169965761699161698c8585856001616207565b6145f5565b6161ff565b6169a961698c8585856000036000616207565b600003949350505050565b60008082600f0b126169d05761699161698c8585856001616134565b6169a961698c8585856000036000616134565b808204910615150190565b60008115616a9657600073ffffffffffffffffffffffffffffffffffffffff841115616a4357616a3e846c01000000000000000000000000876fffffffffffffffffffffffffffffffff16614659565b616a64565b6fffffffffffffffffffffffffffffffff8516606085901b81616a6257fe5b045b9050616a8e616a8973ffffffffffffffffffffffffffffffffffffffff8816836154d1565b6175fa565b9150506161ff565b600073ffffffffffffffffffffffffffffffffffffffff841115616ae357616ade846c01000000000000000000000000876fffffffffffffffffffffffffffffffff16615a15565b616b03565b616b03606085901b6fffffffffffffffffffffffffffffffff87166169e3565b9050808673ffffffffffffffffffffffffffffffffffffffff1611616b2757600080fd5b73ffffffffffffffffffffffffffffffffffffffff86160390506161ff565b600082616b545750836161ff565b7bffffffffffffffffffffffffffffffff000000000000000000000000606085901b168215616c1b5773ffffffffffffffffffffffffffffffffffffffff861684810290858281616ba157fe5b041415616bdf57818101828110616bdd57616bd3838973ffffffffffffffffffffffffffffffffffffffff1683615a15565b93505050506161ff565b505b616c1282616c0d878a73ffffffffffffffffffffffffffffffffffffffff168681616c0657fe5b04906154d1565b6169e3565b925050506161ff565b73ffffffffffffffffffffffffffffffffffffffff861684810290858281616c3f57fe5b04148015616c4c57508082115b616c5557600080fd5b808203616bd3616a898473ffffffffffffffffffffffffffffffffffffffff8b1684615a15565b60008363ffffffff168363ffffffff1611158015616ca657508363ffffffff168263ffffffff1611155b15616cc2578163ffffffff168363ffffffff1611159050614720565b60008463ffffffff168463ffffffff1611616cea578363ffffffff1664010000000001616cf2565b8363ffffffff165b64ffffffffff16905060008563ffffffff168463ffffffff1611616d23578363ffffffff1664010000000001616d2b565b8363ffffffff165b64ffffffffff169091111595945050505050565b616d47617659565b616d4f617659565b60008361ffff168560010161ffff1681616d6557fe5b0661ffff169050600060018561ffff16830103905060005b506002818301048961ffff87168281616d9257fe5b0661ffff8110616d9e57fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b602084015273ffffffffffffffffffffffffffffffffffffffff6b0100000000000000000000008204169183019190915260ff7f010000000000000000000000000000000000000000000000000000000000000090910416151560608201819052909550616e3a57806001019250616d7d565b898661ffff168260010181616e4b57fe5b0661ffff8110616e5757fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b602084015273ffffffffffffffffffffffffffffffffffffffff6b0100000000000000000000008204169183019190915260ff7f010000000000000000000000000000000000000000000000000000000000000090910416151560608201528551909450600090616ef3908b908b616c7c565b9050808015616f0c5750616f0c8a8a8760000151616c7c565b15616f175750616f34565b80616f2757600182039250616f2e565b8160010193505b50616d7d565b5050509550959350505050565b60028a810b900b600090815260208c90526040812080546fffffffffffffffffffffffffffffffff1682616f75828d614a2d565b9050846fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff16111561700a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4c4f000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6fffffffffffffffffffffffffffffffff828116159082161581141594501561715a578c60020b8e60020b1361710e57600183018b9055600283018a90556003830180547fffffffffff0000000000000000000000000000000000000000ffffffffffffff1667010000000000000073ffffffffffffffffffffffffffffffffffffffff8c1602177fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001666ffffffffffffff60068b900b16177fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b0100000000000000000000000000000000000000000000000000000063ffffffff8a16021790555b6003830180547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790555b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff8216178355856171d15782546171cc906171c7907001000000000000000000000000000000009004600f90810b810b908f900b614643565b6156a8565b6171ff565b82546171ff906171c7907001000000000000000000000000000000009004600f90810b810b908f900b614627565b8354600f9190910b6fffffffffffffffffffffffffffffffff9081167001000000000000000000000000000000000291161790925550909c9b505050505050505050505050565b8060020b8260020b8161725557fe5b0760020b1561726357600080fd5b60008061727e8360020b8560020b8161727857fe5b05615e92565b600191820b820b60009081526020979097526040909620805460ff9097169190911b90951890945550505050565b600285810b80820b60009081526020899052604080822088850b850b83529082209193849391929184918291908a900b126172f257505060018201546002830154617305565b8360010154880391508360020154870390505b6000808b60020b8b60020b12156173275750506001830154600284015461733a565b84600101548a0391508460020154890390505b92909803979097039b96909503949094039850939650505050505050565b6040805160a08101825285546fffffffffffffffffffffffffffffffff90811682526001870154602083015260028701549282019290925260038601548083166060830152700100000000000000000000000000000000900490911660808201526000600f85900b61744b5781516fffffffffffffffffffffffffffffffff1661744357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4e50000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b50805161745a565b81516174579086614a2d565b90505b60006174948360200151860384600001516fffffffffffffffffffffffffffffffff16700100000000000000000000000000000000614659565b905060006174d08460400151860385600001516fffffffffffffffffffffffffffffffff16700100000000000000000000000000000000614659565b905086600f0b6000146175185787547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff84161788555b60018801869055600288018590556fffffffffffffffffffffffffffffffff821615158061755857506000816fffffffffffffffffffffffffffffffff16115b156175c4576003880180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff91821685018216178082167001000000000000000000000000000000009182900483168501909216021790555b5050505050505050565b600290810b810b6000908152602092909252604082208281556001810183905590810182905560030155565b8073ffffffffffffffffffffffffffffffffffffffff81168114613e7157600080fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040805160808101825260008082526020820181905291810182905260608101919091529056fea2646970667358221220c186b9a0089f20a2f3c54ff0d2f772daf3a8b3a2ab747f3ca54c05872070e7f164736f6c63430007060033"; + "0x6101206040523480156200001257600080fd5b503060601b60805260408051630890357360e41b81529051600091339163890357309160048082019260a092909190829003018186803b1580156200005657600080fd5b505afa1580156200006b573d6000803e3d6000fd5b505050506040513d60a08110156200008257600080fd5b508051602080830151604084015160608086015160809096015160e896871b6001600160e81b03191660c052600180546001600160a01b03199081166001600160a01b039586161790915560008054909116939094169290921790925592901b6001600160601b03191660a052600282810b900b90921b60e0529150620001149082906200012c811b62003de517901c565b60801b6001600160801b03191661010052506200019a565b60008082600281900b620d89e719816200014257fe5b05029050600083600281900b620d89e8816200015a57fe5b0502905060008460020b83830360020b816200017257fe5b0560010190508062ffffff166001600160801b038016816200019057fe5b0495945050505050565b60805160601c60a05160601c60c05160e81c60e05160e81c6101005160801c6176b66200021b60003980612c30528061684b5280616882525080610ed15280613a3052806168b652806168e8525080611039528061236052806123975280613a70525080612d4d528061303e5280613a0c525080613e8e52506176b66000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806370cf754a116100ee578063c45a015511610097578063ddca3f4311610071578063ddca3f43146108bd578063f3058399146108dd578063f30dba93146108e5578063f637731d1461097d576101ae565b8063c45a01551461088e578063d0c93a7c14610896578063d21220a7146108b5576101ae565b8063883bdbfd116100c8578063883bdbfd146106da578063a34123a7146107e3578063a38807f214610826576101ae565b806370cf754a146106575780638206a4d11461065f57806385b6672914610687576101ae565b80633850c7bd1161015b578063490e6cbc11610135578063490e6cbc146104d55780634f1eb3d81461056e578063514ea4bf146105d55780635339c29614610637576101ae565b80633850c7bd1461039d5780633c8a7d8d1461040357806346141319146104bb576101ae565b80631ad8b03b1161018c5780631ad8b03b146102cd578063252c09d71461031657806332148f671461037a576101ae565b80630dfe1681146101b3578063128acb08146101e45780631a686502146102a0575b600080fd5b6101bb6109b0565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b610287600480360360a08110156101fa57600080fd5b73ffffffffffffffffffffffffffffffffffffffff82358116926020810135151592604082013592606083013516919081019060a08101608082013564010000000081111561024857600080fd5b82018360208201111561025a57600080fd5b8035906020019184600183028401116401000000008311171561027c57600080fd5b5090925090506109cc565b6040805192835260208301919091528051918290030190f35b6102a8611a6f565b604080516fffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6102d5611a87565b60405180836fffffffffffffffffffffffffffffffff168152602001826fffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b6103336004803603602081101561032c57600080fd5b5035611ab7565b6040805163ffffffff909516855260069390930b602085015273ffffffffffffffffffffffffffffffffffffffff9091168383015215156060830152519081900360800190f35b61039b6004803603602081101561039057600080fd5b503561ffff16611b2e565b005b6103a5611d12565b6040805173ffffffffffffffffffffffffffffffffffffffff909816885260029690960b602088015261ffff9485168787015292841660608701529216608085015260ff90911660a0840152151560c0830152519081900360e00190f35b610287600480360360a081101561041957600080fd5b73ffffffffffffffffffffffffffffffffffffffff823516916020810135600290810b92604083013590910b916fffffffffffffffffffffffffffffffff6060820135169181019060a08101608082013564010000000081111561047c57600080fd5b82018360208201111561048e57600080fd5b803590602001918460018302840111640100000000831117156104b057600080fd5b509092509050611df7565b6104c361220e565b60408051918252519081900360200190f35b61039b600480360360808110156104eb57600080fd5b73ffffffffffffffffffffffffffffffffffffffff823516916020810135916040820135919081019060808101606082013564010000000081111561052f57600080fd5b82018360208201111561054157600080fd5b8035906020019184600183028401116401000000008311171561056357600080fd5b509092509050612214565b6102d5600480360360a081101561058457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813516906020810135600290810b91604081013590910b906fffffffffffffffffffffffffffffffff6060820135811691608001351661287a565b6105f2600480360360208110156105eb57600080fd5b5035612bc9565b604080516fffffffffffffffffffffffffffffffff96871681526020810195909552848101939093529084166060840152909216608082015290519081900360a00190f35b6104c36004803603602081101561064d57600080fd5b503560010b612c1c565b6102a8612c2e565b61039b6004803603604081101561067557600080fd5b5060ff81358116916020013516612c52565b6102d56004803603606081101561069d57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813516906fffffffffffffffffffffffffffffffff60208201358116916040013516612f3e565b61074a600480360360208110156106f057600080fd5b81019060208101813564010000000081111561070b57600080fd5b82018360208201111561071d57600080fd5b8035906020019184602083028401116401000000008311171561073f57600080fd5b5090925090506133cc565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561078e578181015183820152602001610776565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156107cd5781810151838201526020016107b5565b5050505090500194505050505060405180910390f35b610287600480360360608110156107f957600080fd5b508035600290810b91602081013590910b90604001356fffffffffffffffffffffffffffffffff1661349f565b6108506004803603604081101561083c57600080fd5b508035600290810b9160200135900b6136f7565b6040805160069490940b845273ffffffffffffffffffffffffffffffffffffffff909216602084015263ffffffff1682820152519081900360600190f35b6101bb613a0a565b61089e613a2e565b6040805160029290920b8252519081900360200190f35b6101bb613a52565b6108c5613a6e565b6040805162ffffff9092168252519081900360200190f35b6104c3613a92565b610905600480360360208110156108fb57600080fd5b503560020b613a98565b604080516fffffffffffffffffffffffffffffffff9099168952600f9790970b602089015287870195909552606087019390935260069190910b608086015273ffffffffffffffffffffffffffffffffffffffff1660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b61039b6004803603602081101561099357600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16613b5d565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6000806109d7613e76565b85610a4357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4153000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6040805160e0810182526002805473ffffffffffffffffffffffffffffffffffffffff81168352740100000000000000000000000000000000000000008104820b820b90910b602083015261ffff7701000000000000000000000000000000000000000000000082048116938301939093527901000000000000000000000000000000000000000000000000008104831660608301527b010000000000000000000000000000000000000000000000000000008104909216608082015260ff7d0100000000000000000000000000000000000000000000000000000000008304811660a08301527e01000000000000000000000000000000000000000000000000000000000000909204909116151560c08201819052610bc457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b87610c3657806000015173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16118015610c31575073fffd8963efd1fc6a506488495d951d5263988d2673ffffffffffffffffffffffffffffffffffffffff8716105b610c8f565b806000015173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16108015610c8f57506401000276a373ffffffffffffffffffffffffffffffffffffffff8716115b610cfa57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f53504c0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556040805160c08101909152600090808a610d475760048460a0015160ff16901c610d5a565b60108460a0015160ff1681610d5857fe5b065b60ff1681526006546fffffffffffffffffffffffffffffffff166020820152604001610d84613eba565b63ffffffff168152602001600060060b8152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020016000151581525090506000808913905060006040518060e001604052808b815260200160008152602001856000015173ffffffffffffffffffffffffffffffffffffffff168152602001856020015160020b81526020018c610e1a57600454610e1e565b6003545b815260200160006fffffffffffffffffffffffffffffffff16815260200184602001516fffffffffffffffffffffffffffffffff1681525090505b805115801590610e9957508873ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1614155b1561136857610ea661761d565b604082015173ffffffffffffffffffffffffffffffffffffffff1681526060820151610ef6906008907f00000000000000000000000000000000000000000000000000000000000000008f613ebe565b15156040830152600290810b810b602083018190527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff27618910b1215610f5f577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff276186020820152610f7e565b6020810151620d89e860029190910b1315610f7e57620d89e860208201525b610f8b816020015161403c565b73ffffffffffffffffffffffffffffffffffffffff166060820152604082015161105d908d610fec578b73ffffffffffffffffffffffffffffffffffffffff16836060015173ffffffffffffffffffffffffffffffffffffffff1611611020565b8b73ffffffffffffffffffffffffffffffffffffffff16836060015173ffffffffffffffffffffffffffffffffffffffff16105b61102e578260600151611030565b8b5b60c085015185517f00000000000000000000000000000000000000000000000000000000000000006143cf565b60c085015260a0840152608083015273ffffffffffffffffffffffffffffffffffffffff16604083015282156110cc576110a08160c001518260800151016145f5565b825103825260a08101516110c2906110b7906145f5565b602084015190614627565b6020830152611107565b6110d98160a001516145f5565b825101825260c08101516080820151611101916110f691016145f5565b602084015190614643565b60208301525b835160ff1615611156576000846000015160ff168260c001518161112757fe5b60c0840180519290910491829003905260a0840180519091016fffffffffffffffffffffffffffffffff169052505b60c08201516fffffffffffffffffffffffffffffffff16156111b4576111a88160c001517001000000000000000000000000000000008460c001516fffffffffffffffffffffffffffffffff16614659565b60808301805190910190525b806060015173ffffffffffffffffffffffffffffffffffffffff16826040015173ffffffffffffffffffffffffffffffffffffffff16141561130d578060400151156112e4578360a0015161126557611236846040015160008760200151886040015188602001518a60600151600a614727909695949392919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff166080860152600690810b900b6060850152600160a08501525b60006112b182602001518e61127c57600354611282565b84608001515b8f611291578560800151611295565b6004545b608089015160608a015160408b015160079594939291906148f8565b90508c156112bd576000035b6112cb8360c0015182614a2d565b6fffffffffffffffffffffffffffffffff1660c0840152505b8b6112f35780602001516112fc565b60018160200151035b600290810b900b6060830152611362565b806000015173ffffffffffffffffffffffffffffffffffffffff16826040015173ffffffffffffffffffffffffffffffffffffffff1614611362576113558260400151614b71565b600290810b900b60608301525b50610e59565b836020015160020b816060015160020b146114e0576000806113b686604001518660400151886020015188602001518a606001518b60800151600a614f3d909695949392919063ffffffff16565b60408501516060860151600280547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1679010000000000000000000000000000000000000000000000000061ffff95861602177fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff16770100000000000000000000000000000000000000000000009590941694909402929092177fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000062ffffff93850b9390931692909202919091177fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161790555061152a9050565b6040810151600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b8060c001516fffffffffffffffffffffffffffffffff1683602001516fffffffffffffffffffffffffffffffff16146115a35760c0810151600680547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9092169190911790555b8a1561161d57608081015160035560a08101516fffffffffffffffffffffffffffffffff16156116185760a0810151600580547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff918216909301169190911790555b611682565b608081015160045560a08101516fffffffffffffffffffffffffffffffff16156116825760a0810151600580546fffffffffffffffffffffffffffffffff80821670010000000000000000000000000000000092839004821690940116029190911790555b8115158b15151461169b57602081015181518b036116a8565b80600001518a0381602001515b90965094508a1561181c5760008512156116e4576001546116e49073ffffffffffffffffffffffffffffffffffffffff168d6000889003615183565b60006116ee61535f565b90503373ffffffffffffffffffffffffffffffffffffffff1663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561177f57600080fd5b505af1158015611793573d6000803e3d6000fd5b5050505061179f61535f565b6117a982896154d1565b111561181657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4949410000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b50611987565b60008612156118535761185360008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d88600003615183565b600061185d6154e1565b90503373ffffffffffffffffffffffffffffffffffffffff1663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156118ee57600080fd5b505af1158015611902573d6000803e3d6000fd5b5050505061190e6154e1565b61191882886154d1565b111561198557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4949410000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b505b60408082015160c083015160608085015184518b8152602081018b905273ffffffffffffffffffffffffffffffffffffffff948516818701526fffffffffffffffffffffffffffffffff9093169183019190915260020b60808201529151908e169133917fc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca679181900360a00190a35050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550919890975095505050505050565b6006546fffffffffffffffffffffffffffffffff1681565b6005546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b600a8161ffff8110611ac857600080fd5b015463ffffffff81169150640100000000810460060b906b010000000000000000000000810473ffffffffffffffffffffffffffffffffffffffff16907f0100000000000000000000000000000000000000000000000000000000000000900460ff1684565b6002547e01000000000000000000000000000000000000000000000000000000000000900460ff16611bc157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055611bf1613e76565b6002547b01000000000000000000000000000000000000000000000000000000900461ffff166000611c25600a83856155b4565b6002805461ffff8084167b0100000000000000000000000000000000000000000000000000000081027fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff90931692909217909255919250831614611cc4576040805161ffff80851682528316602082015281517fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a929181900390910190a15b5050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550565b6002805473ffffffffffffffffffffffffffffffffffffffff811691740100000000000000000000000000000000000000008204900b9061ffff77010000000000000000000000000000000000000000000000820481169179010000000000000000000000000000000000000000000000000081048216917b010000000000000000000000000000000000000000000000000000008204169060ff7d01000000000000000000000000000000000000000000000000000000000082048116917e0100000000000000000000000000000000000000000000000000000000000090041687565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff16611e8f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556fffffffffffffffffffffffffffffffff8516611ed357600080fd5b600080611f3760405180608001604052808c73ffffffffffffffffffffffffffffffffffffffff1681526020018b60020b81526020018a60020b8152602001611f2d8a6fffffffffffffffffffffffffffffffff166156a8565b600f0b90526156b9565b92509250508193508092506000806000861115611f5957611f5661535f565b91505b8415611f6a57611f676154e1565b90505b3373ffffffffffffffffffffffffffffffffffffffff1663d348799787878b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015611ff957600080fd5b505af115801561200d573d6000803e3d6000fd5b5050505060008611156120995761202261535f565b61202c83886154d1565b111561209957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4d30000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b841561211e576120a76154e1565b6120b182876154d1565b111561211e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4d31000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8960020b8b60020b8d73ffffffffffffffffffffffffffffffffffffffff167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde338d8b8b604051808573ffffffffffffffffffffffffffffffffffffffff168152602001846fffffffffffffffffffffffffffffffff16815260200183815260200182815260200194505050505060405180910390a45050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550919890975095505050505050565b60045481565b6002547e01000000000000000000000000000000000000000000000000000000000000900460ff166122a757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690556122d7613e76565b6006546fffffffffffffffffffffffffffffffff168061235857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f4c00000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600061238d867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f4240615a15565b905060006123c4867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f4240615a15565b905060006123d061535f565b905060006123dc6154e1565b90508815612408576000546124089073ffffffffffffffffffffffffffffffffffffffff168b8b615183565b8715612432576001546124329073ffffffffffffffffffffffffffffffffffffffff168b8a615183565b3373ffffffffffffffffffffffffffffffffffffffff1663e9cbafb085858a8a6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156124c157600080fd5b505af11580156124d5573d6000803e3d6000fd5b5050505060006124e361535f565b905060006124ef6154e1565b9050816124fc85886154d1565b111561256957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4630000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8061257484876154d1565b11156125e157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4631000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b83820383820381156126ca576002547d0100000000000000000000000000000000000000000000000000000000009004600f166000811561262e578160ff16848161262857fe5b04612631565b60005b90506fffffffffffffffffffffffffffffffff81161561268e57600580546fffffffffffffffffffffffffffffffff8082168401167fffffffffffffffffffffffffffffffff000000000000000000000000000000009091161790555b6126be8185037001000000000000000000000000000000008d6fffffffffffffffffffffffffffffffff16614659565b60038054909101905550505b80156127a4576002547d010000000000000000000000000000000000000000000000000000000000900460041c600f1660008115612714578160ff16838161270e57fe5b04612717565b60005b90506fffffffffffffffffffffffffffffffff81161561276857600580546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008083048216850182160291161790555b6127988184037001000000000000000000000000000000008d6fffffffffffffffffffffffffffffffff16614659565b60048054909101905550505b8d73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a35050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179055505050505050505050505050565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff1661291257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16905560006129496009338989615a6d565b60038101549091506fffffffffffffffffffffffffffffffff90811690861611612973578461298b565b60038101546fffffffffffffffffffffffffffffffff165b60038201549093506fffffffffffffffffffffffffffffffff7001000000000000000000000000000000009091048116908516116129c957836129f5565b600381015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff165b91506fffffffffffffffffffffffffffffffff831615612a7e576003810180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff918216869003821617909155600054612a7e9173ffffffffffffffffffffffffffffffffffffffff909116908a908616615183565b6fffffffffffffffffffffffffffffffff821615612afd576003810180546fffffffffffffffffffffffffffffffff700100000000000000000000000000000000808304821686900382160291811691909117909155600154612afd9173ffffffffffffffffffffffffffffffffffffffff909116908a908516615183565b6040805173ffffffffffffffffffffffffffffffffffffffff8a1681526fffffffffffffffffffffffffffffffff80861660208301528416818301529051600288810b92908a900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a450600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905590969095509350505050565b60096020526000908152604090208054600182015460028301546003909301546fffffffffffffffffffffffffffffffff9283169391928181169170010000000000000000000000000000000090041685565b60086020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6002547e01000000000000000000000000000000000000000000000000000000000000900460ff16612ce557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055604080517f8da5cb5b000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b158015612d9357600080fd5b505afa158015612da7573d6000803e3d6000fd5b505050506040513d6020811015612dbd57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff163314612de057600080fd5b60ff82161580612e03575060048260ff1610158015612e035750600a8260ff1611155b8015612e2d575060ff81161580612e2d575060048160ff1610158015612e2d5750600a8160ff1611155b612e3657600080fd5b60028054610ff0600484901b16840160ff9081167d0100000000000000000000000000000000000000000000000000000000009081027fffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841617909355919004167f973d8d92bb299f4af6ce49b52a8adb85ae46b9f214c4c4fc06ac77401237b1336010826040805160ff9390920683168252600f600486901c16602083015286831682820152918516606082015290519081900360800190a15050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0100000000000000000000000000000000000000000000000000000000000017905550565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff16612fd657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055604080517f8da5cb5b000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b15801561308457600080fd5b505afa158015613098573d6000803e3d6000fd5b505050506040513d60208110156130ae57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1633146130d157600080fd5b6005546fffffffffffffffffffffffffffffffff908116908516116130f6578361310c565b6005546fffffffffffffffffffffffffffffffff165b6005549092506fffffffffffffffffffffffffffffffff7001000000000000000000000000000000009091048116908416116131485782613172565b60055470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff165b90506fffffffffffffffffffffffffffffffff82161561323e576005546fffffffffffffffffffffffffffffffff838116911614156131d1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b600580547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff91821685900382161790915560005461323e9173ffffffffffffffffffffffffffffffffffffffff9091169087908516615183565b6fffffffffffffffffffffffffffffffff811615613311576005546fffffffffffffffffffffffffffffffff8281167001000000000000000000000000000000009092041614156132ac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b600580546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008083048216859003821602918116919091179091556001546133119173ffffffffffffffffffffffffffffffffffffffff9091169087908416615183565b604080516fffffffffffffffffffffffffffffffff808516825283166020820152815173ffffffffffffffffffffffffffffffffffffffff88169233927f596b573906218d3411850b26a6b437d6c4522fdb43d2d2386263f86d50b8b151929081900390910190a3600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790559094909350915050565b6060806133d7613e76565b6134946133e2613eba565b8585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060028054600654600a9695945074010000000000000000000000000000000000000000820490920b925061ffff7701000000000000000000000000000000000000000000000082048116926fffffffffffffffffffffffffffffffff1691790100000000000000000000000000000000000000000000000000900416615ae4565b915091509250929050565b60025460009081907e01000000000000000000000000000000000000000000000000000000000000900460ff1661353757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4c4f4b0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681556040805160808101825233815287830b60208201529186900b90820152600090819081906135b390606081016135a66fffffffffffffffffffffffffffffffff8a166156a8565b600003600f0b90526156b9565b92509250925081600003945080600003935060008511806135d45750600084115b15613641576003830180546fffffffffffffffffffffffffffffffff808216808901821670010000000000000000000000000000000093849004831689019092169092029091177fffffffffffffffffffffffffffffffff00000000000000000000000000000000161790555b604080516fffffffffffffffffffffffffffffffff88168152602081018790528082018690529051600289810b92908b900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a45050600280547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179055509094909350915050565b6000806000613704613e76565b61370e8585615c8e565b600285810b810b60009081526007602052604080822087840b90930b825281206003830154600681900b93670100000000000000820473ffffffffffffffffffffffffffffffffffffffff169284927b01000000000000000000000000000000000000000000000000000000810463ffffffff1692849290917f0100000000000000000000000000000000000000000000000000000000000000900460ff16806137b757600080fd5b6003820154600681900b9850670100000000000000810473ffffffffffffffffffffffffffffffffffffffff1696507b01000000000000000000000000000000000000000000000000000000810463ffffffff1694507f0100000000000000000000000000000000000000000000000000000000000000900460ff168061383d57600080fd5b50506040805160e0810182526002805473ffffffffffffffffffffffffffffffffffffffff81168352740100000000000000000000000000000000000000008104820b820b820b6020840181905261ffff7701000000000000000000000000000000000000000000000083048116958501959095527901000000000000000000000000000000000000000000000000008204851660608501527b010000000000000000000000000000000000000000000000000000008204909416608084015260ff7d0100000000000000000000000000000000000000000000000000000000008204811660a08501527e0100000000000000000000000000000000000000000000000000000000000090910416151560c08301529093508e810b91900b1215905061397757509390940396509003935090039050613a03565b8a60020b816020015160020b12156139f4576000613993613eba565b60208301516040840151600654606086015193945060009384936139d293600a938893879392916fffffffffffffffffffffffffffffffff1690614727565b9a9003989098039b505094909603929092039650909103039250613a03915050565b50949093039650039350900390505b9250925092565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b60035481565b60076020526000908152604090208054600182015460028301546003909301546fffffffffffffffffffffffffffffffff831693700100000000000000000000000000000000909304600f0b9290600681900b90670100000000000000810473ffffffffffffffffffffffffffffffffffffffff16907b01000000000000000000000000000000000000000000000000000000810463ffffffff16907f0100000000000000000000000000000000000000000000000000000000000000900460ff1688565b60025473ffffffffffffffffffffffffffffffffffffffff1615613be257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4149000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6000613bed82614b71565b9050600080613c05613bfd613eba565b600a90615e0f565b6040805160e08101825273ffffffffffffffffffffffffffffffffffffffff8816808252600288810b6020808501829052600085870181905261ffff898116606088018190529089166080880181905260a0880192909252600160c09097019690965283547e010000000000000000000000000000000000000000000000000000000000007fffffffffffffffffffffffff000000000000000000000000000000000000000090911686177fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000062ffffff86880b1602177fffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff16790100000000000000000000000000000000000000000000000000909702969096177fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b01000000000000000000000000000000000000000000000000000000909102177fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169490941790915583519182529181019190915281519395509193507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c9592918290030190a150505050565b60008082600281900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2761881613e1657fe5b05029050600083600281900b620d89e881613e2d57fe5b0502905060008460020b83830360020b81613e4457fe5b0560010190508062ffffff166fffffffffffffffffffffffffffffffff801681613e6a57fe5b0493505050505b919050565b3073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614613eb857600080fd5b565b4290565b60008060008460020b8660020b81613ed257fe5b05905060008660020b128015613ef957508460020b8660020b81613ef257fe5b0760020b15155b15613f21577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b8315613fb457600080613f3383615e92565b600182810b810b600090815260208d9052604090205460ff83169190911b80017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190811680151597509294509092509085613f9657888360ff16860302613fa9565b88613fa082615ea4565b840360ff168603025b965050505050614032565b600080613fc383600101615e92565b91509150600060018260ff166001901b031990506000818b60008660010b60010b815260200190815260200160002054169050806000141595508561401557888360ff0360ff1686600101010261402b565b888361402083615f51565b0360ff168660010101025b9650505050505b5094509492505050565b60008060008360020b12614053578260020b61405b565b8260020b6000035b9050620d89e88111156140cf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f5400000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6000600182166140f057700100000000000000000000000000000000614102565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615614136576ffff97272373d413259a46990580e213a0260801c5b6004821615614155576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614174576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615614193576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156141b2576fff973b41fa98c081472e6896dfb254c00260801c5b60408216156141d1576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156141f0576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615614210576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615614230576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614250576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614270576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614290576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156142b0576fa9f746462d870fdf8a65dc1f90e061e50260801c5b6140008216156142d0576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156142f0576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614311576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614331576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614350576d2216e584f5fa1ea926041bedfe980260801c5b6208000082161561436d576b048a170391f7dc42444e8fa20260801c5b60008460020b13156143a657807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff816143a257fe5b0490505b6401000000008106156143ba5760016143bd565b60005b60ff16602082901c0192505050919050565b600080808073ffffffffffffffffffffffffffffffffffffffff808916908a1610158187128015906144615760006144158989620f42400362ffffff16620f4240614659565b90508261442e576144298c8c8c6001616134565b61443b565b61443b8b8d8c6001616207565b955085811061444c578a965061445b565b6144588c8b838661631f565b96505b506144ab565b81614478576144738b8b8b6000616207565b614485565b6144858a8c8b6000616134565b9350838860000310614499578995506144ab565b6144a88b8a8a60000385616381565b95505b73ffffffffffffffffffffffffffffffffffffffff8a811690871614821561451b578080156144d75750815b6144ed576144e8878d8c6001616207565b6144ef565b855b95508080156144fc575081155b6145125761450d878d8c6000616134565b614514565b845b9450614565565b8080156145255750815b61453b576145368c888c6001616134565b61453d565b855b955080801561454a575081155b6145605761455b8c888c6000616207565b614562565b845b94505b8115801561457557508860000385115b15614581578860000394505b8180156145ba57508a73ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614155b156145c95785890393506145e6565b6145e3868962ffffff168a620f42400362ffffff16615a15565b93505b50505095509550955095915050565b60007f8000000000000000000000000000000000000000000000000000000000000000821061462357600080fd5b5090565b8082038281131560008312151461463d57600080fd5b92915050565b8181018281121560008312151461463d57600080fd5b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858709868602925082811090839003039050806146ad57600084116146a257600080fd5b508290049050614720565b8084116146b957600080fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b60008063ffffffff87166147ff576000898661ffff1661ffff811061474857fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b60208501526b010000000000000000000000830473ffffffffffffffffffffffffffffffffffffffff16948401949094527f010000000000000000000000000000000000000000000000000000000000000090910460ff16151560608301529092508a16146147eb576147e8818a89886163e3565b90505b8060200151816040015192509250506148ec565b8688036000806148148c8c858c8c8c8c6164b2565b91509150816000015163ffffffff168363ffffffff1614156148465781602001518260400151945094505050506148ec565b805163ffffffff8481169116141561486e5780602001518160400151945094505050506148ec565b8151815160208085015190840151918390039286039163ffffffff80841692908516910360060b8161489c57fe5b05028460200151018263ffffffff168263ffffffff16866040015186604001510373ffffffffffffffffffffffffffffffffffffffff1602816148db57fe5b048560400151019650965050505050505b97509795505050505050565b600295860b860b60009081526020979097526040909620600181018054909503909455938301805490920390915560038201805463ffffffff7b0100000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff6701000000000000008085048216909603169094027fffffffffff0000000000000000000000000000000000000000ffffffffffffff90921691909117600681810b90960390950b66ffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000909516949094178281048516909503909316027fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff909316929092179055547001000000000000000000000000000000009004600f0b90565b60008082600f0b1215614ad957826fffffffffffffffffffffffffffffffff168260000384039150816fffffffffffffffffffffffffffffffff1610614ad457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4c53000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b61463d565b826fffffffffffffffffffffffffffffffff168284019150816fffffffffffffffffffffffffffffffff16101561463d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4c41000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60006401000276a373ffffffffffffffffffffffffffffffffffffffff831610801590614bc7575073fffd8963efd1fc6a506488495d951d5263988d2673ffffffffffffffffffffffffffffffffffffffff8316105b614c3257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f5200000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b77ffffffffffffffffffffffffffffffffffffffff00000000602083901b166fffffffffffffffffffffffffffffffff811160071b81811c67ffffffffffffffff811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c97908811961790941790921717909117171760808110614cdc57607f810383901c9150614ce6565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808f0160401b60c09190911c678000000000000000161760c19b909b1c674000000000000000169a909a1760c29990991c672000000000000000169890981760c39790971c671000000000000000169690961760c49590951c670800000000000000169490941760c59390931c670400000000000000169290921760c69190911c670200000000000000161760c79190911c670100000000000000161760c89190911c6680000000000000161760c99190911c6640000000000000161760ca9190911c6620000000000000161760cb9190911c6610000000000000161760cc9190911c6608000000000000161760cd9190911c66040000000000001617693627a301d71055774c8581027ffffffffffffffffffffffffffffffffffd709b7e5480fba5a50fed5e62ffc5568101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b14614f2e578873ffffffffffffffffffffffffffffffffffffffff16614f058261403c565b73ffffffffffffffffffffffffffffffffffffffff161115614f275781614f29565b805b614f30565b815b9998505050505050505050565b6000806000898961ffff1661ffff8110614f5357fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b60208501526b010000000000000000000000830473ffffffffffffffffffffffffffffffffffffffff16948401949094527f010000000000000000000000000000000000000000000000000000000000000090910460ff161515606083015290925089161415614ff457888592509250506148ec565b8461ffff168461ffff1611801561501557506001850361ffff168961ffff16145b1561502257839150615026565b8491505b8161ffff168960010161ffff168161503a57fe5b069250615049818989896163e3565b8a8461ffff1661ffff811061505a57fe5b8251910180546020840151604085015160609095015115157f0100000000000000000000000000000000000000000000000000000000000000027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff9096166b010000000000000000000000027fff0000000000000000000000000000000000000000ffffffffffffffffffffff60069390930b66ffffffffffffff16640100000000027fffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff63ffffffff9097167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009095169490941795909516929092171692909217929092161790555097509795505050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b6020831061525857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161521b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146152ba576040519150601f19603f3d011682016040523d82523d6000602084013e6152bf565b606091505b50915091508180156152ed5750805115806152ed57508080602001905160208110156152ea57600080fd5b50515b61535857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5446000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b5050505050565b60008054604080513060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000178152915181518594859473ffffffffffffffffffffffffffffffffffffffff9091169392918291908083835b6020831061543157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016153f4565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114615491576040519150601f19603f3d011682016040523d82523d6000602084013e615496565b606091505b50915091508180156154aa57506020815110155b6154b357600080fd5b8080602001905160208110156154c857600080fd5b50519250505090565b8082018281101561463d57600080fd5b600154604080513060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000178152915181516000948594859473ffffffffffffffffffffffffffffffffffffffff9092169391928291908083836020831061543157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016153f4565b6000808361ffff161161562857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f4900000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b8261ffff168261ffff161161563e575081614720565b825b8261ffff168161ffff16101561569f576001858261ffff1661ffff811061566357fe5b0180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff92909216919091179055600101615640565b50909392505050565b80600f81900b8114613e7157600080fd5b60008060006156c6613e76565b6156d884602001518560400151615c8e565b6040805160e0810182526002805473ffffffffffffffffffffffffffffffffffffffff81168352740100000000000000000000000000000000000000008104820b820b90910b602080840182905261ffff770100000000000000000000000000000000000000000000008404811685870152790100000000000000000000000000000000000000000000000000840481166060808701919091527b010000000000000000000000000000000000000000000000000000008504909116608086015260ff7d0100000000000000000000000000000000000000000000000000000000008504811660a08701527e01000000000000000000000000000000000000000000000000000000000000909404909316151560c0850152885190890151948901519289015193946158109491939092909190616776565b93508460600151600f0b600014615a0d57846020015160020b816020015160020b12156158655761585e615847866020015161403c565b615854876040015161403c565b8760600151616970565b9250615a0d565b846040015160020b816020015160020b12156159e35760065460408201516fffffffffffffffffffffffffffffffff909116906158c0906158a4613eba565b602085015160608601516080870151600a949392918791614f3d565b600280547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1679010000000000000000000000000000000000000000000000000061ffff93841602177fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff167701000000000000000000000000000000000000000000000093909216929092021790558151604087015161596e91906159649061403c565b8860600151616970565b935061598c615980876020015161403c565b835160608901516169b4565b925061599c818760600151614a2d565b600680547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9290921691909117905550615a0d565b615a0a6159f3866020015161403c565b615a00876040015161403c565b87606001516169b4565b91505b509193909250565b6000615a22848484614659565b905060008280615a2e57fe5b8486091115614720577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110615a6357600080fd5b6001019392505050565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080860191909152600293840b60e890811b60348701529290930b90911b60378401528051808403601a018152603a90930181528251928201929092206000908152929052902090565b60608060008361ffff1611615b5a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f4900000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b865167ffffffffffffffff81118015615b7257600080fd5b50604051908082528060200260200182016040528015615b9c578160200160208202803683370190505b509150865167ffffffffffffffff81118015615bb757600080fd5b50604051908082528060200260200182016040528015615be1578160200160208202803683370190505b50905060005b8751811015615c8157615c128a8a8a8481518110615c0157fe5b60200260200101518a8a8a8a614727565b848381518110615c1e57fe5b60200260200101848481518110615c3157fe5b602002602001018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152508260060b60060b81525050508080600101915050615be7565b5097509795505050505050565b8060020b8260020b12615d0257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f544c550000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff27618600283900b1215615d9557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f544c4d0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b620d89e8600282900b1315615e0b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f54554d0000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b5050565b6040805160808101825263ffffffff928316808252600060208301819052928201929092526001606090910181905283547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000169091179091167f010000000000000000000000000000000000000000000000000000000000000017909155908190565b60020b600881901d9161010090910790565b6000808211615eb257600080fd5b7001000000000000000000000000000000008210615ed257608091821c91015b680100000000000000008210615eea57604091821c91015b6401000000008210615efe57602091821c91015b620100008210615f1057601091821c91015b6101008210615f2157600891821c91015b60108210615f3157600491821c91015b60048210615f4157600291821c91015b60028210613e7157600101919050565b6000808211615f5f57600080fd5b5060ff6fffffffffffffffffffffffffffffffff821615615fa1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001615fa9565b608082901c91505b67ffffffffffffffff821615615fe0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc001615fe8565b604082901c91505b63ffffffff82161561601b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001616023565b602082901c91505b61ffff821615616054577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00161605c565b601082901c91505b60ff82161561608c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff801616094565b600882901c91505b600f8216156160c4577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc016160cc565b600482901c91505b60038216156160fc577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01616104565b600282901c91505b6001821615613e71577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01919050565b60008373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16111561616e579293925b816161ba576161b5836fffffffffffffffffffffffffffffffff1686860373ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000614659565b6161fc565b6161fc836fffffffffffffffffffffffffffffffff1686860373ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000615a15565b90505b949350505050565b60008373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161115616241579293925b7bffffffffffffffffffffffffffffffff000000000000000000000000606084901b1673ffffffffffffffffffffffffffffffffffffffff868603811690871661628a57600080fd5b836162d4578673ffffffffffffffffffffffffffffffffffffffff166162c783838973ffffffffffffffffffffffffffffffffffffffff16614659565b816162ce57fe5b04616314565b6163146162f883838973ffffffffffffffffffffffffffffffffffffffff16615a15565b8873ffffffffffffffffffffffffffffffffffffffff166169e3565b979650505050505050565b6000808573ffffffffffffffffffffffffffffffffffffffff161161634357600080fd5b6000846fffffffffffffffffffffffffffffffff161161636257600080fd5b81616374576161b585858560016169ee565b6161fc8585856001616b46565b6000808573ffffffffffffffffffffffffffffffffffffffff16116163a557600080fd5b6000846fffffffffffffffffffffffffffffffff16116163c457600080fd5b816163d6576161b58585856000616b46565b6161fc85858560006169ee565b6163eb617659565b600085600001518503905060405180608001604052808663ffffffff1681526020018263ffffffff168660020b0288602001510160060b81526020016000856fffffffffffffffffffffffffffffffff161161644857600161644a565b845b6fffffffffffffffffffffffffffffffff1673ffffffff00000000000000000000000000000000608085901b168161647e57fe5b0488604001510173ffffffffffffffffffffffffffffffffffffffff16815260200160011515815250915050949350505050565b6164ba617659565b6164c2617659565b888561ffff1661ffff81106164d357fe5b60408051608081018252919092015463ffffffff81168083526401000000008204600690810b810b900b60208401526b010000000000000000000000820473ffffffffffffffffffffffffffffffffffffffff16938301939093527f0100000000000000000000000000000000000000000000000000000000000000900460ff1615156060820152925061656990899089616c7c565b156165a1578663ffffffff16826000015163ffffffff16141561658b576148ec565b81616598838989886163e3565b915091506148ec565b888361ffff168660010161ffff16816165b657fe5b0661ffff1661ffff81106165c657fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b602084015273ffffffffffffffffffffffffffffffffffffffff6b0100000000000000000000008204169183019190915260ff7f0100000000000000000000000000000000000000000000000000000000000000909104161515606082018190529092506166df57604080516080810182528a5463ffffffff811682526401000000008104600690810b810b900b60208301526b010000000000000000000000810473ffffffffffffffffffffffffffffffffffffffff16928201929092527f010000000000000000000000000000000000000000000000000000000000000090910460ff161515606082015291505b6166ee88836000015189616c7c565b61675957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4f4c440000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6167668989898887616d3f565b9150915097509795505050505050565b60006167856009878787615a6d565b60035460045491925090600080600f87900b156169105760006167a6613eba565b60028054600654929350600092839261683592600a928792869274010000000000000000000000000000000000000000810490910b9161ffff7701000000000000000000000000000000000000000000000083048116926fffffffffffffffffffffffffffffffff90921691790100000000000000000000000000000000000000000000000000900416614727565b909250905061686f60078d8b8d8b8b87898b60007f0000000000000000000000000000000000000000000000000000000000000000616f41565b94506168a660078c8b8d8b8b87898b60017f0000000000000000000000000000000000000000000000000000000000000000616f41565b935084156168da576168da60088d7f0000000000000000000000000000000000000000000000000000000000000000617246565b831561690c5761690c60088c7f0000000000000000000000000000000000000000000000000000000000000000617246565b5050505b60008061692260078c8c8b8a8a6172ac565b9092509050616933878a8484617358565b600089600f0b12156169615783156169505761695060078c6175ce565b82156169615761696160078b6175ce565b50505050505095945050505050565b60008082600f0b126169965761699161698c8585856001616207565b6145f5565b6161ff565b6169a961698c8585856000036000616207565b600003949350505050565b60008082600f0b126169d05761699161698c8585856001616134565b6169a961698c8585856000036000616134565b808204910615150190565b60008115616a9657600073ffffffffffffffffffffffffffffffffffffffff841115616a4357616a3e846c01000000000000000000000000876fffffffffffffffffffffffffffffffff16614659565b616a64565b6fffffffffffffffffffffffffffffffff8516606085901b81616a6257fe5b045b9050616a8e616a8973ffffffffffffffffffffffffffffffffffffffff8816836154d1565b6175fa565b9150506161ff565b600073ffffffffffffffffffffffffffffffffffffffff841115616ae357616ade846c01000000000000000000000000876fffffffffffffffffffffffffffffffff16615a15565b616b03565b616b03606085901b6fffffffffffffffffffffffffffffffff87166169e3565b9050808673ffffffffffffffffffffffffffffffffffffffff1611616b2757600080fd5b73ffffffffffffffffffffffffffffffffffffffff86160390506161ff565b600082616b545750836161ff565b7bffffffffffffffffffffffffffffffff000000000000000000000000606085901b168215616c1b5773ffffffffffffffffffffffffffffffffffffffff861684810290858281616ba157fe5b041415616bdf57818101828110616bdd57616bd3838973ffffffffffffffffffffffffffffffffffffffff1683615a15565b93505050506161ff565b505b616c1282616c0d878a73ffffffffffffffffffffffffffffffffffffffff168681616c0657fe5b04906154d1565b6169e3565b925050506161ff565b73ffffffffffffffffffffffffffffffffffffffff861684810290858281616c3f57fe5b04148015616c4c57508082115b616c5557600080fd5b808203616bd3616a898473ffffffffffffffffffffffffffffffffffffffff8b1684615a15565b60008363ffffffff168363ffffffff1611158015616ca657508363ffffffff168263ffffffff1611155b15616cc2578163ffffffff168363ffffffff1611159050614720565b60008463ffffffff168463ffffffff1611616cea578363ffffffff1664010000000001616cf2565b8363ffffffff165b64ffffffffff16905060008563ffffffff168463ffffffff1611616d23578363ffffffff1664010000000001616d2b565b8363ffffffff165b64ffffffffff169091111595945050505050565b616d47617659565b616d4f617659565b60008361ffff168560010161ffff1681616d6557fe5b0661ffff169050600060018561ffff16830103905060005b506002818301048961ffff87168281616d9257fe5b0661ffff8110616d9e57fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b602084015273ffffffffffffffffffffffffffffffffffffffff6b0100000000000000000000008204169183019190915260ff7f010000000000000000000000000000000000000000000000000000000000000090910416151560608201819052909550616e3a57806001019250616d7d565b898661ffff168260010181616e4b57fe5b0661ffff8110616e5757fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b602084015273ffffffffffffffffffffffffffffffffffffffff6b0100000000000000000000008204169183019190915260ff7f010000000000000000000000000000000000000000000000000000000000000090910416151560608201528551909450600090616ef3908b908b616c7c565b9050808015616f0c5750616f0c8a8a8760000151616c7c565b15616f175750616f34565b80616f2757600182039250616f2e565b8160010193505b50616d7d565b5050509550959350505050565b60028a810b900b600090815260208c90526040812080546fffffffffffffffffffffffffffffffff1682616f75828d614a2d565b9050846fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff16111561700a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4c4f000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6fffffffffffffffffffffffffffffffff828116159082161581141594501561715a578c60020b8e60020b1361710e57600183018b9055600283018a90556003830180547fffffffffff0000000000000000000000000000000000000000ffffffffffffff1667010000000000000073ffffffffffffffffffffffffffffffffffffffff8c1602177fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001666ffffffffffffff60068b900b16177fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b0100000000000000000000000000000000000000000000000000000063ffffffff8a16021790555b6003830180547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790555b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff8216178355856171d15782546171cc906171c7907001000000000000000000000000000000009004600f90810b810b908f900b614643565b6156a8565b6171ff565b82546171ff906171c7907001000000000000000000000000000000009004600f90810b810b908f900b614627565b8354600f9190910b6fffffffffffffffffffffffffffffffff9081167001000000000000000000000000000000000291161790925550909c9b505050505050505050505050565b8060020b8260020b8161725557fe5b0760020b1561726357600080fd5b60008061727e8360020b8560020b8161727857fe5b05615e92565b600191820b820b60009081526020979097526040909620805460ff9097169190911b90951890945550505050565b600285810b80820b60009081526020899052604080822088850b850b83529082209193849391929184918291908a900b126172f257505060018201546002830154617305565b8360010154880391508360020154870390505b6000808b60020b8b60020b12156173275750506001830154600284015461733a565b84600101548a0391508460020154890390505b92909803979097039b96909503949094039850939650505050505050565b6040805160a08101825285546fffffffffffffffffffffffffffffffff90811682526001870154602083015260028701549282019290925260038601548083166060830152700100000000000000000000000000000000900490911660808201526000600f85900b61744b5781516fffffffffffffffffffffffffffffffff1661744357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f4e50000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b50805161745a565b81516174579086614a2d565b90505b60006174948360200151860384600001516fffffffffffffffffffffffffffffffff16700100000000000000000000000000000000614659565b905060006174d08460400151860385600001516fffffffffffffffffffffffffffffffff16700100000000000000000000000000000000614659565b905086600f0b6000146175185787547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff84161788555b60018801869055600288018590556fffffffffffffffffffffffffffffffff821615158061755857506000816fffffffffffffffffffffffffffffffff16115b156175c4576003880180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff91821685018216178082167001000000000000000000000000000000009182900483168501909216021790555b5050505050505050565b600290810b810b6000908152602092909252604082208281556001810183905590810182905560030155565b8073ffffffffffffffffffffffffffffffffffffffff81168114613e7157600080fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040805160808101825260008082526020820181905291810182905260608101919091529056fea26469706673582212201fcbecb1d9e9f1158d1b7c84a14edd13205608d6d039a4a8b0a736e0ca3fd4b264736f6c63430007060033"; type UniswapV3PoolConstructorParams = | [signer?: Signer] diff --git a/packages/flash-swap/test/integration/flashUniswapV3/FlashUniswapV3.ts b/packages/flash-swap/test/integration/flashUniswapV3/FlashUniswapV3.ts index e02b8c59..b544c050 100644 --- a/packages/flash-swap/test/integration/flashUniswapV3/FlashUniswapV3.ts +++ b/packages/flash-swap/test/integration/flashUniswapV3/FlashUniswapV3.ts @@ -6,10 +6,10 @@ export function integrationTestFlashUniswapV3(): void { beforeEach(async function () { const { balanceSheet, + dai, fintroller, flashUniswapV3, hToken, - uniswapV3Pool, uniswapV3PositionManager, usdc, usdcPriceFeed, @@ -17,10 +17,10 @@ export function integrationTestFlashUniswapV3(): void { wbtcPriceFeed, } = await this.loadFixture(integrationFixture); this.contracts.balanceSheet = balanceSheet; + this.contracts.dai = dai; this.contracts.fintroller = fintroller; this.contracts.flashUniswapV3 = flashUniswapV3; this.contracts.hToken = hToken; - this.contracts.uniswapV3Pool = uniswapV3Pool; this.contracts.uniswapV3PositionManager = uniswapV3PositionManager; this.contracts.usdc = usdc; this.contracts.usdcPriceFeed = usdcPriceFeed; diff --git a/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/flashLiquidate.ts b/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/flashLiquidate.ts index 17923eb5..1dd48521 100644 --- a/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/flashLiquidate.ts +++ b/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/flashLiquidate.ts @@ -1,10 +1,11 @@ import { BigNumber } from "@ethersproject/bignumber"; import { MaxUint256 } from "@ethersproject/constants"; import { Zero } from "@ethersproject/constants"; -import { LIQUIDATION_INCENTIVES } from "@hifi/constants"; +import { DEFAULT_FEE, LIQUIDATION_INCENTIVES } from "@hifi/constants"; import { BalanceSheetErrors, FlashUniswapV3Errors } from "@hifi/errors"; import { USDC, WBTC, getNow, hUSDC, price } from "@hifi/helpers"; import { expect } from "chai"; +import { utils } from "ethers"; import { toBn } from "evm-bn"; import { IFlashUniswapV3 } from "../../../../../src/types/contracts/uniswap-v3/IFlashUniswapV3"; @@ -20,11 +21,15 @@ async function getFlashLiquidateParams( ): Promise { const borrower: string = this.signers.borrower.address; const bond: string = this.contracts.hToken.address; + const underlying: string = this.contracts.usdc.address; const params: FlashLiquidateParams = { borrower: borrower, bond: bond, collateral: collateral, - poolFee: await this.contracts.uniswapV3Pool.connect(this.signers.raider).fee(), + path: utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [underlying, DEFAULT_FEE, this.contracts.dai.address, DEFAULT_FEE, collateral], + ), turnout: turnout, underlyingAmount: underlyingAmount, }; @@ -37,7 +42,7 @@ export function shouldBehaveLikeFlashLiquidate(): void { const collateralCeiling: BigNumber = USDC("1e6"); const debtCeiling: BigNumber = hUSDC("1e6"); const depositCollateralAmount: BigNumber = WBTC("1"); - const profitCollateralAmount: BigNumber = WBTC("0.59979736"); + const profitCollateralAmount: BigNumber = WBTC("0.59959926"); const swapUnderlyingAmount: BigNumber = USDC("10000"); context("when the collateral is the same as the underlying", function () { @@ -57,8 +62,29 @@ export function shouldBehaveLikeFlashLiquidate(): void { // Set the oracle price to 1 USDC = $1. await this.contracts.usdcPriceFeed.setPrice(price("1")); - // Set up the Uniswap V3 Pool and mint a position. - await mintPoolReserves.call(this, "100000000000000"); + // Set up the WBTC-DAI Uniswap V3 Pool and mint a position. + await mintPoolReserves.call( + this, + this.contracts.wbtc, + this.contracts.dai, + DEFAULT_FEE, + // Create a position with price range 1 WBTC ~ 25k DAI. + "1", + "250000000000000", + "10000000000000000000000", + ); + + // Set up the DAI-USDC Uniswap V3 Pool and mint a position. + await mintPoolReserves.call( + this, + this.contracts.usdc, + this.contracts.dai, + DEFAULT_FEE, + // Create a position with price range 1 DAI ~ 1 USDC. + "1", + "1000000000000", + "10000000000000000000000", + ); // List the bond in the Fintroller. await this.contracts.fintroller.connect(this.signers.admin).listBond(this.contracts.hToken.address); @@ -191,10 +217,10 @@ export function shouldBehaveLikeFlashLiquidate(): void { }); context("when the repay amount is equal to the seized amount", function () { - const subsidyCollateralAmount: BigNumber = WBTC("0.00051608"); - const liquidationIncentive = toBn("1.00051608"); - const repayCollateralAmount: BigNumber = WBTC("1.00051608"); - const seizeCollateralAmount: BigNumber = WBTC("1.00051608"); + const subsidyCollateralAmount: BigNumber = WBTC("0.00100342"); + const liquidationIncentive = toBn("1.00100342"); + const repayCollateralAmount: BigNumber = WBTC("1.00100342"); + const seizeCollateralAmount: BigNumber = WBTC("1.00100342"); const swapUnderlyingAmount: BigNumber = USDC("25000"); let params: FlashLiquidateParams; @@ -225,10 +251,10 @@ export function shouldBehaveLikeFlashLiquidate(): void { expect(oldCollateralBalance).to.equal(newCollateralBalance); }); - it("emits a FlashSwapAndLiquidateBorrow event", async function () { + it("emits a FlashLiquidate event", async function () { const contractCall = this.contracts.flashUniswapV3.connect(this.signers.liquidator).flashLiquidate(params); await expect(contractCall) - .to.emit(this.contracts.flashUniswapV3, "FlashSwapAndLiquidateBorrow") + .to.emit(this.contracts.flashUniswapV3, "FlashLiquidate") .withArgs( this.signers.liquidator.address, this.signers.borrower.address, @@ -244,9 +270,9 @@ export function shouldBehaveLikeFlashLiquidate(): void { }); context("when the repay amount is greater than the seized amount", function () { - const subsidyCollateralAmount: BigNumber = WBTC("0.20062309"); + const subsidyCollateralAmount: BigNumber = WBTC("0.20120474"); const seizeCollateralAmount: BigNumber = WBTC("1"); - const repayCollateralAmount: BigNumber = WBTC("1.20062309"); + const repayCollateralAmount: BigNumber = WBTC("1.20120474"); const swapUnderlyingAmount: BigNumber = USDC("30000"); beforeEach(async function () { @@ -295,12 +321,12 @@ export function shouldBehaveLikeFlashLiquidate(): void { expect(oldCollateralBalance.sub(newCollateralBalance)).to.equal(subsidyCollateralAmount); }); - it("emits a FlashSwapAndLiquidateBorrow event", async function () { + it("emits a FlashLiquidate event", async function () { const contractCall = this.contracts.flashUniswapV3 .connect(this.signers.liquidator) .flashLiquidate(params); await expect(contractCall) - .to.emit(this.contracts.flashUniswapV3, "FlashSwapAndLiquidateBorrow") + .to.emit(this.contracts.flashUniswapV3, "FlashLiquidate") .withArgs( this.signers.liquidator.address, this.signers.borrower.address, @@ -317,7 +343,7 @@ export function shouldBehaveLikeFlashLiquidate(): void { }); context("when the repay amount is less than the seized amount", function () { - const repayCollateralAmount: BigNumber = WBTC("0.40020264"); + const repayCollateralAmount: BigNumber = WBTC("0.40040074"); const seizeCollateralAmount: BigNumber = WBTC("1"); beforeEach(async function () { @@ -352,12 +378,12 @@ export function shouldBehaveLikeFlashLiquidate(): void { expect(newCollateralBalance.sub(profitCollateralAmount)).to.equal(oldCollateralBalance); }); - it("emits a FlashSwapAndLiquidateBorrow event", async function () { + it("emits a FlashLiquidate event", async function () { const contractCall = this.contracts.flashUniswapV3 .connect(this.signers.liquidator) .flashLiquidate(params); await expect(contractCall) - .to.emit(this.contracts.flashUniswapV3, "FlashSwapAndLiquidateBorrow") + .to.emit(this.contracts.flashUniswapV3, "FlashLiquidate") .withArgs( this.signers.liquidator.address, this.signers.borrower.address, diff --git a/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/index.ts b/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/index.ts index bfd08fd7..a046ad33 100644 --- a/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/index.ts +++ b/packages/flash-swap/test/integration/flashUniswapV3/effects/uniswapV3SwapCallback/index.ts @@ -1,27 +1,46 @@ import { defaultAbiCoder } from "@ethersproject/abi"; import { BigNumber } from "@ethersproject/bignumber"; import { Zero } from "@ethersproject/constants"; +import { DEFAULT_FEE } from "@hifi/constants"; import { FlashUniswapV3Errors } from "@hifi/errors"; import { USDC, WBTC } from "@hifi/helpers"; import { expect } from "chai"; +import { utils } from "ethers"; import { shouldBehaveLikeFlashLiquidate } from "./flashLiquidate"; -async function getSwapCallbackData( - this: Mocha.Context, - collateral: string, - token0: string, - token1: string, - fee: number, -): Promise { - const types = ["address", "address", "address", "address", "address", "uint24", "address", "int256", "uint256"]; +async function getSwapCallbackData(this: Mocha.Context, collateral: string): Promise { const bond: string = this.contracts.hToken.address; const borrower: string = this.signers.borrower.address; const sender: string = this.signers.raider.address; + const underlying: string = await this.contracts.hToken.underlying(); const turnout: string = String(WBTC("0.001")); const underlyingAmount: string = String(USDC("10000")); - const values = [bond, borrower, collateral, token0, token1, fee, sender, turnout, underlyingAmount]; - const data: string = defaultAbiCoder.encode(types, values); + const data: string = defaultAbiCoder.encode( + [ + `tuple( + address bond, + address borrower, + address collateral, + bytes path, + address sender, + int256 turnout, + uint256 underlyingAmount + )`, + ], + [ + { + bond, + borrower, + collateral, + path: utils.solidityPack(["address", "uint24", "address"], [underlying, DEFAULT_FEE, collateral]), + sender, + turnout, + underlyingAmount, + }, + ], + ); + return data; } @@ -41,14 +60,7 @@ export function shouldBehaveLikeUniswapV3SwapCallback(): void { let data: string; beforeEach(async function () { - const { token0, token1, fee } = this.contracts.uniswapV3Pool; - data = await getSwapCallbackData.call( - this, - this.contracts.wbtc.address, - await token0(), - await token1(), - await fee(), - ); + data = await getSwapCallbackData.call(this, this.contracts.wbtc.address); }); context("when the caller is not the UniswapV3Pool contract", function () { diff --git a/packages/flash-swap/test/shared/fixtures.ts b/packages/flash-swap/test/shared/fixtures.ts index 43c1aabe..19e52ade 100644 --- a/packages/flash-swap/test/shared/fixtures.ts +++ b/packages/flash-swap/test/shared/fixtures.ts @@ -1,6 +1,15 @@ import { Signer } from "@ethersproject/abstract-signer"; import { keccak256 } from "@ethersproject/keccak256"; -import { H_TOKEN_MATURITY_ONE_YEAR, WETH_DECIMALS, WETH_NAME, WETH_SYMBOL } from "@hifi/constants"; +import { + DAI_DECIMALS, + DAI_NAME, + DAI_SYMBOL, + DEFAULT_FEE, + H_TOKEN_MATURITY_ONE_YEAR, + WETH_DECIMALS, + WETH_NAME, + WETH_SYMBOL, +} from "@hifi/constants"; import { USDC_DECIMALS, USDC_NAME, USDC_SYMBOL, WBTC_DECIMALS, WBTC_NAME, WBTC_SYMBOL } from "@hifi/constants"; import { getHTokenName, getHTokenSymbol } from "@hifi/helpers"; import type { BalanceSheetV2 } from "@hifi/protocol/dist/types/contracts/core/balance-sheet/BalanceSheetV2"; @@ -19,21 +28,19 @@ import type { GodModeUniswapV2Factory } from "../../src/types/contracts/uniswap- import type { GodModeUniswapV2Pair } from "../../src/types/contracts/uniswap-v2/test/GodModeUniswapV2Pair"; import type { MaliciousPair as MaliciousV2Pair } from "../../src/types/contracts/uniswap-v2/test/MaliciousPair"; import type { FlashUniswapV3 } from "../../src/types/contracts/uniswap-v3/FlashUniswapV3"; -import type { UniswapV3Pool } from "../../src/types/contracts/uniswap-v3/UniswapV3Pool"; import type { GodModeNonfungiblePositionManager } from "../../src/types/contracts/uniswap-v3/test/GodModeNonfungiblePositionManager"; import { GodModeUniswapV2Pair__factory } from "../../src/types/factories/contracts/uniswap-v2/test/GodModeUniswapV2Pair__factory"; -import { UniswapV3Pool__factory } from "../../src/types/factories/contracts/uniswap-v3/UniswapV3Pool__factory"; import { deployGodModeErc20 } from "./deployers"; type IntegrationFixtureReturnType = { balanceSheet: BalanceSheetV2; + dai: GodModeErc20; fintroller: Fintroller; flashUniswapV2: FlashUniswapV2; flashUniswapV3: FlashUniswapV3; hToken: GodModeHToken; maliciousV2Pair: MaliciousV2Pair; uniswapV2Pair: GodModeUniswapV2Pair; - uniswapV3Pool: UniswapV3Pool; uniswapV3PositionManager: GodModeNonfungiblePositionManager; usdc: GodModeErc20; usdcPriceFeed: SimplePriceFeed; @@ -45,6 +52,7 @@ export async function integrationFixture(signers: Signer[]): Promise( await waffle.deployContract(deployer, uniswapV3FactoryArtifact, []) ); - await uniswapV3Factory.createPool(wbtc.address, usdc.address, 500); - const v3PoolAddress: string = await uniswapV3Factory.getPool(wbtc.address, usdc.address, 500); - - const uniswapV3Pool: UniswapV3Pool = UniswapV3Pool__factory.connect(v3PoolAddress, deployer); + // Create pools for WBTC-DAI and DAI-USDC. + await uniswapV3Factory.createPool(wbtc.address, dai.address, DEFAULT_FEE); + await uniswapV3Factory.createPool(dai.address, usdc.address, DEFAULT_FEE); const weth: GodModeErc20 = await deployGodModeErc20(deployer, WETH_NAME, WETH_SYMBOL, WETH_DECIMALS); @@ -131,13 +138,13 @@ export async function integrationFixture(signers: Signer[]): Promise { - // Create a position with price range 1 WBTC ~ 20k USDC. - const [reserve0, reserve1] = ["1", "250"]; +export async function mintUniswapV3PoolReserves( + this: Mocha.Context, + token0: GodModeErc20, + token1: GodModeErc20, + fee: number, + reserve0: string, + reserve1: string, + mintLiquidity: string, +): Promise { await this.contracts.uniswapV3PositionManager.createAndInitializePoolIfNecessary( - this.contracts.wbtc.address, - this.contracts.usdc.address, - 500, + token0.address, + token1.address, + fee, new bn(reserve1).div(reserve0).sqrt().multipliedBy(new bn(2).pow(96)).integerValue(3).toString(), ); - const [tickSpacing, fee, liquidity, [sqrtPriceX96, tick]] = await Promise.all([ - this.contracts.uniswapV3Pool.tickSpacing(), - this.contracts.uniswapV3Pool.fee(), - this.contracts.uniswapV3Pool.liquidity(), - this.contracts.uniswapV3Pool.slot0(), - ]); - let usdc, wbtc; { - const { name, symbol, decimals } = this.contracts.wbtc; - wbtc = new Token(31337, this.contracts.wbtc.address, await decimals(), await symbol(), await name()); + const { name, symbol, decimals } = token0; + wbtc = new Token(31337, token0.address, await decimals(), await symbol(), await name()); } { - const { name, symbol, decimals } = this.contracts.usdc; - usdc = new Token(31337, this.contracts.usdc.address, await decimals(), await symbol(), await name()); + const { name, symbol, decimals } = token1; + usdc = new Token(31337, token1.address, await decimals(), await symbol(), await name()); } + const uniswapV3Pool: UniswapV3Pool = UniswapV3Pool__factory.connect( + computePoolAddress({ + factoryAddress: await this.contracts.uniswapV3PositionManager.factory(), + tokenA: wbtc, + tokenB: usdc, + fee: fee, + }), + this.signers.admin, + ); + + const [tickSpacing, liquidity, [sqrtPriceX96, tick]] = await Promise.all([ + uniswapV3Pool.tickSpacing(), + uniswapV3Pool.liquidity(), + uniswapV3Pool.slot0(), + ]); + const pool = new Pool(wbtc, usdc, fee, sqrtPriceX96.toString(), liquidity.toString(), tick); const position = new Position({ @@ -83,19 +100,19 @@ export async function mintUniswapV3PoolReserves(this: Mocha.Context, mintLiquidi const { amount0: amount0Desired, amount1: amount1Desired } = position.mintAmounts; // Mint WBTC to the admin address. - await this.contracts.wbtc.__godMode_mint(this.signers.admin.address, amount0Desired.toString()); + await token0.__godMode_mint(this.signers.admin.address, amount0Desired.toString()); // Mint USDC to the admin address. - await this.contracts.usdc.__godMode_mint(this.signers.admin.address, amount1Desired.toString()); + await token1.__godMode_mint(this.signers.admin.address, amount1Desired.toString()); - await this.contracts.wbtc.approve(this.contracts.uniswapV3PositionManager.address, amount0Desired.toString()); + await token0.approve(this.contracts.uniswapV3PositionManager.address, amount0Desired.toString()); - await this.contracts.usdc.approve(this.contracts.uniswapV3PositionManager.address, amount1Desired.toString()); + await token1.approve(this.contracts.uniswapV3PositionManager.address, amount1Desired.toString()); await this.contracts.uniswapV3PositionManager.mint( { - token0: this.contracts.wbtc.address, - token1: this.contracts.usdc.address, + token0: token0.address, + token1: token1.address, fee: fee, tickLower: nearestUsableTick(tick, tickSpacing) - tickSpacing * 2, tickUpper: nearestUsableTick(tick, tickSpacing) + tickSpacing * 2, diff --git a/packages/flash-swap/test/shared/types.ts b/packages/flash-swap/test/shared/types.ts index 270ff60a..eaa457c9 100644 --- a/packages/flash-swap/test/shared/types.ts +++ b/packages/flash-swap/test/shared/types.ts @@ -9,7 +9,6 @@ import type { FlashUniswapV2 } from "../../src/types/contracts/uniswap-v2/FlashU import type { GodModeUniswapV2Pair } from "../../src/types/contracts/uniswap-v2/test/GodModeUniswapV2Pair"; import type { MaliciousPair as MaliciousV2Pair } from "../../src/types/contracts/uniswap-v2/test/MaliciousPair"; import type { FlashUniswapV3 } from "../../src/types/contracts/uniswap-v3/FlashUniswapV3"; -import type { UniswapV3Pool } from "../../src/types/contracts/uniswap-v3/UniswapV3Pool"; import type { GodModeNonfungiblePositionManager } from "../../src/types/contracts/uniswap-v3/test/GodModeNonfungiblePositionManager"; declare module "mocha" { @@ -21,6 +20,7 @@ declare module "mocha" { export interface Contracts { balanceSheet: BalanceSheetV2; + dai: GodModeErc20; fintroller: Fintroller; flashUniswapV2: FlashUniswapV2; flashUniswapV3: FlashUniswapV3; @@ -29,7 +29,6 @@ export interface Contracts { usdc: GodModeErc20; usdcPriceFeed: SimplePriceFeed; uniswapV2Pair: GodModeUniswapV2Pair; - uniswapV3Pool: UniswapV3Pool; uniswapV3PositionManager: GodModeNonfungiblePositionManager; wbtc: GodModeErc20; wbtcPriceFeed: SimplePriceFeed;