Overview
ETH Balance
ETH Value
$0.00Latest 25 from a total of 4,275,765 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create Position | 147782407 | 4 secs ago | IN | 0 ETH | 0.000000002827 | ||||
| Close Position | 147782407 | 4 secs ago | IN | 0 ETH | 0.000000001216 | ||||
| Create Position | 147782407 | 4 secs ago | IN | 0 ETH | 0.000000000468 | ||||
| Close Position | 147782406 | 6 secs ago | IN | 0 ETH | 0.000000001216 | ||||
| Create Position | 147782406 | 6 secs ago | IN | 0 ETH | 0.000000000468 | ||||
| Close Position | 147782390 | 38 secs ago | IN | 0 ETH | 0.000000001214 | ||||
| Create Position | 147782390 | 38 secs ago | IN | 0 ETH | 0.000000000467 | ||||
| Close Position | 147782390 | 38 secs ago | IN | 0 ETH | 0.000000001214 | ||||
| Create Position | 147782390 | 38 secs ago | IN | 0 ETH | 0.000000000467 | ||||
| Close Position | 147782390 | 38 secs ago | IN | 0 ETH | 0.000000001214 | ||||
| Create Position | 147782390 | 38 secs ago | IN | 0 ETH | 0.000000000467 | ||||
| Close Position | 147782389 | 40 secs ago | IN | 0 ETH | 0.000000001214 | ||||
| Create Position | 147782389 | 40 secs ago | IN | 0 ETH | 0.000000000466 | ||||
| Close Position | 147782386 | 46 secs ago | IN | 0 ETH | 0.000000014937 | ||||
| Create Position | 147782385 | 48 secs ago | IN | 0 ETH | 0.000000002825 | ||||
| Close Position | 147782385 | 48 secs ago | IN | 0 ETH | 0.000000001208 | ||||
| Create Position | 147782385 | 48 secs ago | IN | 0 ETH | 0.00000000046 | ||||
| Close Position | 147782384 | 50 secs ago | IN | 0 ETH | 0.000000001208 | ||||
| Create Position | 147782384 | 50 secs ago | IN | 0 ETH | 0.00000000046 | ||||
| Close Position | 147782383 | 52 secs ago | IN | 0 ETH | 0.000000014666 | ||||
| Create Position | 147782382 | 54 secs ago | IN | 0 ETH | 0.000000002822 | ||||
| Close Position | 147782382 | 54 secs ago | IN | 0 ETH | 0.000000001208 | ||||
| Create Position | 147782382 | 54 secs ago | IN | 0 ETH | 0.00000000046 | ||||
| Close Position | 147782381 | 56 secs ago | IN | 0 ETH | 0.000000014934 | ||||
| Create Position | 147782380 | 58 secs ago | IN | 0 ETH | 0.000000002849 |
View more zero value Internal Transactions in Advanced View mode
Cross-Chain Transactions
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "https://github.com/Aperture-Finance/uni-v3-lib/blob/main/src/LiquidityAmounts.sol";
import "https://github.com/Aperture-Finance/uni-v3-lib/blob/main/src/TickMath.sol";
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
}
interface INonfungiblePositionManager {
struct MintParams {
address token0;
address token1;
int24 tickSpacing;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
uint160 sqrtPriceX96;
}
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
struct PositionInfo {
uint96 nonce;
address operator;
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint128 liquidity;
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
uint128 tokensOwed0;
uint128 tokensOwed1;
}
function mint(MintParams calldata params) external returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
function decreaseLiquidity(DecreaseLiquidityParams calldata params) external returns (uint256 amount0, uint256 amount1);
function collect(CollectParams calldata params) external returns (uint256 amount0, uint256 amount1);
function burn(uint256 tokenId) external;
function positions(uint256 tokenId) external view returns (PositionInfo memory);
}
interface ICLGauge {
function deposit(uint256 tokenId) external;
function withdraw(uint256 tokenId) external;
// Read functions
function token0() external view returns (address);
function token1() external view returns (address);
function tickSpacing() external view returns (int24);
function pool() external view returns (address);
function stakedValues(address user) external view returns (uint256[] memory staked);
function rewardToken() external view returns (address);
}
interface IPool {
// Read functions
function slot0() external view returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 unlocked
);
function gauge() external view returns (address);
function nft() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function tickSpacing() external view returns (int24);
function stakedLiquidity() external view returns (uint128);
}
interface IQuoterV2 {
struct QuoteExactInputParams {
bytes path;
uint256 amountIn;
}
function quoteExactInput(bytes memory path, uint256 amountIn)
external
returns (
uint256 amountOut,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksCrossedList,
uint256 gasEstimate
);
}
contract HunterContract is IERC721Receiver {
IPool private pool;
ICLGauge private gauge;
INonfungiblePositionManager private nftManager;
address private owner;
address private rewardTokenAddress;
mapping(uint256 => address) private tokenOwners;
address private token0;
address private token1;
int24 private tickSpacing;
uint8 public partialDeposit = 1;
uint128 public partialLiquidityPercantage = 100;
uint256 private balance0;
uint256 private balance1;
uint256 private amountInToken0;
uint256 private amountInToken1;
address private constant AERODROME_UNI_ROUTER = 0x4bF3E32de155359D1D75e8B474b66848221142fc;
address private constant AERODROME_V2_QUOTER = 0x89D8218ed5fF1e46d8dcd33fb0bbeE3be1621466;
bool public swapAfterClose = true;
constructor(address _poolAddress) {
pool = IPool(_poolAddress);
gauge = ICLGauge(pool.gauge());
nftManager = INonfungiblePositionManager(pool.nft());
token0 = pool.token0();
token1 = pool.token1();
tickSpacing = pool.tickSpacing();
rewardTokenAddress = gauge.rewardToken();
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}
function onERC721Received(
address,
address from,
uint256 tokenId,
bytes calldata
)
external
override
returns (bytes4)
{
tokenOwners[tokenId] = from;
IERC721(address(nftManager)).approve(address(gauge), tokenId);
return IERC721Receiver.onERC721Received.selector;
}
function withdrawNFT(uint256 tokenId) external onlyOwner {
gauge.withdraw(tokenId);
}
function withdrawNFTtoOwner(uint256 tokenId) external onlyOwner {
gauge.withdraw(tokenId);
IERC721(address(nftManager)).safeTransferFrom(address(this), owner, tokenId);
}
function createPosition(uint256 deadlineBlockNumber) external onlyOwner {
require(block.number == deadlineBlockNumber, "block number deadline");
require(gauge.stakedValues(address(this)).length == 0, "positions already exist");
INonfungiblePositionManager.MintParams memory params = _getPositionParameters();
require(IERC20(params.token0).transferFrom(msg.sender, address(this), params.amount0Desired), "Transfer of token0 failed");
require(IERC20(params.token1).transferFrom(msg.sender, address(this), params.amount1Desired), "Transfer of token1 failed");
require(IERC20(params.token0).approve(address(nftManager), 0), "Approval for token0 failed");
require(IERC20(params.token0).approve(address(nftManager), params.amount0Desired), "Approval for token0 failed");
require(IERC20(params.token1).approve(address(nftManager), 0), "Approval for token1 failed");
require(IERC20(params.token1).approve(address(nftManager), params.amount1Desired), "Approval for token1 failed");
(uint256 tokenId, , uint256 amount0, uint256 amount1) = nftManager.mint(params);
amountInToken0 = amount0;
amountInToken1 = amount1;
uint256 refund0 = params.amount0Desired - amount0;
uint256 refund1 = params.amount1Desired - amount1;
if (refund0 > 0) {
require(IERC20(params.token0).transfer(msg.sender, refund0), "Refund of token0 failed");
}
if (refund1 > 0) {
require(IERC20(params.token1).transfer(msg.sender, refund1), "Refund of token1 failed");
}
require(IERC721(address(nftManager)).ownerOf(tokenId) == msg.sender, "You are not the owner of this NFT");
IERC721(address(nftManager)).safeTransferFrom(msg.sender, address(this), tokenId);
gauge.deposit(tokenId);
}
function closePosition(uint256 deadlineBlockNumber) external onlyOwner {
require(block.number <= deadlineBlockNumber, "block number deadline");
uint256[] memory openedPositionsIds = gauge.stakedValues(address(this));
require(openedPositionsIds.length > 0, "No open positions");
uint256 tokenId = openedPositionsIds[0];
uint128 liquidity = _getPositionLiquidity(tokenId);
INonfungiblePositionManager.DecreaseLiquidityParams memory decreaseParams = INonfungiblePositionManager.DecreaseLiquidityParams({
tokenId: tokenId,
liquidity: liquidity,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp + 5
});
INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({
tokenId: tokenId,
recipient: owner,
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
});
require(tokenOwners[tokenId] == msg.sender, "Unauthorized withdrawal");
gauge.withdraw(tokenId);
IERC721(address(nftManager)).safeTransferFrom(address(this), msg.sender, tokenId);
delete tokenOwners[tokenId];
(uint256 amount0, uint256 amount1) = nftManager.decreaseLiquidity(decreaseParams);
nftManager.collect(collectParams);
nftManager.burn(tokenId);
// SWAP
uint256 diff;
if (amount0 > amountInToken0) {
diff = amount0 - amountInToken0;
} else {
diff = amountInToken0 - amount0;
}
if (diff > 100000 && swapAfterClose) {
swapDisbalance(amount0, amount1);
}
uint256 balance = IERC20(rewardTokenAddress).balanceOf(address(this));
if (balance > 0) {
require(IERC20(rewardTokenAddress).transfer(owner, balance), "Token transfer failed");
}
balance0 = IERC20(token0).balanceOf(owner);
balance1 = IERC20(token1).balanceOf(owner);
}
function transferTokens(address tokenAddress) external onlyOwner {
IERC20 token = IERC20(tokenAddress);
uint256 balance = token.balanceOf(address(this));
// require(balance > 0, "No tokens to transfer");
require(token.transfer(owner, balance), "Token transfer failed");
}
function approveUnlimitedSpending(address tokenAddress) external onlyOwner {
IERC20 token = IERC20(tokenAddress);
require(token.approve(owner, type(uint256).max), "Approval failed");
}
function approveAllNFTsForOwner() external onlyOwner {
IERC721(address(nftManager)).setApprovalForAll(owner, true);
}
function _getPositionParameters() internal returns (INonfungiblePositionManager.MintParams memory) {
(uint160 sqrtPriceX96, int24 tickLower, int24 tickUpper) = _calculateTicks();
uint128 liquidity = pool.stakedLiquidity(); // change for staked
balance0 = IERC20(token0).balanceOf(owner);
balance1 = IERC20(token1).balanceOf(owner);
(uint256 amount0_reduced_liq, uint256 amount1_reduced_liq) = getTargetLiquidityRatio(
liquidity,
sqrtPriceX96,
tickLower,
tickUpper,
balance0,
balance1,
partialLiquidityPercantage
);
uint256 amount0_partial_deposit = balance0 / partialDeposit;
uint256 amount1_partial_deposit = balance1 / partialDeposit;
uint256 amount0 = amount0_reduced_liq < amount0_partial_deposit
? amount0_reduced_liq
: amount0_partial_deposit;
uint256 amount1 = amount1_reduced_liq < amount1_partial_deposit
? amount1_reduced_liq
: amount1_partial_deposit;
require(amount0 > 0, "Owner has no token0");
require(amount1 > 0, "Owner has no token1");
return INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
tickSpacing: tickSpacing,
tickLower: tickLower,
tickUpper: tickUpper,
amount0Desired: amount0,
amount1Desired: amount1,
amount0Min: 0,
amount1Min: 0,
recipient: owner,
deadline: block.timestamp + 5,
sqrtPriceX96: 0
});
}
function _calculateTicks() internal view returns (uint160 sqrtPriceX96, int24 tickLower, int24 tickUpper) {
int24 currentTick;
( sqrtPriceX96 , currentTick, , , , ) = pool.slot0();
int24 tick_divided = currentTick / tickSpacing;
if (currentTick < 0 && tick_divided * tickSpacing != currentTick) {
tick_divided = tick_divided - 1;
}
tickLower = tick_divided * tickSpacing;
tickUpper = tickLower + tickSpacing;
}
function _getPositionLiquidity(uint256 tokenId) internal view returns (uint128) {
INonfungiblePositionManager.PositionInfo memory position = nftManager.positions(tokenId);
return position.liquidity;
}
function setPartialDeposit(uint8 _partialDeposit) external onlyOwner {
require(_partialDeposit > 0, "partialDeposit must be greater than 0");
partialDeposit = _partialDeposit;
}
function setPartialLiquidity(uint8 _partialLiquidityPct) external onlyOwner {
require(_partialLiquidityPct > 0, "partialLiquidity must be greater than 0");
partialLiquidityPercantage = _partialLiquidityPct;
}
function setSwapAfterClose(bool _swapAfterClose) external onlyOwner {
swapAfterClose = _swapAfterClose;
}
function getLastBalancesAndPrice() external view returns (
uint256,
uint256,
uint160
) {
( uint160 sqrtPriceX96, , , , , ) = pool.slot0();
return (balance0, balance1, sqrtPriceX96);
}
function getAmountOutFromAeroQuoter(
bytes memory path,
uint256 amountIn
) public returns (uint256 amountOut) {
// Call quoter and capture only first return value
(amountOut, , , ) = IQuoterV2(AERODROME_V2_QUOTER).quoteExactInput(path, amountIn);
return amountOut;
}
function findBestPathAero(
uint256 amountIn,
bytes[] memory paths
) private returns (bytes memory bestPath, uint256 bestAmountOut) {
bestAmountOut = 0;
for (uint256 i = 0; i < paths.length; i++) {
uint256 amountOut = getAmountOutFromAeroQuoter(paths[i], amountIn);
if (amountOut > bestAmountOut && amountOut > 0) {
bestAmountOut = amountOut;
bestPath = paths[i];
}
}
require(bestAmountOut > 0, "No valid path found");
}
function swapAero(
address inputToken,
uint256 amountIn,
bytes[] memory swapPaths
) public onlyOwner {
require(swapPaths.length > 0, "No paths provided");
// 1. Find best path with highest output
(bytes memory bestPath, uint256 bestAmountOut) = findBestPathAero(
amountIn,
swapPaths
);
IERC20(inputToken).transferFrom(msg.sender, address(this), amountIn);
IERC20(inputToken).approve(AERODROME_UNI_ROUTER, 0);
IERC20(inputToken).approve(AERODROME_UNI_ROUTER, amountIn);
bytes memory encodedParams = abi.encode(
owner,
amountIn,
bestAmountOut,
bestPath,
true
);
bytes[] memory inputs = new bytes[](1);
inputs[0] = encodedParams;
bytes memory rawSwapCalldata = abi.encodeWithSignature(
"execute(bytes,bytes[])",
hex"00",
inputs
);
(bool success, ) = AERODROME_UNI_ROUTER.call(rawSwapCalldata);
require(success, "Swap execution failed");
balance0 = IERC20(token0).balanceOf(owner);
balance1 = IERC20(token1).balanceOf(owner);
}
function getTargetLiquidityRatio(
uint128 liquidity,
uint160 sqrtPriceX96,
int24 tickLower,
int24 tickUpper,
uint256 amount0,
uint256 amount1,
uint128 partialLiquidityPct
) private pure returns (uint256 amount0_reduced, uint256 amount1_reduced) {
uint160 sqrt_ratio_a_x96 = TickMath.getSqrtRatioAtTick(tickLower);
uint160 sqrt_ratio_b_x96 = TickMath.getSqrtRatioAtTick(tickUpper);
uint128 my_liquidity = LiquidityAmounts.getLiquidityForAmounts(sqrtPriceX96, sqrt_ratio_a_x96, sqrt_ratio_b_x96, amount0, amount1);
uint128 target_liquidity = (liquidity * partialLiquidityPct) / 100;
if (my_liquidity > target_liquidity && my_liquidity > 0) {
amount0_reduced = (amount0 * target_liquidity) / my_liquidity;
amount1_reduced = (amount1 * target_liquidity) / my_liquidity;
}
else {
amount0_reduced = amount0;
amount1_reduced = amount1;
}
return (amount0_reduced, amount1_reduced);
}
function swapDisbalance(
uint256 amount0,
uint256 amount1
) public onlyOwner {
if (amount0 > amountInToken0) {
uint256 amount0_dif = amount0 - amountInToken0;
if (amount0_dif > 100000){ // change per token
bytes[] memory paths = new bytes[](2);
paths[0] = hex"0b2c639c533813f4aa9d7837caf62653d097ff850000C89560e827aF36c94D2Ac33a39bCE1Fe78631088Db";
paths[1] = hex"0b2c639c533813f4aa9d7837caf62653d097ff8500006442000000000000000000000000000000000000060000C89560e827aF36c94D2Ac33a39bCE1Fe78631088Db";
swapAero(token0, amount0_dif, paths);
}
}
else if (amount1 > amountInToken1){
uint256 amount1_dif = amount1 - amountInToken1;
if (amount1_dif > 5000000000000000000){ // change per token
bytes[] memory paths = new bytes[](2);
paths[0] = hex"9560e827aF36c94D2Ac33a39bCE1Fe78631088Db0000C80b2c639c533813f4aa9d7837caf62653d097ff85";
paths[1] = hex"9560e827aF36c94D2Ac33a39bCE1Fe78631088Db0000C842000000000000000000000000000000000000060000640b2c639c533813f4aa9d7837caf62653d097ff85";
swapAero(token1, amount1_dif, paths);
}
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import "./TernaryLib.sol";
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-core/blob/main/contracts/libraries/TickMath.sol)
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = 887272;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
/// @dev A threshold used for optimized bounds check, equals `MAX_SQRT_RATIO - MIN_SQRT_RATIO - 1`
uint160 internal constant MAX_SQRT_RATIO_MINUS_MIN_SQRT_RATIO_MINUS_ONE =
1461446703485210103287273052203988822378723970342 - 4295128739 - 1;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
unchecked {
int256 tick256;
assembly {
tick256 := tick
}
uint256 absTick = TernaryLib.abs(tick256);
/// @solidity memory-safe-assembly
assembly {
// Equivalent: if (absTick > MAX_TICK) revert("T");
if gt(absTick, MAX_TICK) {
// selector "Error(string)", [0x1c, 0x20)
mstore(0, 0x08c379a0)
// abi encoding offset
mstore(0x20, 0x20)
// reason string length 1 and 'T', [0x5f, 0x61)
mstore(0x41, 0x0154)
// 4 byte selector + 32 byte offset + 32 byte length + 1 byte reason
revert(0x1c, 0x45)
}
}
// Equivalent to:
// ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
// or ratio = int(2**128 / sqrt(1.0001)) if (absTick & 0x1) else 1 << 128
uint256 ratio;
assembly {
ratio := xor(shl(128, 1), mul(xor(shl(128, 1), 0xfffcb933bd6fad37aa2d162d1a594001), and(absTick, 0x1)))
}
// Iterate through 1th to 19th bit of absTick because MAX_TICK < 2**20
// Equivalent to:
// for i in range(1, 20):
// if absTick & 2 ** i:
// ratio = ratio * (2 ** 128 / 1.0001 ** (2 ** (i - 1))) / 2 ** 128
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
assembly {
// if (tick > 0) ratio = type(uint256).max / ratio;
if sgt(tick, 0) {
ratio := div(not(0), ratio)
}
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
sqrtPriceX96 := shr(32, add(ratio, sub(shl(32, 1), 1)))
}
}
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
// Equivalent: if (sqrtPriceX96 < MIN_SQRT_RATIO || sqrtPriceX96 >= MAX_SQRT_RATIO) revert("R");
// second inequality must be >= because the price can never reach the price at the max tick
/// @solidity memory-safe-assembly
assembly {
// if sqrtPriceX96 < MIN_SQRT_RATIO, the `sub` underflows and `gt` is true
// if sqrtPriceX96 >= MAX_SQRT_RATIO, sqrtPriceX96 - MIN_SQRT_RATIO > MAX_SQRT_RATIO - MIN_SQRT_RATIO - 1
if gt(sub(sqrtPriceX96, MIN_SQRT_RATIO), MAX_SQRT_RATIO_MINUS_MIN_SQRT_RATIO_MINUS_ONE) {
// selector "Error(string)", [0x1c, 0x20)
mstore(0, 0x08c379a0)
// abi encoding offset
mstore(0x20, 0x20)
// reason string length 1 and 'R', [0x5f, 0x61)
mstore(0x41, 0x0152)
// 4 byte selector + 32 byte offset + 32 byte length + 1 byte reason
revert(0x1c, 0x45)
}
}
// Find the most significant bit of `sqrtPriceX96`, 160 > msb >= 32.
uint8 msb;
assembly {
let x := sqrtPriceX96
msb := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
msb := or(msb, shl(6, lt(0xffffffffffffffff, shr(msb, x))))
msb := or(msb, shl(5, lt(0xffffffff, shr(msb, x))))
msb := or(msb, shl(4, lt(0xffff, shr(msb, x))))
msb := or(msb, shl(3, lt(0xff, shr(msb, x))))
msb := or(
msb,
byte(
and(0x1f, shr(shr(msb, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000
)
)
}
// 2**(msb - 95) > sqrtPrice >= 2**(msb - 96)
// the integer part of log_2(sqrtPrice) * 2**64 = (msb - 96) << 64, 8.64 number
int256 log_2X64;
assembly {
log_2X64 := shl(64, sub(msb, 96))
// Get the first 128 significant figures of `sqrtPriceX96`.
// r = sqrtPriceX96 / 2**(msb - 127), where 2**128 > r >= 2**127
// sqrtPrice = 2**(msb - 96) * r / 2**127, in floating point math
// Shift left first because 160 > msb >= 32. If we shift right first, we'll lose precision.
let r := shr(sub(msb, 31), shl(96, sqrtPriceX96))
// Approximate `log_2X64` to 14 binary digits after decimal
// log_2X64 = (msb - 96) * 2**64 + f_0 * 2**63 + f_1 * 2**62 + ......
// sqrtPrice**2 = 2**(2 * (msb - 96)) * (r / 2**127)**2 = 2**(2 * log_2X64 / 2**64) = 2**(2 * (msb - 96) + f_0)
// 2**f_0 = (r / 2**127)**2 = r**2 / 2**255 * 2
// f_0 = 1 if (r**2 >= 2**255) else 0
// sqrtPrice**2 = 2**(2 * (msb - 96) + f_0) * r**2 / 2**(254 + f_0) = 2**(2 * (msb - 96) + f_0) * r' / 2**127
// r' = r**2 / 2**(127 + f_0)
// sqrtPrice**4 = 2**(4 * (msb - 96) + 2 * f_0) * (r' / 2**127)**2
// = 2**(4 * log_2X64 / 2**64) = 2**(4 * (msb - 96) + 2 * f_0 + f_1)
// 2**(f_1) = (r' / 2**127)**2
// f_1 = 1 if (r'**2 >= 2**255) else 0
// Check whether r >= sqrt(2) * 2**127
// 2**256 > r**2 >= 2**254
let square := mul(r, r)
// f = (r**2 >= 2**255)
let f := slt(square, 0)
// r = r**2 >> 128 if r**2 >= 2**255 else r**2 >> 127
r := shr(127, shr(f, square))
log_2X64 := or(shl(63, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(62, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(61, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(60, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(59, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(58, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(57, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(56, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(55, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(54, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(53, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(52, f), log_2X64)
square := mul(r, r)
f := slt(square, 0)
r := shr(127, shr(f, square))
log_2X64 := or(shl(51, f), log_2X64)
log_2X64 := or(shl(50, slt(mul(r, r), 0)), log_2X64)
}
// sqrtPrice = sqrt(1.0001^tick)
// tick = log_{sqrt(1.0001)}(sqrtPrice) = log_2(sqrtPrice) / log_2(sqrt(1.0001))
// 2**64 / log_2(sqrt(1.0001)) = 255738958999603826347141
int24 tickLow;
int24 tickHi;
assembly {
let log_sqrt10001 := mul(log_2X64, 255738958999603826347141) // 128.128 number
tickLow := sar(128, sub(log_sqrt10001, 3402992956809132418596140100660247210))
tickHi := sar(128, add(log_sqrt10001, 291339464771989622907027621153398088495))
}
// Equivalent: tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
if (tickLow != tickHi) {
uint160 sqrtRatioAtTickHi = getSqrtRatioAtTick(tickHi);
assembly {
tick := sub(tickHi, gt(sqrtRatioAtTickHi, sqrtPriceX96))
}
} else {
tick = tickHi;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;
import "@uniswap/v3-core/contracts/libraries/FixedPoint96.sol";
import "./FullMath.sol";
import "./SafeCast.sol";
import "./TernaryLib.sol";
import "./UnsafeMath.sol";
/// @title Liquidity amount functions
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-periphery/blob/main/contracts/libraries/LiquidityAmounts.sol)
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
using UnsafeMath for *;
using SafeCast for uint256;
/// @notice Computes the amount of liquidity received for a given amount of token0 and price range
/// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount0 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount0(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0
) internal pure returns (uint128 liquidity) {
uint256 intermediate = FullMath.mulDivQ96(sqrtRatioAX96, sqrtRatioBX96);
return FullMath.mulDiv(amount0, intermediate, TernaryLib.absDiffU160(sqrtRatioAX96, sqrtRatioBX96)).toUint128();
}
/// @notice Computes the amount of liquidity received for a given amount of token0 and price range
/// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price representing the lower tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the upper tick boundary
/// @param amount0 The amount0 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount0Sorted(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0
) internal pure returns (uint128 liquidity) {
unchecked {
uint256 intermediate = FullMath.mulDivQ96(sqrtRatioAX96, sqrtRatioBX96);
return FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96).toUint128();
}
}
/// @notice Computes the amount of liquidity received for a given amount of token1 and price range
/// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount1 The amount1 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount1(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount1
) internal pure returns (uint128 liquidity) {
return
FullMath
.mulDiv(amount1, FixedPoint96.Q96, TernaryLib.absDiffU160(sqrtRatioAX96, sqrtRatioBX96))
.toUint128();
}
/// @notice Computes the amount of liquidity received for a given amount of token1 and price range
/// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
/// @param sqrtRatioAX96 A sqrt price representing the lower tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the upper tick boundary
/// @param amount1 The amount1 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount1Sorted(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount1
) internal pure returns (uint128 liquidity) {
unchecked {
return FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96).toUint128();
}
}
/// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount of token0 being sent in
/// @param amount1 The amount of token1 being sent in
/// @return liquidity The maximum amount of liquidity received
function getLiquidityForAmounts(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0,
uint256 amount1
) internal pure returns (uint128 liquidity) {
(sqrtRatioAX96, sqrtRatioBX96) = TernaryLib.sort2U160(sqrtRatioAX96, sqrtRatioBX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
liquidity = getLiquidityForAmount0Sorted(sqrtRatioAX96, sqrtRatioBX96, amount0);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
uint128 liquidity0 = getLiquidityForAmount0Sorted(sqrtRatioX96, sqrtRatioBX96, amount0);
uint128 liquidity1 = getLiquidityForAmount1Sorted(sqrtRatioAX96, sqrtRatioX96, amount1);
// liquidity = min(liquidity0, liquidity1);
assembly {
liquidity := xor(liquidity0, mul(xor(liquidity0, liquidity1), lt(liquidity1, liquidity0)))
}
} else {
liquidity = getLiquidityForAmount1Sorted(sqrtRatioAX96, sqrtRatioBX96, amount1);
}
}
/// @notice Computes the amount of token0 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
function getAmount0ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0) {
unchecked {
(sqrtRatioAX96, sqrtRatioBX96) = TernaryLib.sort2U160(sqrtRatioAX96, sqrtRatioBX96);
return
FullMath
.mulDiv(uint256(liquidity) << FixedPoint96.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96)
.div(sqrtRatioAX96);
}
}
/// @notice Computes the amount of token0 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the lower tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the upper tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
function getAmount0ForLiquiditySorted(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0) {
unchecked {
return
FullMath
.mulDiv(uint256(liquidity) << FixedPoint96.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96)
.div(sqrtRatioAX96);
}
}
/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1
function getAmount1ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount1) {
return FullMath.mulDivQ96(liquidity, TernaryLib.absDiffU160(sqrtRatioAX96, sqrtRatioBX96));
}
/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the lower tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the upper tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1
function getAmount1ForLiquiditySorted(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount1) {
unchecked {
return FullMath.mulDivQ96(liquidity, sqrtRatioBX96 - sqrtRatioAX96);
}
}
/// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function getAmountsForLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0, uint256 amount1) {
(sqrtRatioAX96, sqrtRatioBX96) = TernaryLib.sort2U160(sqrtRatioAX96, sqrtRatioBX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
amount0 = getAmount0ForLiquiditySorted(sqrtRatioAX96, sqrtRatioBX96, liquidity);
} else if (sqrtRatioX96 <= sqrtRatioBX96) {
amount0 = getAmount0ForLiquiditySorted(sqrtRatioX96, sqrtRatioBX96, liquidity);
amount1 = getAmount1ForLiquiditySorted(sqrtRatioAX96, sqrtRatioX96, liquidity);
} else {
amount1 = getAmount1ForLiquiditySorted(sqrtRatioAX96, sqrtRatioBX96, liquidity);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
/// @title Library for efficient ternary operations
/// @author Aperture Finance
library TernaryLib {
/// @notice Equivalent to the ternary operator: `condition ? a : b`
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256 res) {
assembly {
res := xor(b, mul(xor(a, b), condition))
}
}
/// @notice Equivalent to the ternary operator: `condition ? a : b`
function ternary(bool condition, address a, address b) internal pure returns (address res) {
assembly {
res := xor(b, mul(xor(a, b), condition))
}
}
/// @notice Equivalent to: `uint256(x < 0 ? -x : x)`
function abs(int256 x) internal pure returns (uint256 y) {
assembly {
// mask = 0 if x >= 0 else -1
let mask := sar(255, x)
// If x >= 0, |x| = x = 0 ^ x
// If x < 0, |x| = ~~|x| = ~(-|x| - 1) = ~(x - 1) = -1 ^ (x - 1)
// Either case, |x| = mask ^ (x + mask)
y := xor(mask, add(mask, x))
}
}
/// @notice Equivalent to: `a > b ? a - b : b - a`
function absDiff(uint256 a, uint256 b) internal pure returns (uint256 res) {
assembly {
// The diff between two `uint256` may overflow `int256`
let diff0 := sub(a, b)
let diff1 := sub(b, a)
res := xor(diff1, mul(xor(diff0, diff1), gt(a, b)))
}
}
/// @notice Equivalent to: `a > b ? a - b : b - a`
function absDiffU160(uint160 a, uint160 b) internal pure returns (uint256 res) {
assembly {
let diff := sub(a, b)
let mask := sar(255, diff)
res := xor(mask, add(mask, diff))
}
}
/// @notice Equivalent to: `a < b ? a : b`
function min(uint256 a, uint256 b) internal pure returns (uint256 res) {
assembly {
res := xor(b, mul(xor(a, b), lt(a, b)))
}
}
/// @notice Equivalent to: `a > b ? a : b`
function max(uint256 a, uint256 b) internal pure returns (uint256 res) {
assembly {
res := xor(b, mul(xor(a, b), gt(a, b)))
}
}
/// @notice Equivalent to: `condition ? (b, a) : (a, b)`
function switchIf(bool condition, uint256 a, uint256 b) internal pure returns (uint256, uint256) {
assembly {
let diff := mul(xor(a, b), condition)
a := xor(a, diff)
b := xor(b, diff)
}
return (a, b);
}
/// @notice Equivalent to: `condition ? (b, a) : (a, b)`
function switchIf(bool condition, address a, address b) internal pure returns (address, address) {
assembly {
let diff := mul(xor(a, b), condition)
a := xor(a, diff)
b := xor(b, diff)
}
return (a, b);
}
/// @notice Sorts two addresses and returns them in ascending order
function sort2(address a, address b) internal pure returns (address, address) {
assembly {
let diff := mul(xor(a, b), lt(b, a))
a := xor(a, diff)
b := xor(b, diff)
}
return (a, b);
}
/// @notice Sorts two uint256s and returns them in ascending order
function sort2(uint256 a, uint256 b) internal pure returns (uint256, uint256) {
assembly {
let diff := mul(xor(a, b), lt(b, a))
a := xor(a, diff)
b := xor(b, diff)
}
return (a, b);
}
/// @notice Sorts two uint160s and returns them in ascending order
function sort2U160(uint160 a, uint160 b) internal pure returns (uint160, uint160) {
assembly {
let diff := mul(xor(a, b), lt(b, a))
a := xor(a, diff)
b := xor(b, diff)
}
return (a, b);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math functions that do not check inputs or outputs
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-core/blob/main/contracts/libraries/UnsafeMath.sol)
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
library UnsafeMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(x, y)
}
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := sub(x, y)
}
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(x, y)
}
}
function div(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := div(x, y)
}
}
/// @notice Returns ceil(x / y)
/// @dev division by 0 has unspecified behavior, and must be checked externally
/// @param x The dividend
/// @param y The divisor
/// @return z The quotient, ceil(x / y)
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(div(x, y), gt(mod(x, y), 0))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
/// @title Safe casting methods
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-core/blob/main/contracts/libraries/SafeCast.sol)
/// @notice Contains methods for safely casting between types
library SafeCast {
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param x The uint256 to be downcasted
/// @return The downcasted integer, now type uint160
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) revert();
return uint160(x);
}
/// @notice Cast a uint256 to a uint128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return The downcasted integer, now type uint128
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) revert();
return uint128(x);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param x The int256 to be downcasted
/// @return The downcasted integer, now type int128
function toInt128(int256 x) internal pure returns (int128) {
unchecked {
if (((1 << 127) + uint256(x)) >> 128 == uint256(0)) return int128(x);
revert();
}
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param x The uint256 to be casted
/// @return The casted integer, now type int256
function toInt256(uint256 x) internal pure returns (int256) {
if (int256(x) >= 0) return int256(x);
revert();
}
/// @notice Cast a uint256 to a int128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return The downcasted integer, now type int128
function toInt128(uint256 x) internal pure returns (int128) {
if (x >= 1 << 127) revert();
return int128(int256(x));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
import "solady/src/utils/FixedPointMathLib.sol";
/// @title Contains 512-bit math functions
/// @author Aperture Finance
/// @author Modified from Uniswap (https://github.com/uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol)
/// @author Credit to Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256) {
return FixedPointMathLib.fullMulDiv(a, b, denominator);
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256) {
return FixedPointMathLib.fullMulDivUp(a, b, denominator);
}
/// @notice Calculates a * b / 2^96 with full precision.
/// @param a The multiplicand
/// @param b The multiplier
/// @return result The 256-bit result
function mulDivQ96(uint256 a, uint256 b) internal pure returns (uint256 result) {
assembly ("memory-safe") {
// 512-bit multiply `[prod1 prod0] = a * b`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = prod1 * 2**256 + prod0`.
// Least significant 256 bits of the product.
let prod0 := mul(a, b)
let mm := mulmod(a, b, not(0))
// Most significant 256 bits of the product.
let prod1 := sub(mm, add(prod0, lt(mm, prod0)))
// Make sure the result is less than `2**256`.
if iszero(gt(0x1000000000000000000000000, prod1)) {
// Store the function selector of `FullMulDivFailed()`.
mstore(0x00, 0xae47f702)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Divide [prod1 prod0] by 2^96.
result := or(shr(96, prod0), shl(160, prod1))
}
}
/// @notice Calculates a * b / 2^128 with full precision.
/// @param a The multiplicand
/// @param b The multiplier
/// @return result The 256-bit result
function mulDivQ128(uint256 a, uint256 b) internal pure returns (uint256 result) {
assembly ("memory-safe") {
// 512-bit multiply `[prod1 prod0] = a * b`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = prod1 * 2**256 + prod0`.
// Least significant 256 bits of the product.
let prod0 := mul(a, b)
let mm := mulmod(a, b, not(0))
// Most significant 256 bits of the product.
let prod1 := sub(mm, add(prod0, lt(mm, prod0)))
// Make sure the result is less than `2**256`.
if iszero(gt(0x100000000000000000000000000000000, prod1)) {
// Store the function selector of `FullMulDivFailed()`.
mstore(0x00, 0xae47f702)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Divide [prod1 prod0] by 2^128.
result := or(shr(128, prod0), shl(128, prod1))
}
}
/// @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
function sqrt(uint256 x) internal pure returns (uint256) {
return FixedPointMathLib.sqrt(x);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;
/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an overflow.
error RPowOverflow();
/// @dev The mantissa is too big to fit.
error MantissaOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, due to an multiplication overflow.
error SMulWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error SDivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The input outside the acceptable domain.
error OutOfDomain();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if gt(x, div(not(0), y)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if iszero(eq(div(z, y), x)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, WAD)
// Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
if iszero(mul(y, eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
// This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
if (x <= -41446531673892822313) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
// an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
if iszero(slt(x, 135305999368893231589)) {
mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
revert(0x1c, 0x04)
}
}
// `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// `k` is in the range `[-61, 195]`.
// Evaluate using a (6, 7)-term rational approximation.
// `p` is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already `2**96` too large.
r := sdiv(p, q)
}
// r should be in the range `(0.09, 0.25) * 2**96`.
// We now need to multiply r by:
// - The scale factor `s ≈ 6.031367120`.
// - The `2**k` factor from the range reduction.
// - The `1e18 / 2**96` factor for base conversion.
// We do this all at once, with an intermediate result in `2**213`
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
// We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
// We do this by multiplying by `2**96 / 10**18`. But since
// `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
// and add `ln(2**96 / 10**18)` at the end.
// Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// We place the check here for more optimal stack operations.
if iszero(sgt(x, 0)) {
mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
revert(0x1c, 0x04)
}
// forgefmt: disable-next-item
r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x := shr(159, shl(r, x))
// Evaluate using a (8, 8)-term rational approximation.
// `p` is made monic, we will multiply by a scale factor later.
// forgefmt: disable-next-item
let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
sar(96, mul(add(43456485725739037958740375743393,
sar(96, mul(add(24828157081833163892658089445524,
sar(96, mul(add(3273285459638523848632254066296,
x), x))), x))), x)), 11111509109440967052023855526967)
p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
// `q` is monic by convention.
let q := add(5573035233440673466300451813936, x)
q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
q := add(909429971244387300277376558375, sar(96, mul(x, q)))
// `p / q` is in the range `(0, 0.125) * 2**96`.
// Finalization, we need to:
// - Multiply by the scale factor `s = 5.549…`.
// - Add `ln(2**96 / 10**18)`.
// - Add `k * ln(2)`.
// - Multiply by `10**18 / 2**96 = 5**18 >> 78`.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already `2**96` too large.
p := sdiv(p, q)
// Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
p := mul(1677202110996718588342820967067443963516166, p)
// Add `ln(2) * k * 5**18 * 2**192`.
// forgefmt: disable-next-item
p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
// Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
// Base conversion: mul `2**18 / 2**192`.
r := sar(174, p)
}
}
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
(int256 wad, int256 p) = (int256(WAD), x);
uint256 c; // Whether we need to avoid catastrophic cancellation.
uint256 i = 4; // Number of iterations.
if (w <= 0x1ffffffffffff) {
if (-0x4000000000000 <= w) {
i = 1; // Inputs near zero only take one step to converge.
} else if (w <= -0x3ffffffffffffff) {
i = 32; // Inputs near `-1/e` take very long to converge.
}
} else if (uint256(w >> 63) == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
// Inline log2 for more performance, since the range is small.
let v := shr(49, w)
let l := shl(3, lt(0xff, v))
l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
c := gt(l, 60)
i := add(2, add(gt(l, 53), c))
}
} else {
int256 ll = lnWad(w = lnWad(w));
/// @solidity memory-safe-assembly
assembly {
// `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
i := add(3, iszero(shr(68, x)))
c := iszero(shr(143, x))
}
if (c == uint256(0)) {
do { // If `x` is big, use Newton's so that intermediate values won't overflow.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := mul(w, div(e, wad))
w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
}
if (p <= w) break;
p = w;
} while (--i != uint256(0));
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
return w;
}
}
do { // Otherwise, use Halley's for faster convergence.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := add(w, wad)
let s := sub(mul(w, e), mul(x, wad))
w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
}
if (p <= w) break;
p = w;
} while (--i != c);
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
// For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
// R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
if (c == uint256(0)) return w;
int256 t = w | 1;
/// @solidity memory-safe-assembly
assembly {
x := sdiv(mul(x, wad), t)
}
x = (t * (wad + lnWad(x)));
/// @solidity memory-safe-assembly
assembly {
w := sdiv(x, add(wad, t))
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a * b == x * y`, with full precision.
function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// 512-bit multiply `[p1 p0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = p1 * 2**256 + p0`.
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
let r := mulmod(x, y, d) // Compute remainder using mulmod.
let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
// Make sure `z` is less than `2**256`. Also prevents `d == 0`.
// Placing the check here seems to give more optimal stack operations.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
d := div(d, t) // Divide `d` by `t`, which is a power of two.
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(2, mul(3, d))
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
z :=
mul(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
z := div(z, d)
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
/// Performs the full 512 bit calculation regardless.
function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z)))
let t := and(d, sub(0, d))
let r := mulmod(x, y, d)
d := div(d, t)
let inv := xor(2, mul(3, d))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
z :=
mul(
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv)
)
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
z = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
z := add(z, 1)
if iszero(z) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
/// Throws if result overflows a uint256.
/// Credit to Philogy under MIT license:
/// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
for {} 1 {} {
if iszero(or(iszero(x), eq(div(z, x), y))) {
let k := and(n, 0xff) // `n`, cleaned.
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
// | p1 | z |
// Before: | p1_0 ¦ p1_1 | z_0 ¦ z_1 |
// Final: | 0 ¦ p1_0 | p1_1 ¦ z_0 |
// Check that final `z` doesn't overflow by checking that p1_0 = 0.
if iszero(shr(k, p1)) {
z := add(shl(sub(256, k), p1), shr(k, z))
break
}
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
z := shr(and(n, 0xff), z)
break
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := div(z, d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(z, d))), div(z, d))
}
}
/// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
/// @solidity memory-safe-assembly
assembly {
let g := n
let r := mod(a, n)
for { let y := 1 } 1 {} {
let q := div(g, r)
let t := g
g := r
r := sub(t, mul(r, q))
let u := x
x := y
y := sub(u, mul(y, q))
if iszero(r) { break }
}
x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
mstore(0x00, 0x65244e4e) // `DivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `max(0, x - y)`.
function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x + y)`.
function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(0, lt(add(x, y), x)), add(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x * y)`.
function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `x != 0 ? x : y`, without branching.
function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != bytes32(0) ? x : y`, without branching.
function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != address(0) ? x : y`, without branching.
function coalesce(address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(shl(96, x))))
}
}
/// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
/// Reverts if the computation overflows.
function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
if x {
z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
let half := shr(1, b) // Divide `b` by 2.
// Divide `y` by 2 every iteration.
for { y := shr(1, y) } y { y := shr(1, y) } {
let xx := mul(x, x) // Store x squared.
let xxRound := add(xx, half) // Round to the nearest number.
// Revert if `xx + half` overflowed, or if `x ** 2` overflows.
if or(lt(xxRound, xx), shr(128, x)) {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
x := div(xxRound, b) // Set `x` to scaled `xxRound`.
// If `y` is odd:
if and(y, 1) {
let zx := mul(z, x) // Compute `z * x`.
let zxRound := add(zx, half) // Round to the nearest number.
// If `z * x` overflowed or `zx + half` overflowed:
if or(xor(div(zx, x), z), lt(zxRound, zx)) {
// Revert if `x` is non-zero.
if x {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
}
z := div(zxRound, b) // Return properly scaled `zxRound`.
}
}
}
}
}
/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
// but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
z := sub(z, lt(div(x, z), z))
}
}
/// @dev Returns the cube root of `x`, rounded down.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// Makeshift lookup table to nudge the approximate log2 result.
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
// Newton-Raphson's.
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
// Round down.
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
z = (1 + sqrt(x)) * 10 ** 9;
z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
z = (1 + cbrt(x)) * 10 ** 12;
z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
}
/// @solidity memory-safe-assembly
assembly {
let p := x
for {} 1 {} {
if iszero(shr(229, p)) {
if iszero(shr(199, p)) {
p := mul(p, 100000000000000000) // 10 ** 17.
break
}
p := mul(p, 100000000) // 10 ** 8.
break
}
if iszero(shr(249, p)) { p := mul(p, 100) }
break
}
let t := mulmod(mul(z, z), z, p)
z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
}
}
/// @dev Returns `sqrt(x * y)`. Also called the geometric mean.
function mulSqrt(uint256 x, uint256 y) internal pure returns (uint256 z) {
if (x == y) return x;
uint256 p = rawMul(x, y);
if (y == rawDiv(p, x)) return sqrt(p);
for (z = saturatingMul(rawAdd(sqrt(x), 1), rawAdd(sqrt(y), 1));; z = avg(z, p)) {
if ((p = fullMulDivUnchecked(x, y, z)) >= z) break;
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { z := mul(z, x) }
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
/// Returns 0 if `x` is zero.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Returns the log2 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log2Up(uint256 x) internal pure returns (uint256 r) {
r = log2(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(r, 1), x))
}
}
/// @dev Returns the log10 of `x`.
/// Returns 0 if `x` is zero.
function log10(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, 100000000000000000000000000000000000000)) {
x := div(x, 100000000000000000000000000000000000000)
r := 38
}
if iszero(lt(x, 100000000000000000000)) {
x := div(x, 100000000000000000000)
r := add(r, 20)
}
if iszero(lt(x, 10000000000)) {
x := div(x, 10000000000)
r := add(r, 10)
}
if iszero(lt(x, 100000)) {
x := div(x, 100000)
r := add(r, 5)
}
r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
}
}
/// @dev Returns the log10 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log10Up(uint256 x) internal pure returns (uint256 r) {
r = log10(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(exp(10, r), x))
}
}
/// @dev Returns the log256 of `x`.
/// Returns 0 if `x` is zero.
function log256(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(shr(3, r), lt(0xff, shr(r, x)))
}
}
/// @dev Returns the log256 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log256Up(uint256 x) internal pure returns (uint256 r) {
r = log256(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(shl(3, r), 1), x))
}
}
/// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
/// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
/// @solidity memory-safe-assembly
assembly {
mantissa := x
if mantissa {
if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
mantissa := div(mantissa, 1000000000000000000000000000000000)
exponent := 33
}
if iszero(mod(mantissa, 10000000000000000000)) {
mantissa := div(mantissa, 10000000000000000000)
exponent := add(exponent, 19)
}
if iszero(mod(mantissa, 1000000000000)) {
mantissa := div(mantissa, 1000000000000)
exponent := add(exponent, 12)
}
if iszero(mod(mantissa, 1000000)) {
mantissa := div(mantissa, 1000000)
exponent := add(exponent, 6)
}
if iszero(mod(mantissa, 10000)) {
mantissa := div(mantissa, 10000)
exponent := add(exponent, 4)
}
if iszero(mod(mantissa, 100)) {
mantissa := div(mantissa, 100)
exponent := add(exponent, 2)
}
if iszero(mod(mantissa, 10)) {
mantissa := div(mantissa, 10)
exponent := add(exponent, 1)
}
}
}
}
/// @dev Convenience function for packing `x` into a smaller number using `sci`.
/// The `mantissa` will be in bits [7..255] (the upper 249 bits).
/// The `exponent` will be in bits [0..6] (the lower 7 bits).
/// Use `SafeCastLib` to safely ensure that the `packed` number is small
/// enough to fit in the desired unsigned integer type:
/// ```
/// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
/// ```
function packSci(uint256 x) internal pure returns (uint256 packed) {
(x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
/// @solidity memory-safe-assembly
assembly {
if shr(249, x) {
mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
revert(0x1c, 0x04)
}
packed := or(shl(7, x), packed)
}
}
/// @dev Convenience function for unpacking a packed number from `packSci`.
function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
unchecked {
unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards zero.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (x & y & 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
}
}
/// @dev Returns greatest common divisor of `x` and `y`.
function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
for { z := x } y {} {
let t := y
y := mod(z, y)
z := t
}
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
internal
pure
returns (uint256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
unchecked {
if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
return a - fullMulDiv(a - b, t - begin, end - begin);
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
internal
pure
returns (int256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
// forgefmt: disable-next-item
unchecked {
if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
uint256(t - begin), uint256(end - begin)));
return int256(uint256(a) - fullMulDiv(uint256(a - b),
uint256(t - begin), uint256(end - begin)));
}
}
/// @dev Returns if `x` is an even number. Some people may need this.
function isEven(uint256 x) internal pure returns (bool) {
return x & uint256(1) == uint256(0);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RAW NUMBER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(x, y)
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mod(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := smod(x, y)
}
}
/// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := addmod(x, y, d)
}
}
/// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mulmod(x, y, d)
}
}
}{
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_poolAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"approveAllNFTsForOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"approveUnlimitedSpending","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"deadlineBlockNumber","type":"uint256"}],"name":"closePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"deadlineBlockNumber","type":"uint256"}],"name":"createPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getAmountOutFromAeroQuoter","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLastBalancesAndPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint160","name":"","type":"uint160"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"partialDeposit","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"partialLiquidityPercantage","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_partialDeposit","type":"uint8"}],"name":"setPartialDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_partialLiquidityPct","type":"uint8"}],"name":"setPartialLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_swapAfterClose","type":"bool"}],"name":"setSwapAfterClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"bytes[]","name":"swapPaths","type":"bytes[]"}],"name":"swapAero","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapAfterClose","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"swapDisbalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"transferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawNFTtoOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040526001600760176101000a81548160ff021916908360ff160217905550606460085f6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506001600d5f6101000a81548160ff02191690831515021790555034801561007e575f5ffd5b5060405161665c38038061665c83398181016040528101906100a0919061062e565b805f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a6f19c846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610148573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061016c919061062e565b60015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610214573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610238919061062e565b60025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102e0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610304919061062e565b60065f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103ac573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103d0919061062e565b60075f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610478573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061049c919061068f565b600760146101000a81548162ffffff021916908360020b62ffffff16021790555060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f7c618c16040518163ffffffff1660e01b8152600401602060405180830381865afa158015610527573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061054b919061062e565b60045f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503360035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506106ba565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105fd826105d4565b9050919050565b61060d816105f3565b8114610617575f5ffd5b50565b5f8151905061062881610604565b92915050565b5f60208284031215610643576106426105d0565b5b5f6106508482850161061a565b91505092915050565b5f8160020b9050919050565b61066e81610659565b8114610678575f5ffd5b50565b5f8151905061068981610665565b92915050565b5f602082840312156106a4576106a36105d0565b5b5f6106b18482850161067b565b91505092915050565b615f95806106c75f395ff3fe608060405234801561000f575f5ffd5b5060043610610114575f3560e01c8063983c2254116100a0578063c52c98751161006f578063c52c9875146102b4578063c584c538146102d0578063cd868cd3146102ec578063d9f4ace0146102f6578063e375ef3c1461031457610114565b8063983c225414610244578063a126d60114610260578063b46378941461027c578063bf3fdd8d1461029857610114565b80632d23e935116100e75780632d23e935146101b05780636a9599e1146101ce5780637c6702fd146101ec57806383730cc3146102085780638fa20bf11461022457610114565b8063010e5b6414610118578063023245d7146101485780630c89a0df14610164578063150b7a0214610180575b5f5ffd5b610132600480360381019061012d9190613ed9565b610330565b60405161013f9190613f42565b60405180910390f35b610162600480360381019061015d9190613f5b565b6103d3565b005b61017e60048036038101906101799190613fe0565b6104ec565b005b61019a60048036038101906101959190614068565b6106da565b6040516101a79190614126565b60405180910390f35b6101b86107e7565b6040516101c5919061415a565b60405180910390f35b6101d66107fa565b6040516101e3919061419d565b60405180910390f35b610206600480360381019061020191906141b6565b61081b565b005b610222600480360381019061021d9190613f5b565b610ae6565b005b61022c61162f565b60405161023b93929190614203565b60405180910390f35b61025e60048036038101906102599190614262565b6116da565b005b61027a60048036038101906102759190613f5b565b6117cc565b005b610296600480360381019061029191906142c2565b6121a0565b005b6102b260048036038101906102ad9190613f5b565b61224b565b005b6102ce60048036038101906102c99190614262565b612410565b005b6102ea60048036038101906102e591906143cb565b612522565b005b6102f4612b38565b005b6102fe612c74565b60405161030b9190614446565b60405180910390f35b61032e60048036038101906103299190613fe0565b612c86565b005b5f7389d8218ed5ff1e46d8dcd33fb0bbee3be162146673ffffffffffffffffffffffffffffffffffffffff1663cdca175384846040518363ffffffff1660e01b81526004016103809291906144bf565b5f604051808303815f875af115801561039b573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906103c391906146e4565b9091509050508091505092915050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610462576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610459906147da565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b81526004016104bc9190613f42565b5f604051808303815f87803b1580156104d3575f5ffd5b505af11580156104e5573d5f5f3e3d5ffd5b5050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461057b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610572906147da565b60405180910390fd5b5f8190505f8173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016105b99190614807565b602060405180830381865afa1580156105d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105f89190614820565b90508173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b815260040161065692919061484b565b6020604051808303815f875af1158015610672573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106969190614886565b6106d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106cc906148fb565b60405180910390fd5b505050565b5f8460055f8681526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b360015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866040518363ffffffff1660e01b81526004016107a792919061484b565b5f604051808303815f87803b1580156107be575f5ffd5b505af11580156107d0573d5f5f3e3d5ffd5b5050505063150b7a0260e01b905095945050505050565b600760179054906101000a900460ff1681565b60085f9054906101000a90046fffffffffffffffffffffffffffffffff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a1906147da565b60405180910390fd5b600b548211156109c5575f600b54836108c39190614946565b9050620186a08111156109bf575f600267ffffffffffffffff8111156108ec576108eb613d82565b5b60405190808252806020026020018201604052801561091f57816020015b606081526020019060019003908161090a5790505b5090506040518060600160405280602b8152602001615ec8602b9139815f8151811061094e5761094d614979565b5b6020026020010181905250604051806080016040528060428152602001615e86604291398160018151811061098657610985614979565b5b60200260200101819052506109bd60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168383612522565b505b50610ae2565b600c54811115610ae1575f600c54826109de9190614946565b9050674563918244f40000811115610adf575f600267ffffffffffffffff811115610a0c57610a0b613d82565b5b604051908082528060200260200182016040528015610a3f57816020015b6060815260200190600190039081610a2a5790505b5090506040518060600160405280602b8152602001615ef3602b9139815f81518110610a6e57610a6d614979565b5b6020026020010181905250604051806080016040528060428152602001615f1e6042913981600181518110610aa657610aa5614979565b5b6020026020010181905250610add60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168383612522565b505b505b5b5050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6c906147da565b60405180910390fd5b804314610bb7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bae906149f0565b60405180910390fd5b5f60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16634b937763306040518263ffffffff1660e01b8152600401610c129190614807565b5f60405180830381865afa158015610c2c573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610c549190614ace565b5114610c95576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8c90614b5f565b60405180910390fd5b5f610c9e612e18565b9050805f015173ffffffffffffffffffffffffffffffffffffffff166323b872dd33308460a001516040518463ffffffff1660e01b8152600401610ce493929190614b7d565b6020604051808303815f875af1158015610d00573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d249190614886565b610d63576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d5a90614bfc565b60405180910390fd5b806020015173ffffffffffffffffffffffffffffffffffffffff166323b872dd33308460c001516040518463ffffffff1660e01b8152600401610da893929190614b7d565b6020604051808303815f875af1158015610dc4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610de89190614886565b610e27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1e90614c64565b60405180910390fd5b805f015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff165f6040518363ffffffff1660e01b8152600401610e86929190614cc4565b6020604051808303815f875af1158015610ea2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ec69190614886565b610f05576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610efc90614d35565b60405180910390fd5b805f015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360a001516040518363ffffffff1660e01b8152600401610f6892919061484b565b6020604051808303815f875af1158015610f84573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fa89190614886565b610fe7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fde90614d35565b60405180910390fd5b806020015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff165f6040518363ffffffff1660e01b8152600401611047929190614cc4565b6020604051808303815f875af1158015611063573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110879190614886565b6110c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110bd90614d9d565b60405180910390fd5b806020015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360c001516040518363ffffffff1660e01b815260040161112a92919061484b565b6020604051808303815f875af1158015611146573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061116a9190614886565b6111a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111a090614d9d565b60405180910390fd5b5f5f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5007d1f856040518263ffffffff1660e01b81526004016112069190614ef7565b6080604051808303815f875af1158015611222573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112469190614f3b565b9350935050925081600b8190555080600c819055505f828560a0015161126c9190614946565b90505f828660c0015161127f9190614946565b90505f82111561134757855f015173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33846040518363ffffffff1660e01b81526004016112c792919061484b565b6020604051808303815f875af11580156112e3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113079190614886565b611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161133d90614fe9565b60405180910390fd5b5b5f81111561140e57856020015173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b815260040161138e92919061484b565b6020604051808303815f875af11580156113aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113ce9190614886565b61140d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161140490615051565b60405180910390fd5b5b3373ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636352211e876040518263ffffffff1660e01b815260040161147f9190613f42565b602060405180830381865afa15801561149a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114be9190615083565b73ffffffffffffffffffffffffffffffffffffffff1614611514576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161150b9061511e565b60405180910390fd5b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e3330886040518463ffffffff1660e01b815260040161157293929190614b7d565b5f604051808303815f87803b158015611589575f5ffd5b505af115801561159b573d5f5f3e3d5ffd5b5050505060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b6b55f25866040518263ffffffff1660e01b81526004016115f99190613f42565b5f604051808303815f87803b158015611610575f5ffd5b505af1158015611622573d5f5f3e3d5ffd5b5050505050505050505050565b5f5f5f5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160c060405180830381865afa15801561169c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116c091906151b1565b50505050509050600954600a548293509350935050909192565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611769576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611760906147da565b60405180910390fd5b5f8160ff16116117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a5906152aa565b60405180910390fd5b80600760176101000a81548160ff021916908360ff16021790555050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461185b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611852906147da565b60405180910390fd5b8043111561189e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611895906149f0565b60405180910390fd5b5f60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16634b937763306040518263ffffffff1660e01b81526004016118f99190614807565b5f60405180830381865afa158015611913573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f8201168201806040525081019061193b9190614ace565b90505f815111611980576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161197790615312565b60405180910390fd5b5f815f8151811061199457611993614979565b5b602002602001015190505f6119a8826132a5565b90505f6040518060a00160405280848152602001836fffffffffffffffffffffffffffffffff1681526020015f81526020015f81526020016005426119ed9190615330565b81525090505f604051806080016040528085815260200160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016fffffffffffffffffffffffffffffffff801681526020016fffffffffffffffffffffffffffffffff801681525090503373ffffffffffffffffffffffffffffffffffffffff1660055f8681526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611b0f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b06906153ad565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d856040518263ffffffff1660e01b8152600401611b699190613f42565b5f604051808303815f87803b158015611b80575f5ffd5b505af1158015611b92573d5f5f3e3d5ffd5b5050505060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e3033876040518463ffffffff1660e01b8152600401611bf493929190614b7d565b5f604051808303815f87803b158015611c0b575f5ffd5b505af1158015611c1d573d5f5f3e3d5ffd5b5050505060055f8581526020019081526020015f205f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c49ccbe856040518263ffffffff1660e01b8152600401611cb09190615440565b60408051808303815f875af1158015611ccb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cef9190615459565b9150915060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fc6f7865846040518263ffffffff1660e01b8152600401611d4d91906154ea565b60408051808303815f875af1158015611d68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d8c9190615459565b505060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342966c68876040518263ffffffff1660e01b8152600401611de89190613f42565b5f604051808303815f87803b158015611dff575f5ffd5b505af1158015611e11573d5f5f3e3d5ffd5b505050505f600b54831115611e3557600b5483611e2e9190614946565b9050611e46565b82600b54611e439190614946565b90505b620186a081118015611e635750600d5f9054906101000a900460ff165b15611e7357611e72838361081b565b5b5f60045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611ece9190614807565b602060405180830381865afa158015611ee9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f0d9190614820565b90505f8111156120145760045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401611f9492919061484b565b6020604051808303815f875af1158015611fb0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fd49190614886565b612013576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161200a906148fb565b60405180910390fd5b5b60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040161208f9190614807565b602060405180830381865afa1580156120aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120ce9190614820565b60098190555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040161214f9190614807565b602060405180830381865afa15801561216a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061218e9190614820565b600a8190555050505050505050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461222f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612226906147da565b60405180910390fd5b80600d5f6101000a81548160ff02191690831515021790555050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122d1906147da565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b81526004016123349190613f42565b5f604051808303815f87803b15801561234b575f5ffd5b505af115801561235d573d5f5f3e3d5ffd5b5050505060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e3060035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518463ffffffff1660e01b81526004016123e093929190614b7d565b5f604051808303815f87803b1580156123f7575f5ffd5b505af1158015612409573d5f5f3e3d5ffd5b5050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461249f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612496906147da565b60405180910390fd5b5f8160ff16116124e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124db90615573565b60405180910390fd5b8060ff1660085f6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146125b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125a8906147da565b60405180910390fd5b5f8151116125f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125eb906155db565b60405180910390fd5b5f5f6126008484613350565b915091508473ffffffffffffffffffffffffffffffffffffffff166323b872dd3330876040518463ffffffff1660e01b815260040161264193929190614b7d565b6020604051808303815f875af115801561265d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126819190614886565b508473ffffffffffffffffffffffffffffffffffffffff1663095ea7b3734bf3e32de155359d1d75e8b474b66848221142fc5f6040518363ffffffff1660e01b81526004016126d1929190614cc4565b6020604051808303815f875af11580156126ed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127119190614886565b508473ffffffffffffffffffffffffffffffffffffffff1663095ea7b3734bf3e32de155359d1d75e8b474b66848221142fc866040518363ffffffff1660e01b815260040161276192919061484b565b6020604051808303815f875af115801561277d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127a19190614886565b505f60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685838560016040516020016127de9594939291906155f9565b60405160208183030381529060405290505f600167ffffffffffffffff81111561280b5761280a613d82565b5b60405190808252806020026020018201604052801561283e57816020015b60608152602001906001900390816128295790505b50905081815f8151811061285557612854614979565b5b60200260200101819052505f81604051602401612872919061577e565b6040516020818303038152906040527f24856bc3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f734bf3e32de155359d1d75e8b474b66848221142fc73ffffffffffffffffffffffffffffffffffffffff168260405161292b91906157eb565b5f604051808303815f865af19150503d805f8114612964576040519150601f19603f3d011682016040523d82523d5f602084013e612969565b606091505b50509050806129ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129a49061584b565b60405180910390fd5b60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612a289190614807565b602060405180830381865afa158015612a43573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a679190614820565b60098190555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612ae89190614807565b602060405180830381865afa158015612b03573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b279190614820565b600a81905550505050505050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612bc7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612bbe906147da565b60405180910390fd5b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a22cb46560035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660016040518363ffffffff1660e01b8152600401612c45929190615869565b5f604051808303815f87803b158015612c5c575f5ffd5b505af1158015612c6e573d5f5f3e3d5ffd5b50505050565b600d5f9054906101000a900460ff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612d15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d0c906147da565b60405180910390fd5b5f8190508073ffffffffffffffffffffffffffffffffffffffff1663095ea7b360035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612d9592919061484b565b6020604051808303815f875af1158015612db1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612dd59190614886565b612e14576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e0b906158da565b60405180910390fd5b5050565b612e20613ca3565b5f5f5f612e2b613417565b9250925092505f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633ab04b206040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e9b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ebf91906158f8565b905060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612f3c9190614807565b602060405180830381865afa158015612f57573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f7b9190614820565b60098190555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612ffc9190614807565b602060405180830381865afa158015613017573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061303b9190614820565b600a819055505f5f61307383878787600954600a5460085f9054906101000a90046fffffffffffffffffffffffffffffffff16613560565b915091505f600760179054906101000a900460ff1660ff166009546130989190615950565b90505f600760179054906101000a900460ff1660ff16600a546130bb9190615950565b90505f8285106130cb57826130cd565b845b90505f8285106130dd57826130df565b845b90505f8211613123576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161311a906159ca565b60405180910390fd5b5f8111613165576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161315c90615a32565b60405180910390fd5b60405180610180016040528060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600760149054906101000a900460020b60020b81526020018a60020b81526020018960020b81526020018381526020018281526020015f81526020015f815260200160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016005426132779190615330565b81526020015f73ffffffffffffffffffffffffffffffffffffffff168152509a505050505050505050505090565b5f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166399fbab88846040518263ffffffff1660e01b81526004016133019190613f42565b61018060405180830381865afa15801561331d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133419190615bec565b90508060e00151915050919050565b60605f5f90505f5f90505b83518110156133cd575f61338985838151811061337b5761337a614979565b5b602002602001015187610330565b9050828111801561339957505f81115b156133bf578092508482815181106133b4576133b3614979565b5b602002602001015193505b50808060010191505061335b565b505f8111613410576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161340790615c62565b60405180910390fd5b9250929050565b5f5f5f5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160c060405180830381865afa158015613484573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134a891906151b1565b9091925090915090505080925081955050505f600760149054906101000a900460020b826134d69190615c80565b90505f8260020b12801561350a57508160020b600760149054906101000a900460020b826135049190615ce8565b60020b14155b1561351f5760018161351c9190615d24565b90505b600760149054906101000a900460020b8161353a9190615ce8565b9350600760149054906101000a900460020b846135579190615d7e565b92505050909192565b5f5f5f61356c88613684565b90505f61357888613684565b90505f6135888b84848b8b6139b6565b90505f6064878e6135999190615dd8565b6135a39190615e14565b9050806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161180156135e957505f826fffffffffffffffffffffffffffffffff16115b1561366d57816fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff168a61361f9190615e44565b6136299190615950565b9550816fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff168961365c9190615e44565b6136669190615950565b9450613674565b8895508794505b5050505097509795505050505050565b5f5f8290505f61369382613a88565b9050620d89e88111156136b7576308c379a05f52602080526101546041526045601cfd5b5f600182166ffffcb933bd6fad37aa2d162d1a594001600160801b1802600160801b1890505f60028316146137005760806ffff97272373d413259a46990580e213a8202901c90505b5f60048316146137245760806ffff2e50f5f656932ef12357cf3c7fdcc8202901c90505b5f60088316146137485760806fffe5caca7e10e4e61c3624eaa0941cd08202901c90505b5f601083161461376c5760806fffcb9843d60f6159c9db58835c9266448202901c90505b5f60208316146137905760806fff973b41fa98c081472e6896dfb254c08202901c90505b5f60408316146137b45760806fff2ea16466c96a3843ec78b326b528618202901c90505b5f60808316146137d85760806ffe5dee046a99a2a811c461f1969c30538202901c90505b5f6101008316146137fd5760806ffcbe86c7900a88aedcffc83b479aa3a48202901c90505b5f6102008316146138225760806ff987a7253ac413176f2b074cf7815e548202901c90505b5f6104008316146138475760806ff3392b0822b70005940c7a398e4b70f38202901c90505b5f61080083161461386c5760806fe7159475a2c29b7443b29c7fa6e889d98202901c90505b5f6110008316146138915760806fd097f3bdfd2022b8845ad8f792aa58258202901c90505b5f6120008316146138b65760806fa9f746462d870fdf8a65dc1f90e061e58202901c90505b5f6140008316146138db5760806f70d869a156d2a1b890bb3df62baf32f78202901c90505b5f6180008316146139005760806f31be135f97d08fd981231505542fcfa68202901c90505b5f620100008316146139265760806f09aa508b5b7a84e1c677de54f3e99bc98202901c90505b5f6202000083161461394b5760806e5d6af8dedb81196699c329225ee6048202901c90505b5f6204000083161461396f5760806d2216e584f5fa1ea926041bedfe988202901c90505b5f620800008316146139915760806b048a170391f7dc42444e8fa28202901c90505b5f8513156139a057805f190490505b60018060201b03810160201c9350505050919050565b5f6139c18585613a9a565b80955081965050508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1611613a0e57613a07858585613abb565b9050613a7f565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161015613a70575f613a4e878686613abb565b90505f613a5c878986613b2a565b905081811081831802821892505050613a7e565b613a7b858584613b2a565b90505b5b95945050505050565b5f8160ff1d8281018118915050919050565b5f5f8383108385180280851894508084189350508383915091509250929050565b5f5f613af38573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16613b6c565b9050613b20613b1b848388880373ffffffffffffffffffffffffffffffffffffffff16613bb1565b613bc6565b9150509392505050565b5f613b63613b5e836c0100000000000000000000000087870373ffffffffffffffffffffffffffffffffffffffff16613bb1565b613bc6565b90509392505050565b5f8183025f1983850981811082018103806c0100000000000000000000000011613b9d5763ae47f7025f526004601cfd5b8060a01b8360601c17935050505092915050565b5f613bbd848484613beb565b90509392505050565b5f7001000000000000000000000000000000008210613be3575f5ffd5b819050919050565b5f8284029050600115613c9c5781838583041485151702613c96575f198385098181108201810383858709845f038516828611613c2f5763ae47f7025f526004601cfd5b808604955085600302600218808702600203810290508087026002038102905080870260020381029050808702600203810290508087026002038102905080818802600203028284880304600184855f030401888611870302170295505050505050613c9c565b81810490505b9392505050565b6040518061018001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f60020b81526020015f60020b81526020015f60020b81526020015f81526020015f81526020015f81526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b613db882613d72565b810181811067ffffffffffffffff82111715613dd757613dd6613d82565b5b80604052505050565b5f613de9613d59565b9050613df58282613daf565b919050565b5f67ffffffffffffffff821115613e1457613e13613d82565b5b613e1d82613d72565b9050602081019050919050565b828183375f83830152505050565b5f613e4a613e4584613dfa565b613de0565b905082815260208101848484011115613e6657613e65613d6e565b5b613e71848285613e2a565b509392505050565b5f82601f830112613e8d57613e8c613d6a565b5b8135613e9d848260208601613e38565b91505092915050565b5f819050919050565b613eb881613ea6565b8114613ec2575f5ffd5b50565b5f81359050613ed381613eaf565b92915050565b5f5f60408385031215613eef57613eee613d62565b5b5f83013567ffffffffffffffff811115613f0c57613f0b613d66565b5b613f1885828601613e79565b9250506020613f2985828601613ec5565b9150509250929050565b613f3c81613ea6565b82525050565b5f602082019050613f555f830184613f33565b92915050565b5f60208284031215613f7057613f6f613d62565b5b5f613f7d84828501613ec5565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f613faf82613f86565b9050919050565b613fbf81613fa5565b8114613fc9575f5ffd5b50565b5f81359050613fda81613fb6565b92915050565b5f60208284031215613ff557613ff4613d62565b5b5f61400284828501613fcc565b91505092915050565b5f5ffd5b5f5ffd5b5f5f83601f84011261402857614027613d6a565b5b8235905067ffffffffffffffff8111156140455761404461400b565b5b6020830191508360018202830111156140615761406061400f565b5b9250929050565b5f5f5f5f5f6080868803121561408157614080613d62565b5b5f61408e88828901613fcc565b955050602061409f88828901613fcc565b94505060406140b088828901613ec5565b935050606086013567ffffffffffffffff8111156140d1576140d0613d66565b5b6140dd88828901614013565b92509250509295509295909350565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b614120816140ec565b82525050565b5f6020820190506141395f830184614117565b92915050565b5f60ff82169050919050565b6141548161413f565b82525050565b5f60208201905061416d5f83018461414b565b92915050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b61419781614173565b82525050565b5f6020820190506141b05f83018461418e565b92915050565b5f5f604083850312156141cc576141cb613d62565b5b5f6141d985828601613ec5565b92505060206141ea85828601613ec5565b9150509250929050565b6141fd81613f86565b82525050565b5f6060820190506142165f830186613f33565b6142236020830185613f33565b61423060408301846141f4565b949350505050565b6142418161413f565b811461424b575f5ffd5b50565b5f8135905061425c81614238565b92915050565b5f6020828403121561427757614276613d62565b5b5f6142848482850161424e565b91505092915050565b5f8115159050919050565b6142a18161428d565b81146142ab575f5ffd5b50565b5f813590506142bc81614298565b92915050565b5f602082840312156142d7576142d6613d62565b5b5f6142e4848285016142ae565b91505092915050565b5f67ffffffffffffffff82111561430757614306613d82565b5b602082029050602081019050919050565b5f61432a614325846142ed565b613de0565b9050808382526020820190506020840283018581111561434d5761434c61400f565b5b835b8181101561439457803567ffffffffffffffff81111561437257614371613d6a565b5b80860161437f8982613e79565b8552602085019450505060208101905061434f565b5050509392505050565b5f82601f8301126143b2576143b1613d6a565b5b81356143c2848260208601614318565b91505092915050565b5f5f5f606084860312156143e2576143e1613d62565b5b5f6143ef86828701613fcc565b935050602061440086828701613ec5565b925050604084013567ffffffffffffffff81111561442157614420613d66565b5b61442d8682870161439e565b9150509250925092565b6144408161428d565b82525050565b5f6020820190506144595f830184614437565b92915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6144918261445f565b61449b8185614469565b93506144ab818560208601614479565b6144b481613d72565b840191505092915050565b5f6040820190508181035f8301526144d78185614487565b90506144e66020830184613f33565b9392505050565b5f815190506144fb81613eaf565b92915050565b5f67ffffffffffffffff82111561451b5761451a613d82565b5b602082029050602081019050919050565b61453581613f86565b811461453f575f5ffd5b50565b5f815190506145508161452c565b92915050565b5f61456861456384614501565b613de0565b9050808382526020820190506020840283018581111561458b5761458a61400f565b5b835b818110156145b457806145a08882614542565b84526020840193505060208101905061458d565b5050509392505050565b5f82601f8301126145d2576145d1613d6a565b5b81516145e2848260208601614556565b91505092915050565b5f67ffffffffffffffff82111561460557614604613d82565b5b602082029050602081019050919050565b5f63ffffffff82169050919050565b61462e81614616565b8114614638575f5ffd5b50565b5f8151905061464981614625565b92915050565b5f61466161465c846145eb565b613de0565b905080838252602082019050602084028301858111156146845761468361400f565b5b835b818110156146ad5780614699888261463b565b845260208401935050602081019050614686565b5050509392505050565b5f82601f8301126146cb576146ca613d6a565b5b81516146db84826020860161464f565b91505092915050565b5f5f5f5f608085870312156146fc576146fb613d62565b5b5f614709878288016144ed565b945050602085015167ffffffffffffffff81111561472a57614729613d66565b5b614736878288016145be565b935050604085015167ffffffffffffffff81111561475757614756613d66565b5b614763878288016146b7565b9250506060614774878288016144ed565b91505092959194509250565b5f82825260208201905092915050565b7f43616c6c6572206973206e6f7420746865206f776e65720000000000000000005f82015250565b5f6147c4601783614780565b91506147cf82614790565b602082019050919050565b5f6020820190508181035f8301526147f1816147b8565b9050919050565b61480181613fa5565b82525050565b5f60208201905061481a5f8301846147f8565b92915050565b5f6020828403121561483557614834613d62565b5b5f614842848285016144ed565b91505092915050565b5f60408201905061485e5f8301856147f8565b61486b6020830184613f33565b9392505050565b5f8151905061488081614298565b92915050565b5f6020828403121561489b5761489a613d62565b5b5f6148a884828501614872565b91505092915050565b7f546f6b656e207472616e73666572206661696c656400000000000000000000005f82015250565b5f6148e5601583614780565b91506148f0826148b1565b602082019050919050565b5f6020820190508181035f830152614912816148d9565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61495082613ea6565b915061495b83613ea6565b925082820390508181111561497357614972614919565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f626c6f636b206e756d62657220646561646c696e6500000000000000000000005f82015250565b5f6149da601583614780565b91506149e5826149a6565b602082019050919050565b5f6020820190508181035f830152614a07816149ce565b9050919050565b5f67ffffffffffffffff821115614a2857614a27613d82565b5b602082029050602081019050919050565b5f614a4b614a4684614a0e565b613de0565b90508083825260208201905060208402830185811115614a6e57614a6d61400f565b5b835b81811015614a975780614a8388826144ed565b845260208401935050602081019050614a70565b5050509392505050565b5f82601f830112614ab557614ab4613d6a565b5b8151614ac5848260208601614a39565b91505092915050565b5f60208284031215614ae357614ae2613d62565b5b5f82015167ffffffffffffffff811115614b0057614aff613d66565b5b614b0c84828501614aa1565b91505092915050565b7f706f736974696f6e7320616c72656164792065786973740000000000000000005f82015250565b5f614b49601783614780565b9150614b5482614b15565b602082019050919050565b5f6020820190508181035f830152614b7681614b3d565b9050919050565b5f606082019050614b905f8301866147f8565b614b9d60208301856147f8565b614baa6040830184613f33565b949350505050565b7f5472616e73666572206f6620746f6b656e30206661696c6564000000000000005f82015250565b5f614be6601983614780565b9150614bf182614bb2565b602082019050919050565b5f6020820190508181035f830152614c1381614bda565b9050919050565b7f5472616e73666572206f6620746f6b656e31206661696c6564000000000000005f82015250565b5f614c4e601983614780565b9150614c5982614c1a565b602082019050919050565b5f6020820190508181035f830152614c7b81614c42565b9050919050565b5f819050919050565b5f819050919050565b5f614cae614ca9614ca484614c82565b614c8b565b613ea6565b9050919050565b614cbe81614c94565b82525050565b5f604082019050614cd75f8301856147f8565b614ce46020830184614cb5565b9392505050565b7f417070726f76616c20666f7220746f6b656e30206661696c65640000000000005f82015250565b5f614d1f601a83614780565b9150614d2a82614ceb565b602082019050919050565b5f6020820190508181035f830152614d4c81614d13565b9050919050565b7f417070726f76616c20666f7220746f6b656e31206661696c65640000000000005f82015250565b5f614d87601a83614780565b9150614d9282614d53565b602082019050919050565b5f6020820190508181035f830152614db481614d7b565b9050919050565b614dc481613fa5565b82525050565b5f8160020b9050919050565b614ddf81614dca565b82525050565b614dee81613ea6565b82525050565b614dfd81613f86565b82525050565b61018082015f820151614e185f850182614dbb565b506020820151614e2b6020850182614dbb565b506040820151614e3e6040850182614dd6565b506060820151614e516060850182614dd6565b506080820151614e646080850182614dd6565b5060a0820151614e7760a0850182614de5565b5060c0820151614e8a60c0850182614de5565b5060e0820151614e9d60e0850182614de5565b50610100820151614eb2610100850182614de5565b50610120820151614ec7610120850182614dbb565b50610140820151614edc610140850182614de5565b50610160820151614ef1610160850182614df4565b50505050565b5f61018082019050614f0b5f830184614e03565b92915050565b614f1a81614173565b8114614f24575f5ffd5b50565b5f81519050614f3581614f11565b92915050565b5f5f5f5f60808587031215614f5357614f52613d62565b5b5f614f60878288016144ed565b9450506020614f7187828801614f27565b9350506040614f82878288016144ed565b9250506060614f93878288016144ed565b91505092959194509250565b7f526566756e64206f6620746f6b656e30206661696c65640000000000000000005f82015250565b5f614fd3601783614780565b9150614fde82614f9f565b602082019050919050565b5f6020820190508181035f83015261500081614fc7565b9050919050565b7f526566756e64206f6620746f6b656e31206661696c65640000000000000000005f82015250565b5f61503b601783614780565b915061504682615007565b602082019050919050565b5f6020820190508181035f8301526150688161502f565b9050919050565b5f8151905061507d81613fb6565b92915050565b5f6020828403121561509857615097613d62565b5b5f6150a58482850161506f565b91505092915050565b7f596f7520617265206e6f7420746865206f776e6572206f662074686973204e465f8201527f5400000000000000000000000000000000000000000000000000000000000000602082015250565b5f615108602183614780565b9150615113826150ae565b604082019050919050565b5f6020820190508181035f830152615135816150fc565b9050919050565b61514581614dca565b811461514f575f5ffd5b50565b5f815190506151608161513c565b92915050565b5f61ffff82169050919050565b61517c81615166565b8114615186575f5ffd5b50565b5f8151905061519781615173565b92915050565b5f815190506151ab81614238565b92915050565b5f5f5f5f5f5f60c087890312156151cb576151ca613d62565b5b5f6151d889828a01614542565b96505060206151e989828a01615152565b95505060406151fa89828a01615189565b945050606061520b89828a01615189565b935050608061521c89828a01615189565b92505060a061522d89828a0161519d565b9150509295509295509295565b7f7061727469616c4465706f736974206d757374206265206772656174657220745f8201527f68616e2030000000000000000000000000000000000000000000000000000000602082015250565b5f615294602583614780565b915061529f8261523a565b604082019050919050565b5f6020820190508181035f8301526152c181615288565b9050919050565b7f4e6f206f70656e20706f736974696f6e730000000000000000000000000000005f82015250565b5f6152fc601183614780565b9150615307826152c8565b602082019050919050565b5f6020820190508181035f830152615329816152f0565b9050919050565b5f61533a82613ea6565b915061534583613ea6565b925082820190508082111561535d5761535c614919565b5b92915050565b7f556e617574686f72697a6564207769746864726177616c0000000000000000005f82015250565b5f615397601783614780565b91506153a282615363565b602082019050919050565b5f6020820190508181035f8301526153c48161538b565b9050919050565b6153d481614173565b82525050565b60a082015f8201516153ee5f850182614de5565b50602082015161540160208501826153cb565b5060408201516154146040850182614de5565b5060608201516154276060850182614de5565b50608082015161543a6080850182614de5565b50505050565b5f60a0820190506154535f8301846153da565b92915050565b5f5f6040838503121561546f5761546e613d62565b5b5f61547c858286016144ed565b925050602061548d858286016144ed565b9150509250929050565b608082015f8201516154ab5f850182614de5565b5060208201516154be6020850182614dbb565b5060408201516154d160408501826153cb565b5060608201516154e460608501826153cb565b50505050565b5f6080820190506154fd5f830184615497565b92915050565b7f7061727469616c4c6971756964697479206d75737420626520677265617465725f8201527f207468616e203000000000000000000000000000000000000000000000000000602082015250565b5f61555d602783614780565b915061556882615503565b604082019050919050565b5f6020820190508181035f83015261558a81615551565b9050919050565b7f4e6f2070617468732070726f76696465640000000000000000000000000000005f82015250565b5f6155c5601183614780565b91506155d082615591565b602082019050919050565b5f6020820190508181035f8301526155f2816155b9565b9050919050565b5f60a08201905061560c5f8301886147f8565b6156196020830187613f33565b6156266040830186613f33565b81810360608301526156388185614487565b90506156476080830184614437565b9695505050505050565b5f5f82015250565b5f615665600183614780565b915061567082615651565b602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f82825260208201905092915050565b5f6156be8261445f565b6156c881856156a4565b93506156d8818560208601614479565b6156e181613d72565b840191505092915050565b5f6156f783836156b4565b905092915050565b5f602082019050919050565b5f6157158261567b565b61571f8185615685565b93508360208202850161573185615695565b805f5b8581101561576c578484038952815161574d85826156ec565b9450615758836156ff565b925060208a01995050600181019050615734565b50829750879550505050505092915050565b5f6040820190508181035f83015261579581615659565b905081810360208301526157a9818461570b565b905092915050565b5f81905092915050565b5f6157c58261445f565b6157cf81856157b1565b93506157df818560208601614479565b80840191505092915050565b5f6157f682846157bb565b915081905092915050565b7f5377617020657865637574696f6e206661696c656400000000000000000000005f82015250565b5f615835601583614780565b915061584082615801565b602082019050919050565b5f6020820190508181035f83015261586281615829565b9050919050565b5f60408201905061587c5f8301856147f8565b6158896020830184614437565b9392505050565b7f417070726f76616c206661696c656400000000000000000000000000000000005f82015250565b5f6158c4600f83614780565b91506158cf82615890565b602082019050919050565b5f6020820190508181035f8301526158f1816158b8565b9050919050565b5f6020828403121561590d5761590c613d62565b5b5f61591a84828501614f27565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61595a82613ea6565b915061596583613ea6565b92508261597557615974615923565b5b828204905092915050565b7f4f776e657220686173206e6f20746f6b656e30000000000000000000000000005f82015250565b5f6159b4601383614780565b91506159bf82615980565b602082019050919050565b5f6020820190508181035f8301526159e1816159a8565b9050919050565b7f4f776e657220686173206e6f20746f6b656e31000000000000000000000000005f82015250565b5f615a1c601383614780565b9150615a27826159e8565b602082019050919050565b5f6020820190508181035f830152615a4981615a10565b9050919050565b5f5ffd5b5f6bffffffffffffffffffffffff82169050919050565b615a7481615a54565b8114615a7e575f5ffd5b50565b5f81519050615a8f81615a6b565b92915050565b5f62ffffff82169050919050565b615aac81615a95565b8114615ab6575f5ffd5b50565b5f81519050615ac781615aa3565b92915050565b5f6101808284031215615ae357615ae2615a50565b5b615aee610180613de0565b90505f615afd84828501615a81565b5f830152506020615b108482850161506f565b6020830152506040615b248482850161506f565b6040830152506060615b388482850161506f565b6060830152506080615b4c84828501615ab9565b60808301525060a0615b6084828501615152565b60a08301525060c0615b7484828501615152565b60c08301525060e0615b8884828501614f27565b60e083015250610100615b9d848285016144ed565b61010083015250610120615bb3848285016144ed565b61012083015250610140615bc984828501614f27565b61014083015250610160615bdf84828501614f27565b6101608301525092915050565b5f6101808284031215615c0257615c01613d62565b5b5f615c0f84828501615acd565b91505092915050565b7f4e6f2076616c6964207061746820666f756e64000000000000000000000000005f82015250565b5f615c4c601383614780565b9150615c5782615c18565b602082019050919050565b5f6020820190508181035f830152615c7981615c40565b9050919050565b5f615c8a82614dca565b9150615c9583614dca565b925082615ca557615ca4615923565b5b60015f0383147fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000083141615615cdd57615cdc614919565b5b828205905092915050565b5f615cf282614dca565b9150615cfd83614dca565b9250828202615d0b81614dca565b9150808214615d1d57615d1c614919565b5b5092915050565b5f615d2e82614dca565b9150615d3983614dca565b92508282039050627fffff81137fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000082121715615d7857615d77614919565b5b92915050565b5f615d8882614dca565b9150615d9383614dca565b925082820190507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000008112627fffff82131715615dd257615dd1614919565b5b92915050565b5f615de282614173565b9150615ded83614173565b9250828202615dfb81614173565b9150808214615e0d57615e0c614919565b5b5092915050565b5f615e1e82614173565b9150615e2983614173565b925082615e3957615e38615923565b5b828204905092915050565b5f615e4e82613ea6565b9150615e5983613ea6565b9250828202615e6781613ea6565b91508282048414831517615e7e57615e7d614919565b5b509291505056fe0b2c639c533813f4aa9d7837caf62653d097ff8500006442000000000000000000000000000000000000060000c89560e827af36c94d2ac33a39bce1fe78631088db0b2c639c533813f4aa9d7837caf62653d097ff850000c89560e827af36c94d2ac33a39bce1fe78631088db9560e827af36c94d2ac33a39bce1fe78631088db0000c80b2c639c533813f4aa9d7837caf62653d097ff859560e827af36c94d2ac33a39bce1fe78631088db0000c842000000000000000000000000000000000000060000640b2c639c533813f4aa9d7837caf62653d097ff85a2646970667358221220e2fe62d41b13eb171141ff08f605f53fa9a32ae44103486b0ef3b8ddf7764bdf64736f6c634300081e00330000000000000000000000007cfc2da3ba598ef4de692905fedca32565ab836e
Deployed Bytecode
0x608060405234801561000f575f5ffd5b5060043610610114575f3560e01c8063983c2254116100a0578063c52c98751161006f578063c52c9875146102b4578063c584c538146102d0578063cd868cd3146102ec578063d9f4ace0146102f6578063e375ef3c1461031457610114565b8063983c225414610244578063a126d60114610260578063b46378941461027c578063bf3fdd8d1461029857610114565b80632d23e935116100e75780632d23e935146101b05780636a9599e1146101ce5780637c6702fd146101ec57806383730cc3146102085780638fa20bf11461022457610114565b8063010e5b6414610118578063023245d7146101485780630c89a0df14610164578063150b7a0214610180575b5f5ffd5b610132600480360381019061012d9190613ed9565b610330565b60405161013f9190613f42565b60405180910390f35b610162600480360381019061015d9190613f5b565b6103d3565b005b61017e60048036038101906101799190613fe0565b6104ec565b005b61019a60048036038101906101959190614068565b6106da565b6040516101a79190614126565b60405180910390f35b6101b86107e7565b6040516101c5919061415a565b60405180910390f35b6101d66107fa565b6040516101e3919061419d565b60405180910390f35b610206600480360381019061020191906141b6565b61081b565b005b610222600480360381019061021d9190613f5b565b610ae6565b005b61022c61162f565b60405161023b93929190614203565b60405180910390f35b61025e60048036038101906102599190614262565b6116da565b005b61027a60048036038101906102759190613f5b565b6117cc565b005b610296600480360381019061029191906142c2565b6121a0565b005b6102b260048036038101906102ad9190613f5b565b61224b565b005b6102ce60048036038101906102c99190614262565b612410565b005b6102ea60048036038101906102e591906143cb565b612522565b005b6102f4612b38565b005b6102fe612c74565b60405161030b9190614446565b60405180910390f35b61032e60048036038101906103299190613fe0565b612c86565b005b5f7389d8218ed5ff1e46d8dcd33fb0bbee3be162146673ffffffffffffffffffffffffffffffffffffffff1663cdca175384846040518363ffffffff1660e01b81526004016103809291906144bf565b5f604051808303815f875af115801561039b573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906103c391906146e4565b9091509050508091505092915050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610462576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610459906147da565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b81526004016104bc9190613f42565b5f604051808303815f87803b1580156104d3575f5ffd5b505af11580156104e5573d5f5f3e3d5ffd5b5050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461057b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610572906147da565b60405180910390fd5b5f8190505f8173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016105b99190614807565b602060405180830381865afa1580156105d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105f89190614820565b90508173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b815260040161065692919061484b565b6020604051808303815f875af1158015610672573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106969190614886565b6106d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106cc906148fb565b60405180910390fd5b505050565b5f8460055f8681526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b360015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866040518363ffffffff1660e01b81526004016107a792919061484b565b5f604051808303815f87803b1580156107be575f5ffd5b505af11580156107d0573d5f5f3e3d5ffd5b5050505063150b7a0260e01b905095945050505050565b600760179054906101000a900460ff1681565b60085f9054906101000a90046fffffffffffffffffffffffffffffffff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a1906147da565b60405180910390fd5b600b548211156109c5575f600b54836108c39190614946565b9050620186a08111156109bf575f600267ffffffffffffffff8111156108ec576108eb613d82565b5b60405190808252806020026020018201604052801561091f57816020015b606081526020019060019003908161090a5790505b5090506040518060600160405280602b8152602001615ec8602b9139815f8151811061094e5761094d614979565b5b6020026020010181905250604051806080016040528060428152602001615e86604291398160018151811061098657610985614979565b5b60200260200101819052506109bd60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168383612522565b505b50610ae2565b600c54811115610ae1575f600c54826109de9190614946565b9050674563918244f40000811115610adf575f600267ffffffffffffffff811115610a0c57610a0b613d82565b5b604051908082528060200260200182016040528015610a3f57816020015b6060815260200190600190039081610a2a5790505b5090506040518060600160405280602b8152602001615ef3602b9139815f81518110610a6e57610a6d614979565b5b6020026020010181905250604051806080016040528060428152602001615f1e6042913981600181518110610aa657610aa5614979565b5b6020026020010181905250610add60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168383612522565b505b505b5b5050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6c906147da565b60405180910390fd5b804314610bb7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bae906149f0565b60405180910390fd5b5f60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16634b937763306040518263ffffffff1660e01b8152600401610c129190614807565b5f60405180830381865afa158015610c2c573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610c549190614ace565b5114610c95576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8c90614b5f565b60405180910390fd5b5f610c9e612e18565b9050805f015173ffffffffffffffffffffffffffffffffffffffff166323b872dd33308460a001516040518463ffffffff1660e01b8152600401610ce493929190614b7d565b6020604051808303815f875af1158015610d00573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d249190614886565b610d63576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d5a90614bfc565b60405180910390fd5b806020015173ffffffffffffffffffffffffffffffffffffffff166323b872dd33308460c001516040518463ffffffff1660e01b8152600401610da893929190614b7d565b6020604051808303815f875af1158015610dc4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610de89190614886565b610e27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1e90614c64565b60405180910390fd5b805f015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff165f6040518363ffffffff1660e01b8152600401610e86929190614cc4565b6020604051808303815f875af1158015610ea2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ec69190614886565b610f05576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610efc90614d35565b60405180910390fd5b805f015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360a001516040518363ffffffff1660e01b8152600401610f6892919061484b565b6020604051808303815f875af1158015610f84573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fa89190614886565b610fe7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fde90614d35565b60405180910390fd5b806020015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff165f6040518363ffffffff1660e01b8152600401611047929190614cc4565b6020604051808303815f875af1158015611063573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110879190614886565b6110c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110bd90614d9d565b60405180910390fd5b806020015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b360025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360c001516040518363ffffffff1660e01b815260040161112a92919061484b565b6020604051808303815f875af1158015611146573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061116a9190614886565b6111a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111a090614d9d565b60405180910390fd5b5f5f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5007d1f856040518263ffffffff1660e01b81526004016112069190614ef7565b6080604051808303815f875af1158015611222573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112469190614f3b565b9350935050925081600b8190555080600c819055505f828560a0015161126c9190614946565b90505f828660c0015161127f9190614946565b90505f82111561134757855f015173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33846040518363ffffffff1660e01b81526004016112c792919061484b565b6020604051808303815f875af11580156112e3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113079190614886565b611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161133d90614fe9565b60405180910390fd5b5b5f81111561140e57856020015173ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b815260040161138e92919061484b565b6020604051808303815f875af11580156113aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113ce9190614886565b61140d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161140490615051565b60405180910390fd5b5b3373ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636352211e876040518263ffffffff1660e01b815260040161147f9190613f42565b602060405180830381865afa15801561149a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114be9190615083565b73ffffffffffffffffffffffffffffffffffffffff1614611514576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161150b9061511e565b60405180910390fd5b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e3330886040518463ffffffff1660e01b815260040161157293929190614b7d565b5f604051808303815f87803b158015611589575f5ffd5b505af115801561159b573d5f5f3e3d5ffd5b5050505060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b6b55f25866040518263ffffffff1660e01b81526004016115f99190613f42565b5f604051808303815f87803b158015611610575f5ffd5b505af1158015611622573d5f5f3e3d5ffd5b5050505050505050505050565b5f5f5f5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160c060405180830381865afa15801561169c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116c091906151b1565b50505050509050600954600a548293509350935050909192565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611769576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611760906147da565b60405180910390fd5b5f8160ff16116117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a5906152aa565b60405180910390fd5b80600760176101000a81548160ff021916908360ff16021790555050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461185b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611852906147da565b60405180910390fd5b8043111561189e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611895906149f0565b60405180910390fd5b5f60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16634b937763306040518263ffffffff1660e01b81526004016118f99190614807565b5f60405180830381865afa158015611913573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f8201168201806040525081019061193b9190614ace565b90505f815111611980576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161197790615312565b60405180910390fd5b5f815f8151811061199457611993614979565b5b602002602001015190505f6119a8826132a5565b90505f6040518060a00160405280848152602001836fffffffffffffffffffffffffffffffff1681526020015f81526020015f81526020016005426119ed9190615330565b81525090505f604051806080016040528085815260200160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016fffffffffffffffffffffffffffffffff801681526020016fffffffffffffffffffffffffffffffff801681525090503373ffffffffffffffffffffffffffffffffffffffff1660055f8681526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611b0f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b06906153ad565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d856040518263ffffffff1660e01b8152600401611b699190613f42565b5f604051808303815f87803b158015611b80575f5ffd5b505af1158015611b92573d5f5f3e3d5ffd5b5050505060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e3033876040518463ffffffff1660e01b8152600401611bf493929190614b7d565b5f604051808303815f87803b158015611c0b575f5ffd5b505af1158015611c1d573d5f5f3e3d5ffd5b5050505060055f8581526020019081526020015f205f6101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c49ccbe856040518263ffffffff1660e01b8152600401611cb09190615440565b60408051808303815f875af1158015611ccb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cef9190615459565b9150915060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fc6f7865846040518263ffffffff1660e01b8152600401611d4d91906154ea565b60408051808303815f875af1158015611d68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d8c9190615459565b505060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342966c68876040518263ffffffff1660e01b8152600401611de89190613f42565b5f604051808303815f87803b158015611dff575f5ffd5b505af1158015611e11573d5f5f3e3d5ffd5b505050505f600b54831115611e3557600b5483611e2e9190614946565b9050611e46565b82600b54611e439190614946565b90505b620186a081118015611e635750600d5f9054906101000a900460ff165b15611e7357611e72838361081b565b5b5f60045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611ece9190614807565b602060405180830381865afa158015611ee9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f0d9190614820565b90505f8111156120145760045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401611f9492919061484b565b6020604051808303815f875af1158015611fb0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fd49190614886565b612013576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161200a906148fb565b60405180910390fd5b5b60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040161208f9190614807565b602060405180830381865afa1580156120aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120ce9190614820565b60098190555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040161214f9190614807565b602060405180830381865afa15801561216a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061218e9190614820565b600a8190555050505050505050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461222f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612226906147da565b60405180910390fd5b80600d5f6101000a81548160ff02191690831515021790555050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122d1906147da565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b81526004016123349190613f42565b5f604051808303815f87803b15801561234b575f5ffd5b505af115801561235d573d5f5f3e3d5ffd5b5050505060025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e3060035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518463ffffffff1660e01b81526004016123e093929190614b7d565b5f604051808303815f87803b1580156123f7575f5ffd5b505af1158015612409573d5f5f3e3d5ffd5b5050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461249f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612496906147da565b60405180910390fd5b5f8160ff16116124e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124db90615573565b60405180910390fd5b8060ff1660085f6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146125b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125a8906147da565b60405180910390fd5b5f8151116125f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125eb906155db565b60405180910390fd5b5f5f6126008484613350565b915091508473ffffffffffffffffffffffffffffffffffffffff166323b872dd3330876040518463ffffffff1660e01b815260040161264193929190614b7d565b6020604051808303815f875af115801561265d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126819190614886565b508473ffffffffffffffffffffffffffffffffffffffff1663095ea7b3734bf3e32de155359d1d75e8b474b66848221142fc5f6040518363ffffffff1660e01b81526004016126d1929190614cc4565b6020604051808303815f875af11580156126ed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127119190614886565b508473ffffffffffffffffffffffffffffffffffffffff1663095ea7b3734bf3e32de155359d1d75e8b474b66848221142fc866040518363ffffffff1660e01b815260040161276192919061484b565b6020604051808303815f875af115801561277d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127a19190614886565b505f60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685838560016040516020016127de9594939291906155f9565b60405160208183030381529060405290505f600167ffffffffffffffff81111561280b5761280a613d82565b5b60405190808252806020026020018201604052801561283e57816020015b60608152602001906001900390816128295790505b50905081815f8151811061285557612854614979565b5b60200260200101819052505f81604051602401612872919061577e565b6040516020818303038152906040527f24856bc3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f734bf3e32de155359d1d75e8b474b66848221142fc73ffffffffffffffffffffffffffffffffffffffff168260405161292b91906157eb565b5f604051808303815f865af19150503d805f8114612964576040519150601f19603f3d011682016040523d82523d5f602084013e612969565b606091505b50509050806129ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016129a49061584b565b60405180910390fd5b60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612a289190614807565b602060405180830381865afa158015612a43573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a679190614820565b60098190555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612ae89190614807565b602060405180830381865afa158015612b03573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b279190614820565b600a81905550505050505050505050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612bc7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612bbe906147da565b60405180910390fd5b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a22cb46560035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660016040518363ffffffff1660e01b8152600401612c45929190615869565b5f604051808303815f87803b158015612c5c575f5ffd5b505af1158015612c6e573d5f5f3e3d5ffd5b50505050565b600d5f9054906101000a900460ff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612d15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d0c906147da565b60405180910390fd5b5f8190508073ffffffffffffffffffffffffffffffffffffffff1663095ea7b360035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612d9592919061484b565b6020604051808303815f875af1158015612db1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612dd59190614886565b612e14576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e0b906158da565b60405180910390fd5b5050565b612e20613ca3565b5f5f5f612e2b613417565b9250925092505f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633ab04b206040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e9b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ebf91906158f8565b905060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612f3c9190614807565b602060405180830381865afa158015612f57573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f7b9190614820565b60098190555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a0823160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612ffc9190614807565b602060405180830381865afa158015613017573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061303b9190614820565b600a819055505f5f61307383878787600954600a5460085f9054906101000a90046fffffffffffffffffffffffffffffffff16613560565b915091505f600760179054906101000a900460ff1660ff166009546130989190615950565b90505f600760179054906101000a900460ff1660ff16600a546130bb9190615950565b90505f8285106130cb57826130cd565b845b90505f8285106130dd57826130df565b845b90505f8211613123576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161311a906159ca565b60405180910390fd5b5f8111613165576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161315c90615a32565b60405180910390fd5b60405180610180016040528060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600760149054906101000a900460020b60020b81526020018a60020b81526020018960020b81526020018381526020018281526020015f81526020015f815260200160035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016005426132779190615330565b81526020015f73ffffffffffffffffffffffffffffffffffffffff168152509a505050505050505050505090565b5f5f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166399fbab88846040518263ffffffff1660e01b81526004016133019190613f42565b61018060405180830381865afa15801561331d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133419190615bec565b90508060e00151915050919050565b60605f5f90505f5f90505b83518110156133cd575f61338985838151811061337b5761337a614979565b5b602002602001015187610330565b9050828111801561339957505f81115b156133bf578092508482815181106133b4576133b3614979565b5b602002602001015193505b50808060010191505061335b565b505f8111613410576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161340790615c62565b60405180910390fd5b9250929050565b5f5f5f5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160c060405180830381865afa158015613484573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134a891906151b1565b9091925090915090505080925081955050505f600760149054906101000a900460020b826134d69190615c80565b90505f8260020b12801561350a57508160020b600760149054906101000a900460020b826135049190615ce8565b60020b14155b1561351f5760018161351c9190615d24565b90505b600760149054906101000a900460020b8161353a9190615ce8565b9350600760149054906101000a900460020b846135579190615d7e565b92505050909192565b5f5f5f61356c88613684565b90505f61357888613684565b90505f6135888b84848b8b6139b6565b90505f6064878e6135999190615dd8565b6135a39190615e14565b9050806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161180156135e957505f826fffffffffffffffffffffffffffffffff16115b1561366d57816fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff168a61361f9190615e44565b6136299190615950565b9550816fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff168961365c9190615e44565b6136669190615950565b9450613674565b8895508794505b5050505097509795505050505050565b5f5f8290505f61369382613a88565b9050620d89e88111156136b7576308c379a05f52602080526101546041526045601cfd5b5f600182166ffffcb933bd6fad37aa2d162d1a594001600160801b1802600160801b1890505f60028316146137005760806ffff97272373d413259a46990580e213a8202901c90505b5f60048316146137245760806ffff2e50f5f656932ef12357cf3c7fdcc8202901c90505b5f60088316146137485760806fffe5caca7e10e4e61c3624eaa0941cd08202901c90505b5f601083161461376c5760806fffcb9843d60f6159c9db58835c9266448202901c90505b5f60208316146137905760806fff973b41fa98c081472e6896dfb254c08202901c90505b5f60408316146137b45760806fff2ea16466c96a3843ec78b326b528618202901c90505b5f60808316146137d85760806ffe5dee046a99a2a811c461f1969c30538202901c90505b5f6101008316146137fd5760806ffcbe86c7900a88aedcffc83b479aa3a48202901c90505b5f6102008316146138225760806ff987a7253ac413176f2b074cf7815e548202901c90505b5f6104008316146138475760806ff3392b0822b70005940c7a398e4b70f38202901c90505b5f61080083161461386c5760806fe7159475a2c29b7443b29c7fa6e889d98202901c90505b5f6110008316146138915760806fd097f3bdfd2022b8845ad8f792aa58258202901c90505b5f6120008316146138b65760806fa9f746462d870fdf8a65dc1f90e061e58202901c90505b5f6140008316146138db5760806f70d869a156d2a1b890bb3df62baf32f78202901c90505b5f6180008316146139005760806f31be135f97d08fd981231505542fcfa68202901c90505b5f620100008316146139265760806f09aa508b5b7a84e1c677de54f3e99bc98202901c90505b5f6202000083161461394b5760806e5d6af8dedb81196699c329225ee6048202901c90505b5f6204000083161461396f5760806d2216e584f5fa1ea926041bedfe988202901c90505b5f620800008316146139915760806b048a170391f7dc42444e8fa28202901c90505b5f8513156139a057805f190490505b60018060201b03810160201c9350505050919050565b5f6139c18585613a9a565b80955081965050508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1611613a0e57613a07858585613abb565b9050613a7f565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161015613a70575f613a4e878686613abb565b90505f613a5c878986613b2a565b905081811081831802821892505050613a7e565b613a7b858584613b2a565b90505b5b95945050505050565b5f8160ff1d8281018118915050919050565b5f5f8383108385180280851894508084189350508383915091509250929050565b5f5f613af38573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16613b6c565b9050613b20613b1b848388880373ffffffffffffffffffffffffffffffffffffffff16613bb1565b613bc6565b9150509392505050565b5f613b63613b5e836c0100000000000000000000000087870373ffffffffffffffffffffffffffffffffffffffff16613bb1565b613bc6565b90509392505050565b5f8183025f1983850981811082018103806c0100000000000000000000000011613b9d5763ae47f7025f526004601cfd5b8060a01b8360601c17935050505092915050565b5f613bbd848484613beb565b90509392505050565b5f7001000000000000000000000000000000008210613be3575f5ffd5b819050919050565b5f8284029050600115613c9c5781838583041485151702613c96575f198385098181108201810383858709845f038516828611613c2f5763ae47f7025f526004601cfd5b808604955085600302600218808702600203810290508087026002038102905080870260020381029050808702600203810290508087026002038102905080818802600203028284880304600184855f030401888611870302170295505050505050613c9c565b81810490505b9392505050565b6040518061018001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f60020b81526020015f60020b81526020015f60020b81526020015f81526020015f81526020015f81526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b613db882613d72565b810181811067ffffffffffffffff82111715613dd757613dd6613d82565b5b80604052505050565b5f613de9613d59565b9050613df58282613daf565b919050565b5f67ffffffffffffffff821115613e1457613e13613d82565b5b613e1d82613d72565b9050602081019050919050565b828183375f83830152505050565b5f613e4a613e4584613dfa565b613de0565b905082815260208101848484011115613e6657613e65613d6e565b5b613e71848285613e2a565b509392505050565b5f82601f830112613e8d57613e8c613d6a565b5b8135613e9d848260208601613e38565b91505092915050565b5f819050919050565b613eb881613ea6565b8114613ec2575f5ffd5b50565b5f81359050613ed381613eaf565b92915050565b5f5f60408385031215613eef57613eee613d62565b5b5f83013567ffffffffffffffff811115613f0c57613f0b613d66565b5b613f1885828601613e79565b9250506020613f2985828601613ec5565b9150509250929050565b613f3c81613ea6565b82525050565b5f602082019050613f555f830184613f33565b92915050565b5f60208284031215613f7057613f6f613d62565b5b5f613f7d84828501613ec5565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f613faf82613f86565b9050919050565b613fbf81613fa5565b8114613fc9575f5ffd5b50565b5f81359050613fda81613fb6565b92915050565b5f60208284031215613ff557613ff4613d62565b5b5f61400284828501613fcc565b91505092915050565b5f5ffd5b5f5ffd5b5f5f83601f84011261402857614027613d6a565b5b8235905067ffffffffffffffff8111156140455761404461400b565b5b6020830191508360018202830111156140615761406061400f565b5b9250929050565b5f5f5f5f5f6080868803121561408157614080613d62565b5b5f61408e88828901613fcc565b955050602061409f88828901613fcc565b94505060406140b088828901613ec5565b935050606086013567ffffffffffffffff8111156140d1576140d0613d66565b5b6140dd88828901614013565b92509250509295509295909350565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b614120816140ec565b82525050565b5f6020820190506141395f830184614117565b92915050565b5f60ff82169050919050565b6141548161413f565b82525050565b5f60208201905061416d5f83018461414b565b92915050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b61419781614173565b82525050565b5f6020820190506141b05f83018461418e565b92915050565b5f5f604083850312156141cc576141cb613d62565b5b5f6141d985828601613ec5565b92505060206141ea85828601613ec5565b9150509250929050565b6141fd81613f86565b82525050565b5f6060820190506142165f830186613f33565b6142236020830185613f33565b61423060408301846141f4565b949350505050565b6142418161413f565b811461424b575f5ffd5b50565b5f8135905061425c81614238565b92915050565b5f6020828403121561427757614276613d62565b5b5f6142848482850161424e565b91505092915050565b5f8115159050919050565b6142a18161428d565b81146142ab575f5ffd5b50565b5f813590506142bc81614298565b92915050565b5f602082840312156142d7576142d6613d62565b5b5f6142e4848285016142ae565b91505092915050565b5f67ffffffffffffffff82111561430757614306613d82565b5b602082029050602081019050919050565b5f61432a614325846142ed565b613de0565b9050808382526020820190506020840283018581111561434d5761434c61400f565b5b835b8181101561439457803567ffffffffffffffff81111561437257614371613d6a565b5b80860161437f8982613e79565b8552602085019450505060208101905061434f565b5050509392505050565b5f82601f8301126143b2576143b1613d6a565b5b81356143c2848260208601614318565b91505092915050565b5f5f5f606084860312156143e2576143e1613d62565b5b5f6143ef86828701613fcc565b935050602061440086828701613ec5565b925050604084013567ffffffffffffffff81111561442157614420613d66565b5b61442d8682870161439e565b9150509250925092565b6144408161428d565b82525050565b5f6020820190506144595f830184614437565b92915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6144918261445f565b61449b8185614469565b93506144ab818560208601614479565b6144b481613d72565b840191505092915050565b5f6040820190508181035f8301526144d78185614487565b90506144e66020830184613f33565b9392505050565b5f815190506144fb81613eaf565b92915050565b5f67ffffffffffffffff82111561451b5761451a613d82565b5b602082029050602081019050919050565b61453581613f86565b811461453f575f5ffd5b50565b5f815190506145508161452c565b92915050565b5f61456861456384614501565b613de0565b9050808382526020820190506020840283018581111561458b5761458a61400f565b5b835b818110156145b457806145a08882614542565b84526020840193505060208101905061458d565b5050509392505050565b5f82601f8301126145d2576145d1613d6a565b5b81516145e2848260208601614556565b91505092915050565b5f67ffffffffffffffff82111561460557614604613d82565b5b602082029050602081019050919050565b5f63ffffffff82169050919050565b61462e81614616565b8114614638575f5ffd5b50565b5f8151905061464981614625565b92915050565b5f61466161465c846145eb565b613de0565b905080838252602082019050602084028301858111156146845761468361400f565b5b835b818110156146ad5780614699888261463b565b845260208401935050602081019050614686565b5050509392505050565b5f82601f8301126146cb576146ca613d6a565b5b81516146db84826020860161464f565b91505092915050565b5f5f5f5f608085870312156146fc576146fb613d62565b5b5f614709878288016144ed565b945050602085015167ffffffffffffffff81111561472a57614729613d66565b5b614736878288016145be565b935050604085015167ffffffffffffffff81111561475757614756613d66565b5b614763878288016146b7565b9250506060614774878288016144ed565b91505092959194509250565b5f82825260208201905092915050565b7f43616c6c6572206973206e6f7420746865206f776e65720000000000000000005f82015250565b5f6147c4601783614780565b91506147cf82614790565b602082019050919050565b5f6020820190508181035f8301526147f1816147b8565b9050919050565b61480181613fa5565b82525050565b5f60208201905061481a5f8301846147f8565b92915050565b5f6020828403121561483557614834613d62565b5b5f614842848285016144ed565b91505092915050565b5f60408201905061485e5f8301856147f8565b61486b6020830184613f33565b9392505050565b5f8151905061488081614298565b92915050565b5f6020828403121561489b5761489a613d62565b5b5f6148a884828501614872565b91505092915050565b7f546f6b656e207472616e73666572206661696c656400000000000000000000005f82015250565b5f6148e5601583614780565b91506148f0826148b1565b602082019050919050565b5f6020820190508181035f830152614912816148d9565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61495082613ea6565b915061495b83613ea6565b925082820390508181111561497357614972614919565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f626c6f636b206e756d62657220646561646c696e6500000000000000000000005f82015250565b5f6149da601583614780565b91506149e5826149a6565b602082019050919050565b5f6020820190508181035f830152614a07816149ce565b9050919050565b5f67ffffffffffffffff821115614a2857614a27613d82565b5b602082029050602081019050919050565b5f614a4b614a4684614a0e565b613de0565b90508083825260208201905060208402830185811115614a6e57614a6d61400f565b5b835b81811015614a975780614a8388826144ed565b845260208401935050602081019050614a70565b5050509392505050565b5f82601f830112614ab557614ab4613d6a565b5b8151614ac5848260208601614a39565b91505092915050565b5f60208284031215614ae357614ae2613d62565b5b5f82015167ffffffffffffffff811115614b0057614aff613d66565b5b614b0c84828501614aa1565b91505092915050565b7f706f736974696f6e7320616c72656164792065786973740000000000000000005f82015250565b5f614b49601783614780565b9150614b5482614b15565b602082019050919050565b5f6020820190508181035f830152614b7681614b3d565b9050919050565b5f606082019050614b905f8301866147f8565b614b9d60208301856147f8565b614baa6040830184613f33565b949350505050565b7f5472616e73666572206f6620746f6b656e30206661696c6564000000000000005f82015250565b5f614be6601983614780565b9150614bf182614bb2565b602082019050919050565b5f6020820190508181035f830152614c1381614bda565b9050919050565b7f5472616e73666572206f6620746f6b656e31206661696c6564000000000000005f82015250565b5f614c4e601983614780565b9150614c5982614c1a565b602082019050919050565b5f6020820190508181035f830152614c7b81614c42565b9050919050565b5f819050919050565b5f819050919050565b5f614cae614ca9614ca484614c82565b614c8b565b613ea6565b9050919050565b614cbe81614c94565b82525050565b5f604082019050614cd75f8301856147f8565b614ce46020830184614cb5565b9392505050565b7f417070726f76616c20666f7220746f6b656e30206661696c65640000000000005f82015250565b5f614d1f601a83614780565b9150614d2a82614ceb565b602082019050919050565b5f6020820190508181035f830152614d4c81614d13565b9050919050565b7f417070726f76616c20666f7220746f6b656e31206661696c65640000000000005f82015250565b5f614d87601a83614780565b9150614d9282614d53565b602082019050919050565b5f6020820190508181035f830152614db481614d7b565b9050919050565b614dc481613fa5565b82525050565b5f8160020b9050919050565b614ddf81614dca565b82525050565b614dee81613ea6565b82525050565b614dfd81613f86565b82525050565b61018082015f820151614e185f850182614dbb565b506020820151614e2b6020850182614dbb565b506040820151614e3e6040850182614dd6565b506060820151614e516060850182614dd6565b506080820151614e646080850182614dd6565b5060a0820151614e7760a0850182614de5565b5060c0820151614e8a60c0850182614de5565b5060e0820151614e9d60e0850182614de5565b50610100820151614eb2610100850182614de5565b50610120820151614ec7610120850182614dbb565b50610140820151614edc610140850182614de5565b50610160820151614ef1610160850182614df4565b50505050565b5f61018082019050614f0b5f830184614e03565b92915050565b614f1a81614173565b8114614f24575f5ffd5b50565b5f81519050614f3581614f11565b92915050565b5f5f5f5f60808587031215614f5357614f52613d62565b5b5f614f60878288016144ed565b9450506020614f7187828801614f27565b9350506040614f82878288016144ed565b9250506060614f93878288016144ed565b91505092959194509250565b7f526566756e64206f6620746f6b656e30206661696c65640000000000000000005f82015250565b5f614fd3601783614780565b9150614fde82614f9f565b602082019050919050565b5f6020820190508181035f83015261500081614fc7565b9050919050565b7f526566756e64206f6620746f6b656e31206661696c65640000000000000000005f82015250565b5f61503b601783614780565b915061504682615007565b602082019050919050565b5f6020820190508181035f8301526150688161502f565b9050919050565b5f8151905061507d81613fb6565b92915050565b5f6020828403121561509857615097613d62565b5b5f6150a58482850161506f565b91505092915050565b7f596f7520617265206e6f7420746865206f776e6572206f662074686973204e465f8201527f5400000000000000000000000000000000000000000000000000000000000000602082015250565b5f615108602183614780565b9150615113826150ae565b604082019050919050565b5f6020820190508181035f830152615135816150fc565b9050919050565b61514581614dca565b811461514f575f5ffd5b50565b5f815190506151608161513c565b92915050565b5f61ffff82169050919050565b61517c81615166565b8114615186575f5ffd5b50565b5f8151905061519781615173565b92915050565b5f815190506151ab81614238565b92915050565b5f5f5f5f5f5f60c087890312156151cb576151ca613d62565b5b5f6151d889828a01614542565b96505060206151e989828a01615152565b95505060406151fa89828a01615189565b945050606061520b89828a01615189565b935050608061521c89828a01615189565b92505060a061522d89828a0161519d565b9150509295509295509295565b7f7061727469616c4465706f736974206d757374206265206772656174657220745f8201527f68616e2030000000000000000000000000000000000000000000000000000000602082015250565b5f615294602583614780565b915061529f8261523a565b604082019050919050565b5f6020820190508181035f8301526152c181615288565b9050919050565b7f4e6f206f70656e20706f736974696f6e730000000000000000000000000000005f82015250565b5f6152fc601183614780565b9150615307826152c8565b602082019050919050565b5f6020820190508181035f830152615329816152f0565b9050919050565b5f61533a82613ea6565b915061534583613ea6565b925082820190508082111561535d5761535c614919565b5b92915050565b7f556e617574686f72697a6564207769746864726177616c0000000000000000005f82015250565b5f615397601783614780565b91506153a282615363565b602082019050919050565b5f6020820190508181035f8301526153c48161538b565b9050919050565b6153d481614173565b82525050565b60a082015f8201516153ee5f850182614de5565b50602082015161540160208501826153cb565b5060408201516154146040850182614de5565b5060608201516154276060850182614de5565b50608082015161543a6080850182614de5565b50505050565b5f60a0820190506154535f8301846153da565b92915050565b5f5f6040838503121561546f5761546e613d62565b5b5f61547c858286016144ed565b925050602061548d858286016144ed565b9150509250929050565b608082015f8201516154ab5f850182614de5565b5060208201516154be6020850182614dbb565b5060408201516154d160408501826153cb565b5060608201516154e460608501826153cb565b50505050565b5f6080820190506154fd5f830184615497565b92915050565b7f7061727469616c4c6971756964697479206d75737420626520677265617465725f8201527f207468616e203000000000000000000000000000000000000000000000000000602082015250565b5f61555d602783614780565b915061556882615503565b604082019050919050565b5f6020820190508181035f83015261558a81615551565b9050919050565b7f4e6f2070617468732070726f76696465640000000000000000000000000000005f82015250565b5f6155c5601183614780565b91506155d082615591565b602082019050919050565b5f6020820190508181035f8301526155f2816155b9565b9050919050565b5f60a08201905061560c5f8301886147f8565b6156196020830187613f33565b6156266040830186613f33565b81810360608301526156388185614487565b90506156476080830184614437565b9695505050505050565b5f5f82015250565b5f615665600183614780565b915061567082615651565b602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f82825260208201905092915050565b5f6156be8261445f565b6156c881856156a4565b93506156d8818560208601614479565b6156e181613d72565b840191505092915050565b5f6156f783836156b4565b905092915050565b5f602082019050919050565b5f6157158261567b565b61571f8185615685565b93508360208202850161573185615695565b805f5b8581101561576c578484038952815161574d85826156ec565b9450615758836156ff565b925060208a01995050600181019050615734565b50829750879550505050505092915050565b5f6040820190508181035f83015261579581615659565b905081810360208301526157a9818461570b565b905092915050565b5f81905092915050565b5f6157c58261445f565b6157cf81856157b1565b93506157df818560208601614479565b80840191505092915050565b5f6157f682846157bb565b915081905092915050565b7f5377617020657865637574696f6e206661696c656400000000000000000000005f82015250565b5f615835601583614780565b915061584082615801565b602082019050919050565b5f6020820190508181035f83015261586281615829565b9050919050565b5f60408201905061587c5f8301856147f8565b6158896020830184614437565b9392505050565b7f417070726f76616c206661696c656400000000000000000000000000000000005f82015250565b5f6158c4600f83614780565b91506158cf82615890565b602082019050919050565b5f6020820190508181035f8301526158f1816158b8565b9050919050565b5f6020828403121561590d5761590c613d62565b5b5f61591a84828501614f27565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61595a82613ea6565b915061596583613ea6565b92508261597557615974615923565b5b828204905092915050565b7f4f776e657220686173206e6f20746f6b656e30000000000000000000000000005f82015250565b5f6159b4601383614780565b91506159bf82615980565b602082019050919050565b5f6020820190508181035f8301526159e1816159a8565b9050919050565b7f4f776e657220686173206e6f20746f6b656e31000000000000000000000000005f82015250565b5f615a1c601383614780565b9150615a27826159e8565b602082019050919050565b5f6020820190508181035f830152615a4981615a10565b9050919050565b5f5ffd5b5f6bffffffffffffffffffffffff82169050919050565b615a7481615a54565b8114615a7e575f5ffd5b50565b5f81519050615a8f81615a6b565b92915050565b5f62ffffff82169050919050565b615aac81615a95565b8114615ab6575f5ffd5b50565b5f81519050615ac781615aa3565b92915050565b5f6101808284031215615ae357615ae2615a50565b5b615aee610180613de0565b90505f615afd84828501615a81565b5f830152506020615b108482850161506f565b6020830152506040615b248482850161506f565b6040830152506060615b388482850161506f565b6060830152506080615b4c84828501615ab9565b60808301525060a0615b6084828501615152565b60a08301525060c0615b7484828501615152565b60c08301525060e0615b8884828501614f27565b60e083015250610100615b9d848285016144ed565b61010083015250610120615bb3848285016144ed565b61012083015250610140615bc984828501614f27565b61014083015250610160615bdf84828501614f27565b6101608301525092915050565b5f6101808284031215615c0257615c01613d62565b5b5f615c0f84828501615acd565b91505092915050565b7f4e6f2076616c6964207061746820666f756e64000000000000000000000000005f82015250565b5f615c4c601383614780565b9150615c5782615c18565b602082019050919050565b5f6020820190508181035f830152615c7981615c40565b9050919050565b5f615c8a82614dca565b9150615c9583614dca565b925082615ca557615ca4615923565b5b60015f0383147fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000083141615615cdd57615cdc614919565b5b828205905092915050565b5f615cf282614dca565b9150615cfd83614dca565b9250828202615d0b81614dca565b9150808214615d1d57615d1c614919565b5b5092915050565b5f615d2e82614dca565b9150615d3983614dca565b92508282039050627fffff81137fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000082121715615d7857615d77614919565b5b92915050565b5f615d8882614dca565b9150615d9383614dca565b925082820190507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000008112627fffff82131715615dd257615dd1614919565b5b92915050565b5f615de282614173565b9150615ded83614173565b9250828202615dfb81614173565b9150808214615e0d57615e0c614919565b5b5092915050565b5f615e1e82614173565b9150615e2983614173565b925082615e3957615e38615923565b5b828204905092915050565b5f615e4e82613ea6565b9150615e5983613ea6565b9250828202615e6781613ea6565b91508282048414831517615e7e57615e7d614919565b5b509291505056fe0b2c639c533813f4aa9d7837caf62653d097ff8500006442000000000000000000000000000000000000060000c89560e827af36c94d2ac33a39bce1fe78631088db0b2c639c533813f4aa9d7837caf62653d097ff850000c89560e827af36c94d2ac33a39bce1fe78631088db9560e827af36c94d2ac33a39bce1fe78631088db0000c80b2c639c533813f4aa9d7837caf62653d097ff859560e827af36c94d2ac33a39bce1fe78631088db0000c842000000000000000000000000000000000000060000640b2c639c533813f4aa9d7837caf62653d097ff85a2646970667358221220e2fe62d41b13eb171141ff08f605f53fa9a32ae44103486b0ef3b8ddf7764bdf64736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007cfc2da3ba598ef4de692905fedca32565ab836e
-----Decoded View---------------
Arg [0] : _poolAddress (address): 0x7cfc2Da3ba598ef4De692905feDcA32565AB836E
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000007cfc2da3ba598ef4de692905fedca32565ab836e
Net Worth in USD
Net Worth in ETH
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.