Source Code
Latest 25 from a total of 10,735 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Rebalance | 32888309 | 236 days ago | IN | 0 ETH | 0.00001041 | ||||
| Rebalance | 32886816 | 236 days ago | IN | 0 ETH | 0.00001023 | ||||
| Rebalance | 32867483 | 237 days ago | IN | 0 ETH | 0.00000891 | ||||
| Rebalance | 32859071 | 237 days ago | IN | 0 ETH | 0.00002154 | ||||
| Rebalance | 32859011 | 237 days ago | IN | 0 ETH | 0.00001387 | ||||
| Rebalance | 32856736 | 237 days ago | IN | 0 ETH | 0.0000253 | ||||
| Rebalance | 32851090 | 237 days ago | IN | 0 ETH | 0.00000084 | ||||
| Rebalance | 32844015 | 237 days ago | IN | 0 ETH | 0.0000214 | ||||
| Rebalance | 32843955 | 237 days ago | IN | 0 ETH | 0.0000234 | ||||
| Rebalance | 32839218 | 237 days ago | IN | 0 ETH | 0.00002526 | ||||
| Rebalance | 32839162 | 237 days ago | IN | 0 ETH | 0.00000435 | ||||
| Rebalance | 32834248 | 238 days ago | IN | 0 ETH | 0.00000626 | ||||
| Rebalance | 32825480 | 238 days ago | IN | 0 ETH | 0.00001276 | ||||
| Rebalance | 32823976 | 238 days ago | IN | 0 ETH | 0.00001558 | ||||
| Rebalance | 32822772 | 238 days ago | IN | 0 ETH | 0.00001443 | ||||
| Rebalance | 32815823 | 238 days ago | IN | 0 ETH | 0.00001028 | ||||
| Rebalance | 32811433 | 238 days ago | IN | 0 ETH | 0.0000196 | ||||
| Rebalance | 32809093 | 238 days ago | IN | 0 ETH | 0.00001822 | ||||
| Rebalance | 32807053 | 238 days ago | IN | 0 ETH | 0.00001575 | ||||
| Rebalance | 32786310 | 239 days ago | IN | 0 ETH | 0.00000581 | ||||
| Rebalance | 32773096 | 239 days ago | IN | 0 ETH | 0.00002111 | ||||
| Rebalance | 32770213 | 239 days ago | IN | 0 ETH | 0.00001572 | ||||
| Rebalance | 32763504 | 239 days ago | IN | 0 ETH | 0.00000623 | ||||
| Rebalance | 32762175 | 239 days ago | IN | 0 ETH | 0.0000057 | ||||
| Rebalance | 32759711 | 239 days ago | IN | 0 ETH | 0.00000897 |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RebalancerSpot
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { FixedPointMathLib } from "../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { Rebalancer } from "./Rebalancer.sol";
import { SlipstreamLogic } from "./libraries/SlipstreamLogic.sol";
import { TickMath } from "../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/TickMath.sol";
import { TwapLogic } from "../libraries/TwapLogic.sol";
import { UniswapV3Logic } from "./libraries/UniswapV3Logic.sol";
/**
* @title Permissioned rebalancer for Uniswap V3 and Slipstream Liquidity Positions.
* @notice The Rebalancer will act as an Asset Manager for Arcadia Spot Accounts.
* It will allow third parties to trigger the rebalancing functionality for a Liquidity Position in the Account.
* The owner of an Arcadia Account should set an initiator via setAccountInfo() that will be permisionned to rebalance
* all Liquidity Positions held in that Account.
* @dev The contract prevents frontrunning/sandwiching by comparing the actual pool price with a pool price calculated from a TWAP.
* The tolerance in terms of price deviation is specific to the initiator but limited by a global MAX_TOLERANCE.
* @dev The contract guarantees a limited slippage with each rebalance by enforcing a minimum amount of liquidity that must be added,
* based on a hypothetical optimal swap through the pool itself without slippage.
* This protects the Account owners from incompetent or malicious initiators who route swaps poorly, or try to skim off liquidity from the position.
*/
contract RebalancerSpot is Rebalancer {
using FixedPointMathLib for uint256;
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param maxTolerance The maximum allowed deviation of the actual pool price for any initiator,
* relative to the price calculated with trusted external prices of both assets, with 18 decimals precision.
* @param maxInitiatorFee The maximum fee an initiator can set,
* relative to the ideal amountIn, with 18 decimals precision.
* @param minLiquidityRatio The ratio of the minimum amount of liquidity that must be minted,
* relative to the hypothetical amount of liquidity when we rebalance without slippage, with 18 decimals precision.
*/
constructor(uint256 maxTolerance, uint256 maxInitiatorFee, uint256 minLiquidityRatio)
Rebalancer(maxTolerance, maxInitiatorFee, minLiquidityRatio)
{ }
/* ///////////////////////////////////////////////////////////////
PUBLIC POSITION VIEW FUNCTIONS
/////////////////////////////////////////////////////////////// */
/**
* @notice Fetches all required position data from external contracts.
* @param positionManager The contract address of the Position Manager.
* @param oldId The oldId of the Liquidity Position.
* @param tickLower The lower tick of the newly minted position.
* @param tickUpper The upper tick of the newly minted position.
* @param initiator The address of the initiator.
* @return position Struct with the position data.
*/
function getPositionState(
address positionManager,
uint256 oldId,
int24 tickLower,
int24 tickUpper,
address initiator
) public view override returns (PositionState memory position) {
// Get data of the Liquidity Position.
(int24 tickCurrent, int24 tickRange) = positionManager == address(UniswapV3Logic.POSITION_MANAGER)
? UniswapV3Logic._getPositionState(position, oldId, tickLower == tickUpper)
// Logic holds for both Slipstream and staked Slipstream positions.
: SlipstreamLogic._getPositionState(position, oldId);
// Store the new ticks for the rebalance
if (tickLower == tickUpper) {
// Round current tick down to a tick that is a multiple of the tick spacing (can be initialised).
// We do not handle the edge cases where the new ticks might exceed MIN_TICK or MAX_TICK.
// This will result in a revert during the mint, if ever needed a different rebalancer has to be deployed.
tickCurrent = tickCurrent / position.tickSpacing * position.tickSpacing;
// For tick ranges that are an even multiple of the tick spacing, we use a symmetric spacing around the current tick.
// For uneven multiples, the smaller part is below the current tick.
position.tickLower = tickCurrent - tickRange / (2 * position.tickSpacing) * position.tickSpacing;
position.tickUpper = position.tickLower + tickRange;
} else {
(position.tickLower, position.tickUpper) = (tickLower, tickUpper);
}
position.sqrtRatioLower = TickMath.getSqrtPriceAtTick(position.tickLower);
position.sqrtRatioUpper = TickMath.getSqrtPriceAtTick(position.tickUpper);
// Calculate the time weighted average tick over 300s.
// This is to prevent sandwiching attacks when swapping and/or adding liquidity.
int24 twat = TwapLogic._getTwat(position.pool);
// Get the time weighted average sqrtPriceX96 over 300s.
uint256 twaSqrtPriceX96 = TickMath.getSqrtPriceAtTick(twat);
// Calculate the upper and lower bounds of sqrtPriceX96 for the Pool to be in acceptable balanced state.
// We do not handle the edge cases where exceed MIN_SQRT_RATIO or MAX_SQRT_RATIO.
// This will result in a revert during swapViaPool, if ever needed a different rebalancer has to be deployed.
position.lowerBoundSqrtPriceX96 =
twaSqrtPriceX96.mulDivDown(initiatorInfo[initiator].lowerSqrtPriceDeviation, 1e18);
position.upperBoundSqrtPriceX96 =
twaSqrtPriceX96.mulDivDown(initiatorInfo[initiator].upperSqrtPriceDeviation, 1e18);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { ActionData, IActionBase } from "../../lib/accounts-v2/src/interfaces/IActionBase.sol";
import { ArcadiaLogic } from "./libraries/ArcadiaLogic.sol";
import { BurnLogic } from "./libraries/BurnLogic.sol";
import { ERC20, SafeTransferLib } from "../../lib/accounts-v2/lib/solmate/src/utils/SafeTransferLib.sol";
import { FeeLogic } from "./libraries/FeeLogic.sol";
import { FixedPointMathLib } from "../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { FullMath } from "../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/FullMath.sol";
import { IAccount } from "./interfaces/IAccount.sol";
import { IPool } from "./interfaces/IPool.sol";
import { IPositionManager } from "./interfaces/IPositionManager.sol";
import { IStrategyHook } from "./interfaces/IStrategyHook.sol";
import { MintLogic } from "./libraries/MintLogic.sol";
import { PricingLogic } from "./libraries/PricingLogic.sol";
import { RebalanceLogic } from "./libraries/RebalanceLogic.sol";
import { SafeApprove } from "./libraries/SafeApprove.sol";
import { SlipstreamLogic } from "./libraries/SlipstreamLogic.sol";
import { StakedSlipstreamLogic } from "./libraries/StakedSlipstreamLogic.sol";
import { SwapLogic } from "./libraries/SwapLogic.sol";
import { TickMath } from "../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/TickMath.sol";
import { UniswapV3Logic } from "./libraries/UniswapV3Logic.sol";
/**
* @title Permissioned rebalancer for Uniswap V3 and Slipstream Liquidity Positions.
* @notice The Rebalancer will act as an Asset Manager for Arcadia Accounts.
* It will allow third parties to trigger the rebalancing functionality for a Liquidity Position in the Account.
* The owner of an Arcadia Account should set an initiator via setAccountInfo() that will be permisionned to rebalance
* all Liquidity Positions held in that Account.
* @dev The contract prevents frontrunning/sandwiching by comparing the actual pool price with a pool price calculated from trusted
* price feeds (oracles). The tolerance in terms of price deviation is specific to the initiator but limited by a global MAX_TOLERANCE.
* @dev The contract guarantees a limited slippage with each rebalance by enforcing a minimum amount of liquidity that must be added,
* based on a hypothetical optimal swap through the pool itself without slippage.
* This protects the Account owners from incompetent or malicious initiators who route swaps poorly, or try to skim off liquidity from the position.
*/
contract Rebalancer is IActionBase {
using FixedPointMathLib for uint256;
using SafeApprove for ERC20;
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The maximum lower deviation of the pools actual sqrtPriceX96,
// The maximum deviation of the actual pool price, in % with 18 decimals precision.
uint256 public immutable MAX_TOLERANCE;
// The maximum fee an initiator can set, with 18 decimals precision.
uint256 public immutable MAX_INITIATOR_FEE;
// The ratio that limits the amount of slippage of the swap, with 18 decimals precision.
// It is defined as the quotient between the minimal amount of liquidity that must be added,
// and the amount of liquidity that would be added if the swap was executed through the pool without slippage.
// MIN_LIQUIDITY_RATIO = minLiquidity / liquidityWithoutSlippage
uint256 public immutable MIN_LIQUIDITY_RATIO;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// The Account to compound the fees for, used as transient storage.
address internal account;
// A mapping from initiator to rebalancing fee.
mapping(address initiator => InitiatorInfo) public initiatorInfo;
// A mapping that sets the approved initiator per account.
mapping(address account => address initiator) public accountToInitiator;
// A mapping that sets a strategy hook per account.
mapping(address account => address hook) public strategyHook;
// A struct with the state of a specific position, only used in memory.
struct PositionState {
address pool;
address token0;
address token1;
uint24 fee;
int24 tickSpacing;
int24 tickUpper;
int24 tickLower;
uint128 liquidity;
uint160 sqrtRatioLower;
uint160 sqrtRatioUpper;
uint256 sqrtPriceX96;
uint256 lowerBoundSqrtPriceX96;
uint256 upperBoundSqrtPriceX96;
}
// A struct with information for each specific initiator
struct InitiatorInfo {
uint64 upperSqrtPriceDeviation;
uint64 lowerSqrtPriceDeviation;
uint64 fee;
uint64 minLiquidityRatio;
}
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error InitiatorNotValid();
error InsufficientLiquidity();
error InvalidValue();
error NotAnAccount();
error OnlyAccount();
error OnlyAccountOwner();
error OnlyPool();
error OnlyPositionManager();
error Reentered();
error UnbalancedPool();
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event AccountInfoSet(address indexed account, address indexed initiator, address indexed strategyHook);
event Rebalance(address indexed account, address indexed positionManager, uint256 oldId, uint256 newId);
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param maxTolerance The maximum allowed deviation of the actual pool price for any initiator,
* relative to the price calculated with trusted external prices of both assets, with 18 decimals precision.
* @param maxInitiatorFee The maximum fee an initiator can set,
* relative to the ideal amountIn, with 18 decimals precision.
* @param minLiquidityRatio The ratio of the minimum amount of liquidity that must be minted,
* relative to the hypothetical amount of liquidity when we rebalance without slippage, with 18 decimals precision.
*/
constructor(uint256 maxTolerance, uint256 maxInitiatorFee, uint256 minLiquidityRatio) {
MAX_TOLERANCE = maxTolerance;
MAX_INITIATOR_FEE = maxInitiatorFee;
MIN_LIQUIDITY_RATIO = minLiquidityRatio;
}
/* ///////////////////////////////////////////////////////////////
REBALANCING LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Rebalances a UniswapV3 or Slipstream Liquidity Position, owned by an Arcadia Account.
* @param account_ The Arcadia Account owning the position.
* @param positionManager The contract address of the Position Manager.
* @param oldId The oldId of the Liquidity Position to rebalance.
* @param tickLower The new lower tick to rebalance to.
* @param tickUpper The new upper tick to rebalance to.
* @dev When tickLower and tickUpper are equal, ticks will be updated with same tick-spacing as current position
* and with a balanced, 50/50 ratio around current tick.
*/
function rebalance(
address account_,
address positionManager,
uint256 oldId,
int24 tickLower,
int24 tickUpper,
bytes calldata swapData
) external {
// If the initiator is set, account_ is an actual Arcadia Account.
if (account != address(0)) revert Reentered();
if (accountToInitiator[account_] != msg.sender) revert InitiatorNotValid();
// Store Account address, used to validate the caller of the executeAction() callback and serves as a reentrancy guard.
account = account_;
// Encode data for the flash-action.
bytes memory actionData =
ArcadiaLogic._encodeAction(positionManager, oldId, msg.sender, tickLower, tickUpper, swapData);
// Call flashAction() with this contract as actionTarget.
IAccount(account_).flashAction(address(this), actionData);
// Reset account.
account = address(0);
}
/**
* @notice Callback function called by the Arcadia Account during the flashAction.
* @param rebalanceData A bytes object containing a struct with the assetData of the position and the address of the initiator.
* @return depositData A struct with the asset data of the Liquidity Position and with the leftovers after mint, if any.
* @dev The Liquidity Position is already transferred to this contract before executeAction() is called.
* @dev When rebalancing we will burn the current Liquidity Position and mint a new one with a new tokenId.
*/
function executeAction(bytes calldata rebalanceData) external override returns (ActionData memory depositData) {
// Caller should be the Account, provided as input in rebalance().
if (msg.sender != account) revert OnlyAccount();
// Cache the strategy hook.
address hook = strategyHook[msg.sender];
// Decode rebalanceData.
bytes memory swapData;
address positionManager;
uint256 oldId;
uint256 newId;
PositionState memory position;
address initiator;
{
ActionData memory assetData;
int24 tickLower;
int24 tickUpper;
(assetData, initiator, tickLower, tickUpper, swapData) =
abi.decode(rebalanceData, (ActionData, address, int24, int24, bytes));
positionManager = assetData.assets[0];
oldId = assetData.assetIds[0];
// Fetch and cache all position related data.
position = getPositionState(positionManager, oldId, tickLower, tickUpper, initiator);
}
// If set, call the strategy hook before the rebalance (view function).
// This can be used to enforce additional constraints on the rebalance, specific to the Account/Id.
// Such as:
// - Directional preferences.
// - Minimum Cool Down Periods.
// - Excluding rebalancing of certain positions.
// - ...
if (hook != address(0)) {
IStrategyHook(hook).beforeRebalance(
msg.sender, positionManager, oldId, position.tickLower, position.tickUpper
);
}
// Check that pool is initially balanced.
// Prevents sandwiching attacks when swapping and/or adding liquidity.
if (isPoolUnbalanced(position)) revert UnbalancedPool();
// Remove liquidity of the position and claim outstanding fees/rewards.
(uint256 balance0, uint256 balance1, uint256 reward) = BurnLogic._burn(positionManager, oldId, position);
{
// Get the rebalance parameters.
// These are calculated based on a hypothetical swap through the pool, without slippage.
(uint256 minLiquidity, bool zeroToOne, uint256 amountInitiatorFee, uint256 amountIn, uint256 amountOut) =
RebalanceLogic._getRebalanceParams(
initiatorInfo[initiator].minLiquidityRatio,
position.fee,
initiatorInfo[initiator].fee,
position.sqrtPriceX96,
position.sqrtRatioLower,
position.sqrtRatioUpper,
balance0,
balance1
);
// Do the actual swap to rebalance the position.
// This can be done either directly through the pool, or via a router with custom swap data.
// For swaps directly through the pool, if slippage is bigger than calculated, the transaction will not immediately revert,
// but excess slippage will be subtracted from the initiatorFee.
// For swaps via a router, tokenOut should be the limiting factor when increasing liquidity.
(balance0, balance1) = SwapLogic._swap(
swapData,
positionManager,
position,
zeroToOne,
amountInitiatorFee,
amountIn,
amountOut,
balance0,
balance1
);
// Check that the pool is still balanced after the swap.
if (isPoolUnbalanced(position)) revert UnbalancedPool();
// Mint the new liquidity position.
// We mint with the total available balances of token0 and token1, not subtracting the initiator fee.
// Leftovers must be in tokenIn, otherwise the total tokenIn balance will be added as liquidity,
// and the initiator fee will be 0 (but the transaction will not revert).
uint256 liquidity;
(newId, liquidity, balance0, balance1) = MintLogic._mint(positionManager, position, balance0, balance1);
// Check that the actual liquidity of the position is above the minimum threshold.
// This prevents loss of principal of the liquidity position due to slippage,
// or malicious initiators who remove liquidity during a custom swap.
if (liquidity < minLiquidity) revert InsufficientLiquidity();
// Transfer fee to the initiator.
(balance0, balance1) = FeeLogic._transfer(
initiator, zeroToOne, amountInitiatorFee, position.token0, position.token1, balance0, balance1
);
}
// Approve Account to redeposit Liquidity Position and leftovers.
{
uint256 count = 1;
IPositionManager(positionManager).approve(msg.sender, newId);
if (balance0 > 0) {
ERC20(position.token0).safeApproveWithRetry(msg.sender, balance0);
count = 2;
}
if (balance1 > 0) {
ERC20(position.token1).safeApproveWithRetry(msg.sender, balance1);
++count;
}
if (reward > 0) {
ERC20(StakedSlipstreamLogic.REWARD_TOKEN).safeApproveWithRetry(msg.sender, reward);
++count;
}
// Encode deposit data for the flash-action.
depositData =
ArcadiaLogic._encodeDeposit(positionManager, newId, position, count, balance0, balance1, reward);
}
// If set, call the strategy hook after the rebalance (non view function).
// Can be used to check additional constraints and persist state changes on the hook.
if (hook != address(0)) IStrategyHook(hook).afterRebalance(msg.sender, positionManager, oldId, newId);
emit Rebalance(msg.sender, positionManager, oldId, newId);
return depositData;
}
/* ///////////////////////////////////////////////////////////////
SWAP CALLBACK
/////////////////////////////////////////////////////////////// */
/**
* @notice Callback after executing a swap via IPool.swap.
* @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
* the end of the swap. If positive, the callback must send that amount of token0 to the pool.
* @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
* the end of the swap. If positive, the callback must send that amount of token1 to the pool.
* @param data Any data passed by this contract via the IPool.swap() call.
*/
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
// Check that callback came from an actual Uniswap V3 or Slipstream pool.
(address positionManager, address token0, address token1, uint24 feeOrTickSpacing) =
abi.decode(data, (address, address, address, uint24));
if (positionManager == address(UniswapV3Logic.POSITION_MANAGER)) {
if (UniswapV3Logic._computePoolAddress(token0, token1, feeOrTickSpacing) != msg.sender) revert OnlyPool();
} else {
// Logic holds for both Slipstream and staked Slipstream positions.
if (SlipstreamLogic._computePoolAddress(token0, token1, int24(feeOrTickSpacing)) != msg.sender) {
revert OnlyPool();
}
}
if (amount0Delta > 0) {
ERC20(token0).safeTransfer(msg.sender, uint256(amount0Delta));
} else if (amount1Delta > 0) {
ERC20(token1).safeTransfer(msg.sender, uint256(amount1Delta));
}
}
/* ///////////////////////////////////////////////////////////////
PUBLIC POSITION VIEW FUNCTIONS
/////////////////////////////////////////////////////////////// */
/**
* @notice returns if the pool of a Liquidity Position is unbalanced.
* @param position Struct with the position data.
* @return isPoolUnbalanced_ Bool indicating if the pool is unbalanced.
*/
function isPoolUnbalanced(PositionState memory position) public pure returns (bool isPoolUnbalanced_) {
// Check if current priceX96 of the Pool is within accepted tolerance of the calculated trusted priceX96.
isPoolUnbalanced_ = position.sqrtPriceX96 <= position.lowerBoundSqrtPriceX96
|| position.sqrtPriceX96 >= position.upperBoundSqrtPriceX96;
}
/**
* @notice Fetches all required position data from external contracts.
* @param positionManager The contract address of the Position Manager.
* @param oldId The oldId of the Liquidity Position.
* @param tickLower The lower tick of the newly minted position.
* @param tickUpper The upper tick of the newly minted position.
* @param initiator The address of the initiator.
* @return position Struct with the position data.
*/
function getPositionState(
address positionManager,
uint256 oldId,
int24 tickLower,
int24 tickUpper,
address initiator
) public view virtual returns (PositionState memory position) {
// Get data of the Liquidity Position.
(int24 tickCurrent, int24 tickRange) = positionManager == address(UniswapV3Logic.POSITION_MANAGER)
? UniswapV3Logic._getPositionState(position, oldId, tickLower == tickUpper)
// Logic holds for both Slipstream and staked Slipstream positions.
: SlipstreamLogic._getPositionState(position, oldId);
// Store the new ticks for the rebalance
if (tickLower == tickUpper) {
// Round current tick down to a tick that is a multiple of the tick spacing (can be initialised).
// We do not handle the edge cases where the new ticks might exceed MIN_TICK or MAX_TICK.
// This will result in a revert during the mint, if ever needed a different rebalancer has to be deployed.
tickCurrent = tickCurrent / position.tickSpacing * position.tickSpacing;
// For tick ranges that are an even multiple of the tick spacing, we use a symmetric spacing around the current tick.
// For uneven multiples, the smaller part is below the current tick.
position.tickLower = tickCurrent - tickRange / (2 * position.tickSpacing) * position.tickSpacing;
position.tickUpper = position.tickLower + tickRange;
} else {
(position.tickLower, position.tickUpper) = (tickLower, tickUpper);
}
position.sqrtRatioLower = TickMath.getSqrtPriceAtTick(position.tickLower);
position.sqrtRatioUpper = TickMath.getSqrtPriceAtTick(position.tickUpper);
// Get trusted USD prices for 1e18 gwei of token0 and token1.
(uint256 usdPriceToken0, uint256 usdPriceToken1) =
ArcadiaLogic._getValuesInUsd(position.token0, position.token1);
// Calculate the square root of the relative rate sqrt(token1/token0) from the trusted USD price of both tokens.
uint256 trustedSqrtPriceX96 = PricingLogic._getSqrtPriceX96(usdPriceToken0, usdPriceToken1);
// Calculate the upper and lower bounds of sqrtPriceX96 for the Pool to be balanced.
// We do not handle the edge cases where exceed MIN_SQRT_RATIO or MAX_SQRT_RATIO.
// This will result in a revert during swapViaPool, if ever needed a different rebalancer has to be deployed.
position.lowerBoundSqrtPriceX96 =
trustedSqrtPriceX96.mulDivDown(initiatorInfo[initiator].lowerSqrtPriceDeviation, 1e18);
position.upperBoundSqrtPriceX96 =
trustedSqrtPriceX96.mulDivDown(initiatorInfo[initiator].upperSqrtPriceDeviation, 1e18);
}
/* ///////////////////////////////////////////////////////////////
ACCOUNT LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Sets the required information for an Account.
* @param account_ The contract address of the Arcadia Account to set the information for.
* @param initiator The address of the initiator.
* @param hook The contract address of the hook.
* @dev An initiator will be permissioned to rebalance any
* Liquidity Position held in the specified Arcadia Account.
* @dev If the hook is set to address(0), the hook will be disabled.
* @dev When an Account is transferred to a new owner,
* the asset manager itself (this contract) and hence its initiator and hook will no longer be allowed by the Account.
*/
function setAccountInfo(address account_, address initiator, address hook) external {
if (account != address(0)) revert Reentered();
if (!ArcadiaLogic.FACTORY.isAccount(account_)) revert NotAnAccount();
if (msg.sender != IAccount(account_).owner()) revert OnlyAccountOwner();
accountToInitiator[account_] = initiator;
strategyHook[account_] = hook;
emit AccountInfoSet(account_, initiator, hook);
}
/* ///////////////////////////////////////////////////////////////
INITIATORS LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Sets the information requested for an initiator.
* @param tolerance The maximum deviation of the actual pool price,
* relative to the price calculated with trusted external prices of both assets, with 18 decimals precision.
* @param fee The fee paid to the initiator, with 18 decimals precision.
* @param minLiquidityRatio The ratio of the minimum amount of liquidity that must be minted,
* relative to the hypothetical amount of liquidity when we rebalance without slippage, with 18 decimals precision.
* @dev The tolerance for the pool price will be converted to an upper and lower max sqrtPrice deviation,
* using the square root of the basis (one with 18 decimals precision) +- tolerance (18 decimals precision).
* The tolerance boundaries are symmetric around the price, but taking the square root will result in a different
* allowed deviation of the sqrtPriceX96 for the lower and upper boundaries.
*/
function setInitiatorInfo(uint256 tolerance, uint256 fee, uint256 minLiquidityRatio) external {
if (account != address(0)) revert Reentered();
// Cache struct
InitiatorInfo memory initiatorInfo_ = initiatorInfo[msg.sender];
// Calculation required for checks.
uint64 upperSqrtPriceDeviation = uint64(FixedPointMathLib.sqrt((1e18 + tolerance) * 1e18));
// Check if initiator is already set.
if (initiatorInfo_.minLiquidityRatio > 0) {
// If so, the initiator can only change parameters to more favourable values for users.
if (
fee > initiatorInfo_.fee || upperSqrtPriceDeviation > initiatorInfo_.upperSqrtPriceDeviation
|| minLiquidityRatio < initiatorInfo_.minLiquidityRatio || minLiquidityRatio > 1e18
) revert InvalidValue();
} else {
// If not, the parameters can not exceed certain thresholds.
if (
fee > MAX_INITIATOR_FEE || tolerance > MAX_TOLERANCE || minLiquidityRatio < MIN_LIQUIDITY_RATIO
|| minLiquidityRatio > 1e18
) {
revert InvalidValue();
}
}
initiatorInfo_.fee = uint64(fee);
initiatorInfo_.minLiquidityRatio = uint64(minLiquidityRatio);
initiatorInfo_.lowerSqrtPriceDeviation = uint64(FixedPointMathLib.sqrt((1e18 - tolerance) * 1e18));
initiatorInfo_.upperSqrtPriceDeviation = upperSqrtPriceDeviation;
initiatorInfo[msg.sender] = initiatorInfo_;
}
/* ///////////////////////////////////////////////////////////////
ERC721 HANDLER FUNCTION
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns the onERC721Received selector.
* @dev Required to receive ERC721 tokens via safeTransferFrom.
*/
function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
/* ///////////////////////////////////////////////////////////////
NATIVE ETH HANDLER
/////////////////////////////////////////////////////////////// */
/**
* @notice Receives native ether.
* @dev Required since the Slipstream Non Fungible Position Manager sends full ether balance to caller
* on an increaseLiquidity.
* @dev Funds received can not be reclaimed, the receive only serves as a protection against griefing attacks.
*/
receive() external payable {
if (msg.sender != address(SlipstreamLogic.POSITION_MANAGER)) revert OnlyPositionManager();
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { ICLPool } from "../interfaces/ICLPool.sol";
import { ICLPositionManager } from "../interfaces/ICLPositionManager.sol";
import { PoolAddress } from "./slipstream/PoolAddress.sol";
import { Rebalancer } from "../Rebalancer.sol";
library SlipstreamLogic {
// The Slipstream Factory contract.
address internal constant CL_FACTORY = 0x5e7BB104d84c7CB9B682AaC2F3d509f5F406809A;
// The Slipstream Pool Implementation contract.
address internal constant POOL_IMPLEMENTATION = 0xeC8E5342B19977B4eF8892e02D8DAEcfa1315831;
// The Slipstream NonfungiblePositionManager contract.
ICLPositionManager internal constant POSITION_MANAGER =
ICLPositionManager(0x827922686190790b37229fd06084350E74485b72);
/**
* @notice Computes the contract address of a Slipstream Pool.
* @param token0 The contract address of token0.
* @param token1 The contract address of token1.
* @param tickSpacing The tick spacing of the Pool.
* @return pool The contract address of the Slipstream Pool.
*/
function _computePoolAddress(address token0, address token1, int24 tickSpacing)
internal
pure
returns (address pool)
{
pool = PoolAddress.computeAddress(POOL_IMPLEMENTATION, CL_FACTORY, token0, token1, tickSpacing);
}
/**
* @notice Fetches Slipstream specific position data from external contracts.
* @param position Struct with the position data.
* @param id The id of the Liquidity Position.
* @return tickCurrent The current tick of the pool.
* @return tickRange The tick range of the position.
*/
function _getPositionState(Rebalancer.PositionState memory position, uint256 id)
internal
view
returns (int24 tickCurrent, int24 tickRange)
{
// Get data of the Liquidity Position.
int24 tickLower;
int24 tickUpper;
(,, position.token0, position.token1, position.tickSpacing, tickLower, tickUpper, position.liquidity,,,,) =
POSITION_MANAGER.positions(id);
tickRange = tickUpper - tickLower;
// Get data of the Liquidity Pool.
position.pool = _computePoolAddress(position.token0, position.token1, position.tickSpacing);
(position.sqrtPriceX96, tickCurrent,,,,) = ICLPool(position.pool).slot0();
position.fee = ICLPool(position.pool).fee();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {BitMath} from "./BitMath.sol";
import {CustomRevert} from "./CustomRevert.sol";
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @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 {
using CustomRevert for bytes4;
/// @notice Thrown when the tick passed to #getSqrtPriceAtTick is not between MIN_TICK and MAX_TICK
error InvalidTick(int24 tick);
/// @notice Thrown when the price passed to #getTickAtSqrtPrice does not correspond to a price between MIN_TICK and MAX_TICK
error InvalidSqrtPrice(uint160 sqrtPriceX96);
/// @dev The minimum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**-128
/// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**128
/// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
int24 internal constant MAX_TICK = 887272;
/// @dev The minimum tick spacing value drawn from the range of type int16 that is greater than 0, i.e. min from the range [1, 32767]
int24 internal constant MIN_TICK_SPACING = 1;
/// @dev The maximum tick spacing value drawn from the range of type int16, i.e. max from the range [1, 32767]
int24 internal constant MAX_TICK_SPACING = type(int16).max;
/// @dev The minimum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_PRICE = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_PRICE = 1461446703485210103287273052203988822378723970342;
/// @dev A threshold used for optimized bounds check, equals `MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1`
uint160 internal constant MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE =
1461446703485210103287273052203988822378723970342 - 4295128739 - 1;
/// @notice Given a tickSpacing, compute the maximum usable tick
function maxUsableTick(int24 tickSpacing) internal pure returns (int24) {
unchecked {
return (MAX_TICK / tickSpacing) * tickSpacing;
}
}
/// @notice Given a tickSpacing, compute the minimum usable tick
function minUsableTick(int24 tickSpacing) internal pure returns (int24) {
unchecked {
return (MIN_TICK / tickSpacing) * tickSpacing;
}
}
/// @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 price of the two assets (currency1/currency0)
/// at the given tick
function getSqrtPriceAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
unchecked {
uint256 absTick;
assembly ("memory-safe") {
tick := signextend(2, tick)
// mask = 0 if tick >= 0 else -1 (all 1s)
let mask := sar(255, tick)
// if tick >= 0, |tick| = tick = 0 ^ tick
// if tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = (-1) ^ (tick - 1)
// either way, |tick| = mask ^ (tick + mask)
absTick := xor(mask, add(mask, tick))
}
if (absTick > uint256(int256(MAX_TICK))) InvalidTick.selector.revertWith(tick);
// The tick is decomposed into bits, and for each bit with index i that is set, the product of 1/sqrt(1.0001^(2^i))
// is calculated (using Q128.128). The constants used for this calculation are rounded to the nearest integer
// Equivalent to:
// price = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
// or price = int(2**128 / sqrt(1.0001)) if (absTick & 0x1) else 1 << 128
uint256 price;
assembly ("memory-safe") {
price := xor(shl(128, 1), mul(xor(shl(128, 1), 0xfffcb933bd6fad37aa2d162d1a594001), and(absTick, 0x1)))
}
if (absTick & 0x2 != 0) price = (price * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) price = (price * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) price = (price * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) price = (price * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) price = (price * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) price = (price * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) price = (price * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) price = (price * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) price = (price * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) price = (price * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) price = (price * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) price = (price * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) price = (price * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) price = (price * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) price = (price * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) price = (price * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) price = (price * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) price = (price * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) price = (price * 0x48a170391f7dc42444e8fa2) >> 128;
assembly ("memory-safe") {
// if (tick > 0) price = type(uint256).max / price;
if sgt(tick, 0) { price := div(not(0), price) }
// 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 getTickAtSqrtPrice of the output price is always consistent
// `sub(shl(32, 1), 1)` is `type(uint32).max`
// `price + type(uint32).max` will not overflow because `price` fits in 192 bits
sqrtPriceX96 := shr(32, add(price, sub(shl(32, 1), 1)))
}
}
}
/// @notice Calculates the greatest tick value such that getSqrtPriceAtTick(tick) <= sqrtPriceX96
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_PRICE, as MIN_SQRT_PRICE is the lowest value getSqrtPriceAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt price for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the getSqrtPriceAtTick(tick) is less than or equal to the input sqrtPriceX96
function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
unchecked {
// Equivalent: if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 >= MAX_SQRT_PRICE) revert InvalidSqrtPrice();
// second inequality must be >= because the price can never reach the price at the max tick
// if sqrtPriceX96 < MIN_SQRT_PRICE, the `sub` underflows and `gt` is true
// if sqrtPriceX96 >= MAX_SQRT_PRICE, sqrtPriceX96 - MIN_SQRT_PRICE > MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1
if ((sqrtPriceX96 - MIN_SQRT_PRICE) > MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE) {
InvalidSqrtPrice.selector.revertWith(sqrtPriceX96);
}
uint256 price = uint256(sqrtPriceX96) << 32;
uint256 r = price;
uint256 msb = BitMath.mostSignificantBit(r);
if (msb >= 128) r = price >> (msb - 127);
else r = price << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // Q22.128 number
// Magic number represents the ceiling of the maximum value of the error when approximating log_sqrt10001(x)
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
// Magic number represents the minimum value of the error when approximating log_sqrt10001(x), when
// sqrtPrice is from the range (2^-64, 2^64). This is safe as MIN_SQRT_PRICE is more than 2^-64. If MIN_SQRT_PRICE
// is changed, this may need to be changed too
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtPriceAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { FixedPointMathLib } from "../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { ICLPool } from "../interfaces/ICLPool.sol";
library TwapLogic {
// The Number of seconds in the past from which to calculate the time-weighted tick.
uint32 public constant TWAT_INTERVAL = 5 minutes;
/**
* @notice Calculates the time weighted average tick over 300s.
* @param pool The liquidity pool.
* @return tick The time weighted average tick over 300s.
* @dev We do not use the TWAT price to calculate the current value of the asset.
* It is used only to ensure that the deposited Liquidity range and thus
* the risk of exposure manipulation is acceptable.
*/
function _getTwat(address pool) internal view returns (int24 tick) {
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[1] = TWAT_INTERVAL; // We take a 5 minute time interval.
(int56[] memory tickCumulatives,) = ICLPool(pool).observe(secondsAgos);
tick = int24((tickCumulatives[0] - tickCumulatives[1]) / int32(TWAT_INTERVAL));
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { IUniswapV3Pool } from "../interfaces/IUniswapV3Pool.sol";
import { IUniswapV3PositionManager } from "../interfaces/IUniswapV3PositionManager.sol";
import { PoolAddress } from "../../../lib/accounts-v2/src/asset-modules/UniswapV3/libraries/PoolAddress.sol";
import { Rebalancer } from "../Rebalancer.sol";
library UniswapV3Logic {
// The Uniswap V3 Factory contract.
address internal constant UNISWAP_V3_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
// The Uniswap V3 NonfungiblePositionManager contract.
IUniswapV3PositionManager internal constant POSITION_MANAGER =
IUniswapV3PositionManager(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1);
/**
* @notice Computes the contract address of a Uniswap V3 Pool.
* @param token0 The contract address of token0.
* @param token1 The contract address of token1.
* @param fee The fee of the Pool.
* @return pool The contract address of the Uniswap V3 Pool.
*/
function _computePoolAddress(address token0, address token1, uint24 fee) internal pure returns (address pool) {
pool = PoolAddress.computeAddress(UNISWAP_V3_FACTORY, token0, token1, fee);
}
/**
* @notice Fetches Uniswap V3 specific position data from external contracts.
* @param position Struct with the position data.
* @param id The id of the Liquidity Position.
* @param getTickSpacing Bool indicating if the tick spacing should be fetched.
* @return tickCurrent The current tick of the pool.
* @return tickRange The tick range of the position.
*/
function _getPositionState(Rebalancer.PositionState memory position, uint256 id, bool getTickSpacing)
internal
view
returns (int24 tickCurrent, int24 tickRange)
{
// Get data of the Liquidity Position.
int24 tickLower;
int24 tickUpper;
(,, position.token0, position.token1, position.fee, tickLower, tickUpper, position.liquidity,,,,) =
POSITION_MANAGER.positions(id);
tickRange = tickUpper - tickLower;
// Get data of the Liquidity Pool.
position.pool = _computePoolAddress(position.token0, position.token1, position.fee);
(position.sqrtPriceX96, tickCurrent,,,,,) = IUniswapV3Pool(position.pool).slot0();
if (getTickSpacing) position.tickSpacing = IUniswapV3Pool(position.pool).tickSpacing();
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.22;
// Struct with information to pass to and from the actionTarget.
struct ActionData {
// Array of the contract addresses of the assets.
address[] assets;
// Array of the IDs of the assets.
uint256[] assetIds;
// Array with the amounts of the assets.
uint256[] assetAmounts;
// Array with the types of the assets.
uint256[] assetTypes;
}
interface IActionBase {
/**
* @notice Calls an external target contract with arbitrary calldata.
* @param actionTargetData A bytes object containing the encoded input for the actionTarget.
* @return resultData An actionAssetData struct with the final balances of this actionTarget contract.
*/
function executeAction(bytes calldata actionTargetData) external returns (ActionData memory);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { ActionData } from "../../../lib/accounts-v2/src/interfaces/IActionBase.sol";
import { AssetValueAndRiskFactors } from "../../../lib/accounts-v2/src/libraries/AssetValuationLib.sol";
import { IFactory } from "../interfaces/IFactory.sol";
import { IPermit2 } from "../../../lib/accounts-v2/src/interfaces/IPermit2.sol";
import { IRegistry } from "../interfaces/IRegistry.sol";
import { Rebalancer } from "../Rebalancer.sol";
import { StakedSlipstreamLogic } from "./StakedSlipstreamLogic.sol";
library ArcadiaLogic {
// The contract address of the Arcadia Factory.
IFactory internal constant FACTORY = IFactory(0xDa14Fdd72345c4d2511357214c5B89A919768e59);
// The contract address of the Arcadia Registry.
IRegistry internal constant REGISTRY = IRegistry(0xd0690557600eb8Be8391D1d97346e2aab5300d5f);
/**
* @notice Returns the trusted USD prices for 1e18 gwei of token0 and token1.
* @param token0 The contract address of token0.
* @param token1 The contract address of token1.
* @param usdPriceToken0 The USD price of 1e18 gwei of token0, with 18 decimals precision.
* @param usdPriceToken1 The USD price of 1e18 gwei of token1, with 18 decimals precision.
*/
function _getValuesInUsd(address token0, address token1)
internal
view
returns (uint256 usdPriceToken0, uint256 usdPriceToken1)
{
address[] memory assets = new address[](2);
assets[0] = token0;
assets[1] = token1;
uint256[] memory assetAmounts = new uint256[](2);
assetAmounts[0] = 1e18;
assetAmounts[1] = 1e18;
AssetValueAndRiskFactors[] memory valuesAndRiskFactors =
REGISTRY.getValuesInUsd(address(0), assets, new uint256[](2), assetAmounts);
(usdPriceToken0, usdPriceToken1) = (valuesAndRiskFactors[0].assetValue, valuesAndRiskFactors[1].assetValue);
}
/**
* @notice Encodes the action data for the flash-action used to rebalance a Liquidity Position.
* @param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @param initiator The address of the initiator.
* @param tickLower The new lower tick to rebalance the position to.
* @param tickUpper The new upper tick to rebalancer the position to.
* @param swapData Arbitrary calldata provided by an initiator for a swap.
* @return actionData Bytes string with the encoded data.
*/
function _encodeAction(
address positionManager,
uint256 id,
address initiator,
int24 tickLower,
int24 tickUpper,
bytes calldata swapData
) internal pure returns (bytes memory actionData) {
// Encode Uniswap V3 position that has to be withdrawn from and deposited back into the Account.
address[] memory assets_ = new address[](1);
assets_[0] = positionManager;
uint256[] memory assetIds_ = new uint256[](1);
assetIds_[0] = id;
uint256[] memory assetAmounts_ = new uint256[](1);
assetAmounts_[0] = 1;
uint256[] memory assetTypes_ = new uint256[](1);
assetTypes_[0] = 2;
ActionData memory assetData =
ActionData({ assets: assets_, assetIds: assetIds_, assetAmounts: assetAmounts_, assetTypes: assetTypes_ });
// Empty data objects that have to be encoded when calling flashAction(), but that are not used for this specific flash-action.
bytes memory signature;
ActionData memory transferFromOwner;
IPermit2.PermitBatchTransferFrom memory permit;
// Data required by this contract when Account does the executeAction() callback during the flash-action.
bytes memory rebalanceData = abi.encode(assetData, initiator, tickLower, tickUpper, swapData);
// Encode the actionData.
actionData = abi.encode(assetData, transferFromOwner, permit, signature, rebalanceData);
}
/**
* @notice Encodes the deposit data after the flash-action used to rebalance the Liquidity Position.
* @param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @param position Struct with the position data.
* @param count The number of assets to deposit.
* @param balance0 The amount of token0 to deposit.
* @param balance1 The amount of token1 to deposit.
* @param reward The amount of reward token to deposit.
* @return depositData Bytes string with the encoded data.
*/
function _encodeDeposit(
address positionManager,
uint256 id,
Rebalancer.PositionState memory position,
uint256 count,
uint256 balance0,
uint256 balance1,
uint256 reward
) internal pure returns (ActionData memory depositData) {
depositData.assets = new address[](count);
depositData.assetIds = new uint256[](count);
depositData.assetAmounts = new uint256[](count);
depositData.assetTypes = new uint256[](count);
// Add newly minted Liquidity Position.
depositData.assets[0] = positionManager;
depositData.assetIds[0] = id;
depositData.assetAmounts[0] = 1;
depositData.assetTypes[0] = 2;
// Track the next index for the ERC20 tokens.
uint256 index = 1;
if (balance0 > 0) {
depositData.assets[1] = position.token0;
depositData.assetAmounts[1] = balance0;
depositData.assetTypes[1] = 1;
index = 2;
}
if (balance1 > 0) {
depositData.assets[index] = position.token1;
depositData.assetAmounts[index] = balance1;
depositData.assetTypes[index] = 1;
++index;
}
if (reward > 0) {
depositData.assets[index] = StakedSlipstreamLogic.REWARD_TOKEN;
depositData.assetAmounts[index] = reward;
depositData.assetTypes[index] = 1;
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { CollectParams, DecreaseLiquidityParams, IPositionManager } from "../interfaces/IPositionManager.sol";
import { IStakedSlipstreamAM } from "../interfaces/IStakedSlipstreamAM.sol";
import { Rebalancer } from "../Rebalancer.sol";
import { SlipstreamLogic } from "./SlipstreamLogic.sol";
import { StakedSlipstreamLogic } from "./StakedSlipstreamLogic.sol";
library BurnLogic {
/**
* @notice Burns the Liquidity Position.
* @param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @param position Struct with the position data.
* @return balance0 The amount of token0 claimed.
* @return balance1 The amount of token1 claimed.
* @return rewards The amount of reward token claimed.
*/
function _burn(address positionManager, uint256 id, Rebalancer.PositionState memory position)
internal
returns (uint256 balance0, uint256 balance1, uint256 rewards)
{
// If position is a staked slipstream position, first unstake the position.
bool staked;
if (
positionManager == address(StakedSlipstreamLogic.STAKED_SLIPSTREAM_AM)
|| positionManager == address(StakedSlipstreamLogic.STAKED_SLIPSTREAM_WRAPPER)
) {
// Staking rewards are deposited back into the account at the end of the transaction.
// Or, if rewardToken is an underlying token of the position, added to the balances
rewards = IStakedSlipstreamAM(positionManager).burn(id);
// After the position is unstaked, it becomes a slipstream position.
positionManager = address(SlipstreamLogic.POSITION_MANAGER);
staked = true;
}
// Remove liquidity of the position and claim outstanding fees to get full amounts of token0 and token1
// for rebalance.
IPositionManager(positionManager).decreaseLiquidity(
DecreaseLiquidityParams({
tokenId: id,
liquidity: position.liquidity,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);
// We assume that the amount of tokens to collect never exceeds type(uint128).max.
(balance0, balance1) = IPositionManager(positionManager).collect(
CollectParams({
tokenId: id,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
// Burn the position
IPositionManager(positionManager).burn(id);
// If the reward token is the same as one of the underlying tokens, update the token-balance instead.
if (staked) {
if (StakedSlipstreamLogic.REWARD_TOKEN == position.token0) {
(balance0, rewards) = (balance0 + rewards, 0);
} else if (StakedSlipstreamLogic.REWARD_TOKEN == position.token1) {
(balance1, rewards) = (balance1 + rewards, 0);
}
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { ERC20, SafeTransferLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/SafeTransferLib.sol";
library FeeLogic {
using SafeTransferLib for ERC20;
/**
* @notice Transfers the initiator fee to the initiator.
* @param initiator The address of the initiator.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param amountInitiatorFee The amount of initiator fee.
* @param token0 The contract address of token0.
* @param token1 The contract address of token1.
* @param balance0 The balance of token0 before transferring the initiator fee.
* @param balance1 The balance of token1 before transferring the initiator fee.
* @return balance0 The balance of token0 after transferring the initiator fee.
* @return balance1 The balance of token1 after transferring the initiator fee.
*/
function _transfer(
address initiator,
bool zeroToOne,
uint256 amountInitiatorFee,
address token0,
address token1,
uint256 balance0,
uint256 balance1
) internal returns (uint256, uint256) {
unchecked {
if (zeroToOne) {
(balance0, amountInitiatorFee) =
balance0 > amountInitiatorFee ? (balance0 - amountInitiatorFee, amountInitiatorFee) : (0, balance0);
if (amountInitiatorFee > 0) ERC20(token0).safeTransfer(initiator, amountInitiatorFee);
} else {
(balance1, amountInitiatorFee) =
balance1 > amountInitiatorFee ? (balance1 - amountInitiatorFee, amountInitiatorFee) : (0, balance1);
if (amountInitiatorFee > 0) ERC20(token1).safeTransfer(initiator, amountInitiatorFee);
}
return (balance0, balance1);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contains 512-bit math functions
/// @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 {
/// @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 result) {
unchecked {
// 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
uint256 prod0 = a * b; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
assembly ("memory-safe") {
result := div(prod0, denominator)
}
return result;
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly ("memory-safe") {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly ("memory-safe") {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = (0 - denominator) & denominator;
// Divide denominator by power of two
assembly ("memory-safe") {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly ("memory-safe") {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly ("memory-safe") {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// 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 *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the preconditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
/// @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 result) {
unchecked {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) != 0) {
require(++result > 0);
}
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IAccount {
function flashAction(address actionTarget, bytes calldata actionData) external;
function owner() external returns (address owner_);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface IPool {
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
function liquidity() external view returns (uint128 liquidity);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
interface IPositionManager {
function approve(address spender, uint256 tokenId) external;
function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
function burn(uint256 tokenId) external payable;
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IStrategyHook {
function beforeRebalance(
address account,
address positionManager,
uint256 oldId,
int24 newTickLower,
int24 newTickUpper
) external view;
function afterRebalance(address account, address positionManager, uint256 oldId, uint256 newId) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { ERC20, SafeApprove } from "./SafeApprove.sol";
import { ICLPositionManager } from "../interfaces/ICLPositionManager.sol";
import { IStakedSlipstreamAM } from "../interfaces/IStakedSlipstreamAM.sol";
import { IUniswapV3PositionManager } from "../interfaces/IUniswapV3PositionManager.sol";
import { Rebalancer } from "../Rebalancer.sol";
import { SlipstreamLogic } from "./SlipstreamLogic.sol";
import { StakedSlipstreamLogic } from "./StakedSlipstreamLogic.sol";
import { UniswapV3Logic } from "./UniswapV3Logic.sol";
library MintLogic {
using SafeApprove for ERC20;
/**
* @notice Mints a new Liquidity Position.
* @param positionManager The contract address of the Position Manager.
* @param position Struct with the position data.
* @param balance0 The balance of token0 before minting liquidity.
* @param balance1 The balance of token1 before minting liquidity.
* @return newTokenId The id of the new Liquidity Position.
* @return liquidity The amount of liquidity minted.
* @return balance0_ The remaining balance of token0 after minting liquidity.
* @return balance1_ The remaining balance of token1 after minting liquidity.
*/
function _mint(
address positionManager,
Rebalancer.PositionState memory position,
uint256 balance0,
uint256 balance1
) internal returns (uint256 newTokenId, uint256 liquidity, uint256 balance0_, uint256 balance1_) {
// Before position can be staked, we have to create a slipstream position.
address stakedPositionManager;
if (
positionManager == StakedSlipstreamLogic.STAKED_SLIPSTREAM_AM
|| positionManager == StakedSlipstreamLogic.STAKED_SLIPSTREAM_WRAPPER
) {
stakedPositionManager = positionManager;
positionManager = address(SlipstreamLogic.POSITION_MANAGER);
}
ERC20(position.token0).safeApproveWithRetry(positionManager, balance0);
ERC20(position.token1).safeApproveWithRetry(positionManager, balance1);
uint256 amount0;
uint256 amount1;
(newTokenId, liquidity, amount0, amount1) = (positionManager == address(UniswapV3Logic.POSITION_MANAGER))
? UniswapV3Logic.POSITION_MANAGER.mint(
IUniswapV3PositionManager.MintParams({
token0: position.token0,
token1: position.token1,
fee: position.fee,
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: balance0,
amount1Desired: balance1,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
})
)
: SlipstreamLogic.POSITION_MANAGER.mint(
ICLPositionManager.MintParams({
token0: position.token0,
token1: position.token1,
tickSpacing: position.tickSpacing,
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: balance0,
amount1Desired: balance1,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp,
sqrtPriceX96: 0
})
);
// Update balances.
balance0_ = balance0 - amount0;
balance1_ = balance1 - amount1;
// If position is a staked slipstream position, stake the position.
if (stakedPositionManager != address(0)) {
SlipstreamLogic.POSITION_MANAGER.approve(stakedPositionManager, newTokenId);
IStakedSlipstreamAM(stakedPositionManager).mint(newTokenId);
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { FixedPoint96 } from "../../../lib/accounts-v2/src/asset-modules/UniswapV3/libraries/FixedPoint96.sol";
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { FullMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/FullMath.sol";
import { TickMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/TickMath.sol";
library PricingLogic {
using FixedPointMathLib for uint256;
// The binary precision of sqrtPriceX96 squared.
uint256 internal constant Q192 = FixedPoint96.Q96 ** 2;
/**
* @notice Calculates the value of one token in the other token for a given amountIn and sqrtPriceX96.
* Does not take into account slippage and fees.
* @param sqrtPriceX96 The square root of the price (token1/token0), with 96 binary precision.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param amountIn The amount that of tokenIn that must be swapped to tokenOut.
* @return amountOut The amount of tokenOut.
* @dev Function will revert for all pools where the sqrtPriceX96 is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPriceX96 of a pool exceeds type(uint128).max, a different rebalancer has to be deployed,
* which does two consecutive mulDivs.
*/
function _getSpotValue(uint256 sqrtPriceX96, bool zeroToOne, uint256 amountIn)
internal
pure
returns (uint256 amountOut)
{
amountOut = zeroToOne
? FullMath.mulDiv(amountIn, sqrtPriceX96 ** 2, Q192)
: FullMath.mulDiv(amountIn, Q192, sqrtPriceX96 ** 2);
}
/**
* @notice Calculates the sqrtPriceX96 (token1/token0) from trusted USD prices of both tokens.
* @param priceToken0 The price of 1e18 tokens of token0 in USD, with 18 decimals precision.
* @param priceToken1 The price of 1e18 tokens of token1 in USD, with 18 decimals precision.
* @return sqrtPriceX96 The square root of the price (token1/token0), with 96 binary precision.
* @dev The price in Uniswap V3 is defined as:
* price = amountToken1/amountToken0.
* The usdPriceToken is defined as: usdPriceToken = amountUsd/amountToken.
* => amountToken = amountUsd/usdPriceToken.
* Hence we can derive the Uniswap V3 price as:
* price = (amountUsd/usdPriceToken1)/(amountUsd/usdPriceToken0) = usdPriceToken0/usdPriceToken1.
*/
function _getSqrtPriceX96(uint256 priceToken0, uint256 priceToken1) internal pure returns (uint160 sqrtPriceX96) {
if (priceToken1 == 0) return TickMath.MAX_SQRT_PRICE;
// Both priceTokens have 18 decimals precision and result of division should have 28 decimals precision.
// -> multiply by 1e28
// priceXd28 will overflow if priceToken0 is greater than 1.158e+49.
// For WBTC (which only has 8 decimals) this would require a bitcoin price greater than 115 792 089 237 316 198 989 824 USD/BTC.
uint256 priceXd28 = priceToken0.mulDivDown(1e28, priceToken1);
// Square root of a number with 28 decimals precision has 14 decimals precision.
uint256 sqrtPriceXd14 = FixedPointMathLib.sqrt(priceXd28);
// Change sqrtPrice from a decimal fixed point number with 14 digits to a binary fixed point number with 96 digits.
// Unsafe cast: Cast will only overflow when priceToken0/priceToken1 >= 2^128.
sqrtPriceX96 = uint160((sqrtPriceXd14 << FixedPoint96.RESOLUTION) / 1e14);
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { FullMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/FullMath.sol";
import { LiquidityAmounts } from "../libraries/uniswap-v3/LiquidityAmounts.sol";
import { PricingLogic } from "./PricingLogic.sol";
library RebalanceLogic {
using FixedPointMathLib for uint256;
// The binary precision of sqrtPriceX96 squared.
uint256 internal constant Q192 = PricingLogic.Q192;
/**
* @notice Returns the parameters and constraints to rebalance the position.
* Both parameters and constraints are calculated based on a hypothetical swap (in the pool itself with fees but without slippage),
* that maximizes the amount of liquidity that can be added to the positions (no leftovers of either token0 or token1).
* @param minLiquidityRatio The ratio of the minimum amount of liquidity that must be minted,
* relative to the hypothetical amount of liquidity when we rebalance without slippage, with 18 decimals precision.
* @param poolFee The fee of the pool, with 6 decimals precision.
* @param initiatorFee The fee of the initiator, with 18 decimals precision.
* @param sqrtPrice The square root of the price (token1/token0), with 96 binary precision.
* @param sqrtRatioLower The square root price of the lower tick of the liquidity position, with 96 binary precision.
* @param sqrtRatioUpper The square root price of the upper tick of the liquidity position, with 96 binary precision.
* @param balance0 The amount of token0 that is available for the rebalance.
* @param balance1 The amount of token1 that is available for the rebalance.
* @return minLiquidity The minimum amount of liquidity that must be added to the position.
* @return zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @return amountInitiatorFee The amount of initiator fee, in tokenIn.
* @return amountIn An approximation of the amount of tokenIn, based on the optimal swap through the pool itself without slippage.
* @return amountOut An approximation of the amount of tokenOut, based on the optimal swap through the pool itself without slippage.
*/
function _getRebalanceParams(
uint256 minLiquidityRatio,
uint256 poolFee,
uint256 initiatorFee,
uint256 sqrtPrice,
uint256 sqrtRatioLower,
uint256 sqrtRatioUpper,
uint256 balance0,
uint256 balance1
)
internal
pure
returns (uint256 minLiquidity, bool zeroToOne, uint256 amountInitiatorFee, uint256 amountIn, uint256 amountOut)
{
// Total fee is pool fee + initiator fee, with 18 decimals precision.
// Since Uniswap uses 6 decimals precision for the fee, we have to multiply the pool fee by 1e12.
uint256 fee;
unchecked {
fee = initiatorFee + poolFee * 1e12;
}
// Calculate the swap parameters
(zeroToOne, amountIn, amountOut) =
_getSwapParams(sqrtPrice, sqrtRatioLower, sqrtRatioUpper, balance0, balance1, fee);
// Calculate the maximum amount of liquidity that can be added to the position.
{
uint256 liquidity = LiquidityAmounts.getLiquidityForAmounts(
uint160(sqrtPrice),
uint160(sqrtRatioLower),
uint160(sqrtRatioUpper),
zeroToOne ? balance0 - amountIn : balance0 + amountOut,
zeroToOne ? balance1 + amountOut : balance1 - amountIn
);
minLiquidity = liquidity.mulDivDown(minLiquidityRatio, 1e18);
}
// Get initiator fee amount and the actual amountIn of the swap (without initiator fee).
unchecked {
amountInitiatorFee = amountIn.mulDivDown(initiatorFee, 1e18);
amountIn = amountIn - amountInitiatorFee;
}
}
/**
* @notice Calculates the swap parameters, calculated based on a hypothetical swap (in the pool itself with fees but without slippage).
* that maximizes the amount of liquidity that can be added to the positions (no leftovers of either token0 or token1).
* @param sqrtPrice The square root of the price (token1/token0), with 96 binary precision.
* @param sqrtRatioLower The square root price of the lower tick of the liquidity position, with 96 binary precision.
* @param sqrtRatioUpper The square root price of the upper tick of the liquidity position, with 96 binary precision.
* @param balance0 The amount of token0 that is available for the rebalance.
* @param balance1 The amount of token1 that is available for the rebalance.
* @param fee The swapping fees, with 18 decimals precision.
* @return zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @return amountIn An approximation of the amount of tokenIn, based on the optimal swap through the pool itself without slippage.
* @return amountOut An approximation of the amount of tokenOut, based on the optimal swap through the pool itself without slippage.
* @dev The swap parameters are derived as follows:
* 1) First we check if the position is in or out of range.
* - If the current price is above the position, the solution is trivial: we swap the full position to token1.
* - If the current price is below the position, similar, we swap the full position to token0.
* - If the position is in range we proceed with step 2.
*
* 2) If the position is in range, we start with calculating the "Target Ratio" and "Current Ratio".
* Both ratio's are defined as the value of the amount of token1 compared to the total value of the position:
* R = valueToken1 / [valueToken0 + valueToken1]
* If we express all values in token1 and use the current pool price to denominate token0 in token1:
* R = amount1 / [amount0 * sqrtPrice² + amount1]
*
* a) The "Target Ratio" (R_target) is the ratio of the new liquidity position.
* It is calculated with the current price and the upper and lower prices of the liquidity position,
* see _getTargetRatio() for the derivation.
* To maximise the liquidity of the new position, the balances after the swap should approximate it as close as possible to not have any leftovers.
* b) The "Current Ratio" (R_current) is the ratio of the current token balances, it is calculated as follows:
* R_current = balance1 / [balance0 * sqrtPrice² + balance1].
*
* 3) From R_target and R_current we can finally derive the direction of the swap, amountIn and amountOut.
* If R_current is smaller than R_target, we have to swap an amount of token0 to token1, and vice versa.
* amountIn and amountOut can be found by solving the following equalities:
* a) The ratio of the token balances after the swap equal the "Target Ratio".
* b) The swap between token0 and token1 is done in the pool itself,
* taking into account fees, but ignoring slippage (-> sqrtPrice remains constant).
*
* If R_current < R_target (swap token0 to token1):
* a) R_target = [amount1 + amoutOut] / [(amount0 - amountIn) * sqrtPrice² + (amount1 + amoutOut)].
* b) amountOut = (1 - fee) * amountIn * sqrtPrice².
* => amountOut = [(R_target - R_current) * (amount0 * sqrtPrice² + amount1)] / [1 + R_target * fee / (1 - fee)].
*
* If R_current > R_target (swap token1 to token0):
* a) R_target = [(amount1 - amountIn)] / [(amount0 + amoutOut) * sqrtPrice² + (amount1 - amountIn)].
* b) amountOut = (1 - fee) * amountIn / sqrtPrice².
* => amountIn = [(R_current - R_target) * (amount0 * sqrtPrice² + amount1)] / (1 - R_target * fee).
*/
function _getSwapParams(
uint256 sqrtPrice,
uint256 sqrtRatioLower,
uint256 sqrtRatioUpper,
uint256 balance0,
uint256 balance1,
uint256 fee
) internal pure returns (bool zeroToOne, uint256 amountIn, uint256 amountOut) {
if (sqrtPrice >= sqrtRatioUpper) {
// New position is out of range and fully in token 1.
// Rebalance to a single-sided liquidity position in token 1.
zeroToOne = true;
amountIn = balance0;
amountOut = _getAmountOut(sqrtPrice, true, balance0, fee);
} else if (sqrtPrice <= sqrtRatioLower) {
// New position is out of range and fully in token 0.
// Rebalance to a single-sided liquidity position in token 0.
amountIn = balance1;
amountOut = _getAmountOut(sqrtPrice, false, balance1, fee);
} else {
// Get target ratio in token1 terms.
uint256 targetRatio = _getTargetRatio(sqrtPrice, sqrtRatioLower, sqrtRatioUpper);
// Calculate the total position value in token1 equivalent:
uint256 token0ValueInToken1 = PricingLogic._getSpotValue(sqrtPrice, true, balance0);
uint256 totalValueInToken1 = balance1 + token0ValueInToken1;
unchecked {
// Calculate the current ratio of liquidity in token1 terms.
uint256 currentRatio = balance1.mulDivDown(1e18, totalValueInToken1);
if (currentRatio < targetRatio) {
// Swap token0 partially to token1.
zeroToOne = true;
{
uint256 denominator = 1e18 + targetRatio.mulDivDown(fee, 1e18 - fee);
amountOut = (targetRatio - currentRatio).mulDivDown(totalValueInToken1, denominator);
}
amountIn = _getAmountIn(sqrtPrice, true, amountOut, fee);
} else {
// Swap token1 partially to token0.
zeroToOne = false;
{
uint256 denominator = 1e18 - targetRatio.mulDivDown(fee, 1e18);
amountIn = (currentRatio - targetRatio).mulDivDown(totalValueInToken1, denominator);
}
amountOut = _getAmountOut(sqrtPrice, false, amountIn, fee);
}
}
}
}
/**
* @notice Calculates the amountOut for a given amountIn and sqrtPriceX96 for a hypothetical
* swap though the pool itself with fees but without slippage.
* @param sqrtPriceX96 The square root of the price (token1/token0), with 96 binary precision.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param amountIn The amount of tokenIn that must be swapped to tokenOut.
* @param fee The total fee on amountIn, with 18 decimals precision.
* @return amountOut The amount of tokenOut.
* @dev Function will revert for all pools where the sqrtPriceX96 is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPriceX96 of a pool exceeds type(uint128).max, a different rebalancer has to be deployed,
* which does two consecutive mulDivs.
*/
function _getAmountOut(uint256 sqrtPriceX96, bool zeroToOne, uint256 amountIn, uint256 fee)
internal
pure
returns (uint256 amountOut)
{
require(sqrtPriceX96 <= type(uint128).max);
unchecked {
uint256 amountInWithoutFees = (1e18 - fee).mulDivDown(amountIn, 1e18);
amountOut = zeroToOne
? FullMath.mulDiv(amountInWithoutFees, sqrtPriceX96 ** 2, Q192)
: FullMath.mulDiv(amountInWithoutFees, Q192, sqrtPriceX96 ** 2);
}
}
/**
* @notice Calculates the amountIn for a given amountOut and sqrtPriceX96 for a hypothetical
* swap though the pool itself with fees but without slippage.
* @param sqrtPriceX96 The square root of the price (token1/token0), with 96 binary precision.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param amountOut The amount that tokenOut that must be swapped.
* @param fee The total fee on amountIn, with 18 decimals precision.
* @return amountIn The amount of tokenIn.
* @dev Function will revert for all pools where the sqrtPriceX96 is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPriceX96 of a pool exceeds type(uint128).max, a different rebalancer has to be deployed,
* which does two consecutive mulDivs.
*/
function _getAmountIn(uint256 sqrtPriceX96, bool zeroToOne, uint256 amountOut, uint256 fee)
internal
pure
returns (uint256 amountIn)
{
require(sqrtPriceX96 <= type(uint128).max);
unchecked {
uint256 amountInWithoutFees = zeroToOne
? FullMath.mulDiv(amountOut, Q192, sqrtPriceX96 ** 2)
: FullMath.mulDiv(amountOut, sqrtPriceX96 ** 2, Q192);
amountIn = amountInWithoutFees.mulDivDown(1e18, 1e18 - fee);
}
}
/**
* @notice Calculates the ratio of how much of the total value of a liquidity position has to be provided in token1.
* @param sqrtPriceX96 The square root of the current pool price (token1/token0), with 96 binary precision.
* @param sqrtRatioLower The square root price of the lower tick of the liquidity position, with 96 binary precision.
* @param sqrtRatioUpper The square root price of the upper tick of the liquidity position, with 96 binary precision.
* @return targetRatio The ratio of the value of token1 compared to the total value of the position, with 18 decimals precision.
* @dev Function will revert for all pools where the sqrtPriceX96 is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPriceX96 of a pool exceeds type(uint128).max, a different rebalancer has to be deployed,
* which does two consecutive mulDivs.
* @dev Derivation of the formula:
* 1) The ratio is defined as:
* R = valueToken1 / [valueToken0 + valueToken1]
* If we express all values in token1 and use the current pool price to denominate token0 in token1:
* R = amount1 / [amount0 * sqrtPrice² + amount1]
* 2) Amount0 for a given liquidity position of a Uniswap V3 pool is given as:
* Amount0 = liquidity * (sqrtRatioUpper - sqrtPrice) / (sqrtRatioUpper * sqrtPrice)
* 3) Amount1 for a given liquidity position of a Uniswap V3 pool is given as:
* Amount1 = liquidity * (sqrtPrice - sqrtRatioLower)
* 4) Combining 1), 2) and 3) and simplifying we get:
* R = [sqrtPrice - sqrtRatioLower] / [2 * sqrtPrice - sqrtRatioLower - sqrtPrice² / sqrtRatioUpper]
*/
function _getTargetRatio(uint256 sqrtPriceX96, uint256 sqrtRatioLower, uint256 sqrtRatioUpper)
internal
pure
returns (uint256 targetRatio)
{
require(sqrtPriceX96 <= type(uint128).max);
// Unchecked: sqrtPriceX96 is always bigger than sqrtRatioLower.
// Unchecked: sqrtPriceX96 is always smaller than sqrtRatioUpper -> sqrtPriceX96 > sqrtPriceX96 ** 2 / sqrtRatioUpper.
unchecked {
uint256 numerator = sqrtPriceX96 - sqrtRatioLower;
uint256 denominator = 2 * sqrtPriceX96 - sqrtRatioLower - sqrtPriceX96 ** 2 / sqrtRatioUpper;
targetRatio = numerator.mulDivDown(1e18, denominator);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import { ERC20 } from "../../../lib/accounts-v2/lib/solmate/src/tokens/ERC20.sol";
library SafeApprove {
/**
* @notice Approves an amount of token for a spender.
* @param token The contract address of the token being approved.
* @param to The spender.
* @param amount the amount of token being approved.
* @dev Copied from Solady safeApproveWithRetry (MIT): https://github.com/Vectorized/solady/blob/main/src/utils/SafeTransferLib.sol
* @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
* If the initial attempt to approve fails, attempts to reset the approved amount to zero,
* then retries the approval again (some tokens, e.g. USDT, requires this).
* Reverts upon failure.
*/
function safeApproveWithRetry(ERC20 token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
library StakedSlipstreamLogic {
// The contract address of the Reward Token (Aero).
address internal constant REWARD_TOKEN = 0x940181a94A35A4569E4529A3CDfB74e38FD98631;
// The Staked Slipstream Asset Module contract.
address internal constant STAKED_SLIPSTREAM_AM = 0x1Dc7A0f5336F52724B650E39174cfcbbEdD67bF1;
// The Wrapped Staked Slipstream Asset Module contract.
address internal constant STAKED_SLIPSTREAM_WRAPPER = 0xD74339e0F10fcE96894916B93E5Cc7dE89C98272;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { ERC20, SafeApprove } from "./SafeApprove.sol";
import { ICLPool } from "../interfaces/ICLPool.sol";
import { IPool } from "../interfaces/IPool.sol";
import { IUniswapV3Pool } from "../interfaces/IUniswapV3Pool.sol";
import { Rebalancer } from "../Rebalancer.sol";
import { RebalanceOptimizationMath } from "./RebalanceOptimizationMath.sol";
import { UniswapV3Logic } from "./UniswapV3Logic.sol";
library SwapLogic {
using SafeApprove for ERC20;
/**
* @notice Swaps one token for another to rebalance the Liquidity Position.
* @param swapData Arbitrary calldata provided by an initiator for the swap.
* @param positionManager The contract address of the Position Manager.
* @param position Struct with the position data.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param amountInitiatorFee The amount of initiator fee, in tokenIn.
* @param amountIn An approximation of the amount of tokenIn, based on the optimal swap through the pool itself without slippage.
* @param amountOut An approximation of the amount of tokenOut, based on the optimal swap through the pool itself without slippage.
* @param balance0 The balance of token0 before the swap.
* @param balance1 The balance of token1 before the swap.
* @return balance0_ The balance of token0 after the swap.
* @return balance1_ The balance of token1 after the swap.
*/
function _swap(
bytes memory swapData,
address positionManager,
Rebalancer.PositionState memory position,
bool zeroToOne,
uint256 amountInitiatorFee,
uint256 amountIn,
uint256 amountOut,
uint256 balance0,
uint256 balance1
) internal returns (uint256 balance0_, uint256 balance1_) {
// Don't do swaps with zero amount.
if (amountIn == 0) return (balance0, balance1);
// Do the actual swap to rebalance the position.
// This can be done either directly through the pool, or via a router with custom swap data.
if (swapData.length == 0) {
// Calculate a more accurate amountOut, with slippage.
amountOut = RebalanceOptimizationMath._getAmountOutWithSlippage(
zeroToOne,
position.fee,
IPool(position.pool).liquidity(),
uint160(position.sqrtPriceX96),
position.sqrtRatioLower,
position.sqrtRatioUpper,
zeroToOne ? balance0 - amountInitiatorFee : balance0,
zeroToOne ? balance1 : balance1 - amountInitiatorFee,
amountIn,
amountOut
);
(balance0_, balance1_) = _swapViaPool(positionManager, position, zeroToOne, amountOut, balance0, balance1);
} else {
(balance0_, balance1_) = _swapViaRouter(positionManager, position, zeroToOne, swapData);
}
}
/**
* @notice Swaps one token for another, directly through the pool itself.
* @param positionManager The contract address of the Position Manager.
* @param position Struct with the position data.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param amountOut The amount of tokenOut that must be swapped to.
* @param balance0 The balance of token0 before the swap.
* @param balance1 The balance of token1 before the swap.
* @return balance0_ The balance of token0 after the swap.
* @return balance1_ The balance of token1 after the swap.
*/
function _swapViaPool(
address positionManager,
Rebalancer.PositionState memory position,
bool zeroToOne,
uint256 amountOut,
uint256 balance0,
uint256 balance1
) internal returns (uint256 balance0_, uint256 balance1_) {
// Pool should still be balanced (within tolerance boundaries) after the swap.
uint160 sqrtPriceLimitX96 =
uint160(zeroToOne ? position.lowerBoundSqrtPriceX96 : position.upperBoundSqrtPriceX96);
// Encode the swap data.
bytes memory data = (positionManager == address(UniswapV3Logic.POSITION_MANAGER))
? abi.encode(positionManager, position.token0, position.token1, position.fee)
// Logic holds for both Slipstream and staked Slipstream positions.
: abi.encode(positionManager, position.token0, position.token1, position.tickSpacing);
// Do the swap.
// Callback (external function) must be implemented in the main contract.
(int256 deltaAmount0, int256 deltaAmount1) =
IPool(position.pool).swap(address(this), zeroToOne, -int256(amountOut), sqrtPriceLimitX96, data);
// Check that pool is still balanced.
// If sqrtPriceLimitX96 is reached before an amountOut of tokenOut is received, the pool is not balanced anymore.
// By setting the sqrtPriceX96 to sqrtPriceLimitX96, the transaction will revert on the balance check.
if (amountOut > (zeroToOne ? uint256(-deltaAmount1) : uint256(-deltaAmount0))) {
position.sqrtPriceX96 = sqrtPriceLimitX96;
}
// Update the balances.
balance0_ = zeroToOne ? balance0 - uint256(deltaAmount0) : balance0 + uint256(-deltaAmount0);
balance1_ = zeroToOne ? balance1 + uint256(-deltaAmount1) : balance1 - uint256(deltaAmount1);
}
/**
* @notice Swaps one token for another, directly through the pool itself.
* @param positionManager The contract address of the Position Manager.
* @param position Struct with the position data.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param swapData Arbitrary calldata provided by an initiator for the swap.
* @return balance0 The balance of token0 after the swap.
* @return balance1 The balance of token1 after the swap.
* @dev Initiator has to route swap in such a way that at least minLiquidity of liquidity is added to the position after the swap.
* And leftovers must be in tokenIn, otherwise the total tokenIn balance will be added as liquidity,
* and the initiator fee will be 0 (but the transaction will not revert)
*/
function _swapViaRouter(
address positionManager,
Rebalancer.PositionState memory position,
bool zeroToOne,
bytes memory swapData
) internal returns (uint256 balance0, uint256 balance1) {
// Decode the swap data.
(address router, uint256 amountIn, bytes memory data) = abi.decode(swapData, (address, uint256, bytes));
// Approve token to swap.
address tokenToSwap = zeroToOne ? position.token0 : position.token1;
ERC20(tokenToSwap).safeApproveWithRetry(router, amountIn);
// Execute arbitrary swap.
(bool success, bytes memory result) = router.call(data);
require(success, string(result));
// Pool should still be balanced (within tolerance boundaries) after the swap.
// Since the swap went potentially through the pool itself (but does not have to),
// the sqrtPriceX96 might have moved and brought the pool out of balance.
// By fetching the sqrtPriceX96, the transaction will revert in that case on the balance check.
if (positionManager == address(UniswapV3Logic.POSITION_MANAGER)) {
(position.sqrtPriceX96,,,,,,) = IUniswapV3Pool(position.pool).slot0();
} else {
// Logic holds for both Slipstream and staked Slipstream positions.
(position.sqrtPriceX96,,,,,) = ICLPool(position.pool).slot0();
}
// Update the balances.
balance0 = ERC20(position.token0).balanceOf(address(this));
balance1 = ERC20(position.token1).balanceOf(address(this));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface ICLPool {
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
bool unlocked
);
function fee() external view returns (uint24);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
/// @title Non-fungible token for positions
/// @notice Wraps Slipstream positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface ICLPositionManager {
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;
}
function positions(uint256 tokenId)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
int24 tickSpacing,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
function approve(address spender, uint256 tokenId) external;
function mint(MintParams calldata params)
external
payable
returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
}// https://github.com/velodrome-finance/slipstream/blob/main/contracts/periphery/libraries/PoolAddress.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
/// @title Provides functions for deriving a pool address from the factory, tokens, and the tickSpacing
library PoolAddress {
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param poolImplementation The contract address of the Slipstream Pool implementation.
/// @param factory The contract address of the Slipstream factory.
/// @param token0 Contract address of token0.
/// @param token1 Contract address of token1.
/// @param tickSpacing The tick spacing of the pool
/// @return pool The contract address of the pool
function computeAddress(
address poolImplementation,
address factory,
address token0,
address token1,
int24 tickSpacing
) internal pure returns (address pool) {
require(token0 < token1);
pool = predictDeterministicAddress({
master: poolImplementation,
salt: keccak256(abi.encode(token0, token1, tickSpacing)),
deployer: factory
});
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address master, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
/// @author Solady (https://github.com/Vectorized/solady/blob/8200a70e8dc2a77ecb074fc2e99a2a0d36547522/src/utils/LibBit.sol)
library BitMath {
/// @notice Returns the index of the most significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @param x the value for which to compute the most significant bit, must be greater than 0
/// @return r the index of the most significant bit
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
assembly ("memory-safe") {
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)),
0x0706060506020500060203020504000106050205030304010505030400000000))
}
}
/// @notice Returns the index of the least significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @param x the value for which to compute the least significant bit, must be greater than 0
/// @return r the index of the least significant bit
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
assembly ("memory-safe") {
// Isolate the least significant bit.
x := and(x, sub(0, x))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Library for reverting with custom errors efficiently
/// @notice Contains functions for reverting with custom errors with different argument types efficiently
/// @dev To use this library, declare `using CustomRevert for bytes4;` and replace `revert CustomError()` with
/// `CustomError.selector.revertWith()`
/// @dev The functions may tamper with the free memory pointer but it is fine since the call context is exited immediately
library CustomRevert {
/// @dev ERC-7751 error for wrapping bubbled up reverts
error WrappedError(address target, bytes4 selector, bytes reason, bytes details);
/// @dev Reverts with the selector of a custom error in the scratch space
function revertWith(bytes4 selector) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
revert(0, 0x04)
}
}
/// @dev Reverts with a custom error with an address argument in the scratch space
function revertWith(bytes4 selector, address addr) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, and(addr, 0xffffffffffffffffffffffffffffffffffffffff))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with an int24 argument in the scratch space
function revertWith(bytes4 selector, int24 value) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, signextend(2, value))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with a uint160 argument in the scratch space
function revertWith(bytes4 selector, uint160 value) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, and(value, 0xffffffffffffffffffffffffffffffffffffffff))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with two int24 arguments
function revertWith(bytes4 selector, int24 value1, int24 value2) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), signextend(2, value1))
mstore(add(fmp, 0x24), signextend(2, value2))
revert(fmp, 0x44)
}
}
/// @dev Reverts with a custom error with two uint160 arguments
function revertWith(bytes4 selector, uint160 value1, uint160 value2) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
revert(fmp, 0x44)
}
}
/// @dev Reverts with a custom error with two address arguments
function revertWith(bytes4 selector, address value1, address value2) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
revert(fmp, 0x44)
}
}
/// @notice bubble up the revert message returned by a call and revert with a wrapped ERC-7751 error
/// @dev this method can be vulnerable to revert data bombs
function bubbleUpAndRevertWith(
address revertingContract,
bytes4 revertingFunctionSelector,
bytes4 additionalContext
) internal pure {
bytes4 wrappedErrorSelector = WrappedError.selector;
assembly ("memory-safe") {
// Ensure the size of the revert data is a multiple of 32 bytes
let encodedDataSize := mul(div(add(returndatasize(), 31), 32), 32)
let fmp := mload(0x40)
// Encode wrapped error selector, address, function selector, offset, additional context, size, revert reason
mstore(fmp, wrappedErrorSelector)
mstore(add(fmp, 0x04), and(revertingContract, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(
add(fmp, 0x24),
and(revertingFunctionSelector, 0xffffffff00000000000000000000000000000000000000000000000000000000)
)
// offset revert reason
mstore(add(fmp, 0x44), 0x80)
// offset additional context
mstore(add(fmp, 0x64), add(0xa0, encodedDataSize))
// size revert reason
mstore(add(fmp, 0x84), returndatasize())
// revert reason
returndatacopy(add(fmp, 0xa4), 0, returndatasize())
// size additional context
mstore(add(fmp, add(0xa4, encodedDataSize)), 0x04)
// additional context
mstore(
add(fmp, add(0xc4, encodedDataSize)),
and(additionalContext, 0xffffffff00000000000000000000000000000000000000000000000000000000)
)
revert(fmp, add(0xe4, encodedDataSize))
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface ICLPool {
function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
interface IUniswapV3Pool {
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
function tickSpacing() external view returns (int24 tickSpacing);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface IUniswapV3PositionManager {
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function positions(uint256 tokenId)
external
view
returns (
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
payable
returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
}// https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Uniswap V3 factory contract address
/// @param token0 Contract address of token0.
/// @param token1 Contract address of token1.
/// @param fee The fee of the pool.
/// @return pool The contract address of the V3 pool
function computeAddress(address factory, address token0, address token1, uint24 fee)
internal
pure
returns (address pool)
{
require(token0 < token1);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff", factory, keccak256(abi.encode(token0, token1, fee)), POOL_INIT_CODE_HASH
)
)
)
)
);
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.22;
// Struct with risk and valuation related information for a certain asset.
struct AssetValueAndRiskFactors {
// The value of the asset.
uint256 assetValue;
// The collateral factor of the asset, for a given creditor.
uint256 collateralFactor;
// The liquidation factor of the asset, for a given creditor.
uint256 liquidationFactor;
}
/**
* @title Asset Valuation Library
* @author Pragma Labs
* @notice The Asset Valuation Library is responsible for calculating the risk weighted values of combinations of assets.
*/
library AssetValuationLib {
/*///////////////////////////////////////////////////////////////
CONSTANTS
///////////////////////////////////////////////////////////////*/
uint256 internal constant ONE_4 = 10_000;
/*///////////////////////////////////////////////////////////////
RISK FACTORS
///////////////////////////////////////////////////////////////*/
/**
* @notice Calculates the collateral value given a combination of asset values and corresponding collateral factors.
* @param valuesAndRiskFactors Array of asset values and corresponding collateral factors.
* @return collateralValue The collateral value of the given assets.
*/
function _calculateCollateralValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 collateralValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
collateralValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].collateralFactor;
}
collateralValue = collateralValue / ONE_4;
}
/**
* @notice Calculates the liquidation value given a combination of asset values and corresponding liquidation factors.
* @param valuesAndRiskFactors List of asset values and corresponding liquidation factors.
* @return liquidationValue The liquidation value of the given assets.
*/
function _calculateLiquidationValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 liquidationValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
liquidationValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].liquidationFactor;
}
liquidationValue = liquidationValue / ONE_4;
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IFactory {
/**
* @notice Checks if a contract is an Account.
* @param account The contract address of the Account.
* @return bool indicating if the address is an Account or not.
*/
function isAccount(address account) external view returns (bool);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.22;
interface IPermit2 {
/**
* @notice The token and amount details for a transfer signed in the permit transfer signature
*/
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/**
* @notice Used to reconstruct the signed permit message for multiple token transfers
* @dev Do not need to pass in spender address as it is required that it is msg.sender
* @dev Note that a user still signs over a spender address
*/
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/**
* @notice Specifies the recipient address and amount for batched transfers.
* @dev Recipients and amounts correspond to the index of the signed token permissions array.
* @dev Reverts if the requested amount is greater than the permitted signed amount.
*/
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/**
* @notice Transfers multiple tokens using a signed permit message
* @param permit The permit data signed over by the owner
* @param owner The owner of the tokens to transfer
* @param transferDetails Specifies the recipient and requested amount for the token transfer
* @param signature The signature to verify
*/
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
import { AssetValueAndRiskFactors } from "../../../lib/accounts-v2/src/libraries/AssetValuationLib.sol";
interface IRegistry {
function getValuesInUsd(
address creditor,
address[] calldata assets,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (AssetValueAndRiskFactors[] memory valuesAndRiskFactors);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IStakedSlipstreamAM {
function burn(uint256 id) external returns (uint256 rewards);
function mint(uint256 id) external returns (uint256 id_);
function REWARD_TOKEN() external view returns (address rewardToken);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FixedPoint96.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
/// @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;
}// https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/LiquidityAmounts.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.22;
import { FixedPoint96 } from "../../../../lib/accounts-v2/src/asset-modules/UniswapV3/libraries/FixedPoint96.sol";
import { FullMath } from "../../../../lib/accounts-v2/src/asset-modules/UniswapV3/libraries/FullMath.sol";
/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
/// @notice Downcasts uint256 to uint128
/// @param x The uint258 to be downcasted
/// @return y The passed value, downcasted to uint128
function toUint128(uint256 x) internal pure returns (uint128 y) {
require((y = uint128(x)) == x);
}
/// @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 (uint256 liquidity)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96);
return FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96);
}
}
/// @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 (uint256 liquidity)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96);
}
}
/// @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) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
liquidity = toUint128(getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0));
} else if (sqrtRatioX96 < sqrtRatioBX96) {
uint256 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
uint256 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);
liquidity = toUint128(liquidity0 < liquidity1 ? liquidity0 : liquidity1);
} else {
liquidity = toUint128(getLiquidityForAmount1(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 {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(
uint256(liquidity) << FixedPoint96.RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96
) / 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)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
}
}
/// @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) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
} else {
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { LiquidityAmounts } from "../libraries/uniswap-v3/LiquidityAmounts.sol";
import { SqrtPriceMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/SqrtPriceMath.sol";
library RebalanceOptimizationMath {
using FixedPointMathLib for uint256;
// The minimal relative difference between liquidity0 and liquidity1, with 18 decimals precision.
uint256 internal constant CONVERGENCE_THRESHOLD = 1e6;
// The maximal number of iterations to find the optimal swap parameters.
uint256 internal constant MAX_ITERATIONS = 100;
/**
* @notice Iteratively calculates the amountOut for a swap through the pool itself, that maximizes the amount of liquidity that is added.
* The calculations take both fees and slippage into account, but assume constant liquidity.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param fee The fee of the pool, with 6 decimals precision.
* @param usableLiquidity The amount of active liquidity in the pool, at the current tick.
* @param sqrtPriceOld The square root of the pool price (token1/token0) before the swap, with 96 binary precision.
* @param sqrtRatioLower The square root price of the lower tick of the liquidity position, with 96 binary precision.
* @param sqrtRatioUpper The square root price of the upper tick of the liquidity position, with 96 binary precision.
* @param amount0 The balance of token0 before the swap.
* @param amount1 The balance of token1 before the swap.
* @param amountIn An approximation of the amount of tokenIn, based on the optimal swap through the pool itself without slippage.
* @param amountOut An approximation of the amount of tokenOut, based on the optimal swap through the pool itself without slippage.
* @return amountOut The amount of tokenOut.
* @dev The optimal amountIn and amountOut are defined as the amounts that maximize the amount of liquidity that can be added to the position.
* This means that there are no leftovers of either token0 or token1,
* and liquidity0 (calculated via getLiquidityForAmount0) will be exactly equal to liquidity1 (calculated via getLiquidityForAmount1).
* @dev The optimal amountIn and amountOut depend on the sqrtPrice of the pool via the liquidity calculations,
* but the sqrtPrice in turn depends on the amountIn and amountOut via the swap calculations.
* Since both are highly non-linear, this problem is (according to our understanding) not analytically solvable.
* Therefore we use an iterative approach to find the optimal swap parameters.
* The stop criterium is defined when the relative difference between liquidity0 and liquidity1 is below the convergence threshold.
* @dev Convergence is not guaranteed, worst case or the transaction reverts, or a non-optimal swap is performed,
* But then minLiquidity enforces that either enough liquidity is minted or the transaction will revert.
* @dev We assume constant active liquidity when calculating the swap parameters.
* For illiquid pools, or positions that are large relatively to the pool liquidity, this might result in reverting rebalances.
* But since a minimum amount of liquidity is enforced, should not lead to loss of principal.
*/
function _getAmountOutWithSlippage(
bool zeroToOne,
uint256 fee,
uint128 usableLiquidity,
uint160 sqrtPriceOld,
uint160 sqrtRatioLower,
uint160 sqrtRatioUpper,
uint256 amount0,
uint256 amount1,
uint256 amountIn,
uint256 amountOut
) internal pure returns (uint256) {
uint160 sqrtPriceNew;
bool stopCondition;
// We iteratively solve for sqrtPrice, amountOut and amountIn, so that the maximal amount of liquidity can be added to the position.
for (uint256 i = 0; i < MAX_ITERATIONS; ++i) {
// Find a better approximation for sqrtPrice, given the best approximations for the optimal amountIn and amountOut.
sqrtPriceNew = _approximateSqrtPriceNew(zeroToOne, fee, usableLiquidity, sqrtPriceOld, amountIn, amountOut);
// If the position is out of range, we can calculate the exact solution.
if (sqrtPriceNew >= sqrtRatioUpper) {
// New position is out of range and fully in token 1.
// Rebalance to a single-sided liquidity position in token 1.
// We ignore one edge case: Swapping token0 to token1 decreases the sqrtPrice,
// hence a swap for a position that is just out of range might become in range due to slippage.
// This might lead to a suboptimal rebalance, which worst case results in too little liquidity and the rebalance reverts.
return _getAmount1OutFromAmount0In(fee, usableLiquidity, sqrtPriceOld, amount0);
} else if (sqrtPriceNew <= sqrtRatioLower) {
// New position is out of range and fully in token 0.
// Rebalance to a single-sided liquidity position in token 0.
// We ignore one edge case: Swapping token1 to token0 increases the sqrtPrice,
// hence a swap for a position that is just out of range might become in range due to slippage.
// This might lead to a suboptimal rebalance, which worst case results in too little liquidity and the rebalance reverts.
return _getAmount0OutFromAmount1In(fee, usableLiquidity, sqrtPriceOld, amount1);
}
// If the position is not out of range, calculate the amountIn and amountOut, given the new approximated sqrtPrice.
(amountIn, amountOut) = _getSwapParamsExact(zeroToOne, fee, usableLiquidity, sqrtPriceOld, sqrtPriceNew);
// Given the new approximated sqrtPriceNew and its swap amounts,
// calculate a better approximation for the optimal amountIn and amountOut, that would maximise the liquidity provided
// (no leftovers of either token0 or token1).
(stopCondition, amountIn, amountOut) = _approximateOptimalSwapAmounts(
zeroToOne, sqrtRatioLower, sqrtRatioUpper, amount0, amount1, amountIn, amountOut, sqrtPriceNew
);
// Check if stop condition of iteration is met:
// The relative difference between liquidity0 and liquidity1 is below the convergence threshold.
if (stopCondition) return amountOut;
// If not, we do an extra iteration with our better approximated amountIn and amountOut.
}
// If solution did not converge within MAX_ITERATIONS steps, we use the amountOut of the last iteration step.
return amountOut;
}
/**
* @notice Approximates the SqrtPrice after the swap, given an approximation for the amountIn and amountOut that maximise liquidity added.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param fee The fee of the pool, with 6 decimals precision.
* @param usableLiquidity The amount of active liquidity in the pool, at the current tick.
* @param sqrtPriceOld The SqrtPrice before the swap.
* @param amountIn An approximation of the amount of tokenIn, that maximise liquidity added.
* @param amountOut An approximation of the amount of tokenOut, that maximise liquidity added.
* @return sqrtPriceNew The approximation of the SqrtPrice after the swap.
*/
function _approximateSqrtPriceNew(
bool zeroToOne,
uint256 fee,
uint128 usableLiquidity,
uint160 sqrtPriceOld,
uint256 amountIn,
uint256 amountOut
) internal pure returns (uint160 sqrtPriceNew) {
unchecked {
// Calculate the exact sqrtPriceNew for both amountIn and amountOut.
// Both solutions will be different, but they will converge with every iteration closer to the same solution.
uint256 amountInLessFee = amountIn.mulDivDown(1e6 - fee, 1e6);
uint256 sqrtPriceNew0;
uint256 sqrtPriceNew1;
if (zeroToOne) {
sqrtPriceNew0 = SqrtPriceMath.getNextSqrtPriceFromAmount0RoundingUp(
sqrtPriceOld, usableLiquidity, amountInLessFee, true
);
sqrtPriceNew1 = SqrtPriceMath.getNextSqrtPriceFromAmount1RoundingDown(
sqrtPriceOld, usableLiquidity, amountOut, false
);
} else {
sqrtPriceNew0 =
SqrtPriceMath.getNextSqrtPriceFromAmount0RoundingUp(sqrtPriceOld, usableLiquidity, amountOut, false);
sqrtPriceNew1 = SqrtPriceMath.getNextSqrtPriceFromAmount1RoundingDown(
sqrtPriceOld, usableLiquidity, amountInLessFee, true
);
}
// Calculate the new best approximation as the arithmetic average of both solutions (rounded towards current price).
// We could as well use the geometric average, but empirically we found no difference in conversion speed,
// and the geometric average is more expensive to calculate.
// Unchecked + unsafe cast: sqrtPriceNew0 and sqrtPriceNew1 are always smaller than type(uint160).max.
sqrtPriceNew = zeroToOne
? uint160(FixedPointMathLib.unsafeDiv(sqrtPriceNew0 + sqrtPriceNew1, 2))
: uint160(FixedPointMathLib.unsafeDivUp(sqrtPriceNew0 + sqrtPriceNew1, 2));
}
}
/**
* @notice Calculates the amountOut of token1, for a given amountIn of token0.
* @param fee The fee of the pool, with 6 decimals precision.
* @param usableLiquidity The amount of active liquidity in the pool, at the current tick.
* @param sqrtPriceOld The SqrtPrice before the swap.
* @param amount0 The balance of token0 before the swap.
* @return amountOut The amount of token1 that is swapped to.
* @dev The calculations take both fees and slippage into account, but assume constant liquidity.
*/
function _getAmount1OutFromAmount0In(uint256 fee, uint128 usableLiquidity, uint160 sqrtPriceOld, uint256 amount0)
internal
pure
returns (uint256 amountOut)
{
unchecked {
uint256 amountInLessFee = amount0.mulDivUp(1e6 - fee, 1e6);
uint160 sqrtPriceNew = SqrtPriceMath.getNextSqrtPriceFromAmount0RoundingUp(
sqrtPriceOld, usableLiquidity, amountInLessFee, true
);
amountOut = SqrtPriceMath.getAmount1Delta(sqrtPriceNew, sqrtPriceOld, usableLiquidity, false);
}
}
/**
* @notice Calculates the amountOut of token0, for a given amountIn of token1.
* @param fee The fee of the pool, with 6 decimals precision.
* @param usableLiquidity The amount of active liquidity in the pool, at the current tick.
* @param sqrtPriceOld The SqrtPrice before the swap.
* @param amount1 The balance of token1 before the swap.
* @return amountOut The amount of token0 that is swapped to.
* @dev The calculations take both fees and slippage into account, but assume constant liquidity.
*/
function _getAmount0OutFromAmount1In(uint256 fee, uint128 usableLiquidity, uint160 sqrtPriceOld, uint256 amount1)
internal
pure
returns (uint256 amountOut)
{
unchecked {
uint256 amountInLessFee = amount1.mulDivUp(1e6 - fee, 1e6);
uint160 sqrtPriceNew = SqrtPriceMath.getNextSqrtPriceFromAmount1RoundingDown(
sqrtPriceOld, usableLiquidity, amountInLessFee, true
);
amountOut = SqrtPriceMath.getAmount0Delta(sqrtPriceOld, sqrtPriceNew, usableLiquidity, false);
}
}
/**
* @notice Calculates the amountIn and amountOut of token0, for a given SqrtPrice after the swap.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param fee The fee of the pool, with 6 decimals precision.
* @param usableLiquidity The amount of active liquidity in the pool, at the current tick.
* @param sqrtPriceOld The SqrtPrice before the swap.
* @param sqrtPriceNew The SqrtPrice after the swap.
* @return amountIn The amount of tokenIn.
* @return amountOut The amount of tokenOut.
* @dev The calculations take both fees and slippage into account, but assume constant liquidity.
*/
function _getSwapParamsExact(
bool zeroToOne,
uint256 fee,
uint128 usableLiquidity,
uint160 sqrtPriceOld,
uint160 sqrtPriceNew
) internal pure returns (uint256 amountIn, uint256 amountOut) {
unchecked {
if (zeroToOne) {
uint256 amountInLessFee =
SqrtPriceMath.getAmount0Delta(sqrtPriceNew, sqrtPriceOld, usableLiquidity, true);
amountIn = amountInLessFee.mulDivUp(1e6, 1e6 - fee);
amountOut = SqrtPriceMath.getAmount1Delta(sqrtPriceNew, sqrtPriceOld, usableLiquidity, false);
} else {
uint256 amountInLessFee =
SqrtPriceMath.getAmount1Delta(sqrtPriceOld, sqrtPriceNew, usableLiquidity, true);
amountIn = amountInLessFee.mulDivUp(1e6, 1e6 - fee);
amountOut = SqrtPriceMath.getAmount0Delta(sqrtPriceOld, sqrtPriceNew, usableLiquidity, false);
}
}
}
/**
* @notice Approximates the amountIn and amountOut that maximise liquidity added,
* given an approximation for the SqrtPrice after the swap and an approximation of the balances of token0 and token1 after the swap.
* @param zeroToOne Bool indicating if token0 has to be swapped to token1 or opposite.
* @param sqrtRatioLower The square root price of the lower tick of the liquidity position, with 96 binary precision.
* @param sqrtRatioUpper The square root price of the upper tick of the liquidity position, with 96 binary precision.
* @param amount0 The balance of token0 before the swap.
* @param amount1 The balance of token1 before the swap.
* @param amountIn An approximation of the amount of tokenIn, used to calculate the approximated balances after the swap.
* @param amountOut An approximation of the amount of tokenOut, used to calculate the approximated balances after the swap.
* @param sqrtPrice An approximation of the SqrtPrice after the swap.
* @return converged Bool indicating if the stop criterium of iteration is met.
* @return amountIn_ The new approximation of the amount of tokenIn that maximise liquidity added.
* @return amountOut_ The new approximation of the amount of amountOut that maximise liquidity added.
*/
function _approximateOptimalSwapAmounts(
bool zeroToOne,
uint160 sqrtRatioLower,
uint160 sqrtRatioUpper,
uint256 amount0,
uint256 amount1,
uint256 amountIn,
uint256 amountOut,
uint160 sqrtPrice
) internal pure returns (bool, uint256, uint256) {
unchecked {
// Calculate the liquidity for the given approximated sqrtPrice and the approximated balances of token0 and token1 after the swap.
uint256 liquidity0;
uint256 liquidity1;
if (zeroToOne) {
liquidity0 = LiquidityAmounts.getLiquidityForAmount0(
sqrtPrice, sqrtRatioUpper, amount0 > amountIn ? amount0 - amountIn : 0
);
liquidity1 = LiquidityAmounts.getLiquidityForAmount1(sqrtRatioLower, sqrtPrice, amount1 + amountOut);
} else {
liquidity0 = LiquidityAmounts.getLiquidityForAmount0(sqrtPrice, sqrtRatioUpper, amount0 + amountOut);
liquidity1 = LiquidityAmounts.getLiquidityForAmount1(
sqrtRatioLower, sqrtPrice, amount1 > amountIn ? amount1 - amountIn : 0
);
}
// Calculate the relative difference of liquidity0 and liquidity1.
uint256 relDiff = 1e18
- (
liquidity0 < liquidity1
? liquidity0.mulDivDown(1e18, liquidity1)
: liquidity1.mulDivDown(1e18, liquidity0)
);
// In the optimal solution liquidity0 equals liquidity1,
// and there are no leftovers for token0 or token1 after minting the liquidity.
// Hence the relative distance between liquidity0 and liquidity1
// is a good estimator how close we are to the optimal solution.
bool converged = relDiff < CONVERGENCE_THRESHOLD;
// The new approximated liquidity is the minimum of liquidity0 and liquidity1.
// Calculate the new approximated amountIn or amountOut,
// for which this liquidity would be the optimal solution.
if (liquidity0 < liquidity1) {
uint256 amount1New = SqrtPriceMath.getAmount1Delta(
sqrtRatioLower, sqrtPrice, LiquidityAmounts.toUint128(liquidity0), true
);
zeroToOne
// Since amountOut can't be negative, we use 90% of the previous amountOut as a fallback.
? amountOut = amount1New > amount1 ? amount1New - amount1 : amountOut.mulDivDown(9, 10)
: amountIn = amount1 - amount1New;
} else {
uint256 amount0New = SqrtPriceMath.getAmount0Delta(
sqrtPrice, sqrtRatioUpper, LiquidityAmounts.toUint128(liquidity1), true
);
zeroToOne
? amountIn = amount0 - amount0New
// Since amountOut can't be negative, we use 90% of the previous amountOut as a fallback.
: amountOut = amount0New > amount0 ? amount0New - amount0 : amountOut.mulDivDown(9, 10);
}
return (converged, amountIn, amountOut);
}
}
}// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.22;
/// @title Contains 512-bit math functions
/// @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 {
/// @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 result) {
unchecked {
// 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
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// 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 *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
}
return result;
}
/// @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 result) {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {SafeCast} from "./SafeCast.sol";
import {FullMath} from "./FullMath.sol";
import {UnsafeMath} from "./UnsafeMath.sol";
import {FixedPoint96} from "./FixedPoint96.sol";
/// @title Functions based on Q64.96 sqrt price and liquidity
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
library SqrtPriceMath {
using SafeCast for uint256;
error InvalidPriceOrLiquidity();
error InvalidPrice();
error NotEnoughLiquidity();
error PriceOverflow();
/// @notice Gets the next sqrt price given a delta of currency0
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
/// price less in order to not send too much output.
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
/// @param sqrtPX96 The starting price, i.e. before accounting for the currency0 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of currency0 to add or remove from virtual reserves
/// @param add Whether to add or remove the amount of currency0
/// @return The price after adding or removing amount, depending on add
function getNextSqrtPriceFromAmount0RoundingUp(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add)
internal
pure
returns (uint160)
{
// we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
if (amount == 0) return sqrtPX96;
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
if (add) {
unchecked {
uint256 product = amount * sqrtPX96;
if (product / amount == sqrtPX96) {
uint256 denominator = numerator1 + product;
if (denominator >= numerator1) {
// always fits in 160 bits
return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
}
}
}
// denominator is checked for overflow
return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96) + amount));
} else {
unchecked {
uint256 product = amount * sqrtPX96;
// if the product overflows, we know the denominator underflows
// in addition, we must check that the denominator does not underflow
// equivalent: if (product / amount != sqrtPX96 || numerator1 <= product) revert PriceOverflow();
assembly ("memory-safe") {
if iszero(
and(
eq(div(product, amount), and(sqrtPX96, 0xffffffffffffffffffffffffffffffffffffffff)),
gt(numerator1, product)
)
) {
mstore(0, 0xf5c787f1) // selector for PriceOverflow()
revert(0x1c, 0x04)
}
}
uint256 denominator = numerator1 - product;
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
}
}
}
/// @notice Gets the next sqrt price given a delta of currency1
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
/// price less in order to not send too much output.
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
/// @param sqrtPX96 The starting price, i.e., before accounting for the currency1 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of currency1 to add, or remove, from virtual reserves
/// @param add Whether to add, or remove, the amount of currency1
/// @return The price after adding or removing `amount`
function getNextSqrtPriceFromAmount1RoundingDown(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add)
internal
pure
returns (uint160)
{
// if we're adding (subtracting), rounding down requires rounding the quotient down (up)
// in both cases, avoid a mulDiv for most inputs
if (add) {
uint256 quotient = (
amount <= type(uint160).max
? (amount << FixedPoint96.RESOLUTION) / liquidity
: FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
);
return (uint256(sqrtPX96) + quotient).toUint160();
} else {
uint256 quotient = (
amount <= type(uint160).max
? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
: FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
);
// equivalent: if (sqrtPX96 <= quotient) revert NotEnoughLiquidity();
assembly ("memory-safe") {
if iszero(gt(and(sqrtPX96, 0xffffffffffffffffffffffffffffffffffffffff), quotient)) {
mstore(0, 0x4323a555) // selector for NotEnoughLiquidity()
revert(0x1c, 0x04)
}
}
// always fits 160 bits
unchecked {
return uint160(sqrtPX96 - quotient);
}
}
}
/// @notice Gets the next sqrt price given an input amount of currency0 or currency1
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
/// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
/// @param liquidity The amount of usable liquidity
/// @param amountIn How much of currency0, or currency1, is being swapped in
/// @param zeroForOne Whether the amount in is currency0 or currency1
/// @return uint160 The price after adding the input amount to currency0 or currency1
function getNextSqrtPriceFromInput(uint160 sqrtPX96, uint128 liquidity, uint256 amountIn, bool zeroForOne)
internal
pure
returns (uint160)
{
// equivalent: if (sqrtPX96 == 0 || liquidity == 0) revert InvalidPriceOrLiquidity();
assembly ("memory-safe") {
if or(
iszero(and(sqrtPX96, 0xffffffffffffffffffffffffffffffffffffffff)),
iszero(and(liquidity, 0xffffffffffffffffffffffffffffffff))
) {
mstore(0, 0x4f2461b8) // selector for InvalidPriceOrLiquidity()
revert(0x1c, 0x04)
}
}
// round to make sure that we don't pass the target price
return zeroForOne
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
}
/// @notice Gets the next sqrt price given an output amount of currency0 or currency1
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds
/// @param sqrtPX96 The starting price before accounting for the output amount
/// @param liquidity The amount of usable liquidity
/// @param amountOut How much of currency0, or currency1, is being swapped out
/// @param zeroForOne Whether the amount out is currency1 or currency0
/// @return uint160 The price after removing the output amount of currency0 or currency1
function getNextSqrtPriceFromOutput(uint160 sqrtPX96, uint128 liquidity, uint256 amountOut, bool zeroForOne)
internal
pure
returns (uint160)
{
// equivalent: if (sqrtPX96 == 0 || liquidity == 0) revert InvalidPriceOrLiquidity();
assembly ("memory-safe") {
if or(
iszero(and(sqrtPX96, 0xffffffffffffffffffffffffffffffffffffffff)),
iszero(and(liquidity, 0xffffffffffffffffffffffffffffffff))
) {
mstore(0, 0x4f2461b8) // selector for InvalidPriceOrLiquidity()
revert(0x1c, 0x04)
}
}
// round to make sure that we pass the target price
return zeroForOne
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
}
/// @notice Gets the amount0 delta between two prices
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
/// @param sqrtPriceAX96 A sqrt price
/// @param sqrtPriceBX96 Another sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up or down
/// @return uint256 Amount of currency0 required to cover a position of size liquidity between the two passed prices
function getAmount0Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity, bool roundUp)
internal
pure
returns (uint256)
{
unchecked {
if (sqrtPriceAX96 > sqrtPriceBX96) (sqrtPriceAX96, sqrtPriceBX96) = (sqrtPriceBX96, sqrtPriceAX96);
// equivalent: if (sqrtPriceAX96 == 0) revert InvalidPrice();
assembly ("memory-safe") {
if iszero(and(sqrtPriceAX96, 0xffffffffffffffffffffffffffffffffffffffff)) {
mstore(0, 0x00bfc921) // selector for InvalidPrice()
revert(0x1c, 0x04)
}
}
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
uint256 numerator2 = sqrtPriceBX96 - sqrtPriceAX96;
return roundUp
? UnsafeMath.divRoundingUp(FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtPriceBX96), sqrtPriceAX96)
: FullMath.mulDiv(numerator1, numerator2, sqrtPriceBX96) / sqrtPriceAX96;
}
}
/// @notice Equivalent to: `a >= b ? a - b : b - a`
function absDiff(uint160 a, uint160 b) internal pure returns (uint256 res) {
assembly ("memory-safe") {
let diff :=
sub(and(a, 0xffffffffffffffffffffffffffffffffffffffff), and(b, 0xffffffffffffffffffffffffffffffffffffffff))
// mask = 0 if a >= b else -1 (all 1s)
let mask := sar(255, diff)
// if a >= b, res = a - b = 0 ^ (a - b)
// if a < b, res = b - a = ~~(b - a) = ~(-(b - a) - 1) = ~(a - b - 1) = (-1) ^ (a - b - 1)
// either way, res = mask ^ (a - b + mask)
res := xor(mask, add(mask, diff))
}
}
/// @notice Gets the amount1 delta between two prices
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
/// @param sqrtPriceAX96 A sqrt price
/// @param sqrtPriceBX96 Another sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up, or down
/// @return amount1 Amount of currency1 required to cover a position of size liquidity between the two passed prices
function getAmount1Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, uint128 liquidity, bool roundUp)
internal
pure
returns (uint256 amount1)
{
uint256 numerator = absDiff(sqrtPriceAX96, sqrtPriceBX96);
uint256 denominator = FixedPoint96.Q96;
uint256 _liquidity = uint256(liquidity);
/**
* Equivalent to:
* amount1 = roundUp
* ? FullMath.mulDivRoundingUp(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96)
* : FullMath.mulDiv(liquidity, sqrtPriceBX96 - sqrtPriceAX96, FixedPoint96.Q96);
* Cannot overflow because `type(uint128).max * type(uint160).max >> 96 < (1 << 192)`.
*/
amount1 = FullMath.mulDiv(_liquidity, numerator, denominator);
assembly ("memory-safe") {
amount1 := add(amount1, and(gt(mulmod(_liquidity, numerator, denominator), 0), roundUp))
}
}
/// @notice Helper that gets signed currency0 delta
/// @param sqrtPriceAX96 A sqrt price
/// @param sqrtPriceBX96 Another sqrt price
/// @param liquidity The change in liquidity for which to compute the amount0 delta
/// @return int256 Amount of currency0 corresponding to the passed liquidityDelta between the two prices
function getAmount0Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, int128 liquidity)
internal
pure
returns (int256)
{
unchecked {
return liquidity < 0
? getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(-liquidity), false).toInt256()
: -getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(liquidity), true).toInt256();
}
}
/// @notice Helper that gets signed currency1 delta
/// @param sqrtPriceAX96 A sqrt price
/// @param sqrtPriceBX96 Another sqrt price
/// @param liquidity The change in liquidity for which to compute the amount1 delta
/// @return int256 Amount of currency1 corresponding to the passed liquidityDelta between the two prices
function getAmount1Delta(uint160 sqrtPriceAX96, uint160 sqrtPriceBX96, int128 liquidity)
internal
pure
returns (int256)
{
unchecked {
return liquidity < 0
? getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(-liquidity), false).toInt256()
: -getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, uint128(liquidity), true).toInt256();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {CustomRevert} from "./CustomRevert.sol";
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
using CustomRevert for bytes4;
error SafeCastOverflow();
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param x The uint256 to be downcasted
/// @return y The downcasted integer, now type uint160
function toUint160(uint256 x) internal pure returns (uint160 y) {
y = uint160(x);
if (y != x) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a uint128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return y The downcasted integer, now type uint128
function toUint128(uint256 x) internal pure returns (uint128 y) {
y = uint128(x);
if (x != y) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a int128 to a uint128, revert on overflow or underflow
/// @param x The int128 to be casted
/// @return y The casted integer, now type uint128
function toUint128(int128 x) internal pure returns (uint128 y) {
if (x < 0) SafeCastOverflow.selector.revertWith();
y = uint128(x);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param x The int256 to be downcasted
/// @return y The downcasted integer, now type int128
function toInt128(int256 x) internal pure returns (int128 y) {
y = int128(x);
if (y != x) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param x The uint256 to be casted
/// @return y The casted integer, now type int256
function toInt256(uint256 x) internal pure returns (int256 y) {
y = int256(x);
if (y < 0) SafeCastOverflow.selector.revertWith();
}
/// @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) SafeCastOverflow.selector.revertWith();
return int128(int256(x));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Math functions that do not check inputs or outputs
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
library UnsafeMath {
/// @notice Returns ceil(x / y)
/// @dev division by 0 will return 0, and should 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 ("memory-safe") {
z := add(div(x, y), gt(mod(x, y), 0))
}
}
/// @notice Calculates floor(a×b÷denominator)
/// @dev division by 0 will return 0, and should be checked externally
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result, floor(a×b÷denominator)
function simpleMulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
assembly ("memory-safe") {
result := div(mul(a, b), denominator)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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;
}{
"remappings": [
"lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/accounts-v2/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/accounts-v2/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"@ensdomains/=lib/accounts-v2/lib/slipstream/node_modules/@ensdomains/",
"@nomad-xyz/=lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/",
"@openzeppelin/=lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"@openzeppelin/contracts/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/contracts/",
"@solidity-parser/=lib/accounts-v2/lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
"@uniswap/v2-core/contracts/=lib/accounts-v2/./test/utils/fixtures/swap-router-02/",
"@uniswap/v3-core/contracts/=lib/accounts-v2/lib/v3-core/contracts/",
"@uniswap/v3-periphery/contracts/=lib/accounts-v2/lib/v3-periphery/contracts/",
"@uniswap/v4-core/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/",
"ExcessivelySafeCall/=lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/src/",
"accounts-v2/=lib/accounts-v2/",
"base64-sol/=lib/accounts-v2/lib/slipstream/lib/base64/",
"base64/=lib/accounts-v2/lib/slipstream/lib/base64/",
"contracts/=lib/accounts-v2/lib/slipstream/contracts/",
"ds-test/=lib/accounts-v2/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/accounts-v2/lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/accounts-v2/lib/forge-std/src/",
"hardhat/=lib/accounts-v2/lib/slipstream/node_modules/hardhat/",
"openzeppelin-contracts/=lib/accounts-v2/lib/openzeppelin-contracts/contracts/",
"permit2/=lib/accounts-v2/lib/v4-periphery/lib/permit2/",
"slipstream/=lib/accounts-v2/lib/slipstream/",
"solidity-lib/=lib/accounts-v2/lib/slipstream/lib/solidity-lib/contracts/",
"solmate/=lib/accounts-v2/lib/solmate/",
"swap-router-contracts/=lib/accounts-v2/lib/swap-router-contracts/contracts/",
"v3-core/=lib/accounts-v2/lib/v3-core/",
"v3-periphery/=lib/accounts-v2/lib/v3-periphery/contracts/",
"v4-core/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/",
"v4-periphery/=lib/accounts-v2/lib/v4-periphery/",
"lib/slipstream:@openzeppelin/=lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/v3-periphery:@openzeppelin/=lib/accounts-v2/lib/openzeppelin-contracts/",
"lib/v4-periphery:@openzeppelin/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"maxTolerance","type":"uint256"},{"internalType":"uint256","name":"maxInitiatorFee","type":"uint256"},{"internalType":"uint256","name":"minLiquidityRatio","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InitiatorNotValid","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"NotAnAccount","type":"error"},{"inputs":[],"name":"OnlyAccount","type":"error"},{"inputs":[],"name":"OnlyAccountOwner","type":"error"},{"inputs":[],"name":"OnlyPool","type":"error"},{"inputs":[],"name":"OnlyPositionManager","type":"error"},{"inputs":[],"name":"Reentered","type":"error"},{"inputs":[],"name":"UnbalancedPool","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":true,"internalType":"address","name":"strategyHook","type":"address"}],"name":"AccountInfoSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"positionManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newId","type":"uint256"}],"name":"Rebalance","type":"event"},{"inputs":[],"name":"MAX_INITIATOR_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TOLERANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LIQUIDITY_RATIO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accountToInitiator","outputs":[{"internalType":"address","name":"initiator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"rebalanceData","type":"bytes"}],"name":"executeAction","outputs":[{"components":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"assetTypes","type":"uint256[]"}],"internalType":"struct ActionData","name":"depositData","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"positionManager","type":"address"},{"internalType":"uint256","name":"oldId","type":"uint256"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"address","name":"initiator","type":"address"}],"name":"getPositionState","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickSpacing","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint160","name":"sqrtRatioLower","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioUpper","type":"uint160"},{"internalType":"uint256","name":"sqrtPriceX96","type":"uint256"},{"internalType":"uint256","name":"lowerBoundSqrtPriceX96","type":"uint256"},{"internalType":"uint256","name":"upperBoundSqrtPriceX96","type":"uint256"}],"internalType":"struct Rebalancer.PositionState","name":"position","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"}],"name":"initiatorInfo","outputs":[{"internalType":"uint64","name":"upperSqrtPriceDeviation","type":"uint64"},{"internalType":"uint64","name":"lowerSqrtPriceDeviation","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"uint64","name":"minLiquidityRatio","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickSpacing","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint160","name":"sqrtRatioLower","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioUpper","type":"uint160"},{"internalType":"uint256","name":"sqrtPriceX96","type":"uint256"},{"internalType":"uint256","name":"lowerBoundSqrtPriceX96","type":"uint256"},{"internalType":"uint256","name":"upperBoundSqrtPriceX96","type":"uint256"}],"internalType":"struct Rebalancer.PositionState","name":"position","type":"tuple"}],"name":"isPoolUnbalanced","outputs":[{"internalType":"bool","name":"isPoolUnbalanced_","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"address","name":"positionManager","type":"address"},{"internalType":"uint256","name":"oldId","type":"uint256"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bytes","name":"swapData","type":"bytes"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"hook","type":"address"}],"name":"setAccountInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tolerance","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"minLiquidityRatio","type":"uint256"}],"name":"setInitiatorInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"strategyHook","outputs":[{"internalType":"address","name":"hook","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e060405234801562000010575f80fd5b506040516200537c3803806200537c833981016040819052620000339162000045565b60809290925260a05260c05262000071565b5f805f6060848603121562000058575f80fd5b8351925060208401519150604084015190509250925092565b60805160a05160c0516152cb620000b15f395f8181610203015261089b01525f8181610315015261084801525f8181610348015261087101526152cb5ff3fe6080604052600436106100dc575f3560e01c80635a1c94c21161007c578063830e703f11610057578063830e703f1461036a5780639583aef014610389578063a129568d146103bd578063fa461e33146103e9575f80fd5b80635a1c94c2146102e55780635f1bbfc21461030457806370cd8d2714610337575f80fd5b806326473274116100b757806326473274146101a6578063304e0270146101f25780634d3b3985146102335780634df8604a1461025f575f80fd5b80630ace53951461011b5780630f59b86a1461014f578063150b7a021461016e575f80fd5b36610117573373827922686190790b37229fd06084350e74485b721461011557604051637b7524c960e01b815260040160405180910390fd5b005b5f80fd5b348015610126575f80fd5b5061013a610135366004613e40565b610408565b60405190151581526020015b60405180910390f35b34801561015a575f80fd5b50610115610169366004613f6d565b610432565b348015610179575f80fd5b5061018d610188366004613ffe565b610533565b6040516001600160e01b03199091168152602001610146565b3480156101b1575f80fd5b506101da6101c036600461406b565b60036020525f90815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610146565b3480156101fd575f80fd5b506102257f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610146565b34801561023e575f80fd5b5061025261024d366004614086565b610545565b60405161014691906140ea565b34801561026a575f80fd5b506102b261027936600461406b565b60016020525f90815260409020546001600160401b0380821691600160401b8104821691600160801b8204811691600160c01b90041684565b604080516001600160401b0395861681529385166020850152918416918301919091529091166060820152608001610146565b3480156102f0575f80fd5b506101156102ff3660046141f9565b610707565b34801561030f575f80fd5b506102257f000000000000000000000000000000000000000000000000000000000000000081565b348015610342575f80fd5b506102257f000000000000000000000000000000000000000000000000000000000000000081565b348015610375575f80fd5b50610115610384366004614222565b6109ad565b348015610394575f80fd5b506101da6103a336600461406b565b60026020525f90815260409020546001600160a01b031681565b3480156103c8575f80fd5b506103dc6103d736600461426a565b610b72565b6040516101469190614376565b3480156103f4575f80fd5b50610115610403366004614388565b611029565b5f81610160015182610140015111158061042c575081610180015182610140015110155b92915050565b5f546001600160a01b03161561045b5760405163b5dfd9e560e01b815260040160405180910390fd5b6001600160a01b038781165f908152600260205260409020541633146104945760405163fde6c87960e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b0389161781556104bd87873388888888611120565b60405162b9252f60e41b81529091506001600160a01b03891690630b9252f0906104ed9030908590600401614423565b5f604051808303815f87803b158015610504575f80fd5b505af1158015610516573d5f803e3d5ffd5b50505f80546001600160a01b031916905550505050505050505050565b630a85bd0160e11b5b95945050505050565b61054d613cd1565b5f806001600160a01b0388167303a520b32c04bf3beef7beb72e919cf822ed34f1146105825761057d8388611315565b610595565b61059583888760020b8960020b146114ec565b915091508460020b8660020b036106125760808301516105b5818461446e565b6105bf91906144a6565b60808401519092506105d28160026144a6565b6105dc908361446e565b6105e691906144a6565b6105f090836144cc565b60020b60c084018190526106059082906144f1565b60020b60a0840152610625565b600285810b60a085015286900b60c08401525b6106328360c00151611716565b6001600160a01b031661010084015260a083015161064f90611716565b6001600160a01b031661012084015282515f9061066b906119ce565b90505f61067782611716565b6001600160a01b038781165f90815260016020526040902054911691506106b8908290600160401b90046001600160401b0316670de0b6b3a7640000611ae6565b6101608601526001600160a01b0386165f908152600160205260409020546106f39082906001600160401b0316670de0b6b3a7640000611ae6565b610180860152509298975050505050505050565b5f546001600160a01b0316156107305760405163b5dfd9e560e01b815260040160405180910390fd5b335f908152600160209081526040808320815160808101835290546001600160401b038082168352600160401b8204811694830194909452600160801b8104841692820192909252600160c01b9091049091166060820152906107b561079e86670de0b6b3a7640000614516565b6107b090670de0b6b3a7640000614529565b611b01565b60608301519091506001600160401b0316156108465781604001516001600160401b03168411806107fa5750815f01516001600160401b0316816001600160401b0316115b80610811575081606001516001600160401b031683105b806108235750670de0b6b3a764000083115b1561084157604051632a9ffab760e21b815260040160405180910390fd5b6108ed565b7f000000000000000000000000000000000000000000000000000000000000000084118061089357507f000000000000000000000000000000000000000000000000000000000000000085115b806108bd57507f000000000000000000000000000000000000000000000000000000000000000083105b806108cf5750670de0b6b3a764000083115b156108ed57604051632a9ffab760e21b815260040160405180910390fd5b6001600160401b0380851660408401528316606083015261091961079e86670de0b6b3a7640000614540565b6001600160401b0390811660208085019182529282168452335f9081526001909352604092839020845181549251948601516060909601518416600160c01b026001600160c01b03968516600160801b02969096166001600160801b03958516600160401b026fffffffffffffffffffffffffffffffff199094169190941617919091179290921617919091179055505050565b5f546001600160a01b0316156109d65760405163b5dfd9e560e01b815260040160405180910390fd5b604051630972932760e21b81526001600160a01b038416600482015273da14fdd72345c4d2511357214c5b89a919768e59906325ca4c9c90602401602060405180830381865afa158015610a2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a509190614562565b610a6d57604051630ea8370b60e41b815260040160405180910390fd5b826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610aaa573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ace919061457b565b6001600160a01b0316336001600160a01b031614610aff576040516312272fd360e11b815260040160405180910390fd5b6001600160a01b038084165f81815260026020908152604080832080548689166001600160a01b0319918216811790925560039093528184208054968816969093168617909255519092917f343ef5cc595144359c9db657cd7fcef6ecc88d06d17651a8292e553ab73b1c7091a4505050565b610b9d6040518060800160405280606081526020016060815260200160608152602001606081525090565b5f546001600160a01b03163314610bc75760405163f3f6425d60e01b815260040160405180910390fd5b335f908152600360205260408120546001600160a01b0316906060908080610bed613cd1565b5f610c196040518060800160405280606081526020016060815260200160608152602001606081525090565b5f80610c278c8e018e6146fc565b84518051919e509398509396509094509250905f90610c4857610c48614820565b6020026020010151975082602001515f81518110610c6857610c68614820565b60200260200101519650610c7f8888848488610545565b94505050506001600160a01b03871615610d135760c082015160a083015160405163bd5d93c960e01b81523360048201526001600160a01b03888116602483015260448201889052600293840b60648301529190920b608483015288169063bd5d93c99060a4015f6040518083038186803b158015610cfc575f80fd5b505afa158015610d0e573d5f803e3d5ffd5b505050505b610d1c82610408565b15610d3a57604051633a8bf65960e01b815260040160405180910390fd5b5f805f610d48888887611ba5565b6001600160a01b038088165f9081526001602052604081205460608b01516101408c01516101008d01516101208e0151989b5096995094975091958695869586958695610dc0956001600160401b03600160c01b820481169662ffffff90931695600160801b90920416939290811691168e8e611ec6565b94509450945094509450610ddb8e8e8c878787878f8f611f84565b9098509650610de98a610408565b15610e0757604051633a8bf65960e01b815260040160405180910390fd5b5f610e148e8c8b8b61208e565b929e509a50909850905085811015610e3f5760405163bb55fd2760e01b815260040160405180910390fd5b610e568a86868e602001518f604001518e8e61248d565b60405163095ea7b360e01b8152336004820152602481018f9052919a509850600196506001600160a01b038f16955063095ea7b394506044019250610e99915050565b5f604051808303815f87803b158015610eb0575f80fd5b505af1158015610ec2573d5f803e3d5ffd5b505050505f841115610eeb576020860151610ee7906001600160a01b0316338661250a565b5060025b8215610f16576040860151610f0a906001600160a01b0316338561250a565b610f1381614834565b90505b8115610f4757610f3b73940181a94a35a4569e4529a3cdfb74e38fd98631338461250a565b610f4481614834565b90505b610f568988888488888861257f565b9b50506001600160a01b038a1615610fd357604051636ae9e26760e11b81523360048201526001600160a01b03898116602483015260448201899052606482018890528b169063d5d3c4ce906084015f604051808303815f87803b158015610fbc575f80fd5b505af1158015610fce573d5f803e3d5ffd5b505050505b60408051888152602081018890526001600160a01b038a169133917ffea7a9a6e25cd0bbbfa80ce0c7646e61ee5e0551b3fdaaff0642e6f6adcc72e2910160405180910390a35050505050505050505092915050565b5f8080806110398587018761484c565b93509350935093507303a520b32c04bf3beef7beb72e919cf822ed34f16001600160a01b0316846001600160a01b0316036110a6573361107a848484612925565b6001600160a01b0316146110a157604051634b60273560e01b815260040160405180910390fd5b6110d9565b336110b2848484612946565b6001600160a01b0316146110d957604051634b60273560e01b815260040160405180910390fd5b5f8813156110fa576110f56001600160a01b038416338a61297c565b611116565b5f871315611116576111166001600160a01b038316338961297c565b5050505050505050565b6040805160018082528183019092526060915f91906020808301908036833701905050905088815f8151811061115857611158614820565b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092525f9181602001602082028036833701905050905088815f815181106111a7576111a7614820565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090506001815f815181106111e9576111e9614820565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090506002815f8151811061122b5761122b614820565b6020026020010181815250505f604051806080016040528086815260200185815260200184815260200183815250905060606112886040518060800160405280606081526020016060815260200160608152602001606081525090565b6112aa6040518060600160405280606081526020015f81526020015f81525090565b5f848f8f8f8f8f6040516020016112c6969594939291906148a5565b604051602081830303815290604052905084838386846040516020016112f095949392919061490c565b6040516020818303038152906040529950505050505050505050979650505050505050565b60405163133f757160e31b8152600481018290525f9081908190819073827922686190790b37229fd06084350e74485b72906399fbab889060240161018060405180830381865afa15801561136c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139091906149fe565b505050506001600160801b031660e08e015260029290920b60808d01526001600160a01b0392831660408d01529290911660208b015290945092506113d99150839050826144cc565b92506113f2866020015187604001518860800151612946565b6001600160a01b031680875260408051633850c7bd60e01b81529051633850c7bd9160048082019260c0929091908290030181865afa158015611437573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061145b9190614ae7565b5050506001600160a01b039283166101408a01525087516040805163ddca3f4360e01b8152905192975092169163ddca3f439160048083019260209291908290030181865afa1580156114b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114d49190614b5c565b62ffffff166060909601959095525090939092509050565b60405163133f757160e31b8152600481018390525f908190819081907303a520b32c04bf3beef7beb72e919cf822ed34f1906399fbab889060240161018060405180830381865afa158015611543573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115679190614b77565b909192939495969798999a509091929394959697989950909192509091509050508c6020018d6040018e6060018f60e001846001600160801b03166001600160801b0316815250849950859a508662ffffff1662ffffff16815250866001600160a01b03166001600160a01b0316815250866001600160a01b03166001600160a01b0316815250505050505050818161160091906144cc565b9250611619876020015188604001518960600151612925565b6001600160a01b031680885260408051633850c7bd60e01b81529051633850c7bd9160048082019260e0929091908290030181865afa15801561165e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116829190614bdf565b5050506001600160a01b039093166101408b015250945050841561170c57865f01516001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116df573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117039190614c6c565b60020b60808801525b5050935093915050565b60020b5f60ff82901d80830118620d89e881111561173f5761173f6345c3193d60e11b84612a04565b7001fffcb933bd6fad37aa2d162d1a5940016001821602600160801b18600282161561177b576ffff97272373d413259a46990580e213a0260801c5b600482161561179a576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b60088216156117b9576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b60108216156117d8576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156117f7576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615611816576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615611835576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615611855576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615611875576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615611895576ff3392b0822b70005940c7a398e4b70f30260801c5b6108008216156118b5576fe7159475a2c29b7443b29c7fa6e889d90260801c5b6110008216156118d5576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156118f5576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615611915576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615611935576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615611956576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615611976576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615611995576d2216e584f5fa1ea926041bedfe980260801c5b620800008216156119b2576b048a170391f7dc42444e8fa20260801c5b5f8413156119be575f19045b63ffffffff0160201c9392505050565b6040805160028082526060820183525f928392919060208301908036833701905050905061012c81600181518110611a0857611a08614820565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0385169063883bdbfd90611a4b908590600401614c87565b5f60405180830381865afa158015611a65573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a8c9190810190614d35565b50905061012c60030b81600181518110611aa857611aa8614820565b6020026020010151825f81518110611ac257611ac2614820565b6020026020010151611ad49190614df8565b611ade9190614e25565b949350505050565b5f825f190484118302158202611afa575f80fd5b5091020490565b60b581600160881b8110611b1a5760409190911b9060801c5b69010000000000000000008110611b365760209190911b9060401c5b650100000000008110611b4e5760109190911b9060201c5b63010000008110611b645760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b5f8080806001600160a01b038716731dc7a0f5336f52724b650e39174cfcbbedd67bf11480611bf057506001600160a01b03871673d74339e0f10fce96894916b93e5cc7de89c98272145b15611c7b57604051630852cd8d60e31b8152600481018790526001600160a01b038816906342966c68906024016020604051808303815f875af1158015611c39573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c5d9190614e58565b915073827922686190790b37229fd06084350e74485b729650600190505b6040805160a08101825287815260e08701516001600160801b03908116602083019081525f8385018181526060850191825242608086019081529551630624e65f60e11b81529451600486015291519092166024840152516044830152516064820152905160848201526001600160a01b03881690630c49ccbe9060a40160408051808303815f875af1158015611d14573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d389190614e6f565b50506040805160808101825287815230602082019081526001600160801b0382840181815260608401828152945163fc6f786560e01b81529351600485015291516001600160a01b039081166024850152915181166044840152925190921660648201529088169063fc6f78659060840160408051808303815f875af1158015611dc4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611de89190614e6f565b604051630852cd8d60e31b81526004810189905291955093506001600160a01b038816906342966c68906024015f604051808303815f87803b158015611e2c575f80fd5b505af1158015611e3e573d5f803e3d5ffd5b505050508015611ebc5760208501516001600160a01b031673940181a94a35a4569e4529a3cdfb74e38fd9863103611e8457611e7a8285614516565b93505f9150611ebc565b60408501516001600160a01b031673940181a94a35a4569e4529a3cdfb74e38fd9863103611ebc57611eb68284614516565b92505f91505b5093509350939050565b5f8080808064e8d4a510008c028b01611ee38b8b8b8b8b86612a13565b919650935091505f611f2d8c8c8c89611f0557611f00878e614516565b611f0f565b611f0f888e614540565b8a611f2357611f1e898e614540565b612b34565b611f1e888e614516565b6001600160801b03169050611f558f670de0b6b3a764000083611ae69092919063ffffffff16565b9650611f6c9050838d670de0b6b3a7640000611ae6565b93508383039250509850985098509850989350505050565b5f80855f03611f97575082905081612080565b8a515f0361206e57612054888a6060015162ffffff168b5f01516001600160a01b0316631a6865026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fec573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120109190614e91565b8c61014001518d61010001518e61012001518e61202d578a612037565b6120378e8c614540565b8f61204b576120468f8c614540565b61204d565b8a5b8e8e612bf3565b94506120648a8a8a888888612cbc565b9092509050612080565b61207a8a8a8a8e612ed5565b90925090505b995099975050505050505050565b5f808080806001600160a01b038916731dc7a0f5336f52724b650e39174cfcbbedd67bf114806120da57506001600160a01b03891673d74339e0f10fce96894916b93e5cc7de89c98272145b156120f7575073827922686190790b37229fd06084350e74485b72975b6020880151612110906001600160a01b03168a8961250a565b6040880151612129906001600160a01b03168a8861250a565b5f806001600160a01b038b167303a520b32c04bf3beef7beb72e919cf822ed34f1146122665773827922686190790b37229fd06084350e74485b726001600160a01b031663b5007d1f6040518061018001604052808d602001516001600160a01b031681526020018d604001516001600160a01b031681526020018d6080015160020b81526020018d60c0015160020b81526020018d60a0015160020b81526020018c81526020018b81526020015f81526020015f8152602001306001600160a01b031681526020014281526020015f6001600160a01b03168152506040518263ffffffff1660e01b81526004016122219190614eac565b6080604051808303815f875af115801561223d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122619190614f85565b61236b565b7303a520b32c04bf3beef7beb72e919cf822ed34f16001600160a01b031663883164566040518061016001604052808d602001516001600160a01b031681526020018d604001516001600160a01b031681526020018d6060015162ffffff1681526020018d60c0015160020b81526020018d60a0015160020b81526020018c81526020018b81526020015f81526020015f8152602001306001600160a01b03168152602001428152506040518263ffffffff1660e01b815260040161232b9190614fc0565b6080604051808303815f875af1158015612347573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061236b9190614f85565b9299506001600160801b03909116975092509050612389828a614540565b94506123958189614540565b93506001600160a01b0383161561247f5760405163095ea7b360e01b81526001600160a01b03841660048201526024810188905273827922686190790b37229fd06084350e74485b729063095ea7b3906044015f604051808303815f87803b1580156123ff575f80fd5b505af1158015612411573d5f803e3d5ffd5b505060405163140e25ad60e31b8152600481018a90526001600160a01b038616925063a0712d6891506024016020604051808303815f875af1158015612459573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061247d9190614e58565b505b505050945094509450949050565b5f8087156124cb578684116124a3575f846124a8565b868403875b9750935086156124c6576124c66001600160a01b0387168a8961297c565b6124fc565b8683116124d9575f836124de565b868303875b9750925086156124fc576124fc6001600160a01b0386168a8961297c565b509197909650945050505050565b816014528060345263095ea7b360601b5f5260205f604460105f875af13d1560015f51141716612576575f60345263095ea7b360601b5f525f38604460105f875af1508060345260205f604460105f875af13d1560015f5114171661257657633e3f8f735f526004601cfd5b5f603452505050565b6125aa6040518060800160405280606081526020016060815260200160608152602001606081525090565b846001600160401b038111156125c2576125c2613d3c565b6040519080825280602002602001820160405280156125eb578160200160208202803683370190505b508152846001600160401b0381111561260657612606613d3c565b60405190808252806020026020018201604052801561262f578160200160208202803683370190505b506020820152846001600160401b0381111561264d5761264d613d3c565b604051908082528060200260200182016040528015612676578160200160208202803683370190505b506040820152846001600160401b0381111561269457612694613d3c565b6040519080825280602002602001820160405280156126bd578160200160208202803683370190505b506060820152805180518991905f906126d8576126d8614820565b60200260200101906001600160a01b031690816001600160a01b0316815250508681602001515f8151811061270f5761270f614820565b602002602001018181525050600181604001515f8151811061273357612733614820565b602002602001018181525050600281606001515f8151811061275757612757614820565b6020908102919091010152600184156127f057602087015182518051600190811061278457612784614820565b60200260200101906001600160a01b031690816001600160a01b0316815250508482604001516001815181106127bc576127bc614820565b602002602001018181525050600182606001516001815181106127e1576127e1614820565b60209081029190910101525060025b8315612881576040870151825180518390811061280f5761280f614820565b60200260200101906001600160a01b031690816001600160a01b031681525050838260400151828151811061284657612846614820565b60200260200101818152505060018260600151828151811061286a5761286a614820565b602090810291909101015261287e81614834565b90505b82156129195773940181a94a35a4569e4529a3cdfb74e38fd98631825f015182815181106128b1576128b1614820565b60200260200101906001600160a01b031690816001600160a01b03168152505082826040015182815181106128e8576128e8614820565b60200260200101818152505060018260600151828151811061290c5761290c614820565b6020026020010181815250505b50979650505050505050565b5f611ade7333128a8fc17869897dce68ed026d694621f6fdfd8585856131a7565b5f611ade73ec8e5342b19977b4ef8892e02d8daecfa1315831735e7bb104d84c7cb9b682aac2f3d509f5f406809a86868661328e565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806129fe5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064015b60405180910390fd5b50505050565b815f528060020b60045260245ffd5b5f805f868910612a385760019250859150612a318960018887613351565b9050612b28565b878911612a4e57849150612a31895f8787613351565b5f612a5a8a8a8a6133ad565b90505f612a698b60018a6133f5565b90505f612a768289614516565b90505f612a8c89670de0b6b3a764000084611ae6565b905083811015612ae157600196505f612ab1858a670de0b6b3a7640000819003611ae6565b670de0b6b3a7640000019050612aca8286038483611ae6565b955050612ada8d6001878b613441565b9550612b23565b5f965086612af8858a670de0b6b3a7640000611ae6565b670de0b6b3a7640000039050612b118583038483611ae6565b965050612b208d5f888b613351565b94505b505050505b96509650969350505050565b5f836001600160a01b0316856001600160a01b03161115612b53579293925b846001600160a01b0316866001600160a01b031611612b8657612b7f612b7a86868661349b565b6134ff565b905061053c565b836001600160a01b0316866001600160a01b03161015612bdb575f612bac87868661349b565b90505f612bba878986613519565b9050612bd2818310612bcc57816134ff565b826134ff565b9250505061053c565b612be9612b7a868685613519565b9695505050505050565b5f8080805b6064811015612ca757612c0f8e8e8e8e8a8a613552565b9250886001600160a01b0316836001600160a01b031610612c4057612c368d8d8d8b6135fb565b9350505050612cae565b896001600160a01b0316836001600160a01b031611612c6557612c368d8d8d8a613637565b612c728e8e8e8e87613668565b9096509450612c878e8b8b8b8b8b8b8a6136e2565b909750955091508115612c9f57849350505050612cae565b600101612bf8565b5083925050505b9a9950505050505050505050565b5f805f86612ccf57876101800151612cd6565b8761016001515b90505f6001600160a01b038a167303a520b32c04bf3beef7beb72e919cf822ed34f114612d5c578989602001518a604001518b60800151604051602001612d4894939291906001600160a01b03948516815292841660208401529216604082015260029190910b606082015260800190565b604051602081830303815290604052612db9565b8989602001518a604001518b60600151604051602001612da994939291906001600160a01b03948516815292841660208401529216604082015262ffffff91909116606082015260800190565b6040516020818303038152906040525b90505f808a5f01516001600160a01b031663128acb08308c8c612ddb90615084565b88886040518663ffffffff1660e01b8152600401612dfd95949392919061509e565b60408051808303815f875af1158015612e18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e3c9190614e6f565b9150915089612e5357612e4e82615084565b612e5c565b612e5c81615084565b891115612e74576001600160a01b0384166101408c01525b89612e9157612e8282615084565b612e8c9089614516565b612e9b565b612e9b8289614540565b955089612eb157612eac8188614540565b612ec4565b612eba81615084565b612ec49088614516565b945050505050965096945050505050565b5f805f805f85806020019051810190612eee91906150d8565b9250925092505f87612f04578860400151612f0a565b88602001515b9050612f206001600160a01b038216858561250a565b5f80856001600160a01b031684604051612f3a9190615167565b5f604051808303815f865af19150503d805f8114612f73576040519150601f19603f3d011682016040523d82523d5f602084013e612f78565b606091505b5091509150818190612f9d5760405162461bcd60e51b81526004016129f59190615182565b507303a520b32c04bf3beef7beb72e919cf822ed34f0196001600160a01b038d1601613043578a5f01516001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015613002573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130269190614bdf565b5050506001600160a01b039093166101408f0152506130bd915050565b8a5f01516001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160c060405180830381865afa158015613082573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130a69190614ae7565b5050506001600160a01b039092166101408e015250505b60208b01516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613105573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131299190614e58565b6040808d015190516370a0823160e01b81523060048201529199506001600160a01b0316906370a0823190602401602060405180830381865afa158015613172573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131969190614e58565b965050505050505094509492505050565b5f826001600160a01b0316846001600160a01b0316106131c5575f80fd5b604080516001600160a01b03808716602083015285169181019190915262ffffff83166060820152859060800160408051601f1981840301815290829052805160209182012061326d939290917fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5491016001600160f81b0319815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b60408051601f19818403018152919052805160209091012095945050505050565b5f826001600160a01b0316846001600160a01b0316106132ac575f80fd5b604080516001600160a01b038087166020830152851691810191909152600283900b6060820152612be99087906080016040516020818303038152906040528051906020012087604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b5f6001600160801b03851115613365575f80fd5b5f61337d670de0b6b3a7640000848103908690611ae6565b90508461339b5761339681600160c01b6002890a613847565b612be9565b612be9816002880a600160c01b613847565b5f6001600160801b038411156133c1575f80fd5b8284035f836002870a816133d7576133d7614446565b0460028702869003039050612be982670de0b6b3a764000083611ae6565b5f826134235761341e8261340e6002600160601b615274565b613419600288615274565b613847565b611ade565b611ade82613432600287615274565b6134196002600160601b615274565b5f6001600160801b03851115613455575f80fd5b5f846134725761346d846002880a600160c01b613847565b613484565b61348484600160c01b6002890a613847565b9050612be981670de0b6b3a7640000858103611ae6565b5f826001600160a01b0316846001600160a01b031611156134ba579192915b5f6134dc856001600160a01b0316856001600160a01b0316600160601b6138e3565b90506134f483828787036001600160a01b03166138e3565b9150505b9392505050565b806001600160801b0381168114613514575f80fd5b919050565b5f826001600160a01b0316846001600160a01b03161115613538579192915b611ade82600160601b8686036001600160a01b03166138e3565b5f8061356584620f424089810390611ae6565b90505f8089156135a35761357c8789856001613989565b6001600160a01b031691506135938789875f613a78565b6001600160a01b031690506135d3565b6135af8789875f613989565b6001600160a01b031691506135c78789856001613a78565b6001600160a01b031690505b896135f0576135eb8183016002808206151591040190565b612cae565b600282820104612cae565b5f8061360e83620f424088810390613b56565b90505f61361e8587846001613989565b905061362c8186885f613b79565b979650505050505050565b5f8061364a83620f424088810390613b56565b90505f61365a8587846001613a78565b905061362c8582885f613bc5565b5f8086156136a6575f61367e8486886001613bc5565b905061369081620f4240898103613b56565b925061369e8486885f613b79565b9150506136d8565b5f6136b48585886001613b79565b90506136c681620f4240898103613b56565b92506136d48585885f613bc5565b9150505b9550959350505050565b5f805f805f8c1561371d57613707868c8a8d116136ff575f61349b565b8a8d0361349b565b91506137168c87898c01613519565b9050613749565b61372a868c898d0161349b565b91506137468c878a8c1161373e575f613519565b8a8c03613519565b90505b5f8183106137695761376482670de0b6b3a764000085611ae6565b61377c565b61377c83670de0b6b3a764000084611ae6565b670de0b6b3a7640000039050620f42408110828410156137e6575f6137ac8f8a6137a5886134ff565b6001613b79565b90508f6137be57808c039a508a6137df565b8b81116137d7576137d28a6009600a611ae6565b6137db565b8b81035b9950895b5050613832565b5f6137fc898f6137f5876134ff565b6001613bc5565b90508f613828578c811161381c576138178a6009600a611ae6565b613820565b8c81035b99508961382f565b808d039a508a5b50505b9e989d50969b50969950505050505050505050565b5f838302815f1985870982811083820303915050808411613866575f80fd5b805f03613878575082900490506134f8565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f80805f19858709858702925082811083820303915050805f03613917575f841161390c575f80fd5b5082900490506134f8565b808411613922575f80fd5b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f825f03613998575083611ade565b600160601b600160e01b03606085901b168215613a32576001600160a01b038616848102908582816139cc576139cc614446565b04036139fc578181018281106139fa576139f083896001600160a01b031683613c7d565b9350505050611ade565b505b50613a2a8185613a156001600160a01b038a1683615282565b613a1f9190614516565b808204910615150190565b915050611ade565b6001600160a01b038616848102908582041481831116613a595763f5c787f15f526004601cfd5b8082036139f0613a73846001600160a01b038b1684613c7d565b613cad565b5f8115613adb575f6001600160a01b03841115613aac57613aa784600160601b876001600160801b0316613847565b613ac3565b613ac36001600160801b038616606086901b615282565b9050613a2a613a73826001600160a01b038916614516565b5f6001600160a01b03841115613b0857613b0384600160601b876001600160801b0316613c7d565b613b25565b613b25606085901b6001600160801b038716808204910615150190565b9050806001600160a01b03871611613b4457634323a5555f526004601cfd5b6001600160a01b038616039050611ade565b5f825f190484118302158202613b6a575f80fd5b50910281810615159190040190565b5f6001600160a01b038481169086160360ff81901d90810118600160601b6001600160801b038516613bac818484613847565b9350845f83858409111684019350505050949350505050565b5f836001600160a01b0316856001600160a01b03161115613be4579293925b6001600160a01b038516613bfe5762bfc9215f526004601cfd5b600160601b600160e01b03606084901b166001600160a01b038686031683613c5157866001600160a01b0316613c3e8383896001600160a01b0316613847565b81613c4b57613c4b614446565b0461362c565b61362c613c688383896001600160a01b0316613c7d565b886001600160a01b0316808204910615150190565b5f613c89848484613847565b90508180613c9957613c99614446565b838509156134f857600101806134f8575f80fd5b806001600160a01b0381168114613514576135146393dafdf160e01b805f5260045ffd5b604080516101a0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081019190915290565b634e487b7160e01b5f52604160045260245ffd5b6040516101a081016001600160401b0381118282101715613d7357613d73613d3c565b60405290565b604051608081016001600160401b0381118282101715613d7357613d73613d3c565b604051601f8201601f191681016001600160401b0381118282101715613dc357613dc3613d3c565b604052919050565b6001600160a01b0381168114613ddf575f80fd5b50565b803561351481613dcb565b62ffffff81168114613ddf575f80fd5b803561351481613ded565b8060020b8114613ddf575f80fd5b803561351481613e08565b6001600160801b0381168114613ddf575f80fd5b803561351481613e21565b5f6101a08284031215613e51575f80fd5b613e59613d50565b613e6283613de2565b8152613e7060208401613de2565b6020820152613e8160408401613de2565b6040820152613e9260608401613dfd565b6060820152613ea360808401613e16565b6080820152613eb460a08401613e16565b60a0820152613ec560c08401613e16565b60c0820152613ed660e08401613e35565b60e0820152610100613ee9818501613de2565b90820152610120613efb848201613de2565b9082015261014083810135908201526101608084013590820152610180928301359281019290925250919050565b5f8083601f840112613f39575f80fd5b5081356001600160401b03811115613f4f575f80fd5b602083019150836020828501011115613f66575f80fd5b9250929050565b5f805f805f805f60c0888a031215613f83575f80fd5b8735613f8e81613dcb565b96506020880135613f9e81613dcb565b9550604088013594506060880135613fb581613e08565b93506080880135613fc581613e08565b925060a08801356001600160401b03811115613fdf575f80fd5b613feb8a828b01613f29565b989b979a50959850939692959293505050565b5f805f805f60808688031215614012575f80fd5b853561401d81613dcb565b9450602086013561402d81613dcb565b93506040860135925060608601356001600160401b0381111561404e575f80fd5b61405a88828901613f29565b969995985093965092949392505050565b5f6020828403121561407b575f80fd5b81356134f881613dcb565b5f805f805f60a0868803121561409a575f80fd5b85356140a581613dcb565b94506020860135935060408601356140bc81613e08565b925060608601356140cc81613e08565b915060808601356140dc81613dcb565b809150509295509295909350565b81516001600160a01b031681526101a08101602083015161411660208401826001600160a01b03169052565b50604083015161413160408401826001600160a01b03169052565b506060830151614148606084018262ffffff169052565b50608083015161415d608084018260020b9052565b5060a083015161417260a084018260020b9052565b5060c083015161418760c084018260020b9052565b5060e08301516141a260e08401826001600160801b03169052565b50610100838101516001600160a01b038116848301525050610120838101516001600160a01b0381168483015250506101408381015190830152610160808401519083015261018092830151929091019190915290565b5f805f6060848603121561420b575f80fd5b505081359360208301359350604090920135919050565b5f805f60608486031215614234575f80fd5b833561423f81613dcb565b9250602084013561424f81613dcb565b9150604084013561425f81613dcb565b809150509250925092565b5f806020838503121561427b575f80fd5b82356001600160401b03811115614290575f80fd5b61429c85828601613f29565b90969095509350505050565b5f815180845260208085019450602084015f5b838110156142d7578151875295820195908201906001016142bb565b509495945050505050565b8051608080845281519084018190525f916020919082019060a0860190845b818110156143265783516001600160a01b031683529284019291840191600101614301565b505060208501519250858103602087015261434181846142a8565b925050506040830151848203604086015261435c82826142a8565b9150506060830151848203606086015261053c82826142a8565b602081525f6134f860208301846142e2565b5f805f806060858703121561439b575f80fd5b843593506020850135925060408501356001600160401b038111156143be575f80fd5b6143ca87828801613f29565b95989497509550505050565b5f5b838110156143f05781810151838201526020016143d8565b50505f910152565b5f815180845261440f8160208601602086016143d6565b601f01601f19169290920160200192915050565b6001600160a01b03831681526040602082018190525f90611ade908301846143f8565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f8160020b8360020b8061448457614484614446565b627fffff1982145f198214161561449d5761449d61445a565b90059392505050565b5f8260020b8260020b028060020b91508082146144c5576144c561445a565b5092915050565b600282810b9082900b03627fffff198112627fffff8213171561042c5761042c61445a565b600281810b9083900b01627fffff8113627fffff198212171561042c5761042c61445a565b8082018082111561042c5761042c61445a565b808202811582820484141761042c5761042c61445a565b8181038181111561042c5761042c61445a565b80518015158114613514575f80fd5b5f60208284031215614572575f80fd5b6134f882614553565b5f6020828403121561458b575f80fd5b81516134f881613dcb565b5f6001600160401b038211156145ae576145ae613d3c565b5060051b60200190565b5f82601f8301126145c7575f80fd5b813560206145dc6145d783614596565b613d9b565b8083825260208201915060208460051b8701019350868411156145fd575f80fd5b602086015b8481101561462257803561461581613dcb565b8352918301918301614602565b509695505050505050565b5f82601f83011261463c575f80fd5b8135602061464c6145d783614596565b8083825260208201915060208460051b87010193508684111561466d575f80fd5b602086015b848110156146225780358352918301918301614672565b5f6001600160401b038211156146a1576146a1613d3c565b50601f01601f191660200190565b5f82601f8301126146be575f80fd5b81356146cc6145d782614689565b8181528460208386010111156146e0575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f60a08688031215614710575f80fd5b85356001600160401b0380821115614726575f80fd5b908701906080828a031215614739575f80fd5b614741613d79565b82358281111561474f575f80fd5b61475b8b8286016145b8565b82525060208301358281111561476f575f80fd5b61477b8b82860161462d565b602083015250604083013582811115614792575f80fd5b61479e8b82860161462d565b6040830152506060830135828111156147b5575f80fd5b6147c18b82860161462d565b60608301525096506147d560208901613de2565b95506147e360408901613e16565b94506147f160608901613e16565b93506080880135915080821115614806575f80fd5b50614813888289016146af565b9150509295509295909350565b634e487b7160e01b5f52603260045260245ffd5b5f600182016148455761484561445a565b5060010190565b5f805f806080858703121561485f575f80fd5b843561486a81613dcb565b9350602085013561487a81613dcb565b9250604085013561488a81613dcb565b9150606085013561489a81613ded565b939692955090935050565b60a081525f6148b760a08301896142e2565b60018060a01b03881660208401528660020b60408401528560020b60608401528281036080840152838152838560208301375f602085830101526020601f19601f860116820101915050979650505050505050565b60a081525f61491e60a08301886142e2565b60208382038185015261493182896142e2565b9150604084830360408601526060830188516060855281815180845260808701915085830193505f92505b8083101561498e57835180516001600160a01b031683528601518683015292850192600192909201919084019061495c565b50848b01518587015260408b0151604087015287810360608901526149b3818b6143f8565b9550505050505082810360808401526149cc81856143f8565b98975050505050505050565b80516bffffffffffffffffffffffff81168114613514575f80fd5b805161351481613e21565b5f805f805f805f805f805f806101808d8f031215614a1a575f80fd5b614a238d6149d8565b9b5060208d0151614a3381613dcb565b60408e0151909b50614a4481613dcb565b60608e0151909a50614a5581613dcb565b60808e0151909950614a6681613e08565b60a08e0151909850614a7781613e08565b60c08e0151909750614a8881613e08565b9550614a9660e08e016149f3565b94506101008d015193506101208d01519250614ab56101408e016149f3565b9150614ac46101608e016149f3565b90509295989b509295989b509295989b565b805161ffff81168114613514575f80fd5b5f805f805f8060c08789031215614afc575f80fd5b8651614b0781613dcb565b6020880151909650614b1881613e08565b9450614b2660408801614ad6565b9350614b3460608801614ad6565b9250614b4260808801614ad6565b9150614b5060a08801614553565b90509295509295509295565b5f60208284031215614b6c575f80fd5b81516134f881613ded565b5f805f805f805f805f805f806101808d8f031215614b93575f80fd5b614b9c8d6149d8565b9b5060208d0151614bac81613dcb565b60408e0151909b50614bbd81613dcb565b60608e0151909a50614bce81613dcb565b60808e0151909950614a6681613ded565b5f805f805f805f60e0888a031215614bf5575f80fd5b8751614c0081613dcb565b6020890151909750614c1181613e08565b9550614c1f60408901614ad6565b9450614c2d60608901614ad6565b9350614c3b60808901614ad6565b925060a088015160ff81168114614c50575f80fd5b9150614c5e60c08901614553565b905092959891949750929550565b5f60208284031215614c7c575f80fd5b81516134f881613e08565b602080825282518282018190525f9190848201906040850190845b81811015614cc457835163ffffffff1683529284019291840191600101614ca2565b50909695505050505050565b5f82601f830112614cdf575f80fd5b81516020614cef6145d783614596565b8083825260208201915060208460051b870101935086841115614d10575f80fd5b602086015b84811015614622578051614d2881613dcb565b8352918301918301614d15565b5f8060408385031215614d46575f80fd5b82516001600160401b0380821115614d5c575f80fd5b818501915085601f830112614d6f575f80fd5b81516020614d7f6145d783614596565b82815260059290921b84018101918181019089841115614d9d575f80fd5b948201945b83861015614dc95785518060060b8114614dba575f80fd5b82529482019490820190614da2565b91880151919650909350505080821115614de1575f80fd5b50614dee85828601614cd0565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561042c5761042c61445a565b5f8160060b8360060b80614e3b57614e3b614446565b667fffffffffffff1982145f198214161561449d5761449d61445a565b5f60208284031215614e68575f80fd5b5051919050565b5f8060408385031215614e80575f80fd5b505080516020909101519092909150565b5f60208284031215614ea1575f80fd5b81516134f881613e21565b81516001600160a01b0316815261018081016020830151614ed860208401826001600160a01b03169052565b506040830151614eed604084018260020b9052565b506060830151614f02606084018260020b9052565b506080830151614f17608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151614f5d828501826001600160a01b03169052565b50506101408381015190830152610160928301516001600160a01b0316929091019190915290565b5f805f8060808587031215614f98575f80fd5b845193506020850151614faa81613e21565b6040860151606090960151949790965092505050565b81516001600160a01b0316815261016081016020830151614fec60208401826001600160a01b03169052565b506040830151615003604084018262ffffff169052565b506060830151615018606084018260020b9052565b50608083015161502d608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151615073828501826001600160a01b03169052565b505061014092830151919092015290565b5f600160ff1b82016150985761509861445a565b505f0390565b6001600160a01b0386811682528515156020830152604082018590528316606082015260a0608082018190525f9061362c908301846143f8565b5f805f606084860312156150ea575f80fd5b83516150f581613dcb565b6020850151604086015191945092506001600160401b03811115615117575f80fd5b8401601f81018613615127575f80fd5b80516151356145d782614689565b818152876020838501011115615149575f80fd5b61515a8260208301602086016143d6565b8093505050509250925092565b5f82516151788184602087016143d6565b9190910192915050565b602081525f6134f860208301846143f8565b600181815b808511156151ce57815f19048211156151b4576151b461445a565b808516156151c157918102915b93841c9390800290615199565b509250929050565b5f826151e45750600161042c565b816151f057505f61042c565b816001811461520657600281146152105761522c565b600191505061042c565b60ff8411156152215761522161445a565b50506001821b61042c565b5060208310610133831016604e8410600b841016171561524f575081810a61042c565b6152598383615194565b805f190482111561526c5761526c61445a565b029392505050565b5f6134f860ff8416836151d6565b5f8261529057615290614446565b50049056fea2646970667358221220a1e3ced5eb9000142ed04bf1577a9483a84c813afc2cb9d8600b6b25c387a3a064736f6c63430008160033000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000d99a8cec7e20000
Deployed Bytecode
0x6080604052600436106100dc575f3560e01c80635a1c94c21161007c578063830e703f11610057578063830e703f1461036a5780639583aef014610389578063a129568d146103bd578063fa461e33146103e9575f80fd5b80635a1c94c2146102e55780635f1bbfc21461030457806370cd8d2714610337575f80fd5b806326473274116100b757806326473274146101a6578063304e0270146101f25780634d3b3985146102335780634df8604a1461025f575f80fd5b80630ace53951461011b5780630f59b86a1461014f578063150b7a021461016e575f80fd5b36610117573373827922686190790b37229fd06084350e74485b721461011557604051637b7524c960e01b815260040160405180910390fd5b005b5f80fd5b348015610126575f80fd5b5061013a610135366004613e40565b610408565b60405190151581526020015b60405180910390f35b34801561015a575f80fd5b50610115610169366004613f6d565b610432565b348015610179575f80fd5b5061018d610188366004613ffe565b610533565b6040516001600160e01b03199091168152602001610146565b3480156101b1575f80fd5b506101da6101c036600461406b565b60036020525f90815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610146565b3480156101fd575f80fd5b506102257f0000000000000000000000000000000000000000000000000d99a8cec7e2000081565b604051908152602001610146565b34801561023e575f80fd5b5061025261024d366004614086565b610545565b60405161014691906140ea565b34801561026a575f80fd5b506102b261027936600461406b565b60016020525f90815260409020546001600160401b0380821691600160401b8104821691600160801b8204811691600160c01b90041684565b604080516001600160401b0395861681529385166020850152918416918301919091529091166060820152608001610146565b3480156102f0575f80fd5b506101156102ff3660046141f9565b610707565b34801561030f575f80fd5b506102257f00000000000000000000000000000000000000000000000002c68af0bb14000081565b348015610342575f80fd5b506102257f000000000000000000000000000000000000000000000000002386f26fc1000081565b348015610375575f80fd5b50610115610384366004614222565b6109ad565b348015610394575f80fd5b506101da6103a336600461406b565b60026020525f90815260409020546001600160a01b031681565b3480156103c8575f80fd5b506103dc6103d736600461426a565b610b72565b6040516101469190614376565b3480156103f4575f80fd5b50610115610403366004614388565b611029565b5f81610160015182610140015111158061042c575081610180015182610140015110155b92915050565b5f546001600160a01b03161561045b5760405163b5dfd9e560e01b815260040160405180910390fd5b6001600160a01b038781165f908152600260205260409020541633146104945760405163fde6c87960e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b0389161781556104bd87873388888888611120565b60405162b9252f60e41b81529091506001600160a01b03891690630b9252f0906104ed9030908590600401614423565b5f604051808303815f87803b158015610504575f80fd5b505af1158015610516573d5f803e3d5ffd5b50505f80546001600160a01b031916905550505050505050505050565b630a85bd0160e11b5b95945050505050565b61054d613cd1565b5f806001600160a01b0388167303a520b32c04bf3beef7beb72e919cf822ed34f1146105825761057d8388611315565b610595565b61059583888760020b8960020b146114ec565b915091508460020b8660020b036106125760808301516105b5818461446e565b6105bf91906144a6565b60808401519092506105d28160026144a6565b6105dc908361446e565b6105e691906144a6565b6105f090836144cc565b60020b60c084018190526106059082906144f1565b60020b60a0840152610625565b600285810b60a085015286900b60c08401525b6106328360c00151611716565b6001600160a01b031661010084015260a083015161064f90611716565b6001600160a01b031661012084015282515f9061066b906119ce565b90505f61067782611716565b6001600160a01b038781165f90815260016020526040902054911691506106b8908290600160401b90046001600160401b0316670de0b6b3a7640000611ae6565b6101608601526001600160a01b0386165f908152600160205260409020546106f39082906001600160401b0316670de0b6b3a7640000611ae6565b610180860152509298975050505050505050565b5f546001600160a01b0316156107305760405163b5dfd9e560e01b815260040160405180910390fd5b335f908152600160209081526040808320815160808101835290546001600160401b038082168352600160401b8204811694830194909452600160801b8104841692820192909252600160c01b9091049091166060820152906107b561079e86670de0b6b3a7640000614516565b6107b090670de0b6b3a7640000614529565b611b01565b60608301519091506001600160401b0316156108465781604001516001600160401b03168411806107fa5750815f01516001600160401b0316816001600160401b0316115b80610811575081606001516001600160401b031683105b806108235750670de0b6b3a764000083115b1561084157604051632a9ffab760e21b815260040160405180910390fd5b6108ed565b7f00000000000000000000000000000000000000000000000002c68af0bb14000084118061089357507f000000000000000000000000000000000000000000000000002386f26fc1000085115b806108bd57507f0000000000000000000000000000000000000000000000000d99a8cec7e2000083105b806108cf5750670de0b6b3a764000083115b156108ed57604051632a9ffab760e21b815260040160405180910390fd5b6001600160401b0380851660408401528316606083015261091961079e86670de0b6b3a7640000614540565b6001600160401b0390811660208085019182529282168452335f9081526001909352604092839020845181549251948601516060909601518416600160c01b026001600160c01b03968516600160801b02969096166001600160801b03958516600160401b026fffffffffffffffffffffffffffffffff199094169190941617919091179290921617919091179055505050565b5f546001600160a01b0316156109d65760405163b5dfd9e560e01b815260040160405180910390fd5b604051630972932760e21b81526001600160a01b038416600482015273da14fdd72345c4d2511357214c5b89a919768e59906325ca4c9c90602401602060405180830381865afa158015610a2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a509190614562565b610a6d57604051630ea8370b60e41b815260040160405180910390fd5b826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610aaa573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ace919061457b565b6001600160a01b0316336001600160a01b031614610aff576040516312272fd360e11b815260040160405180910390fd5b6001600160a01b038084165f81815260026020908152604080832080548689166001600160a01b0319918216811790925560039093528184208054968816969093168617909255519092917f343ef5cc595144359c9db657cd7fcef6ecc88d06d17651a8292e553ab73b1c7091a4505050565b610b9d6040518060800160405280606081526020016060815260200160608152602001606081525090565b5f546001600160a01b03163314610bc75760405163f3f6425d60e01b815260040160405180910390fd5b335f908152600360205260408120546001600160a01b0316906060908080610bed613cd1565b5f610c196040518060800160405280606081526020016060815260200160608152602001606081525090565b5f80610c278c8e018e6146fc565b84518051919e509398509396509094509250905f90610c4857610c48614820565b6020026020010151975082602001515f81518110610c6857610c68614820565b60200260200101519650610c7f8888848488610545565b94505050506001600160a01b03871615610d135760c082015160a083015160405163bd5d93c960e01b81523360048201526001600160a01b03888116602483015260448201889052600293840b60648301529190920b608483015288169063bd5d93c99060a4015f6040518083038186803b158015610cfc575f80fd5b505afa158015610d0e573d5f803e3d5ffd5b505050505b610d1c82610408565b15610d3a57604051633a8bf65960e01b815260040160405180910390fd5b5f805f610d48888887611ba5565b6001600160a01b038088165f9081526001602052604081205460608b01516101408c01516101008d01516101208e0151989b5096995094975091958695869586958695610dc0956001600160401b03600160c01b820481169662ffffff90931695600160801b90920416939290811691168e8e611ec6565b94509450945094509450610ddb8e8e8c878787878f8f611f84565b9098509650610de98a610408565b15610e0757604051633a8bf65960e01b815260040160405180910390fd5b5f610e148e8c8b8b61208e565b929e509a50909850905085811015610e3f5760405163bb55fd2760e01b815260040160405180910390fd5b610e568a86868e602001518f604001518e8e61248d565b60405163095ea7b360e01b8152336004820152602481018f9052919a509850600196506001600160a01b038f16955063095ea7b394506044019250610e99915050565b5f604051808303815f87803b158015610eb0575f80fd5b505af1158015610ec2573d5f803e3d5ffd5b505050505f841115610eeb576020860151610ee7906001600160a01b0316338661250a565b5060025b8215610f16576040860151610f0a906001600160a01b0316338561250a565b610f1381614834565b90505b8115610f4757610f3b73940181a94a35a4569e4529a3cdfb74e38fd98631338461250a565b610f4481614834565b90505b610f568988888488888861257f565b9b50506001600160a01b038a1615610fd357604051636ae9e26760e11b81523360048201526001600160a01b03898116602483015260448201899052606482018890528b169063d5d3c4ce906084015f604051808303815f87803b158015610fbc575f80fd5b505af1158015610fce573d5f803e3d5ffd5b505050505b60408051888152602081018890526001600160a01b038a169133917ffea7a9a6e25cd0bbbfa80ce0c7646e61ee5e0551b3fdaaff0642e6f6adcc72e2910160405180910390a35050505050505050505092915050565b5f8080806110398587018761484c565b93509350935093507303a520b32c04bf3beef7beb72e919cf822ed34f16001600160a01b0316846001600160a01b0316036110a6573361107a848484612925565b6001600160a01b0316146110a157604051634b60273560e01b815260040160405180910390fd5b6110d9565b336110b2848484612946565b6001600160a01b0316146110d957604051634b60273560e01b815260040160405180910390fd5b5f8813156110fa576110f56001600160a01b038416338a61297c565b611116565b5f871315611116576111166001600160a01b038316338961297c565b5050505050505050565b6040805160018082528183019092526060915f91906020808301908036833701905050905088815f8151811061115857611158614820565b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092525f9181602001602082028036833701905050905088815f815181106111a7576111a7614820565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090506001815f815181106111e9576111e9614820565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090506002815f8151811061122b5761122b614820565b6020026020010181815250505f604051806080016040528086815260200185815260200184815260200183815250905060606112886040518060800160405280606081526020016060815260200160608152602001606081525090565b6112aa6040518060600160405280606081526020015f81526020015f81525090565b5f848f8f8f8f8f6040516020016112c6969594939291906148a5565b604051602081830303815290604052905084838386846040516020016112f095949392919061490c565b6040516020818303038152906040529950505050505050505050979650505050505050565b60405163133f757160e31b8152600481018290525f9081908190819073827922686190790b37229fd06084350e74485b72906399fbab889060240161018060405180830381865afa15801561136c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139091906149fe565b505050506001600160801b031660e08e015260029290920b60808d01526001600160a01b0392831660408d01529290911660208b015290945092506113d99150839050826144cc565b92506113f2866020015187604001518860800151612946565b6001600160a01b031680875260408051633850c7bd60e01b81529051633850c7bd9160048082019260c0929091908290030181865afa158015611437573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061145b9190614ae7565b5050506001600160a01b039283166101408a01525087516040805163ddca3f4360e01b8152905192975092169163ddca3f439160048083019260209291908290030181865afa1580156114b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114d49190614b5c565b62ffffff166060909601959095525090939092509050565b60405163133f757160e31b8152600481018390525f908190819081907303a520b32c04bf3beef7beb72e919cf822ed34f1906399fbab889060240161018060405180830381865afa158015611543573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115679190614b77565b909192939495969798999a509091929394959697989950909192509091509050508c6020018d6040018e6060018f60e001846001600160801b03166001600160801b0316815250849950859a508662ffffff1662ffffff16815250866001600160a01b03166001600160a01b0316815250866001600160a01b03166001600160a01b0316815250505050505050818161160091906144cc565b9250611619876020015188604001518960600151612925565b6001600160a01b031680885260408051633850c7bd60e01b81529051633850c7bd9160048082019260e0929091908290030181865afa15801561165e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116829190614bdf565b5050506001600160a01b039093166101408b015250945050841561170c57865f01516001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116df573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117039190614c6c565b60020b60808801525b5050935093915050565b60020b5f60ff82901d80830118620d89e881111561173f5761173f6345c3193d60e11b84612a04565b7001fffcb933bd6fad37aa2d162d1a5940016001821602600160801b18600282161561177b576ffff97272373d413259a46990580e213a0260801c5b600482161561179a576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b60088216156117b9576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b60108216156117d8576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156117f7576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615611816576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615611835576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615611855576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615611875576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615611895576ff3392b0822b70005940c7a398e4b70f30260801c5b6108008216156118b5576fe7159475a2c29b7443b29c7fa6e889d90260801c5b6110008216156118d5576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156118f5576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615611915576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615611935576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615611956576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615611976576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615611995576d2216e584f5fa1ea926041bedfe980260801c5b620800008216156119b2576b048a170391f7dc42444e8fa20260801c5b5f8413156119be575f19045b63ffffffff0160201c9392505050565b6040805160028082526060820183525f928392919060208301908036833701905050905061012c81600181518110611a0857611a08614820565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0385169063883bdbfd90611a4b908590600401614c87565b5f60405180830381865afa158015611a65573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a8c9190810190614d35565b50905061012c60030b81600181518110611aa857611aa8614820565b6020026020010151825f81518110611ac257611ac2614820565b6020026020010151611ad49190614df8565b611ade9190614e25565b949350505050565b5f825f190484118302158202611afa575f80fd5b5091020490565b60b581600160881b8110611b1a5760409190911b9060801c5b69010000000000000000008110611b365760209190911b9060401c5b650100000000008110611b4e5760109190911b9060201c5b63010000008110611b645760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b5f8080806001600160a01b038716731dc7a0f5336f52724b650e39174cfcbbedd67bf11480611bf057506001600160a01b03871673d74339e0f10fce96894916b93e5cc7de89c98272145b15611c7b57604051630852cd8d60e31b8152600481018790526001600160a01b038816906342966c68906024016020604051808303815f875af1158015611c39573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c5d9190614e58565b915073827922686190790b37229fd06084350e74485b729650600190505b6040805160a08101825287815260e08701516001600160801b03908116602083019081525f8385018181526060850191825242608086019081529551630624e65f60e11b81529451600486015291519092166024840152516044830152516064820152905160848201526001600160a01b03881690630c49ccbe9060a40160408051808303815f875af1158015611d14573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d389190614e6f565b50506040805160808101825287815230602082019081526001600160801b0382840181815260608401828152945163fc6f786560e01b81529351600485015291516001600160a01b039081166024850152915181166044840152925190921660648201529088169063fc6f78659060840160408051808303815f875af1158015611dc4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611de89190614e6f565b604051630852cd8d60e31b81526004810189905291955093506001600160a01b038816906342966c68906024015f604051808303815f87803b158015611e2c575f80fd5b505af1158015611e3e573d5f803e3d5ffd5b505050508015611ebc5760208501516001600160a01b031673940181a94a35a4569e4529a3cdfb74e38fd9863103611e8457611e7a8285614516565b93505f9150611ebc565b60408501516001600160a01b031673940181a94a35a4569e4529a3cdfb74e38fd9863103611ebc57611eb68284614516565b92505f91505b5093509350939050565b5f8080808064e8d4a510008c028b01611ee38b8b8b8b8b86612a13565b919650935091505f611f2d8c8c8c89611f0557611f00878e614516565b611f0f565b611f0f888e614540565b8a611f2357611f1e898e614540565b612b34565b611f1e888e614516565b6001600160801b03169050611f558f670de0b6b3a764000083611ae69092919063ffffffff16565b9650611f6c9050838d670de0b6b3a7640000611ae6565b93508383039250509850985098509850989350505050565b5f80855f03611f97575082905081612080565b8a515f0361206e57612054888a6060015162ffffff168b5f01516001600160a01b0316631a6865026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fec573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120109190614e91565b8c61014001518d61010001518e61012001518e61202d578a612037565b6120378e8c614540565b8f61204b576120468f8c614540565b61204d565b8a5b8e8e612bf3565b94506120648a8a8a888888612cbc565b9092509050612080565b61207a8a8a8a8e612ed5565b90925090505b995099975050505050505050565b5f808080806001600160a01b038916731dc7a0f5336f52724b650e39174cfcbbedd67bf114806120da57506001600160a01b03891673d74339e0f10fce96894916b93e5cc7de89c98272145b156120f7575073827922686190790b37229fd06084350e74485b72975b6020880151612110906001600160a01b03168a8961250a565b6040880151612129906001600160a01b03168a8861250a565b5f806001600160a01b038b167303a520b32c04bf3beef7beb72e919cf822ed34f1146122665773827922686190790b37229fd06084350e74485b726001600160a01b031663b5007d1f6040518061018001604052808d602001516001600160a01b031681526020018d604001516001600160a01b031681526020018d6080015160020b81526020018d60c0015160020b81526020018d60a0015160020b81526020018c81526020018b81526020015f81526020015f8152602001306001600160a01b031681526020014281526020015f6001600160a01b03168152506040518263ffffffff1660e01b81526004016122219190614eac565b6080604051808303815f875af115801561223d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122619190614f85565b61236b565b7303a520b32c04bf3beef7beb72e919cf822ed34f16001600160a01b031663883164566040518061016001604052808d602001516001600160a01b031681526020018d604001516001600160a01b031681526020018d6060015162ffffff1681526020018d60c0015160020b81526020018d60a0015160020b81526020018c81526020018b81526020015f81526020015f8152602001306001600160a01b03168152602001428152506040518263ffffffff1660e01b815260040161232b9190614fc0565b6080604051808303815f875af1158015612347573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061236b9190614f85565b9299506001600160801b03909116975092509050612389828a614540565b94506123958189614540565b93506001600160a01b0383161561247f5760405163095ea7b360e01b81526001600160a01b03841660048201526024810188905273827922686190790b37229fd06084350e74485b729063095ea7b3906044015f604051808303815f87803b1580156123ff575f80fd5b505af1158015612411573d5f803e3d5ffd5b505060405163140e25ad60e31b8152600481018a90526001600160a01b038616925063a0712d6891506024016020604051808303815f875af1158015612459573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061247d9190614e58565b505b505050945094509450949050565b5f8087156124cb578684116124a3575f846124a8565b868403875b9750935086156124c6576124c66001600160a01b0387168a8961297c565b6124fc565b8683116124d9575f836124de565b868303875b9750925086156124fc576124fc6001600160a01b0386168a8961297c565b509197909650945050505050565b816014528060345263095ea7b360601b5f5260205f604460105f875af13d1560015f51141716612576575f60345263095ea7b360601b5f525f38604460105f875af1508060345260205f604460105f875af13d1560015f5114171661257657633e3f8f735f526004601cfd5b5f603452505050565b6125aa6040518060800160405280606081526020016060815260200160608152602001606081525090565b846001600160401b038111156125c2576125c2613d3c565b6040519080825280602002602001820160405280156125eb578160200160208202803683370190505b508152846001600160401b0381111561260657612606613d3c565b60405190808252806020026020018201604052801561262f578160200160208202803683370190505b506020820152846001600160401b0381111561264d5761264d613d3c565b604051908082528060200260200182016040528015612676578160200160208202803683370190505b506040820152846001600160401b0381111561269457612694613d3c565b6040519080825280602002602001820160405280156126bd578160200160208202803683370190505b506060820152805180518991905f906126d8576126d8614820565b60200260200101906001600160a01b031690816001600160a01b0316815250508681602001515f8151811061270f5761270f614820565b602002602001018181525050600181604001515f8151811061273357612733614820565b602002602001018181525050600281606001515f8151811061275757612757614820565b6020908102919091010152600184156127f057602087015182518051600190811061278457612784614820565b60200260200101906001600160a01b031690816001600160a01b0316815250508482604001516001815181106127bc576127bc614820565b602002602001018181525050600182606001516001815181106127e1576127e1614820565b60209081029190910101525060025b8315612881576040870151825180518390811061280f5761280f614820565b60200260200101906001600160a01b031690816001600160a01b031681525050838260400151828151811061284657612846614820565b60200260200101818152505060018260600151828151811061286a5761286a614820565b602090810291909101015261287e81614834565b90505b82156129195773940181a94a35a4569e4529a3cdfb74e38fd98631825f015182815181106128b1576128b1614820565b60200260200101906001600160a01b031690816001600160a01b03168152505082826040015182815181106128e8576128e8614820565b60200260200101818152505060018260600151828151811061290c5761290c614820565b6020026020010181815250505b50979650505050505050565b5f611ade7333128a8fc17869897dce68ed026d694621f6fdfd8585856131a7565b5f611ade73ec8e5342b19977b4ef8892e02d8daecfa1315831735e7bb104d84c7cb9b682aac2f3d509f5f406809a86868661328e565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806129fe5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064015b60405180910390fd5b50505050565b815f528060020b60045260245ffd5b5f805f868910612a385760019250859150612a318960018887613351565b9050612b28565b878911612a4e57849150612a31895f8787613351565b5f612a5a8a8a8a6133ad565b90505f612a698b60018a6133f5565b90505f612a768289614516565b90505f612a8c89670de0b6b3a764000084611ae6565b905083811015612ae157600196505f612ab1858a670de0b6b3a7640000819003611ae6565b670de0b6b3a7640000019050612aca8286038483611ae6565b955050612ada8d6001878b613441565b9550612b23565b5f965086612af8858a670de0b6b3a7640000611ae6565b670de0b6b3a7640000039050612b118583038483611ae6565b965050612b208d5f888b613351565b94505b505050505b96509650969350505050565b5f836001600160a01b0316856001600160a01b03161115612b53579293925b846001600160a01b0316866001600160a01b031611612b8657612b7f612b7a86868661349b565b6134ff565b905061053c565b836001600160a01b0316866001600160a01b03161015612bdb575f612bac87868661349b565b90505f612bba878986613519565b9050612bd2818310612bcc57816134ff565b826134ff565b9250505061053c565b612be9612b7a868685613519565b9695505050505050565b5f8080805b6064811015612ca757612c0f8e8e8e8e8a8a613552565b9250886001600160a01b0316836001600160a01b031610612c4057612c368d8d8d8b6135fb565b9350505050612cae565b896001600160a01b0316836001600160a01b031611612c6557612c368d8d8d8a613637565b612c728e8e8e8e87613668565b9096509450612c878e8b8b8b8b8b8b8a6136e2565b909750955091508115612c9f57849350505050612cae565b600101612bf8565b5083925050505b9a9950505050505050505050565b5f805f86612ccf57876101800151612cd6565b8761016001515b90505f6001600160a01b038a167303a520b32c04bf3beef7beb72e919cf822ed34f114612d5c578989602001518a604001518b60800151604051602001612d4894939291906001600160a01b03948516815292841660208401529216604082015260029190910b606082015260800190565b604051602081830303815290604052612db9565b8989602001518a604001518b60600151604051602001612da994939291906001600160a01b03948516815292841660208401529216604082015262ffffff91909116606082015260800190565b6040516020818303038152906040525b90505f808a5f01516001600160a01b031663128acb08308c8c612ddb90615084565b88886040518663ffffffff1660e01b8152600401612dfd95949392919061509e565b60408051808303815f875af1158015612e18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e3c9190614e6f565b9150915089612e5357612e4e82615084565b612e5c565b612e5c81615084565b891115612e74576001600160a01b0384166101408c01525b89612e9157612e8282615084565b612e8c9089614516565b612e9b565b612e9b8289614540565b955089612eb157612eac8188614540565b612ec4565b612eba81615084565b612ec49088614516565b945050505050965096945050505050565b5f805f805f85806020019051810190612eee91906150d8565b9250925092505f87612f04578860400151612f0a565b88602001515b9050612f206001600160a01b038216858561250a565b5f80856001600160a01b031684604051612f3a9190615167565b5f604051808303815f865af19150503d805f8114612f73576040519150601f19603f3d011682016040523d82523d5f602084013e612f78565b606091505b5091509150818190612f9d5760405162461bcd60e51b81526004016129f59190615182565b507303a520b32c04bf3beef7beb72e919cf822ed34f0196001600160a01b038d1601613043578a5f01516001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015613002573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130269190614bdf565b5050506001600160a01b039093166101408f0152506130bd915050565b8a5f01516001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160c060405180830381865afa158015613082573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130a69190614ae7565b5050506001600160a01b039092166101408e015250505b60208b01516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015613105573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131299190614e58565b6040808d015190516370a0823160e01b81523060048201529199506001600160a01b0316906370a0823190602401602060405180830381865afa158015613172573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131969190614e58565b965050505050505094509492505050565b5f826001600160a01b0316846001600160a01b0316106131c5575f80fd5b604080516001600160a01b03808716602083015285169181019190915262ffffff83166060820152859060800160408051601f1981840301815290829052805160209182012061326d939290917fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5491016001600160f81b0319815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b60408051601f19818403018152919052805160209091012095945050505050565b5f826001600160a01b0316846001600160a01b0316106132ac575f80fd5b604080516001600160a01b038087166020830152851691810191909152600283900b6060820152612be99087906080016040516020818303038152906040528051906020012087604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b5f6001600160801b03851115613365575f80fd5b5f61337d670de0b6b3a7640000848103908690611ae6565b90508461339b5761339681600160c01b6002890a613847565b612be9565b612be9816002880a600160c01b613847565b5f6001600160801b038411156133c1575f80fd5b8284035f836002870a816133d7576133d7614446565b0460028702869003039050612be982670de0b6b3a764000083611ae6565b5f826134235761341e8261340e6002600160601b615274565b613419600288615274565b613847565b611ade565b611ade82613432600287615274565b6134196002600160601b615274565b5f6001600160801b03851115613455575f80fd5b5f846134725761346d846002880a600160c01b613847565b613484565b61348484600160c01b6002890a613847565b9050612be981670de0b6b3a7640000858103611ae6565b5f826001600160a01b0316846001600160a01b031611156134ba579192915b5f6134dc856001600160a01b0316856001600160a01b0316600160601b6138e3565b90506134f483828787036001600160a01b03166138e3565b9150505b9392505050565b806001600160801b0381168114613514575f80fd5b919050565b5f826001600160a01b0316846001600160a01b03161115613538579192915b611ade82600160601b8686036001600160a01b03166138e3565b5f8061356584620f424089810390611ae6565b90505f8089156135a35761357c8789856001613989565b6001600160a01b031691506135938789875f613a78565b6001600160a01b031690506135d3565b6135af8789875f613989565b6001600160a01b031691506135c78789856001613a78565b6001600160a01b031690505b896135f0576135eb8183016002808206151591040190565b612cae565b600282820104612cae565b5f8061360e83620f424088810390613b56565b90505f61361e8587846001613989565b905061362c8186885f613b79565b979650505050505050565b5f8061364a83620f424088810390613b56565b90505f61365a8587846001613a78565b905061362c8582885f613bc5565b5f8086156136a6575f61367e8486886001613bc5565b905061369081620f4240898103613b56565b925061369e8486885f613b79565b9150506136d8565b5f6136b48585886001613b79565b90506136c681620f4240898103613b56565b92506136d48585885f613bc5565b9150505b9550959350505050565b5f805f805f8c1561371d57613707868c8a8d116136ff575f61349b565b8a8d0361349b565b91506137168c87898c01613519565b9050613749565b61372a868c898d0161349b565b91506137468c878a8c1161373e575f613519565b8a8c03613519565b90505b5f8183106137695761376482670de0b6b3a764000085611ae6565b61377c565b61377c83670de0b6b3a764000084611ae6565b670de0b6b3a7640000039050620f42408110828410156137e6575f6137ac8f8a6137a5886134ff565b6001613b79565b90508f6137be57808c039a508a6137df565b8b81116137d7576137d28a6009600a611ae6565b6137db565b8b81035b9950895b5050613832565b5f6137fc898f6137f5876134ff565b6001613bc5565b90508f613828578c811161381c576138178a6009600a611ae6565b613820565b8c81035b99508961382f565b808d039a508a5b50505b9e989d50969b50969950505050505050505050565b5f838302815f1985870982811083820303915050808411613866575f80fd5b805f03613878575082900490506134f8565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f80805f19858709858702925082811083820303915050805f03613917575f841161390c575f80fd5b5082900490506134f8565b808411613922575f80fd5b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f825f03613998575083611ade565b600160601b600160e01b03606085901b168215613a32576001600160a01b038616848102908582816139cc576139cc614446565b04036139fc578181018281106139fa576139f083896001600160a01b031683613c7d565b9350505050611ade565b505b50613a2a8185613a156001600160a01b038a1683615282565b613a1f9190614516565b808204910615150190565b915050611ade565b6001600160a01b038616848102908582041481831116613a595763f5c787f15f526004601cfd5b8082036139f0613a73846001600160a01b038b1684613c7d565b613cad565b5f8115613adb575f6001600160a01b03841115613aac57613aa784600160601b876001600160801b0316613847565b613ac3565b613ac36001600160801b038616606086901b615282565b9050613a2a613a73826001600160a01b038916614516565b5f6001600160a01b03841115613b0857613b0384600160601b876001600160801b0316613c7d565b613b25565b613b25606085901b6001600160801b038716808204910615150190565b9050806001600160a01b03871611613b4457634323a5555f526004601cfd5b6001600160a01b038616039050611ade565b5f825f190484118302158202613b6a575f80fd5b50910281810615159190040190565b5f6001600160a01b038481169086160360ff81901d90810118600160601b6001600160801b038516613bac818484613847565b9350845f83858409111684019350505050949350505050565b5f836001600160a01b0316856001600160a01b03161115613be4579293925b6001600160a01b038516613bfe5762bfc9215f526004601cfd5b600160601b600160e01b03606084901b166001600160a01b038686031683613c5157866001600160a01b0316613c3e8383896001600160a01b0316613847565b81613c4b57613c4b614446565b0461362c565b61362c613c688383896001600160a01b0316613c7d565b886001600160a01b0316808204910615150190565b5f613c89848484613847565b90508180613c9957613c99614446565b838509156134f857600101806134f8575f80fd5b806001600160a01b0381168114613514576135146393dafdf160e01b805f5260045ffd5b604080516101a0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081019190915290565b634e487b7160e01b5f52604160045260245ffd5b6040516101a081016001600160401b0381118282101715613d7357613d73613d3c565b60405290565b604051608081016001600160401b0381118282101715613d7357613d73613d3c565b604051601f8201601f191681016001600160401b0381118282101715613dc357613dc3613d3c565b604052919050565b6001600160a01b0381168114613ddf575f80fd5b50565b803561351481613dcb565b62ffffff81168114613ddf575f80fd5b803561351481613ded565b8060020b8114613ddf575f80fd5b803561351481613e08565b6001600160801b0381168114613ddf575f80fd5b803561351481613e21565b5f6101a08284031215613e51575f80fd5b613e59613d50565b613e6283613de2565b8152613e7060208401613de2565b6020820152613e8160408401613de2565b6040820152613e9260608401613dfd565b6060820152613ea360808401613e16565b6080820152613eb460a08401613e16565b60a0820152613ec560c08401613e16565b60c0820152613ed660e08401613e35565b60e0820152610100613ee9818501613de2565b90820152610120613efb848201613de2565b9082015261014083810135908201526101608084013590820152610180928301359281019290925250919050565b5f8083601f840112613f39575f80fd5b5081356001600160401b03811115613f4f575f80fd5b602083019150836020828501011115613f66575f80fd5b9250929050565b5f805f805f805f60c0888a031215613f83575f80fd5b8735613f8e81613dcb565b96506020880135613f9e81613dcb565b9550604088013594506060880135613fb581613e08565b93506080880135613fc581613e08565b925060a08801356001600160401b03811115613fdf575f80fd5b613feb8a828b01613f29565b989b979a50959850939692959293505050565b5f805f805f60808688031215614012575f80fd5b853561401d81613dcb565b9450602086013561402d81613dcb565b93506040860135925060608601356001600160401b0381111561404e575f80fd5b61405a88828901613f29565b969995985093965092949392505050565b5f6020828403121561407b575f80fd5b81356134f881613dcb565b5f805f805f60a0868803121561409a575f80fd5b85356140a581613dcb565b94506020860135935060408601356140bc81613e08565b925060608601356140cc81613e08565b915060808601356140dc81613dcb565b809150509295509295909350565b81516001600160a01b031681526101a08101602083015161411660208401826001600160a01b03169052565b50604083015161413160408401826001600160a01b03169052565b506060830151614148606084018262ffffff169052565b50608083015161415d608084018260020b9052565b5060a083015161417260a084018260020b9052565b5060c083015161418760c084018260020b9052565b5060e08301516141a260e08401826001600160801b03169052565b50610100838101516001600160a01b038116848301525050610120838101516001600160a01b0381168483015250506101408381015190830152610160808401519083015261018092830151929091019190915290565b5f805f6060848603121561420b575f80fd5b505081359360208301359350604090920135919050565b5f805f60608486031215614234575f80fd5b833561423f81613dcb565b9250602084013561424f81613dcb565b9150604084013561425f81613dcb565b809150509250925092565b5f806020838503121561427b575f80fd5b82356001600160401b03811115614290575f80fd5b61429c85828601613f29565b90969095509350505050565b5f815180845260208085019450602084015f5b838110156142d7578151875295820195908201906001016142bb565b509495945050505050565b8051608080845281519084018190525f916020919082019060a0860190845b818110156143265783516001600160a01b031683529284019291840191600101614301565b505060208501519250858103602087015261434181846142a8565b925050506040830151848203604086015261435c82826142a8565b9150506060830151848203606086015261053c82826142a8565b602081525f6134f860208301846142e2565b5f805f806060858703121561439b575f80fd5b843593506020850135925060408501356001600160401b038111156143be575f80fd5b6143ca87828801613f29565b95989497509550505050565b5f5b838110156143f05781810151838201526020016143d8565b50505f910152565b5f815180845261440f8160208601602086016143d6565b601f01601f19169290920160200192915050565b6001600160a01b03831681526040602082018190525f90611ade908301846143f8565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f8160020b8360020b8061448457614484614446565b627fffff1982145f198214161561449d5761449d61445a565b90059392505050565b5f8260020b8260020b028060020b91508082146144c5576144c561445a565b5092915050565b600282810b9082900b03627fffff198112627fffff8213171561042c5761042c61445a565b600281810b9083900b01627fffff8113627fffff198212171561042c5761042c61445a565b8082018082111561042c5761042c61445a565b808202811582820484141761042c5761042c61445a565b8181038181111561042c5761042c61445a565b80518015158114613514575f80fd5b5f60208284031215614572575f80fd5b6134f882614553565b5f6020828403121561458b575f80fd5b81516134f881613dcb565b5f6001600160401b038211156145ae576145ae613d3c565b5060051b60200190565b5f82601f8301126145c7575f80fd5b813560206145dc6145d783614596565b613d9b565b8083825260208201915060208460051b8701019350868411156145fd575f80fd5b602086015b8481101561462257803561461581613dcb565b8352918301918301614602565b509695505050505050565b5f82601f83011261463c575f80fd5b8135602061464c6145d783614596565b8083825260208201915060208460051b87010193508684111561466d575f80fd5b602086015b848110156146225780358352918301918301614672565b5f6001600160401b038211156146a1576146a1613d3c565b50601f01601f191660200190565b5f82601f8301126146be575f80fd5b81356146cc6145d782614689565b8181528460208386010111156146e0575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f60a08688031215614710575f80fd5b85356001600160401b0380821115614726575f80fd5b908701906080828a031215614739575f80fd5b614741613d79565b82358281111561474f575f80fd5b61475b8b8286016145b8565b82525060208301358281111561476f575f80fd5b61477b8b82860161462d565b602083015250604083013582811115614792575f80fd5b61479e8b82860161462d565b6040830152506060830135828111156147b5575f80fd5b6147c18b82860161462d565b60608301525096506147d560208901613de2565b95506147e360408901613e16565b94506147f160608901613e16565b93506080880135915080821115614806575f80fd5b50614813888289016146af565b9150509295509295909350565b634e487b7160e01b5f52603260045260245ffd5b5f600182016148455761484561445a565b5060010190565b5f805f806080858703121561485f575f80fd5b843561486a81613dcb565b9350602085013561487a81613dcb565b9250604085013561488a81613dcb565b9150606085013561489a81613ded565b939692955090935050565b60a081525f6148b760a08301896142e2565b60018060a01b03881660208401528660020b60408401528560020b60608401528281036080840152838152838560208301375f602085830101526020601f19601f860116820101915050979650505050505050565b60a081525f61491e60a08301886142e2565b60208382038185015261493182896142e2565b9150604084830360408601526060830188516060855281815180845260808701915085830193505f92505b8083101561498e57835180516001600160a01b031683528601518683015292850192600192909201919084019061495c565b50848b01518587015260408b0151604087015287810360608901526149b3818b6143f8565b9550505050505082810360808401526149cc81856143f8565b98975050505050505050565b80516bffffffffffffffffffffffff81168114613514575f80fd5b805161351481613e21565b5f805f805f805f805f805f806101808d8f031215614a1a575f80fd5b614a238d6149d8565b9b5060208d0151614a3381613dcb565b60408e0151909b50614a4481613dcb565b60608e0151909a50614a5581613dcb565b60808e0151909950614a6681613e08565b60a08e0151909850614a7781613e08565b60c08e0151909750614a8881613e08565b9550614a9660e08e016149f3565b94506101008d015193506101208d01519250614ab56101408e016149f3565b9150614ac46101608e016149f3565b90509295989b509295989b509295989b565b805161ffff81168114613514575f80fd5b5f805f805f8060c08789031215614afc575f80fd5b8651614b0781613dcb565b6020880151909650614b1881613e08565b9450614b2660408801614ad6565b9350614b3460608801614ad6565b9250614b4260808801614ad6565b9150614b5060a08801614553565b90509295509295509295565b5f60208284031215614b6c575f80fd5b81516134f881613ded565b5f805f805f805f805f805f806101808d8f031215614b93575f80fd5b614b9c8d6149d8565b9b5060208d0151614bac81613dcb565b60408e0151909b50614bbd81613dcb565b60608e0151909a50614bce81613dcb565b60808e0151909950614a6681613ded565b5f805f805f805f60e0888a031215614bf5575f80fd5b8751614c0081613dcb565b6020890151909750614c1181613e08565b9550614c1f60408901614ad6565b9450614c2d60608901614ad6565b9350614c3b60808901614ad6565b925060a088015160ff81168114614c50575f80fd5b9150614c5e60c08901614553565b905092959891949750929550565b5f60208284031215614c7c575f80fd5b81516134f881613e08565b602080825282518282018190525f9190848201906040850190845b81811015614cc457835163ffffffff1683529284019291840191600101614ca2565b50909695505050505050565b5f82601f830112614cdf575f80fd5b81516020614cef6145d783614596565b8083825260208201915060208460051b870101935086841115614d10575f80fd5b602086015b84811015614622578051614d2881613dcb565b8352918301918301614d15565b5f8060408385031215614d46575f80fd5b82516001600160401b0380821115614d5c575f80fd5b818501915085601f830112614d6f575f80fd5b81516020614d7f6145d783614596565b82815260059290921b84018101918181019089841115614d9d575f80fd5b948201945b83861015614dc95785518060060b8114614dba575f80fd5b82529482019490820190614da2565b91880151919650909350505080821115614de1575f80fd5b50614dee85828601614cd0565b9150509250929050565b600682810b9082900b03667fffffffffffff198112667fffffffffffff8213171561042c5761042c61445a565b5f8160060b8360060b80614e3b57614e3b614446565b667fffffffffffff1982145f198214161561449d5761449d61445a565b5f60208284031215614e68575f80fd5b5051919050565b5f8060408385031215614e80575f80fd5b505080516020909101519092909150565b5f60208284031215614ea1575f80fd5b81516134f881613e21565b81516001600160a01b0316815261018081016020830151614ed860208401826001600160a01b03169052565b506040830151614eed604084018260020b9052565b506060830151614f02606084018260020b9052565b506080830151614f17608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151614f5d828501826001600160a01b03169052565b50506101408381015190830152610160928301516001600160a01b0316929091019190915290565b5f805f8060808587031215614f98575f80fd5b845193506020850151614faa81613e21565b6040860151606090960151949790965092505050565b81516001600160a01b0316815261016081016020830151614fec60208401826001600160a01b03169052565b506040830151615003604084018262ffffff169052565b506060830151615018606084018260020b9052565b50608083015161502d608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151615073828501826001600160a01b03169052565b505061014092830151919092015290565b5f600160ff1b82016150985761509861445a565b505f0390565b6001600160a01b0386811682528515156020830152604082018590528316606082015260a0608082018190525f9061362c908301846143f8565b5f805f606084860312156150ea575f80fd5b83516150f581613dcb565b6020850151604086015191945092506001600160401b03811115615117575f80fd5b8401601f81018613615127575f80fd5b80516151356145d782614689565b818152876020838501011115615149575f80fd5b61515a8260208301602086016143d6565b8093505050509250925092565b5f82516151788184602087016143d6565b9190910192915050565b602081525f6134f860208301846143f8565b600181815b808511156151ce57815f19048211156151b4576151b461445a565b808516156151c157918102915b93841c9390800290615199565b509250929050565b5f826151e45750600161042c565b816151f057505f61042c565b816001811461520657600281146152105761522c565b600191505061042c565b60ff8411156152215761522161445a565b50506001821b61042c565b5060208310610133831016604e8410600b841016171561524f575081810a61042c565b6152598383615194565b805f190482111561526c5761526c61445a565b029392505050565b5f6134f860ff8416836151d6565b5f8261529057615290614446565b50049056fea2646970667358221220a1e3ced5eb9000142ed04bf1577a9483a84c813afc2cb9d8600b6b25c387a3a064736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000d99a8cec7e20000
-----Decoded View---------------
Arg [0] : maxTolerance (uint256): 10000000000000000
Arg [1] : maxInitiatorFee (uint256): 200000000000000000
Arg [2] : minLiquidityRatio (uint256): 980000000000000000
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000002386f26fc10000
Arg [1] : 00000000000000000000000000000000000000000000000002c68af0bb140000
Arg [2] : 0000000000000000000000000000000000000000000000000d99a8cec7e20000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.