Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
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:
RebalancerSlipstream
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.30;
import { Rebalancer } from "./Rebalancer.sol";
import { Slipstream } from "../base/Slipstream.sol";
/**
* @title Rebalancer for Slipstream Liquidity Positions.
* @notice The Rebalancer is 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 permissioned to rebalance
* all Liquidity Positions held in that Account.
* @dev The initiator will provide a trusted sqrtPrice input at the time of rebalance to mitigate frontrunning risks.
* This input serves as a reference point for calculating the maximum allowed deviation during the rebalancing process,
* ensuring that rebalancing remains within a controlled price range.
* @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 RebalancerSlipstream is Rebalancer, Slipstream {
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The version of the Asset Manager.
string public constant VERSION = "2.1.1";
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param owner_ The address of the Owner.
* @param arcadiaFactory The contract address of the Arcadia Factory.
* @param routerTrampoline The contract address of the Router Trampoline.
* @param positionManager The contract address of the Slipstream Position Manager.
* @param cLFactory The contract address of the Slipstream Factory.
* @param poolImplementation The contract address of the Slipstream Pool Implementation.
* @param rewardToken The contract address of the Reward Token (Aero).
* @param stakedSlipstreamAm The contract address of the Staked Slipstream Asset Module.
* @param stakedSlipstreamWrapper The contract address of the Staked Slipstream Wrapper.
*/
constructor(
address owner_,
address arcadiaFactory,
address routerTrampoline,
address positionManager,
address cLFactory,
address poolImplementation,
address rewardToken,
address stakedSlipstreamAm,
address stakedSlipstreamWrapper
)
Rebalancer(owner_, arcadiaFactory, routerTrampoline)
Slipstream(
positionManager, cLFactory, poolImplementation, rewardToken, stakedSlipstreamAm, stakedSlipstreamWrapper
)
{ }
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
import { AbstractBase } from "../base/AbstractBase.sol";
import { ActionData, IActionBase } from "../../../lib/accounts-v2/src/interfaces/IActionBase.sol";
import { ArcadiaLogic } from "../libraries/ArcadiaLogic.sol";
import { ERC20, SafeTransferLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/SafeTransferLib.sol";
import { ERC721 } from "../../../lib/accounts-v2/lib/solmate/src/tokens/ERC721.sol";
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { Guardian } from "../../guardian/Guardian.sol";
import { IAccount } from "../../interfaces/IAccount.sol";
import { IArcadiaFactory } from "../../interfaces/IArcadiaFactory.sol";
import { IRouterTrampoline } from "../interfaces/IRouterTrampoline.sol";
import { IStrategyHook } from "../interfaces/IStrategyHook.sol";
import { PositionState } from "../state/PositionState.sol";
import { RebalanceLogic, RebalanceParams } from "../libraries/RebalanceLogic.sol";
import { RebalanceOptimizationMath } from "../libraries/RebalanceOptimizationMath.sol";
import { SafeApprove } from "../../libraries/SafeApprove.sol";
import { SafeCastLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/SafeCastLib.sol";
import { TickMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/TickMath.sol";
/**
* @title Abstract Rebalancer for Concentrated Liquidity Positions.
* @notice The Rebalancer is 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 permissioned to rebalance
* all Liquidity Positions held in that Account.
* @dev The initiator will provide a trusted sqrtPrice input at the time of rebalance to mitigate frontrunning risks.
* This input serves as a reference point for calculating the maximum allowed deviation during the rebalancing process,
* ensuring that rebalancing remains within a controlled price range.
* @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.
*/
abstract contract Rebalancer is IActionBase, AbstractBase, Guardian {
using FixedPointMathLib for uint256;
using SafeApprove for ERC20;
using SafeCastLib for uint256;
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The contract address of the Arcadia Factory.
IArcadiaFactory internal immutable ARCADIA_FACTORY;
// The contract address of the Router Trampoline.
IRouterTrampoline internal immutable ROUTER_TRAMPOLINE;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// The Account to rebalance the fees for, used as transient storage.
address internal account;
// A mapping from account to account specific information.
mapping(address account => AccountInfo) public accountInfo;
// A mapping from account to custom metadata.
mapping(address account => bytes data) public metaData;
// A mapping that sets the approved initiator per owner per account.
mapping(address accountOwner => mapping(address account => address initiator)) public accountToInitiator;
// A struct with the account specific parameters.
struct AccountInfo {
// The maximum fee charged on the claimed fees of the liquidity position, with 18 decimals precision.
uint64 maxClaimFee;
// The maximum fee charged on the ideal (without slippage) amountIn by the initiator, with 18 decimals precision.
uint64 maxSwapFee;
// The maximum relative deviation the pool can have from the trustedSqrtPrice, with 18 decimals precision.
uint64 upperSqrtPriceDeviation;
// The minimum relative deviation the pool can have from the trustedSqrtPrice, with 18 decimals precision.
uint64 lowerSqrtPriceDeviation;
// The ratio that limits the amount of slippage of the swap, with 18 decimals precision.
uint64 minLiquidityRatio;
// The contract address of the strategy hook.
address strategyHook;
}
// A struct with the initiator parameters.
struct InitiatorParams {
// The contract address of the position manager.
address positionManager;
// The id of the position.
uint96 oldId;
// The amount of token0 withdrawn from the account.
uint128 amountIn0;
// The amount of token1 withdrawn from the account.
uint128 amountIn1;
// The amount of token0 to send to the account.
uint128 amountOut0;
// The amount of token1 to send to the account.
uint128 amountOut1;
// The sqrtPrice the pool should have, given by the initiator.
uint256 trustedSqrtPrice;
// The fee charged on the claimed fees of the liquidity position, with 18 decimals precision.
uint64 claimFee;
// The fee charged on the ideal (without slippage) amountIn by the initiator, with 18 decimals precision.
uint64 swapFee;
// Calldata provided by the initiator to execute the swap.
bytes swapData;
// Strategy specific Calldata provided by the initiator.
bytes strategyData;
}
// A struct with cached variables.
struct Cache {
// The lower bound the sqrtPrice can have for the pool to be balanced.
uint256 lowerBoundSqrtPrice;
// The lower bound the sqrtPrice can have for the pool to be balanced.
uint256 upperBoundSqrtPrice;
// The sqrtRatio of the lower tick.
uint160 sqrtRatioLower;
// The sqrtRatio of the upper tick.
uint160 sqrtRatioUpper;
}
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error InsufficientLiquidity();
error InvalidAccountVersion();
error InvalidInitiator();
error InvalidPositionManager();
error InvalidValue();
error NotAnAccount();
error OnlyAccount();
error OnlyAccountOwner();
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 owner_ The address of the Owner.
* @param arcadiaFactory The contract address of the Arcadia Factory.
* @param routerTrampoline The contract address of the Router Trampoline.
*/
constructor(address owner_, address arcadiaFactory, address routerTrampoline) Guardian(owner_) {
ARCADIA_FACTORY = IArcadiaFactory(arcadiaFactory);
ROUTER_TRAMPOLINE = IRouterTrampoline(routerTrampoline);
}
/* ///////////////////////////////////////////////////////////////
ACCOUNT LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Optional hook called by the Arcadia Account when calling "setAssetManager()".
* @param accountOwner The current owner of the Arcadia Account.
* param status Bool indicating if the Asset Manager is enabled or disabled.
* @param data Operator specific data, passed by the Account owner.
* @dev No need to check that the Account version is 3 or greater (versions with cross account reentrancy guard),
* since version 1 and 2 don't support the onSetAssetManager hook.
*/
function onSetAssetManager(address accountOwner, bool, bytes calldata data) external {
if (account != address(0)) revert Reentered();
if (!ARCADIA_FACTORY.isAccount(msg.sender)) revert NotAnAccount();
(
address initiator,
uint256 maxClaimFee,
uint256 maxSwapFee,
uint256 maxTolerance,
uint256 minLiquidityRatio,
address strategyHook,
bytes memory strategyData,
bytes memory metaData_
) = abi.decode(data, (address, uint256, uint256, uint256, uint256, address, bytes, bytes));
_setAccountInfo(
msg.sender,
accountOwner,
initiator,
maxClaimFee,
maxSwapFee,
maxTolerance,
minLiquidityRatio,
strategyHook,
strategyData,
metaData_
);
}
/**
* @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 maxClaimFee The maximum fee charged on claimed fees/rewards by the initiator, with 18 decimals precision.
* @param maxSwapFee The maximum fee charged on the ideal (without slippage) amountIn by the initiator, with 18 decimals precision.
* @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 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 strategyHook The contract address of the strategy hook.
* @param strategyData Strategy specific data stored in the hook.
* @param metaData_ Custom metadata to be stored with the account.
*/
function setAccountInfo(
address account_,
address initiator,
uint256 maxClaimFee,
uint256 maxSwapFee,
uint256 maxTolerance,
uint256 minLiquidityRatio,
address strategyHook,
bytes calldata strategyData,
bytes calldata metaData_
) external {
if (account != address(0)) revert Reentered();
if (!ARCADIA_FACTORY.isAccount(account_)) revert NotAnAccount();
address accountOwner = IAccount(account_).owner();
if (msg.sender != accountOwner) revert OnlyAccountOwner();
// Block Account versions without cross account reentrancy guard.
if (IAccount(account_).ACCOUNT_VERSION() < 3) revert InvalidAccountVersion();
_setAccountInfo(
account_,
accountOwner,
initiator,
maxClaimFee,
maxSwapFee,
maxTolerance,
minLiquidityRatio,
strategyHook,
strategyData,
metaData_
);
}
/**
* @notice Sets the required information for an Account.
* @param account_ The contract address of the Arcadia Account to set the information for.
* @param accountOwner The current owner of the Arcadia Account.
* @param initiator The address of the initiator.
* @param maxClaimFee The maximum fee charged on claimed fees/rewards by the initiator, with 18 decimals precision.
* @param maxSwapFee The maximum fee charged on the ideal (without slippage) amountIn by the initiator, with 18 decimals precision.
* @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 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 strategyHook The contract address of the strategy hook.
* @param strategyData Strategy specific data stored in the hook.
* @param metaData_ Custom metadata to be stored with the account.
*/
function _setAccountInfo(
address account_,
address accountOwner,
address initiator,
uint256 maxClaimFee,
uint256 maxSwapFee,
uint256 maxTolerance,
uint256 minLiquidityRatio,
address strategyHook,
bytes memory strategyData,
bytes memory metaData_
) internal {
if (maxClaimFee > 1e18 || maxSwapFee > 1e18 || maxTolerance > 1e18 || minLiquidityRatio > 1e18) {
revert InvalidValue();
}
accountToInitiator[accountOwner][account_] = initiator;
// unsafe cast: fees <= 1e18 < type(uint64).max.
// unsafe cast: upperSqrtPriceDeviation <= √2 * 1e18 < type(uint64).max.
// forge-lint: disable-next-item(unsafe-typecast)
accountInfo[account_] = AccountInfo({
maxClaimFee: uint64(maxClaimFee),
maxSwapFee: uint64(maxSwapFee),
upperSqrtPriceDeviation: uint64(FixedPointMathLib.sqrt((1e18 + maxTolerance) * 1e18)),
lowerSqrtPriceDeviation: uint64(FixedPointMathLib.sqrt((1e18 - maxTolerance) * 1e18)),
minLiquidityRatio: uint64(minLiquidityRatio),
strategyHook: strategyHook
});
metaData[account_] = metaData_;
IStrategyHook(strategyHook).setStrategy(account_, strategyData);
emit AccountInfoSet(account_, initiator, strategyHook);
}
/* ///////////////////////////////////////////////////////////////
REBALANCING LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Rebalances a Concentrated Liquidity Positions, owned by an Arcadia Account.
* @param account_ The contract address of the account.
* @param initiatorParams A struct with the initiator parameters.
* @dev The amountOuts, provided by the initiator with the initiatorParams,
* must be smaller or equal to the rebalancers balance after burning the position or the rebalance reverts.
* To withdraw the full underlying balance of a liquidity position to the account, set amountOut for that token to type(uint128).max.
*/
function rebalance(address account_, InitiatorParams calldata initiatorParams) external whenNotPaused {
// Store Account address, used to validate the caller of the executeAction() callback and serves as a reentrancy guard.
if (account != address(0)) revert Reentered();
account = account_;
// If the initiator is set, account_ is an actual Arcadia Account.
if (accountToInitiator[IAccount(account_).owner()][account_] != msg.sender) revert InvalidInitiator();
if (!isPositionManager(initiatorParams.positionManager)) revert InvalidPositionManager();
// Store Account address, used to validate the caller of the executeAction() callback and serves as a reentrancy guard.
account = account_;
// If leftovers have to be withdrawn from account, get token0 and token1.
address token0;
address token1;
if (initiatorParams.amountIn0 > 0 || initiatorParams.amountIn1 > 0) {
(token0, token1) = _getUnderlyingTokens(initiatorParams.positionManager, initiatorParams.oldId);
}
// Encode data for the flash-action.
bytes memory actionData = ArcadiaLogic._encodeAction(
initiatorParams.positionManager,
initiatorParams.oldId,
token0,
token1,
initiatorParams.amountIn0,
initiatorParams.amountIn1,
abi.encode(msg.sender, initiatorParams)
);
// 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 actionTargetData A bytes object containing the initiator and initiatorParams.
* @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 actionTargetData) external override returns (ActionData memory depositData) {
// Caller should be the Account, provided as input in rebalance().
if (msg.sender != account) revert OnlyAccount();
// Cache accountInfo.
AccountInfo memory accountInfo_ = accountInfo[msg.sender];
// Decode actionTargetData.
(address initiator, InitiatorParams memory initiatorParams) =
abi.decode(actionTargetData, (address, InitiatorParams));
address positionManager = initiatorParams.positionManager;
// Validate initiatorParams.
if (initiatorParams.claimFee > accountInfo_.maxClaimFee || initiatorParams.swapFee > accountInfo_.maxSwapFee) {
revert InvalidValue();
}
// Get all pool and position related state.
PositionState memory position = _getPositionState(positionManager, initiatorParams.oldId);
// Rebalancer has withdrawn the underlying tokens from the Account.
uint256[] memory balances = new uint256[](position.tokens.length);
balances[0] = initiatorParams.amountIn0;
balances[1] = initiatorParams.amountIn1;
uint256[] memory fees = new uint256[](balances.length);
// Call the strategy hook before the rebalance (view function, cannot modify state of pool or old position).
// The strategy hook will return the new ticks of the position
// (we override ticks of the memory pointer of the old position as these are no longer needed after this call).
// Hook can be used to enforce additional strategy specific constraints, specific to the Account/Id.
// Such as:
// - Minimum Cool Down Periods.
// - Excluding rebalancing of certain positions.
// - ...
(position.tickLower, position.tickUpper) = IStrategyHook(accountInfo_.strategyHook)
.beforeRebalance(msg.sender, positionManager, position, initiatorParams.strategyData);
// Cache variables that are gas expensive to calculate and used multiple times.
Cache memory cache = _getCache(accountInfo_, position, initiatorParams.trustedSqrtPrice);
// Check that pool is initially balanced.
// Prevents sandwiching attacks when swapping and/or adding liquidity.
if (!isPoolBalanced(position.sqrtPrice, cache)) revert UnbalancedPool();
// Claim pending yields and update balances.
_claim(balances, fees, positionManager, position, initiatorParams.claimFee);
// If the position is staked, unstake it.
_unstake(balances, positionManager, position);
// Remove liquidity of the position and update balances.
_burn(balances, positionManager, position);
// Check if we want to withdraw the full underlying balance of one of the underlying tokens to the account.
if (initiatorParams.amountOut0 == type(uint128).max) {
initiatorParams.amountOut0 = (balances[0] - fees[0]).safeCastTo128();
} else if (initiatorParams.amountOut1 == type(uint128).max) {
initiatorParams.amountOut1 = (balances[1] - fees[1]).safeCastTo128();
}
// Get the rebalance parameters, based on a hypothetical swap through the pool itself without slippage.
// Reverts if balance is smaller than the required fees and amountOut.
RebalanceParams memory rebalanceParams = RebalanceLogic._getRebalanceParams(
accountInfo_.minLiquidityRatio,
position.fee,
initiatorParams.swapFee,
position.sqrtPrice,
cache.sqrtRatioLower,
cache.sqrtRatioUpper,
balances[0] - fees[0] - initiatorParams.amountOut0,
balances[1] - fees[1] - initiatorParams.amountOut1
);
if (rebalanceParams.zeroToOne) fees[0] += rebalanceParams.amountInitiatorFee;
else fees[1] += rebalanceParams.amountInitiatorFee;
// Do the 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.
// Update balances after the swap.
_swap(balances, fees, initiatorParams, position, rebalanceParams, cache);
// Check that the pool is still balanced after the swap.
// Since the swap went potentially through the pool itself (but does not have to),
// the sqrtPrice might have moved and brought the pool out of balance.
position.sqrtPrice = _getSqrtPrice(position);
if (!isPoolBalanced(position.sqrtPrice, cache)) revert UnbalancedPool();
// As explained before _swap(), tokenOut should be the limiting factor when increasing liquidity
// therefore we only subtract the initiator fee from the amountOut, not from the amountIn.
// Update balances, id and liquidity after the mint.
(uint256 amount0Desired, uint256 amount1Desired) = rebalanceParams.zeroToOne
? (balances[0] - initiatorParams.amountOut0, balances[1] - fees[1] - initiatorParams.amountOut1)
: (balances[0] - fees[0] - initiatorParams.amountOut0, balances[1] - initiatorParams.amountOut1);
_mint(balances, positionManager, position, amount0Desired, amount1Desired);
// 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 (position.liquidity < rebalanceParams.minLiquidity) revert InsufficientLiquidity();
// If the position is staked, stake it.
_stake(balances, positionManager, position);
// Call the strategy hook after the rebalance (non view function).
// Can be used to check additional constraints and persist state changes on the hook.
IStrategyHook(accountInfo_.strategyHook)
.afterRebalance(msg.sender, positionManager, initiatorParams.oldId, position, initiatorParams.strategyData);
// Approve the liquidity position and leftovers to be deposited back into the Account.
// And transfer the initiator fees to the initiator.
uint256 count = _approveAndTransfer(initiator, balances, fees, initiatorParams, positionManager, position);
// Encode deposit data for the flash-action.
depositData = ArcadiaLogic._encodeDeposit(positionManager, position.id, position.tokens, balances, count);
emit Rebalance(msg.sender, positionManager, initiatorParams.oldId, position.id);
}
/* ///////////////////////////////////////////////////////////////
POSITION VALIDATION
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns if the pool of a Liquidity Position is balanced.
* @param sqrtPrice The sqrtPrice of the pool.
* @param cache A struct with cached variables.
* @return isBalanced Bool indicating if the pool is balanced.
*/
function isPoolBalanced(uint256 sqrtPrice, Cache memory cache) public pure returns (bool isBalanced) {
// Check if current price of the Pool is within accepted tolerance of the calculated trusted price.
isBalanced = sqrtPrice > cache.lowerBoundSqrtPrice && sqrtPrice < cache.upperBoundSqrtPrice;
}
/* ///////////////////////////////////////////////////////////////
GETTERS
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns the cached variables.
* @param accountInfo_ A struct with the account specific parameters.
* @param position A struct with position and pool related variables.
* @param trustedSqrtPrice The sqrtPrice the pool should have, given by the initiator.
* @return cache A struct with cached variables.
*/
function _getCache(AccountInfo memory accountInfo_, PositionState memory position, uint256 trustedSqrtPrice)
internal
view
virtual
returns (Cache memory cache)
{
// We do not handle the edge cases where the bounds of the sqrtPrice 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.
cache = Cache({
lowerBoundSqrtPrice: trustedSqrtPrice.mulDivDown(accountInfo_.lowerSqrtPriceDeviation, 1e18),
upperBoundSqrtPrice: trustedSqrtPrice.mulDivDown(accountInfo_.upperSqrtPriceDeviation, 1e18),
sqrtRatioLower: TickMath.getSqrtPriceAtTick(position.tickLower),
sqrtRatioUpper: TickMath.getSqrtPriceAtTick(position.tickUpper)
});
}
/* ///////////////////////////////////////////////////////////////
SWAP LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Swaps one token for another to rebalance the Liquidity Position.
* @param balances The balances of the underlying tokens held by the Rebalancer.
* @param fees The fees of the underlying tokens to be paid to the initiator.
* @param initiatorParams A struct with the initiator parameters.
* @param position A struct with position and pool related variables.
* @param rebalanceParams A struct with the rebalance parameters.
* @param cache A struct with cached variables.
* @dev Must update the balances and sqrtPrice after the swap.
*/
function _swap(
uint256[] memory balances,
uint256[] memory fees,
InitiatorParams memory initiatorParams,
PositionState memory position,
RebalanceParams memory rebalanceParams,
Cache memory cache
) internal virtual {
// Don't do swaps with zero amount.
if (rebalanceParams.amountIn == 0) return;
// 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 (initiatorParams.swapData.length == 0) {
// Calculate a more accurate amountOut, with slippage.
uint256 amount0 = balances[0] - fees[0] - initiatorParams.amountOut0;
uint256 amount1 = balances[1] - fees[1] - initiatorParams.amountOut1;
uint256 amountOut = RebalanceOptimizationMath._getAmountOutWithSlippage(
rebalanceParams.zeroToOne,
position.fee,
_getPoolLiquidity(position),
uint160(position.sqrtPrice),
cache.sqrtRatioLower,
cache.sqrtRatioUpper,
amount0,
amount1,
rebalanceParams.amountIn,
rebalanceParams.amountOut
);
// Don't do swaps with zero amount.
if (amountOut == 0) return;
_swapViaPool(balances, position, rebalanceParams.zeroToOne, amountOut);
} else {
_swapViaRouter(balances, position, rebalanceParams.zeroToOne, initiatorParams.swapData);
}
}
/**
* @notice Swaps one token for another, via a router with custom swap data.
* @param balances The balances of the underlying tokens held by the Rebalancer.
* @param position A struct with position and pool related variables.
* @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.
* @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(
uint256[] memory balances,
PositionState memory position,
bool zeroToOne,
bytes memory swapData
) internal virtual {
// Decode the swap data.
(address router, uint256 amountIn, bytes memory data) = abi.decode(swapData, (address, uint256, bytes));
(address tokenIn, address tokenOut) =
zeroToOne ? (position.tokens[0], position.tokens[1]) : (position.tokens[1], position.tokens[0]);
// Send tokens to the Router Trampoline.
ERC20(tokenIn).safeTransfer(address(ROUTER_TRAMPOLINE), amountIn);
// Execute swap.
(uint256 balanceIn, uint256 balanceOut) = ROUTER_TRAMPOLINE.execute(router, data, tokenIn, tokenOut, amountIn);
// Update the balances.
(balances[0], balances[1]) = zeroToOne
? (balances[0] - amountIn + balanceIn, balances[1] + balanceOut)
: (balances[0] + balanceOut, balances[1] - amountIn + balanceIn);
}
/* ///////////////////////////////////////////////////////////////
APPROVE AND TRANSFER LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Approves the liquidity position and leftovers to be deposited back into the Account
* and transfers the initiator fees to the initiator.
* @param initiator The address of the initiator.
* @param balances The balances of the underlying tokens held by the Rebalancer.
* @param fees The fees of the underlying tokens to be paid to the initiator.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @return count The number of assets approved.
*/
function _approveAndTransfer(
address initiator,
uint256[] memory balances,
uint256[] memory fees,
InitiatorParams memory initiatorParams,
address positionManager,
PositionState memory position
) internal returns (uint256 count) {
// Approve the Liquidity Position.
ERC721(positionManager).approve(msg.sender, position.id);
// Transfer Initiator fees and approve amountOut and leftovers.
address token;
uint256 amountOut;
count = 1;
for (uint256 i; i < balances.length; i++) {
if (i <= 1) amountOut = i == 0 ? initiatorParams.amountOut0 : initiatorParams.amountOut1;
else amountOut = 0;
token = position.tokens[i];
// At least amountOut must be returned to the Account.
// Reverts if balance is smaller than the required amountOut.
if (balances[i] - amountOut > fees[i]) {
balances[i] = balances[i] - fees[i];
} else {
fees[i] = balances[i] - amountOut;
balances[i] = amountOut;
}
// Approve Account to deposit tokens.
if (balances[i] > 0) {
ERC20(token).safeApproveWithRetry(msg.sender, balances[i]);
count++;
}
// Transfer Initiator fees to the initiator.
if (fees[i] > 0) ERC20(token).safeTransfer(initiator, fees[i]);
emit FeePaid(msg.sender, initiator, token, fees[i]);
}
}
/* ///////////////////////////////////////////////////////////////
SKIM LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Recovers any native or ERC20 tokens left on the contract.
* @param token The contract address of the token, or address(0) for native tokens.
*/
function skim(address token) external onlyOwner whenNotPaused {
if (account != address(0)) revert Reentered();
if (token == address(0)) {
(bool success, bytes memory result) = payable(msg.sender).call{ value: address(this).balance }("");
require(success, string(result));
} else {
ERC20(token).safeTransfer(msg.sender, ERC20(token).balanceOf(address(this)));
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
import { AbstractBase } from "./AbstractBase.sol";
import { ICLPositionManager } from "../interfaces/ICLPositionManager.sol";
import { CLMath } from "../libraries/CLMath.sol";
import { ERC20, SafeTransferLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/SafeTransferLib.sol";
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { ICLPool } from "../interfaces/ICLPool.sol";
import { IStakedSlipstream } from "../interfaces/IStakedSlipstream.sol";
import { PositionState } from "../state/PositionState.sol";
import { SlipstreamLogic } from "../libraries/SlipstreamLogic.sol";
import { SafeApprove } from "../../libraries/SafeApprove.sol";
/**
* @title Base implementation for managing Slipstream Liquidity Positions.
*/
abstract contract Slipstream is AbstractBase {
using FixedPointMathLib for uint256;
using SafeApprove for ERC20;
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The contract address of the Slipstream Factory.
address internal immutable CL_FACTORY;
// The contract address of the Slipstream Position Manager.
ICLPositionManager internal immutable POSITION_MANAGER;
// The contract address of the Slipstream Pool Implementation.
address internal immutable POOL_IMPLEMENTATION;
// The contract address of the Reward Token (Aero).
address internal immutable REWARD_TOKEN;
// The contract address of the Staked Slipstream Asset Module.
address internal immutable STAKED_SLIPSTREAM_AM;
// The contract address of the Staked Slipstream Wrapper.
address internal immutable STAKED_SLIPSTREAM_WRAPPER;
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error OnlyPool();
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param positionManager The contract address of the Slipstream Position Manager.
* @param cLFactory The contract address of the Slipstream Factory.
* @param poolImplementation The contract address of the Slipstream Pool Implementation.
* @param rewardToken The contract address of the Reward Token (Aero).
* @param stakedSlipstreamAm The contract address of the Staked Slipstream Asset Module.
* @param stakedSlipstreamWrapper The contract address of the Staked Slipstream Wrapper.
*/
constructor(
address positionManager,
address cLFactory,
address poolImplementation,
address rewardToken,
address stakedSlipstreamAm,
address stakedSlipstreamWrapper
) {
POSITION_MANAGER = ICLPositionManager(positionManager);
CL_FACTORY = cLFactory;
POOL_IMPLEMENTATION = poolImplementation;
REWARD_TOKEN = rewardToken;
STAKED_SLIPSTREAM_AM = stakedSlipstreamAm;
STAKED_SLIPSTREAM_WRAPPER = stakedSlipstreamWrapper;
}
/* ///////////////////////////////////////////////////////////////
POSITION VALIDATION
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns if a position manager matches the position manager(s) of Slipstream.
* @param positionManager the contract address of the position manager to check.
*/
function isPositionManager(address positionManager) public view virtual override returns (bool) {
return (positionManager == address(STAKED_SLIPSTREAM_AM)
|| positionManager == address(STAKED_SLIPSTREAM_WRAPPER)
|| positionManager == address(POSITION_MANAGER));
}
/* ///////////////////////////////////////////////////////////////
GETTERS
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns the underlying assets of the pool.
* param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @return token0 The contract address of token0.
* @return token1 The contract address of token1.
*/
function _getUnderlyingTokens(address, uint256 id)
internal
view
virtual
override
returns (address token0, address token1)
{
(,, token0, token1,,,,,,,,) = POSITION_MANAGER.positions(id);
}
/**
* @notice Returns the position and pool related state.
* @param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @return position A struct with position and pool related variables.
*/
function _getPositionState(address positionManager, uint256 id)
internal
view
virtual
override
returns (PositionState memory position)
{
// Get data of the Liquidity Position.
position.id = id;
address token0;
address token1;
(,, token0, token1, position.tickSpacing, position.tickLower, position.tickUpper, position.liquidity,,,,) =
POSITION_MANAGER.positions(id);
// If it is a non staked position, or the position is staked and the reward token is the same as one of the underlying tokens,
// there are two underlying assets, otherwise there are three.
if (positionManager == address(POSITION_MANAGER) || token0 == REWARD_TOKEN || token1 == REWARD_TOKEN) {
// Positions have two underlying tokens.
position.tokens = new address[](2);
} else {
// Positions have three underlying tokens.
position.tokens = new address[](3);
position.tokens[2] = REWARD_TOKEN;
}
position.tokens[0] = token0;
position.tokens[1] = token1;
// Get data of the Liquidity Pool.
position.pool =
SlipstreamLogic.computeAddress(POOL_IMPLEMENTATION, CL_FACTORY, token0, token1, position.tickSpacing);
(position.sqrtPrice, position.tickCurrent,,,,) = ICLPool(position.pool).slot0();
position.fee = ICLPool(position.pool).fee();
}
/**
* @notice Returns the liquidity of the Pool.
* @param position A struct with position and pool related variables.
* @return liquidity The liquidity of the Pool.
*/
function _getPoolLiquidity(PositionState memory position)
internal
view
virtual
override
returns (uint128 liquidity)
{
liquidity = ICLPool(position.pool).liquidity();
}
/**
* @notice Returns the sqrtPrice of the Pool.
* @param position A struct with position and pool related variables.
* @return sqrtPrice The sqrtPrice of the Pool.
*/
function _getSqrtPrice(PositionState memory position) internal view virtual override returns (uint160 sqrtPrice) {
(sqrtPrice,,,,,) = ICLPool(position.pool).slot0();
}
/* ///////////////////////////////////////////////////////////////
CLAIM LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Claims fees/rewards from a Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param fees The fees of the underlying tokens to be paid to the initiator.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @param claimFee The fee charged on the claimed fees of the liquidity position, with 18 decimals precision.
*/
function _claim(
uint256[] memory balances,
uint256[] memory fees,
address positionManager,
PositionState memory position,
uint256 claimFee
) internal virtual override {
if (positionManager != address(POSITION_MANAGER)) {
// If position is a staked slipstream position, claim the rewards.
uint256 rewards = IStakedSlipstream(positionManager).claimReward(position.id);
uint256 fee = rewards.mulDivDown(claimFee, 1e18);
if (balances.length == 3) {
(balances[2], fees[2]) = (balances[2] + rewards, fees[2] + fee);
emit YieldClaimed(msg.sender, position.tokens[2], rewards);
}
// If rewardToken is an underlying token of the position, add it to the balances.
else if (position.tokens[0] == REWARD_TOKEN) {
(balances[0], fees[0]) = (balances[0] + rewards, fees[0] + fee);
emit YieldClaimed(msg.sender, position.tokens[0], rewards);
} else {
(balances[1], fees[1]) = (balances[1] + rewards, fees[1] + fee);
emit YieldClaimed(msg.sender, position.tokens[1], rewards);
}
} else {
// We assume that the amount of tokens to collect never exceeds type(uint128).max.
(uint256 amount0, uint256 amount1) = POSITION_MANAGER.collect(
ICLPositionManager.CollectParams({
tokenId: position.id,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
balances[0] += amount0;
balances[1] += amount1;
// Calculate claim fees.
fees[0] += amount0.mulDivDown(claimFee, 1e18);
fees[1] += amount1.mulDivDown(claimFee, 1e18);
emit YieldClaimed(msg.sender, position.tokens[0], amount0);
emit YieldClaimed(msg.sender, position.tokens[1], amount1);
}
}
/* ///////////////////////////////////////////////////////////////
STAKING LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Stakes a Liquidity Position.
* param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
*/
function _stake(uint256[] memory, address positionManager, PositionState memory position)
internal
virtual
override
{
// If position is a staked slipstream position, stake the position.
if (positionManager != address(POSITION_MANAGER)) {
POSITION_MANAGER.approve(positionManager, position.id);
IStakedSlipstream(positionManager).mint(position.id);
}
}
/**
* @notice Unstakes a Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @dev Does not emit YieldClaimed event, if necessary first call _claim() to emit the event before unstaking.
*/
function _unstake(uint256[] memory balances, address positionManager, PositionState memory position)
internal
virtual
override
{
// If position is a staked slipstream position, unstake the position.
if (positionManager != address(POSITION_MANAGER)) {
uint256 rewards = IStakedSlipstream(positionManager).burn(position.id);
if (rewards > 0) {
if (balances.length == 3) balances[2] = rewards;
// If rewardToken is an underlying token of the position, add it to the balances.
else if (position.tokens[0] == REWARD_TOKEN) balances[0] += rewards;
else balances[1] += rewards;
}
}
}
/* ///////////////////////////////////////////////////////////////
BURN LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Burns the Liquidity Position.
* @param balances The balances of the underlying tokens.
* param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @dev Does not emit YieldClaimed event, if necessary first call _claim() to emit the event before unstaking.
*/
function _burn(uint256[] memory balances, address, PositionState memory position) internal virtual override {
// Remove liquidity of the position and claim outstanding fees.
POSITION_MANAGER.decreaseLiquidity(
ICLPositionManager.DecreaseLiquidityParams({
tokenId: position.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.
(uint256 amount0, uint256 amount1) = POSITION_MANAGER.collect(
ICLPositionManager.CollectParams({
tokenId: position.id,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
balances[0] += amount0;
balances[1] += amount1;
// Burn the position.
POSITION_MANAGER.burn(position.id);
}
/* ///////////////////////////////////////////////////////////////
SWAP LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Swaps one token for another, directly through the pool itself.
* @param balances The balances of the underlying tokens.
* @param position A struct with position and pool related variables.
* @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.
*/
// forge-lint: disable-next-item(unsafe-typecast)
function _swapViaPool(uint256[] memory balances, PositionState memory position, bool zeroToOne, uint256 amountOut)
internal
virtual
override
{
// Do the swap.
(int256 deltaAmount0, int256 deltaAmount1) = ICLPool(position.pool)
.swap(
address(this),
zeroToOne,
-int256(amountOut),
zeroToOne ? CLMath.MIN_SQRT_PRICE_LIMIT : CLMath.MAX_SQRT_PRICE_LIMIT,
abi.encode(position.tokens[0], position.tokens[1], position.tickSpacing)
);
// Update the balances.
balances[0] = zeroToOne ? balances[0] - uint256(deltaAmount0) : balances[0] + uint256(-deltaAmount0);
balances[1] = zeroToOne ? balances[1] + uint256(-deltaAmount1) : balances[1] - uint256(deltaAmount1);
}
/**
* @notice Callback after executing a swap via ICLPool.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 position.
* @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 position.
* @param data Any data passed by this contract via the ICLPool.swap() call.
*/
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external virtual {
// Check that callback came from an actual Slipstream Pool.
(address token0, address token1, int24 tickSpacing) = abi.decode(data, (address, address, int24));
if (SlipstreamLogic.computeAddress(POOL_IMPLEMENTATION, CL_FACTORY, token0, token1, tickSpacing) != msg.sender)
{
revert OnlyPool();
}
// forge-lint: disable-next-item(unsafe-typecast)
if (amount0Delta > 0) {
ERC20(token0).safeTransfer(msg.sender, uint256(amount0Delta));
} else if (amount1Delta > 0) {
ERC20(token1).safeTransfer(msg.sender, uint256(amount1Delta));
}
}
/* ///////////////////////////////////////////////////////////////
MINT LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Mints a new Liquidity Position.
* @param balances The balances of the underlying tokens.
* param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @param amount0Desired The desired amount of token0 to mint as liquidity.
* @param amount1Desired The desired amount of token1 to mint as liquidity.
*/
function _mint(
uint256[] memory balances,
address,
PositionState memory position,
uint256 amount0Desired,
uint256 amount1Desired
) internal virtual override {
ERC20(position.tokens[0]).safeApproveWithRetry(address(POSITION_MANAGER), amount0Desired);
ERC20(position.tokens[1]).safeApproveWithRetry(address(POSITION_MANAGER), amount1Desired);
uint256 amount0;
uint256 amount1;
(position.id, position.liquidity, amount0, amount1) = POSITION_MANAGER.mint(
ICLPositionManager.MintParams({
token0: position.tokens[0],
token1: position.tokens[1],
tickSpacing: position.tickSpacing,
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp,
sqrtPrice: 0
})
);
balances[0] -= amount0;
balances[1] -= amount1;
}
/* ///////////////////////////////////////////////////////////////
INCREASE LIQUIDITY LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Swaps one token for another to rebalance the Liquidity Position.
* @param balances The balances of the underlying tokens.
* param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @param amount0Desired The desired amount of token0 to add as liquidity.
* @param amount1Desired The desired amount of token1 to add as liquidity.
*/
function _increaseLiquidity(
uint256[] memory balances,
address,
PositionState memory position,
uint256 amount0Desired,
uint256 amount1Desired
) internal virtual override {
ERC20(position.tokens[0]).safeApproveWithRetry(address(POSITION_MANAGER), amount0Desired);
ERC20(position.tokens[1]).safeApproveWithRetry(address(POSITION_MANAGER), amount1Desired);
uint256 amount0;
uint256 amount1;
(position.liquidity, amount0, amount1) = POSITION_MANAGER.increaseLiquidity(
ICLPositionManager.IncreaseLiquidityParams({
tokenId: position.id,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);
balances[0] -= amount0;
balances[1] -= amount1;
}
/* ///////////////////////////////////////////////////////////////
NATIVE ETH HANDLER
/////////////////////////////////////////////////////////////// */
/**
* @notice Receives native ether.
*/
receive() external payable { }
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
import { PositionState } from "../state/PositionState.sol";
/**
* @title Abstract base implementation for managing Liquidity Positions.
*/
abstract contract AbstractBase {
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event FeePaid(address indexed account, address indexed receiver, address indexed asset, uint256 amount);
event YieldClaimed(address indexed account, address indexed asset, uint256 amount);
/* ///////////////////////////////////////////////////////////////
POSITION VALIDATION
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns if a position manager matches the position manager(s) of the protocol.
* @param positionManager The contract address of the position manager to check.
* @return isPositionManager_ Bool indicating if the position manager matches.
*/
function isPositionManager(address positionManager) public view virtual returns (bool isPositionManager_);
/* ///////////////////////////////////////////////////////////////
GETTERS
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns the underlying assets of the pool.
* @param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @return token0 The contract address of token0.
* @return token1 The contract address of token1.
*/
function _getUnderlyingTokens(address positionManager, uint256 id)
internal
view
virtual
returns (address token0, address token1);
/**
* @notice Returns the position and pool related state.
* @param positionManager The contract address of the Position Manager.
* @param id The id of the Liquidity Position.
* @return position A struct with position and pool related variables.
*/
function _getPositionState(address positionManager, uint256 id)
internal
view
virtual
returns (PositionState memory position);
/**
* @notice Returns the liquidity of the Pool.
* @param position A struct with position and pool related variables.
* @return liquidity The liquidity of the Pool.
*/
function _getPoolLiquidity(PositionState memory position) internal view virtual returns (uint128 liquidity);
/**
* @notice Returns the sqrtPrice of the Pool.
* @param position A struct with position and pool related variables.
* @return sqrtPrice The sqrtPrice of the Pool.
*/
function _getSqrtPrice(PositionState memory position) internal view virtual returns (uint160 sqrtPrice);
/* ///////////////////////////////////////////////////////////////
CLAIM LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Claims fees/rewards from a Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param fees The fees of the underlying tokens to be paid to the initiator.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @dev Must update the balances after the claim.
*/
function _claim(
uint256[] memory balances,
uint256[] memory fees,
address positionManager,
PositionState memory position,
uint256 claimFee
) internal virtual;
/* ///////////////////////////////////////////////////////////////
STAKING LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Stakes a Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
*/
function _stake(uint256[] memory balances, address positionManager, PositionState memory position) internal virtual;
/**
* @notice Unstakes a Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
*/
function _unstake(uint256[] memory balances, address positionManager, PositionState memory position)
internal
virtual;
/* ///////////////////////////////////////////////////////////////
BURN LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Burns the Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @dev Must update the balances after the burn.
*/
function _burn(uint256[] memory balances, address positionManager, PositionState memory position) internal virtual;
/* ///////////////////////////////////////////////////////////////
SWAP LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Swaps one token for another, directly through the pool itself.
* @param balances The balances of the underlying tokens.
* @param position A struct with position and pool related variables.
* @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.
* @dev Must update the balances and sqrtPrice after the swap.
*/
function _swapViaPool(uint256[] memory balances, PositionState memory position, bool zeroToOne, uint256 amountOut)
internal
virtual;
/* ///////////////////////////////////////////////////////////////
MINT LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Mints a new Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @param amount0Desired The desired amount of token0 to mint as liquidity.
* @param amount1Desired The desired amount of token1 to mint as liquidity.
* @dev Must update the balances and liquidity and id after the mint.
*/
function _mint(
uint256[] memory balances,
address positionManager,
PositionState memory position,
uint256 amount0Desired,
uint256 amount1Desired
) internal virtual;
/* ///////////////////////////////////////////////////////////////
INCREASE LIQUIDITY LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Swaps one token for another to rebalance the Liquidity Position.
* @param balances The balances of the underlying tokens.
* @param positionManager The contract address of the Position Manager.
* @param position A struct with position and pool related variables.
* @param amount0Desired The desired amount of token0 to add as liquidity.
* @param amount1Desired The desired amount of token1 to add as liquidity.
* @dev Must update the balances and delta liquidity after the increase.
*/
function _increaseLiquidity(
uint256[] memory balances,
address positionManager,
PositionState memory position,
uint256 amount0Desired,
uint256 amount1Desired
) internal virtual;
/* ///////////////////////////////////////////////////////////////
ERC721 HANDLER FUNCTION
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns the onERC721Received selector.
* @dev Required to receive ERC721 tokens via safeTransferFrom.
*/
// forge-lint: disable-next-item(mixed-case-function)
function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
// 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.0;
import { ActionData } from "../../../lib/accounts-v2/src/interfaces/IActionBase.sol";
import { IPermit2 } from "../../../lib/accounts-v2/src/interfaces/IPermit2.sol";
library ArcadiaLogic {
/**
* @notice Encodes the action data for the flash-action used to manage a Liquidity Position.
* @param positionManager The address of the position manager.
* @param id The id of the Liquidity Position.
* @param token0 The contract address of token0.
* @param token1 The contract address of token1.
* @param amount0 The amount of token0 to transfer.
* @param amount1 The amount of token1 to transfer.
* @param actionTargetData The data to be passed to the action target.
* @return actionData Bytes string with the encoded data.
*/
function _encodeAction(
address positionManager,
uint256 id,
address token0,
address token1,
uint256 amount0,
uint256 amount1,
bytes memory actionTargetData
) internal pure returns (bytes memory actionData) {
// Calculate the number of assets to encode.
uint256 count = 1;
if (amount0 > 0) count++;
if (amount1 > 0) count++;
address[] memory assets = new address[](count);
uint256[] memory ids = new uint256[](count);
uint256[] memory amounts = new uint256[](count);
uint256[] memory types = new uint256[](count);
// Encode liquidity position.
assets[0] = positionManager;
ids[0] = id;
amounts[0] = 1;
types[0] = 2;
// Encode underlying assets of the liquidity position.
uint256 index = 1;
if (amount0 > 0) {
assets[1] = token0;
amounts[1] = amount0;
types[1] = 1;
index = 2;
}
if (amount1 > 0) {
assets[index] = token1;
amounts[index] = amount1;
types[index] = 1;
}
ActionData memory assetData =
ActionData({ assets: assets, assetIds: ids, assetAmounts: amounts, assetTypes: types });
// 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;
// Encode the actionData.
actionData = abi.encode(assetData, transferFromOwner, permit, signature, actionTargetData);
}
/**
* @notice Encodes the deposit data after the flash-action is executed.
* @param positionManager The address of the position manager.
* @param id The id of the Liquidity Position.
* @param tokens The contract addresses of the tokens to deposit.
* @param balances The balances of the tokens to deposit.
* @param count The number of tokens to deposit.
* @return depositData Bytes string with the encoded data.
*/
function _encodeDeposit(
address positionManager,
uint256 id,
address[] memory tokens,
uint256[] memory balances,
uint256 count
) internal pure returns (ActionData memory depositData) {
address[] memory assets = new address[](count);
uint256[] memory ids = new uint256[](count);
uint256[] memory amounts = new uint256[](count);
uint256[] memory types = new uint256[](count);
// Encode liquidity position.
assets[0] = positionManager;
ids[0] = id;
amounts[0] = 1;
types[0] = 2;
// Encode underlying assets of the liquidity position.
if (count > 1) {
uint256 i = 1;
for (uint256 j; j < balances.length; j++) {
if (balances[j] > 0) {
assets[i] = tokens[j];
amounts[i] = balances[j];
types[i] = 1;
i++;
}
}
}
depositData = ActionData({ assets: assets, assetIds: ids, assetAmounts: amounts, assetTypes: types });
}
}// 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.
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.
// 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.
success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
// 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 and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
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.
// 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.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// 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 and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
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.
// 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.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// 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 and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}// 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.0;
import { Owned } from "../../lib/accounts-v2/lib/solmate/src/auth/Owned.sol";
/**
* @title Guardian
* @author Pragma Labs
* @notice Pause guardian for an Asset Manager.
*/
abstract contract Guardian is Owned {
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Flag indicating if the Asset Manager is paused.
bool public paused;
// Address of the Guardian.
address public guardian;
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error Paused();
error OnlyGuardian();
/* //////////////////////////////////////////////////////////////
MODIFIERS
////////////////////////////////////////////////////////////// */
/**
* @dev Only guardians can call functions with this modifier.
*/
modifier onlyGuardian() {
_onlyGuardian();
_;
}
function _onlyGuardian() internal view {
if (msg.sender != guardian) revert OnlyGuardian();
}
/**
* @dev Throws if the Asset Manager is paused.
*/
modifier whenNotPaused() {
_whenNotPaused();
_;
}
function _whenNotPaused() internal view {
if (paused) revert Paused();
}
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param owner_ The address of the Owner.
*/
constructor(address owner_) Owned(owner_) { }
/* //////////////////////////////////////////////////////////////
GUARDIAN LOGIC
////////////////////////////////////////////////////////////// */
/**
* @notice Sets a new guardian.
* @param guardian_ The address of the new guardian.
*/
function changeGuardian(address guardian_) external onlyOwner {
guardian = guardian_;
}
/* //////////////////////////////////////////////////////////////
PAUSING LOGIC
////////////////////////////////////////////////////////////// */
/**
* @notice Pauses the Asset Manager.
*/
function pause() external onlyGuardian whenNotPaused {
paused = true;
}
/**
* @notice Sets the pause flag of the Asset Manager.
* @param paused_ Flag indicating if the Asset Manager is paused.
*/
function setPauseFlag(bool paused_) external onlyOwner {
paused = paused_;
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IAccount {
// forge-lint: disable-next-line(mixed-case-function)
function ACCOUNT_VERSION() external returns (uint256 version);
function flashAction(address actionTarget, bytes calldata actionData) external;
function owner() external returns (address owner_);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IArcadiaFactory {
/**
* @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.0;
interface IRouterTrampoline {
function execute(address router, bytes calldata callData, address tokenIn, address tokenOut, uint256 amountIn)
external
returns (uint256 balanceIn, uint256 balanceOut);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
import { PositionState } from "../state/PositionState.sol";
interface IStrategyHook {
function beforeRebalance(
address account,
address positionManager,
PositionState memory position,
bytes memory strategyData
) external view returns (int24 tickLower, int24 tickUpper);
function afterRebalance(
address account,
address positionManager,
uint256 oldId,
PositionState memory position,
bytes memory strategyData
) external;
function setStrategy(address account, bytes calldata strategyData) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
// A struct with the position and pool state.
struct PositionState {
// The contract address of the pool.
address pool;
// The id of the position.
uint256 id;
// The fee of the pool
uint24 fee;
// The tick spacing of the pool.
int24 tickSpacing;
// The current tick of the pool.
int24 tickCurrent;
// The lower tick of the position.
int24 tickUpper;
// The upper tick of the position.
int24 tickLower;
// The liquidity of the position.
uint128 liquidity;
// The sqrtPrice of the pool.
uint256 sqrtPrice;
// The underlying tokens of the pool.
address[] tokens;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { LiquidityAmounts } from "./LiquidityAmounts.sol";
import { CLMath } from "./CLMath.sol";
struct RebalanceParams {
// Bool indicating if token0 has to be swapped to token1 or opposite.
bool zeroToOne;
// The amount of initiator fee, in tokenIn.
uint256 amountInitiatorFee;
// The minimum amount of liquidity that must be added to the position.
uint256 minLiquidity;
// An approximation of the amount of tokenIn, based on the optimal swap through the pool itself without slippage.
uint256 amountIn;
// An approximation of the amount of tokenOut, based on the optimal swap through the pool itself without slippage.
uint256 amountOut;
}
library RebalanceLogic {
using FixedPointMathLib for uint256;
/**
* @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 rebalanceParams A struct with the rebalance parameters.
*/
function _getRebalanceParams(
uint256 minLiquidityRatio,
uint256 poolFee,
uint256 initiatorFee,
uint256 sqrtPrice,
uint256 sqrtRatioLower,
uint256 sqrtRatioUpper,
uint256 balance0,
uint256 balance1
) internal pure returns (RebalanceParams memory rebalanceParams) {
// 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
(bool zeroToOne, uint256 amountIn, uint256 amountOut) =
CLMath._getSwapParams(sqrtPrice, sqrtRatioLower, sqrtRatioUpper, balance0, balance1, fee);
// Calculate the maximum amount of liquidity that can be added to the position.
uint256 minLiquidity;
{
// forge-lint: disable-next-item(unsafe-typecast)
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).
uint256 amountInitiatorFee;
unchecked {
amountInitiatorFee = amountIn.mulDivDown(initiatorFee, 1e18);
amountIn = amountIn - amountInitiatorFee;
}
rebalanceParams = RebalanceParams({
zeroToOne: zeroToOne,
amountInitiatorFee: amountInitiatorFee,
minLiquidity: minLiquidity,
amountIn: amountIn,
amountOut: amountOut
});
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
import { FixedPointMathLib } from "../../../lib/accounts-v2/lib/solmate/src/utils/FixedPointMathLib.sol";
import { LiquidityAmounts } from "./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 criterion 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 maximize 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 maximize 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 maximize liquidity added.
* @param amountOut An approximation of the amount of tokenOut, that maximize 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 maximize 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 criterion of iteration is met.
* @return amountIn_ The new approximation of the amount of tokenIn that maximize liquidity added.
* @return amountOut_ The new approximation of the amount of amountOut that maximize 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/Vectorized/solady/blob/main/src/utils/SafeTransferLib.sol
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
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.
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
function safeCastTo248(uint256 x) internal pure returns (uint248 y) {
require(x < 1 << 248);
y = uint248(x);
}
function safeCastTo240(uint256 x) internal pure returns (uint240 y) {
require(x < 1 << 240);
y = uint240(x);
}
function safeCastTo232(uint256 x) internal pure returns (uint232 y) {
require(x < 1 << 232);
y = uint232(x);
}
function safeCastTo224(uint256 x) internal pure returns (uint224 y) {
require(x < 1 << 224);
y = uint224(x);
}
function safeCastTo216(uint256 x) internal pure returns (uint216 y) {
require(x < 1 << 216);
y = uint216(x);
}
function safeCastTo208(uint256 x) internal pure returns (uint208 y) {
require(x < 1 << 208);
y = uint208(x);
}
function safeCastTo200(uint256 x) internal pure returns (uint200 y) {
require(x < 1 << 200);
y = uint200(x);
}
function safeCastTo192(uint256 x) internal pure returns (uint192 y) {
require(x < 1 << 192);
y = uint192(x);
}
function safeCastTo184(uint256 x) internal pure returns (uint184 y) {
require(x < 1 << 184);
y = uint184(x);
}
function safeCastTo176(uint256 x) internal pure returns (uint176 y) {
require(x < 1 << 176);
y = uint176(x);
}
function safeCastTo168(uint256 x) internal pure returns (uint168 y) {
require(x < 1 << 168);
y = uint168(x);
}
function safeCastTo160(uint256 x) internal pure returns (uint160 y) {
require(x < 1 << 160);
y = uint160(x);
}
function safeCastTo152(uint256 x) internal pure returns (uint152 y) {
require(x < 1 << 152);
y = uint152(x);
}
function safeCastTo144(uint256 x) internal pure returns (uint144 y) {
require(x < 1 << 144);
y = uint144(x);
}
function safeCastTo136(uint256 x) internal pure returns (uint136 y) {
require(x < 1 << 136);
y = uint136(x);
}
function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
require(x < 1 << 128);
y = uint128(x);
}
function safeCastTo120(uint256 x) internal pure returns (uint120 y) {
require(x < 1 << 120);
y = uint120(x);
}
function safeCastTo112(uint256 x) internal pure returns (uint112 y) {
require(x < 1 << 112);
y = uint112(x);
}
function safeCastTo104(uint256 x) internal pure returns (uint104 y) {
require(x < 1 << 104);
y = uint104(x);
}
function safeCastTo96(uint256 x) internal pure returns (uint96 y) {
require(x < 1 << 96);
y = uint96(x);
}
function safeCastTo88(uint256 x) internal pure returns (uint88 y) {
require(x < 1 << 88);
y = uint88(x);
}
function safeCastTo80(uint256 x) internal pure returns (uint80 y) {
require(x < 1 << 80);
y = uint80(x);
}
function safeCastTo72(uint256 x) internal pure returns (uint72 y) {
require(x < 1 << 72);
y = uint72(x);
}
function safeCastTo64(uint256 x) internal pure returns (uint64 y) {
require(x < 1 << 64);
y = uint64(x);
}
function safeCastTo56(uint256 x) internal pure returns (uint56 y) {
require(x < 1 << 56);
y = uint56(x);
}
function safeCastTo48(uint256 x) internal pure returns (uint48 y) {
require(x < 1 << 48);
y = uint48(x);
}
function safeCastTo40(uint256 x) internal pure returns (uint40 y) {
require(x < 1 << 40);
y = uint40(x);
}
function safeCastTo32(uint256 x) internal pure returns (uint32 y) {
require(x < 1 << 32);
y = uint32(x);
}
function safeCastTo24(uint256 x) internal pure returns (uint24 y) {
require(x < 1 << 24);
y = uint24(x);
}
function safeCastTo16(uint256 x) internal pure returns (uint16 y) {
require(x < 1 << 16);
y = uint16(x);
}
function safeCastTo8(uint256 x) internal pure returns (uint8 y) {
require(x < 1 << 8);
y = uint8(x);
}
}// 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;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
interface ICLPositionManager {
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
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 sqrtPrice;
}
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);
function factory() external view returns (address);
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
returns (uint128 liquidity, uint256 amount0, uint256 amount1);
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 mint(MintParams calldata params)
external
payable
returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
import { FixedPoint96 } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/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 CLMath {
using FixedPointMathLib for uint256;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The minimum sqrtPriceLimit for a swap.
uint160 internal constant MIN_SQRT_PRICE_LIMIT = TickMath.MIN_SQRT_PRICE + 1;
// The maximum sqrtPriceLimit for a swap.
uint160 internal constant MAX_SQRT_PRICE_LIMIT = TickMath.MAX_SQRT_PRICE - 1;
// The binary precision of sqrtPrice squared.
uint256 internal constant Q192 = FixedPoint96.Q96 ** 2;
/* //////////////////////////////////////////////////////////////
MATHS
////////////////////////////////////////////////////////////// */
/**
* @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 maximize 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 + amountOut] / [(amount0 - amountIn) * sqrtPrice² + (amount1 + amountOut)].
* 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 + amountOut) * 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 = _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 value of one token in the other token for a given amountIn and sqrtPrice.
* Does not take into account slippage and fees.
* @param sqrtPrice 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 sqrtPrice is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPrice of a pool exceeds type(uint128).max, a different contract has to be deployed,
* which does two consecutive mulDivs.
*/
function _getSpotValue(uint256 sqrtPrice, bool zeroToOne, uint256 amountIn)
internal
pure
returns (uint256 amountOut)
{
amountOut = zeroToOne
? FullMath.mulDiv(amountIn, sqrtPrice ** 2, Q192)
: FullMath.mulDiv(amountIn, Q192, sqrtPrice ** 2);
}
/**
* @notice Calculates the amountOut for a given amountIn and sqrtPrice for a hypothetical
* swap though the pool itself with fees but without slippage.
* @param sqrtPrice 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 sqrtPrice is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPrice of a pool exceeds type(uint128).max, a different contract has to be deployed,
* which does two consecutive mulDivs.
*/
function _getAmountOut(uint256 sqrtPrice, bool zeroToOne, uint256 amountIn, uint256 fee)
internal
pure
returns (uint256 amountOut)
{
require(sqrtPrice <= type(uint128).max);
unchecked {
uint256 amountInWithoutFees = (1e18 - fee).mulDivDown(amountIn, 1e18);
amountOut = zeroToOne
? FullMath.mulDiv(amountInWithoutFees, sqrtPrice ** 2, Q192)
: FullMath.mulDiv(amountInWithoutFees, Q192, sqrtPrice ** 2);
}
}
/**
* @notice Calculates the amountIn for a given amountOut and sqrtPrice for a hypothetical
* swap though the pool itself with fees but without slippage.
* @param sqrtPrice 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 sqrtPrice is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPrice of a pool exceeds type(uint128).max, a different contract has to be deployed,
* which does two consecutive mulDivs.
*/
function _getAmountIn(uint256 sqrtPrice, bool zeroToOne, uint256 amountOut, uint256 fee)
internal
pure
returns (uint256 amountIn)
{
require(sqrtPrice <= type(uint128).max);
unchecked {
uint256 amountInWithoutFees = zeroToOne
? FullMath.mulDiv(amountOut, Q192, sqrtPrice ** 2)
: FullMath.mulDiv(amountOut, sqrtPrice ** 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 sqrtPrice 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 sqrtPrice is bigger than type(uint128).max.
* type(uint128).max is currently more than enough for all supported pools.
* If ever the sqrtPrice of a pool exceeds type(uint128).max, a different contract 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 sqrtPrice, uint256 sqrtRatioLower, uint256 sqrtRatioUpper)
internal
pure
returns (uint256 targetRatio)
{
require(sqrtPrice <= type(uint128).max);
// Unchecked: sqrtPrice is always bigger than sqrtRatioLower.
// Unchecked: sqrtPrice is always smaller than sqrtRatioUpper -> sqrtPrice > sqrtPrice ** 2 / sqrtRatioUpper.
unchecked {
uint256 numerator = sqrtPrice - sqrtRatioLower;
uint256 denominator = 2 * sqrtPrice - sqrtRatioLower - sqrtPrice ** 2 / sqrtRatioUpper;
targetRatio = numerator.mulDivDown(1e18, denominator);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
interface ICLPool {
function fee() external view returns (uint24);
function liquidity() external view returns (uint128 liquidity_);
function slot0()
external
view
returns (
uint160 sqrtPrice,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
bool unlocked
);
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimit,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IStakedSlipstream {
function burn(uint256 id) external returns (uint256 rewards);
function claimReward(uint256 positionId) external returns (uint256 rewards);
function mint(uint256 id) external returns (uint256 id_);
// forge-lint: disable-next-line(mixed-case-function)
function REWARD_TOKEN() external view returns (address rewardToken);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
library SlipstreamLogic {
/**
* @notice Deterministically computes the pool address.
* @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
});
}
/**
* @notice Computes the address of a clone deployed.
* @param master The contract address of the master.
* @param salt The salt of the clone.
* @param deployer The deployer of the clone.
* @return predicted The predicted address of the clone.
*/
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)
}
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
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;
}// 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);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}/**
* https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/LiquidityAmounts.sol
* SPDX-License-Identifier: GPL-2.0-or-later
*/
pragma solidity ^0.8.0;
import { FixedPoint96 } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/FixedPoint96.sol";
import { FullMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/FullMath.sol";
/**
* @title Liquidity amount functions
* @notice Provides functions for computing liquidity amounts from token amounts and prices
*/
// forge-lint: disable-next-item(mixed-case-variable,unsafe-typecast)
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);
}
}
}// 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;
/// @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: 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;
}// 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);
}
}
}
}// 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)
}
}
}{
"remappings": [
"@ensdomains/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/@ensdomains/",
"@nomad-xyz/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/",
"@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"@solidity-parser/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
"@uniswap/v2-core/contracts/=lib/lending-v2/lib/accounts-v2/./test/utils/fixtures/swap-router-02/",
"@uniswap/v3-core/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-core/contracts/",
"@uniswap/v3-periphery/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
"@uniswap/v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/",
"@utils/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/utils/src/",
"ExcessivelySafeCall/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/src/",
"accounts-v2/=lib/lending-v2/lib/accounts-v2/src/",
"arcadia-periphery/=lib/arcadia-periphery/src/",
"asset-managers/=lib/arcadia-periphery/lib/asset-managers/src/",
"base64-sol/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
"base64/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
"contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/contracts/",
"ds-test/=lib/lending-v2/lib/accounts-v2/lib/solmate/lib/ds-test/src/",
"erc4626-tests/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/lending-v2/lib/accounts-v2/lib/forge-std/src/",
"hardhat/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/hardhat/",
"lending-v2/=lib/lending-v2/src/",
"merkl-contracts/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/",
"openzeppelin-contracts-upgradeable-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/",
"openzeppelin-contracts-v3.4/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/contracts/",
"openzeppelin-contracts-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
"openzeppelin-contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/contracts/",
"openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
"oz/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/@openzeppelin/contracts/",
"permit2/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/",
"slipstream/=lib/lending-v2/lib/accounts-v2/lib/slipstream/",
"solady/=lib/lending-v2/lib/accounts-v2/lib/solady/src/",
"solidity-lib/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/solidity-lib/contracts/",
"solmate/=lib/lending-v2/lib/accounts-v2/lib/solmate/",
"swap-router-contracts/=lib/lending-v2/lib/accounts-v2/lib/swap-router-contracts/contracts/",
"v3-core/=lib/lending-v2/lib/accounts-v2/lib/v3-core/",
"v3-periphery/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
"v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/",
"v4-periphery/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/",
"lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
"lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
"lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
"lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/asset-managers/lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/asset-managers/lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/asset-managers/lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/asset-managers/lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
"lib/merkl-contracts:@openzeppelin/contracts/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
"lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
"lib/slipstream:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/v3-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/v4-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/lending-v2/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": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"arcadiaFactory","type":"address"},{"internalType":"address","name":"routerTrampoline","type":"address"},{"internalType":"address","name":"positionManager","type":"address"},{"internalType":"address","name":"cLFactory","type":"address"},{"internalType":"address","name":"poolImplementation","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"address","name":"stakedSlipstreamAm","type":"address"},{"internalType":"address","name":"stakedSlipstreamWrapper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InvalidAccountVersion","type":"error"},{"inputs":[],"name":"InvalidInitiator","type":"error"},{"inputs":[],"name":"InvalidPositionManager","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"NotAnAccount","type":"error"},{"inputs":[],"name":"OnlyAccount","type":"error"},{"inputs":[],"name":"OnlyAccountOwner","type":"error"},{"inputs":[],"name":"OnlyGuardian","type":"error"},{"inputs":[],"name":"OnlyPool","type":"error"},{"inputs":[],"name":"Paused","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":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"YieldClaimed","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accountInfo","outputs":[{"internalType":"uint64","name":"maxClaimFee","type":"uint64"},{"internalType":"uint64","name":"maxSwapFee","type":"uint64"},{"internalType":"uint64","name":"upperSqrtPriceDeviation","type":"uint64"},{"internalType":"uint64","name":"lowerSqrtPriceDeviation","type":"uint64"},{"internalType":"uint64","name":"minLiquidityRatio","type":"uint64"},{"internalType":"address","name":"strategyHook","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"accountToInitiator","outputs":[{"internalType":"address","name":"initiator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian_","type":"address"}],"name":"changeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"actionTargetData","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":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sqrtPrice","type":"uint256"},{"components":[{"internalType":"uint256","name":"lowerBoundSqrtPrice","type":"uint256"},{"internalType":"uint256","name":"upperBoundSqrtPrice","type":"uint256"},{"internalType":"uint160","name":"sqrtRatioLower","type":"uint160"},{"internalType":"uint160","name":"sqrtRatioUpper","type":"uint160"}],"internalType":"struct Rebalancer.Cache","name":"cache","type":"tuple"}],"name":"isPoolBalanced","outputs":[{"internalType":"bool","name":"isBalanced","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"positionManager","type":"address"}],"name":"isPositionManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"metaData","outputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"view","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":"accountOwner","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onSetAssetManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"components":[{"internalType":"address","name":"positionManager","type":"address"},{"internalType":"uint96","name":"oldId","type":"uint96"},{"internalType":"uint128","name":"amountIn0","type":"uint128"},{"internalType":"uint128","name":"amountIn1","type":"uint128"},{"internalType":"uint128","name":"amountOut0","type":"uint128"},{"internalType":"uint128","name":"amountOut1","type":"uint128"},{"internalType":"uint256","name":"trustedSqrtPrice","type":"uint256"},{"internalType":"uint64","name":"claimFee","type":"uint64"},{"internalType":"uint64","name":"swapFee","type":"uint64"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"bytes","name":"strategyData","type":"bytes"}],"internalType":"struct Rebalancer.InitiatorParams","name":"initiatorParams","type":"tuple"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"uint256","name":"maxClaimFee","type":"uint256"},{"internalType":"uint256","name":"maxSwapFee","type":"uint256"},{"internalType":"uint256","name":"maxTolerance","type":"uint256"},{"internalType":"uint256","name":"minLiquidityRatio","type":"uint256"},{"internalType":"address","name":"strategyHook","type":"address"},{"internalType":"bytes","name":"strategyData","type":"bytes"},{"internalType":"bytes","name":"metaData_","type":"bytes"}],"name":"setAccountInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"paused_","type":"bool"}],"name":"setPauseFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","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
610180346101da57601f61584f38819003918201601f19168301916001600160401b038311848410176101de57808492610120946040528339810103126101da57610049816101f2565b610055602083016101f2565b91610062604082016101f2565b61006e606083016101f2565b61007a608084016101f2565b61008660a085016101f2565b9161009360c086016101f2565b936100ad6101006100a660e089016101f2565b97016101f2565b5f80546001600160a01b0319166001600160a01b039099169889178155604051999198907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36001600160a01b0390811660805290811660a0521660e05260c052610100526101205261014052610160526156489081610207823960805181818161092c01526117fa015260a051816147e5015260c0518181816115970152612b90015260e0518181816116cf0152818161268801528181612a9201528181612f1c015281816132d6015281816133e80152818161394c0152613b2e0152610100518181816115b80152612bb1015261012051818181612d0301528181612d3b01528181612d710152818161303201526133760152610140518161168c015261016051816116ff0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101da5756fe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c80630686ddd9146101595780630a73e391146101545780631204f5251461014f578063150b7a021461014a5780632e7df039146101455780632fcb4f041461014057806341dc24da1461013b578063452a9320146101365780635c975abb146101315780635f4860df1461012c5780638456cb59146101275780638da5cb5b146101225780638da92e711461011d578063a129568d14610118578063a7310b5814610113578063a89d6dd41461010e578063bc25cf7714610109578063f2fde38b14610104578063fa461e33146100ff5763ffa1ad740361000e57611643565b611514565b6114a0565b6113c7565b61134d565b611277565b610b62565b610a3f565b610a18565b6109cb565b6108af565b610881565b610859565b610544565b6104f1565b610463565b610409565b610381565b610350565b61026d565b6001600160a01b0381160361016f57565b5f80fd5b60c435906101808261015e565b565b35906101808261015e565b90600182811c921680156101bb575b60208310146101a757565b634e487b7160e01b5f52602260045260245ffd5b91607f169161019c565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b038211176101f457604052565b6101c5565b60a081019081106001600160401b038211176101f457604052565b90601f801991011681019081106001600160401b038211176101f457604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90602061026a928181520190610235565b90565b3461016f57602036600319011261016f5760043561028a8161015e565b6001600160a01b03165f90815260046020526040808220905181549092916102b18261018d565b808552916001811690811561032957506001146102e9575b6102e5846102d981860382610214565b60405191829182610259565b0390f35b5f90815260208120939250905b80821061030f575090915081016020016102d9826102c9565b9192600181602092548385880101520191019092916102f6565b60ff191660208087019190915292151560051b850190920192506102d991508390506102c9565b3461016f57602036600319011261016f5760206103776004356103728161015e565b61168a565b6040519015158152f35b3461016f57604036600319011261016f5760206004356103a08161015e565b602435906103ad8261015e565b6001600160a01b039081165f908152600584526040808220938316825260209390935282902054915191168152f35b9181601f8401121561016f578235916001600160401b03831161016f576020838186019501011161016f57565b3461016f57608036600319011261016f5761042560043561015e565b61043060243561015e565b6064356001600160401b03811161016f5761044f9036906004016103dc565b5050604051630a85bd0160e11b8152602090f35b3461016f5761012036600319011261016f576004356104818161015e565b60243561048d8161015e565b6044359160843560643560a4356104a2610173565b9060e4356001600160401b03811161016f576104c29036906004016103dc565b94909361010435986001600160401b038a1161016f576104e96100189a36906004016103dc565b9990986117aa565b3461016f57602036600319011261016f5760043561050e8161015e565b61052260018060a01b035f54163314611945565b600180546001600160a01b0319166001600160a01b0392909216919091179055005b3461016f57604036600319011261016f576004356105618161015e565b602435906001600160401b03821161016f578160040190610160600319843603011261016f5761058f61259e565b6002546001600160a01b031661084057600280546001600160a01b0319166001600160a01b038316179055604051638da5cb5b60e01b81526001600160a01b03821693906020816004815f895af190811561077a578361061161063393610626935f91610811575b506001600160a01b03165f90815260056020526040902090565b9060018060a01b03165f5260205260405f2090565b546001600160a01b031690565b336001600160a01b03909116036108025761065761065361037285611980565b1590565b6107f357610715926106836106fc9360018060a01b03166001600160601b0360a01b6002541617600255565b5f5f908360448101936001600160801b0361069d8661199b565b16158015906107d0575b61077f575b6001600160801b03808261070a6106eb60646106e46106de60246106d76001600160601b039a611980565b9a016119b6565b9b61199b565b9b0161199b565b916040519b8c913360208401611a3b565b03601f1981018c528b610214565b16961694169061282b565b90803b1561016f5760405162b9252f60e41b8152905f90829081838161073f883060048401611b9a565b03925af1801561077a57610760575b600280546001600160a01b0319169055005b8061076e5f61077493610214565b8061084f565b8061074e565b611745565b6001600160801b0393508092506107969150611980565b50836001600160601b03836107c46107bf6107b3602486016119b6565b6001600160601b031690565b61266a565b959094925050506106ac565b506107ec6107e06064840161199b565b6001600160801b031690565b15156106a7565b63ed5f09f160e01b5f5260045ffd5b6317fb43e560e31b5f5260045ffd5b610833915060203d602011610839575b61082b8183610214565b810190611750565b5f6105f7565b503d610821565b63b5dfd9e560e01b5f5260045ffd5b5f91031261016f57565b3461016f575f36600319011261016f576001546040516001600160a01b039091168152602090f35b3461016f575f36600319011261016f57602060ff5f5460a01c166040519015158152f35b8015150361016f57565b3461016f57606036600319011261016f576004356108cc8161015e565b6108d76024356108a5565b6044356001600160401b03811161016f576108f69036906004016103dc565b6002549192916001600160a01b031661084057604051630972932760e21b81523360048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa90811561077a575f9161099c575b501561098d578261097291610018940190611bd7565b9790966001600160a01b03928316969395939216903361237e565b630ea8370b60e41b5f5260045ffd5b6109be915060203d6020116109c4575b6109b68183610214565b810190611730565b5f61095c565b503d6109ac565b3461016f575f36600319011261016f576001546001600160a01b03163303610a09576109f561259e565b5f805460ff60a01b1916600160a01b179055005b636570ecab60e11b5f5260045ffd5b3461016f575f36600319011261016f575f546040516001600160a01b039091168152602090f35b3461016f57602036600319011261016f57600435610a5c816108a5565b610a7060018060a01b035f54163314611945565b5f805460ff60a01b191691151560a01b60ff60a01b16919091179055005b90602080835192838152019201905f5b818110610aab5750505090565b82516001600160a01b0316845260209384019390920191600101610a9e565b90602080835192838152019201905f5b818110610ae75750505090565b8251845260209384019390920191600101610ada565b61026a916060610b40610b2e610b1c8551608086526080860190610a8e565b60208601518582036020870152610aca565b60408501518482036040860152610aca565b920151906060818403910152610aca565b90602061026a928181520190610afd565b3461016f57602036600319011261016f576004356001600160401b03811161016f57610b929036906004016103dc565b90610b9b611c55565b50600254610bb9906001600160a01b03165b6001600160a01b031690565b330361126857335f908152600360205260409020610be49190610bdb90611c79565b92810190611d30565b80516001600160a01b03169060e0810193610c0685516001600160401b031690565b6001600160401b03610c2e610c2284516001600160401b031690565b6001600160401b031690565b9116118015611231575b611222576020820193610c5e610c586107b387516001600160601b031690565b85612a63565b91610120830193610c70855151611e63565b93610c886107e060408401516001600160801b031690565b610c9186611ea9565b52610ca96107e060608401516001600160801b031690565b610cb286611ebb565b52610cbd8551611e63565b60a084018051919a9091610cdb90610bad906001600160a01b031681565b94610d0960408b610140880198878a519284519586948593849363c92d78a360e01b85523360048601611fc8565b03915afa90811561077a57610d35915f915f916111ed575b5060020b60a08701525b60020b60c0860152565b610d4460c08601518583612dee565b90610100850192610d596106538486516120bd565b611167578d868d610d77610c22610d7e95516001600160401b031690565b928d612f09565b610d89858c8b6132c7565b610d93858a6133de565b858d6080808301938c6001600160801b0380610db688516001600160801b031690565b160361119057610de4610ddf610dce610df193611ea9565b51610dd887611ea9565b519061203a565b6135bf565b6001600160801b03168652565b01516001600160401b0316936040880151610e0e9062ffffff1690565b94610100840151610e25906001600160401b031690565b8751604084015190918f916001600160a01b031660608601519091906001600160a01b031693610e5484611ea9565b51610e5e89611ea9565b51610e689161203a565b8a516001600160801b03166001600160801b0316610e859161203a565b93610e8f90611ebb565b51610e9989611ebb565b51610ea39161203a565b9a60a08a019b8c51610ebb906001600160801b031690565b6001600160801b0316610ecd9161203a565b610ef99790966001600160a01b039081169516936001600160401b039081169262ffffff1691166135d7565b958c82888b8151610f0990151590565b5f149761065397610f4295610f569a61117657610f336020860151610f2d84611ea9565b51612063565b610f3c83611ea9565b526136c7565b610f4e610bad8a6137f8565b8091526120bd565b61116757610fd0918d8a92610f6b8651151590565b1561112d576107e0610fb9610fc79493610dd8610fb2610fac610fa696610fa66107e0610f978d611ea9565b5192516001600160801b031690565b9061203a565b98611ebb565b5191611ebb565b92516001600160801b031690565b905b858a61392a565b6001600160801b036040610fee60e08601516001600160801b031690565b92015191161061111e57610bad610bad6110199261100c858c613b23565b516001600160a01b031690565b88516001600160601b0316935193813b1561016f575f918391838b611055604051998a968795869463bd6884a360e01b86523360048701612070565b03925af194851561077a5760206110ae838a6110bc966102e59e866110fb9b6110ca9b7ffea7a9a6e25cd0bbbfa80ce0c7646e61ee5e0551b3fdaaff0642e6f6adcc72e29e61110a575b506001600160a01b0316613c21565b920196875190519089613e33565b95516001600160601b031690565b9251604080516001600160601b03909516855260208501919091526001600160a01b03909416933393918291820190565b0390a360405191829182610b51565b8061076e5f61111893610214565b5f61109f565b63bb55fd2760e01b5f5260045ffd5b610fa66107e0610fb961115194610dd861114a6111619899611ea9565b5191611ea9565b91610fa66107e0610f978d611ebb565b90610fc9565b633a8bf65960e01b5f5260045ffd5b6111876020860151610f2d84611ebb565b610f3c83611ebb565b60a085016001600160801b036111b06107e083516001600160801b031690565b146111bd575b5050610df1565b6111d9610ddf6111cf6111e694611ebb565b51610dd888611ebb565b6001600160801b03169052565b8c5f6111b6565b610d2b9250611214915060403d60401161121b575b61120c8183610214565b810190611f05565b9091610d21565b503d611202565b632a9ffab760e21b5f5260045ffd5b506101008201516001600160401b03166001600160401b03611260610c2260208501516001600160401b031690565b911611610c38565b63f3f6425d60e01b5f5260045ffd5b3461016f57602036600319011261016f576004356112948161015e565b6001600160a01b039081165f90815260036020908152604091829020805460019091015483516001600160401b03808416825283861c811694820194909452608083811c85168287015260c093841c60608301529382169381019390935290921c90921660a083015290f35b6040519061018061016083610214565b6040519061018060c083610214565b60405190610180608083610214565b6040519061018060a083610214565b6040519061018061018083610214565b3461016f5760a036600319011261016f57600435608036602319011261016f576113b56102e59160405190611381826101d9565b6024358252604435602083015260643561139a8161015e565b60408301526084356113ab8161015e565b60608301526120bd565b60405190151581529081906020820190565b3461016f57602036600319011261016f576004356113e48161015e565b6113f860018060a01b035f54163314611945565b61140061259e565b6002546001600160a01b0316610840576001600160a01b03168061143857506100185f80808047335af16114326120d6565b90612105565b6040516370a0823160e01b815230600482015290602082602481845afa90811561077a57610018925f9261146f575b503390613f49565b61149291925060203d602011611499575b61148a8183610214565b810190611765565b905f611467565b503d611480565b3461016f57602036600319011261016f576004356114bd8161015e565b5f54906114d4336001600160a01b03841614611945565b60018060a01b031680916001600160601b0360a01b16175f55337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b3461016f57606036600319011261016f576024356004356044356001600160401b03811161016f5761154c60609136906004016103dc565b908092918101031261016f578035926115648461015e565b6115dc60406020840135936115788561015e565b01359461158486611eef565b6001600160a01b039384169593169285847f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613fd9565b336001600160a01b0390911603611619575f831315611602575061001892503390613f49565b9150505f811361160e57005b610018913390613f49565b634b60273560e01b5f5260045ffd5b6001600160401b0381116101f457601f01601f191660200190565b3461016f575f36600319011261016f576102e5604051611664604082610214565b6005815264322e312e3160d81b6020820152604051918291602083526020830190610235565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691169081149081156116fd575b81156116cd575090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614919050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316811491506116c3565b9081602091031261016f575161026a816108a5565b6040513d5f823e3d90fd5b9081602091031261016f575161026a8161015e565b9081602091031261016f575190565b92919261178082611628565b9161178e6040519384610214565b82948184528183011161016f578281602093845f960137010152565b989091929394959699979960018060a01b036117cd60025460018060a01b031690565b1661084057604051630972932760e21b81526001600160a01b038b166004820152602081806024810103817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561077a575f91611926575b501561098d57604051638da5cb5b60e01b8152916001600160a01b038b166020846004815f855af193841561077a575f94611905575b506001600160a01b03841633036118f65760205f91600460405180948193635e34633b60e11b83525af1801561077a576003915f916118d7575b50106118c8576118ba6118c2926101809d3691611774565b993691611774565b9861237e565b63a93eca7960e01b5f5260045ffd5b6118f0915060203d6020116114995761148a8183610214565b5f6118a2565b6312272fd360e11b5f5260045ffd5b61191f91945060203d6020116108395761082b8183610214565b925f611868565b61193f915060203d6020116109c4576109b68183610214565b5f611832565b1561194c57565b60405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606490fd5b3561026a8161015e565b6001600160801b0381160361016f57565b3561026a8161198a565b6001600160601b0381160361016f57565b3561026a816119a5565b3590610180826119a5565b35906101808261198a565b35906001600160401b038216820361016f57565b9035601e198236030181121561016f5701602081359101916001600160401b03821161016f57813603831361016f57565b908060209392818452848401375f828201840152601f01601f1916010190565b61026a929160018060a01b0316815260406020820152611a6e60408201611a6184610182565b6001600160a01b03169052565b611a8d611a7d602084016119c0565b6001600160601b03166060830152565b611aac611a9c604084016119cb565b6001600160801b03166080830152565b611acb611abb606084016119cb565b6001600160801b031660a0830152565b611aea611ada608084016119cb565b6001600160801b031660c0830152565b611b09611af960a084016119cb565b6001600160801b031660e0830152565b60c0820135610100820152611b34611b2360e084016119d6565b6001600160401b0316610120830152565b611b55611b4461010084016119d6565b6001600160401b0316610140830152565b611b87611b7b611b696101208501856119ea565b610160808601526101a0850191611a1b565b926101408101906119ea565b91610180603f1982860301910152611a1b565b6001600160a01b03909116815260406020820181905261026a92910190610235565b9080601f8301121561016f5781602061026a93359101611774565b9190916101008184031261016f578035611bf08161015e565b9260208201359260408301359260608101359260808201359260a0830135611c178161015e565b9260c08101356001600160401b03811161016f5783611c37918301611bbc565b9260e08201356001600160401b03811161016f5761026a9201611bbc565b60405190611c62826101d9565b606080838181528160208201528160408201520152565b9060405160c08101908082106001600160401b038311176101f4576101809160405260a0611d2260018396611d02611cf282546001600160401b03811688526001600160401b038160401c166020890152611cec6001600160401b038260801c1660408a01906001600160401b03169052565b60c01c90565b6001600160401b03166060870152565b01546001600160401b038116608085015260401c6001600160a01b031690565b6001600160a01b0316910152565b919060408382031261016f578235611d478161015e565b926020810135906001600160401b03821161016f57016101608183031261016f57611d70611300565b91611d7a82610182565b8352611d88602083016119c0565b6020840152611d99604083016119cb565b6040840152611daa606083016119cb565b6060840152611dbb608083016119cb565b6080840152611dcc60a083016119cb565b60a084015260c082013560c0840152611de760e083016119d6565b60e0840152611df961010083016119d6565b6101008401526101208201356001600160401b03811161016f5781611e1f918401611bbc565b6101208401526101408201356001600160401b03811161016f57611e439201611bbc565b61014082015290565b6001600160401b0381116101f45760051b60200190565b90611e6d82611e4c565b611e7a6040519182610214565b8281528092611e8b601f1991611e4c565b0190602036910137565b634e487b7160e01b5f52603260045260245ffd5b805115611eb65760200190565b611e95565b805160011015611eb65760400190565b805160021015611eb65760600190565b8051821015611eb65760209160051b010190565b8060020b0361016f57565b519061018082611eef565b919082604091031261016f5760208251611f1e81611eef565b92015161026a81611eef565b80516001600160a01b0316825261026a9190610140906101209060208101516020850152611f646040820151604086019062ffffff169052565b60608181015160020b9085015260808181015160020b9085015260a08181015160020b9085015260c08181015160020b9085015260e0818101516001600160801b031690850152610100810151610100850152015191816101208201520190610a8e565b6001600160a01b0391821681529116602082015260806040820181905261026a939192611ff791840190611f2a565b916060818403910152610235565b634e487b7160e01b5f52601160045260245ffd5b670de0b6b3a76400000390670de0b6b3a7640000821161203557565b612005565b9190820391821161203557565b670de0b6b3a7640000019081670de0b6b3a76400001161203557565b9190820180921161203557565b6001600160a01b039182168152911660208201526001600160601b03909116604082015260a06060820181905261026a9391926120af91840190611f2a565b916080818403910152610235565b8151811191826120cc57505090565b6020015111919050565b3d15612100573d906120e782611628565b916120f56040519384610214565b82523d5f602084013e565b606090565b1561210d5750565b60405162461bcd60e51b815260206004820152908190612131906024830190610235565b0390fd5b90670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561203557565b9061223460a060016101809461218f6001600160401b0386511682906001600160401b03166001600160401b0319825416179055565b60208501518154604080880151606089015160809190911b67ffffffffffffffff60801b169390911b6fffffffffffffffff0000000000000000166001600160401b03909216919091179190911760c09190911b6001600160c01b031916178155019261222661220960808301516001600160401b031690565b855467ffffffffffffffff19166001600160401b03909116178555565b01516001600160a01b031690565b815468010000000000000000600160e01b03191660409190911b68010000000000000000600160e01b0316179055565b601f821161227157505050565b5f5260205f20906020601f840160051c830193106122a9575b601f0160051c01905b81811061229e575050565b5f8155600101612293565b909150819061228a565b91909182516001600160401b0381116101f4576122da816122d4845461018d565b84612264565b6020601f821160011461231957819061230a9394955f9261230e575b50508160011b915f199060031b1c19161790565b9055565b015190505f806122f6565b601f1982169061232c845f5260205f2090565b915f5b8181106123665750958360019596971061234e575b505050811b019055565b01515f1960f88460031b161c191690555f8080612344565b9192602060018192868b01518155019401920161232f565b97989390969594929196670de0b6b3a76400008311801561258d575b801561257c575b801561256b575b611222576124e19561248961249a936124796124c5976124088d8f6123e9906106116001600160401b039a60018060a01b03165f52600560205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b6124698661243a610c2261242f61242a612434610c2261242f61242a8a612047565b612135565b614087565b96612019565b9461245882612447611310565b9c168c906001600160401b03169052565b166001600160401b031660208a0152565b6001600160401b03166040880152565b6001600160401b03166060860152565b166001600160401b03166080830152565b6001600160a01b03851660a08201526001600160a01b0387165f908152600360205260409020612159565b6001600160a01b0385165f9081526004602052604090206122b3565b6001600160a01b031692833b1561016f575f6125129160405180938192632f9c799b60e21b83528760048401611b9a565b038183885af1801561077a57612557575b506001600160a01b0390811691167f343ef5cc595144359c9db657cd7fcef6ecc88d06d17651a8292e553ab73b1c705f80a4565b8061076e5f61256593610214565b5f612523565b50670de0b6b3a764000086116123a8565b50670de0b6b3a764000084116123a1565b50670de0b6b3a7640000821161239a565b60ff5f5460a01c166125ac57565b6313d0ff5960e31b5f5260045ffd5b51906101808261198a565b91908261018091031261016f5781516125de816119a5565b9160208101516125ed8161015e565b9160408201516125fc8161015e565b91606081015161260b8161015e565b91608082015161261a81611eef565b9161262760a08201611efa565b9161263460c08301611efa565b9161264160e082016125bb565b91610100820151916101208101519161026a61016061266361014085016125bb565b93016125bb565b60405163133f757160e31b81526004810191909152610180816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa801561077a575f915f916126c557509091565b90506126e991506101803d81116126f8575b6126e18183610214565b8101906125c6565b50505050505050509250905091565b503d6126d7565b5f1981146120355760010190565b6040516080919061271e8382610214565b6003815291601f1901366020840137565b604051606091906127408382610214565b6002815291601f1901366020840137565b60405190606082018281106001600160401b038211176101f4576040525f604083606081528260208201520152565b929493916127996127a79260a0865260a0860190610afd565b908482036020860152610afd565b828103604084015260608101948051956060835286518091526020608084019701905f5b81811061280057505050946120af9160408088602061026a999a01516020850152015191015283810360608501526060610235565b825180516001600160a01b03168a52602090810151818b0152604090990198909201916001016127cb565b929461026a95949294600193801515908161297a575b831515908161296a575b61285487611e63565b9861285e88611e63565b9661288d61287461286e8b611e63565b9a611e63565b9a61287e8d611ea9565b6001600160a01b039091169052565b61289688611ea9565b5260016128a289611ea9565b5260026128ae8a611ea9565b5260019361293a575b505061290c575b5050506128c961131f565b9384526020840152604083015260608201526128fe6128e6611c55565b936128ef612751565b60405195869460208601612780565b03601f198101835282610214565b6001926129206129319361287e848b611edb565b61292a8287611edb565b5285611edb565b525f80806128be565b61294a9192935061287e8a611ebb565b61295386611ebb565b52600161295f87611ebb565b526002905f806128b7565b95612974906126ff565b9561284b565b60029550612841565b6040519061014082018281106001600160401b038211176101f4576040526060610120835f81525f60208201525f60408201525f838201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201520152565b519061ffff8216820361016f57565b91908260c091031261016f578151612a048161015e565b916020810151612a1381611eef565b91612a20604083016129de565b91612a2d606082016129de565b9160a0612a3c608084016129de565b92015161026a816108a5565b9081602091031261016f575162ffffff8116810361016f5790565b612aca90929192612a72612983565b6020810185905260405163133f757160e31b8152600481019590955293917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169061018090849081906024820190565b0381845afa91821561077a57612be293612bd59387935f935f935f905f905f905f96612d99575b50612b3a929160c08a612b2a612b3394612b206060612b8d9d9e9f019b60e08501906001600160801b03169052565b60020b60a0830152565b019060020b9052565b60020b8452565b6001600160a01b0316148015612d63575b8015612d2d575b15612cec57612b5f61272f565b6101208901525b612b868361287e6101208b01612b808861287e8351611ea9565b51611ebb565b5160020b90565b917f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613fd9565b6001600160a01b03168352565b815160049060c090612bfe90610bad906001600160a01b031681565b604051633850c7bd60e01b815292839182905afa801561077a575f915f91612cb1575b5060020b60808401525b6001600160a01b039081166101008401528251600491602091612c5091610bad911681565b60405163ddca3f4360e01b815292839182905afa801561077a57610180915f91612c82575b5062ffffff166040840152565b612ca4915060203d602011612caa575b612c9c8183610214565b810190612a48565b5f612c75565b503d612c92565b612c2b9250612cd8915060c03d60c011612ce5575b612cd08183610214565b8101906129ed565b5050505091909190612c21565b503d612cc6565b612d28612cf761270d565b806101208b015261287e7f000000000000000000000000000000000000000000000000000000000000000091611ecb565b612b66565b506001600160a01b038381167f000000000000000000000000000000000000000000000000000000000000000090911614612b52565b506001600160a01b038481167f000000000000000000000000000000000000000000000000000000000000000090911614612b4b565b612b2a9850612b8d9750612b339650612b3a935060c09250612dca91506101803d81116126f8576126e18183610214565b505050509b9650945095929c919c9b909b9c9b95939450509a999850509192612af1565b9190612ea6612e5d61026a935f6060604051612e09816101d9565b8281528260208201528260408201520152612e57610c226040612e486001600160401b03612e4160608c01516001600160401b031690565b1685614149565b9801516001600160401b031690565b90614149565b91612e85612e7760a0612e7c612e7760c086015160020b90565b614219565b93015160020b90565b92612e8e61131f565b95865260208601526001600160a01b03166040850152565b6001600160a01b03166060830152565b919082604091031261016f576020825192015190565b91909160606001600160801b038160808401958051855260018060a01b036020820151166020860152826040820151166040860152015116910152565b6001600160a01b039283169491929091907f0000000000000000000000000000000000000000000000000000000000000000168581146131085750602084810151604051630ae169a560e41b8152600481019190915295869060249082905f905af194851561077a575f956130e3575b50612f849085614149565b9160038251145f146130115790612fbe91612fb8612fb2612fa888610f2d86611ecb565b95610f2d84611ecb565b91611ecb565b52611ecb565b5261012001516001600160a01b0390612fda9061100c90611ecb565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79080602081015b0390a3565b9092610120019261302561100c8551611ea9565b6001600160a01b039081167f00000000000000000000000000000000000000000000000000000000000000009091160361309e576130869161308061307a61307088610f2d86611ea9565b95610f2d84611ea9565b91611ea9565b52611ea9565b52516001600160a01b0390612fda9061100c90611ea9565b6130cb916130c56130bf6130b588610f2d86611ebb565b95610f2d84611ebb565b91611ebb565b52611ebb565b52516001600160a01b0390612fda9061100c90611ebb565b612f849195506131019060203d6020116114995761148a8183610214565b9490612f79565b849392955061315e60409160205f97015161312161131f565b9081523060208201526001600160801b03818501526001600160801b036060820152835197888094819363fc6f786560e01b835260048301612ecc565b03925af194851561077a575f945f96613266575b50610bad9361100c936131e46130bf6131db8996866131b46130bf8e6131a16132319f610f2d6101209d611ea9565b6131aa85611ea9565b52610f2d84611ebb565b526131cb6131c2828a614149565b610f2d87611ea9565b6131d486611ea9565b528b614149565b610f2d84611ebb565b52019060018060a01b036131fb61100c8451611ea9565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd790602090a351611ebb565b6040519182529033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd790806020810161300c565b6130bf965061323195509361100c936131e46130bf6131db6131b496610120966132aa610bad9b60403d6040116132c0575b6132a28183610214565b810190612eb6565b9990999d50509650969950505050935093613172565b503d613298565b916001600160a01b03918216917f000000000000000000000000000000000000000000000000000000000000000016820361330157505050565b602081810151604051630852cd8d60e31b8152600481019190915292839060249082905f905af191821561077a575f926133bd575b508161334157505050565b825160030361335857509061335590611ecb565b52565b61100c610120613369920151611ea9565b6001600160a01b039081167f0000000000000000000000000000000000000000000000000000000000000000909116036133ad5761307a61335591610f2d84611ea9565b6130bf61335591610f2d84611ebb565b6133d791925060203d6020116114995761148a8183610214565b905f613336565b9060018060a01b037f000000000000000000000000000000000000000000000000000000000000000016916134a76040602084019361344761342c60e087519301516001600160801b031690565b61343461132e565b9283526001600160801b03166020830152565b5f8183018190526060820190815242608083019081528351630624e65f60e11b81528351600482015260208401516001600160801b03166024820152604090930151604484015290516064830152516084820152918290819060a4820190565b03815f885af1801561077a5761359a575b50613503604083516134c861131f565b9081523060208201526001600160801b03818301526001600160801b03606082015281518093819263fc6f786560e01b835260048301612ecc565b03815f885af190811561077a5761352f916130bf915f905f92613576575b506131a190610f2d86611ea9565b525190803b1561016f57604051630852cd8d60e31b815260048101929092525f908290602490829084905af1801561077a576135685750565b8061076e5f61018093610214565b6131a19250613594915060403d6040116132c0576132a28183610214565b91613521565b6135b29060403d6040116132c0576132a28183610214565b506134b8565b1561016f57565b600160801b81101561016f576001600160801b031690565b906107e08661367995613629888c8c899c9b9e9d9b6136729a64e8d4a5100061366d9a5f6080604051613609816101f9565b828152826020820152826040820152826060820152015202019386614539565b9b8c82849f9495939c8183885f146136b657506136459161203a565b955b156136a5575061365691612063565b935b6001600160a01b039081169281169116614640565b614149565b9282614149565b9161368c61368561132e565b9515158652565b8260208601526040850152036060830152608082015290565b90506136b09161203a565b93613658565b90506136c191612063565b95613647565b9293949160608601918251156137ef5761012082015180516137d6575091846137468893610fa66107e060a06137376137b69a99610dd8610fb261373161371a6137108c611ea9565b51610dd886611ea9565b610fa66107e060808c01516001600160801b031690565b99611ebb565b9301516001600160801b031690565b916137518451151590565b9461376260408a015162ffffff1690565b9561376c8a6148f5565b6101008b01516001600160a01b03169162ffffff60806137a9606061379a604089015160018060a01b031690565b9701516001600160a01b031690565b965199015199169061495f565b9182156137d0576137ca6101809451151590565b91614b09565b50505050565b9593505050506137e96101809451151590565b91614799565b50505050505050565b51604051633850c7bd60e01b81529060c090829060049082906001600160a01b03165afa90811561077a575f9161382d575090565b613846915060c03d60c011612ce557612cd08183610214565b505050505090565b919082608091031261016f57815191602081015161386b8161198a565b916060604083015192015190565b81516001600160a01b031681526101808101929161018091906101609081906020818101516001600160a01b03169085015260408181015160020b9085015260608181015160020b9085015260808181015160020b9085015260a0818101519085015260c0808201519085015260e080820151908501526101008082015190850152610120808201516001600160a01b031690850152610140818101519085015201516001600160a01b0316910152565b92613a805f929360809261012086019061394a610bad61100c8451611ea9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031693906139839082908690614c81565b61399d8285613998610bad61100c8851611ebb565b614c81565b613a366139ba61100c6139b361100c8751611ea9565b9551611ebb565b613a2d8a613a236139cf606083015160020b90565b613a196139ee60a06139e560c087015160020b90565b95015160020b90565b95613a096139fa61133d565b6001600160a01b03909c168c52565b6001600160a01b031660208b0152565b60020b6040890152565b60020b6060870152565b60020b84880152565b60a083015260c082015260e08101859052610100810185905230610120820152426101408201528461016082015260405194858094819363b5007d1f60e01b835260048301613879565b03925af191821561077a57613355926130bf925f5f5f925f94613ae0575b5084613abc60209260e0613acd979801906001600160801b03169052565b0152613ac786611ea9565b5161203a565b613ad685611ea9565b52613ac784611ebb565b613acd945060209350613abc9250613b10915060803d608011613b1c575b613b088183610214565b81019061384e565b95509391925090613a9e565b503d613afe565b906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169190831690828203613b615750505050565b602001928351833b1561016f5760405163095ea7b360e01b81526001600160a01b039290921660048301526024820152915f908390604490829084905af190811561077a575f93602093613bd793613c0d575b505160405194858094819363140e25ad60e31b8352600483019190602083019252565b03925af1801561077a57613bee575b8080806137d0565b613c069060203d6020116114995761148a8183610214565b505f613be6565b8061076e87613c1b93610214565b5f613bb4565b602086015193969590949093906001600160a01b0316803b1561016f5760405163095ea7b360e01b815233600482015260248101929092525f908290604490829084905af1801561077a57613e1f575b506001955f5b8251811015613e175760018111613e115780613df0576001600160801b03613ca960808401516001600160801b031690565b165b613cbd61100c83610120890151611edb565b90613ccc81613ac78588611edb565b613cd68488611edb565b511015613dc75750613cf6613ceb8386611edb565b51610dd88488611edb565b613d008386611edb565b525b613d0c8285611edb565b51613d95575b90600191613d208287611edb565b51613d74575b613d308287611edb565b5160405190815260a084901b8490039182169189169033907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e05663190602090a401613c77565b613d90613d818388611edb565b5189858060a01b038416613f49565b613d26565b9790613dbe600192613db9613daa8488611edb565b51338d878060a01b0316614c81565b6126ff565b98909150613d12565b613dd581613ac78588611edb565b613ddf8488611edb565b52613dea8386611edb565b52613d02565b6001600160801b03613e0c60a08401516001600160801b031690565b613ca9565b5f613cab565b505050505050565b8061076e5f613e2d93610214565b5f613c71565b9293909193613e40611c55565b50613e4a82611e63565b946001613e5684611e63565b93613e6081611e63565b95613e77613e6d83611e63565b9861287e8b611ea9565b613e8086611ea9565b5281613e8b87611ea9565b526002613e9788611ea9565b5211613ebd575b5050613ea861131f565b93845260208401526040830152606082015290565b9194909395926001935f5b8651811015613f3957613edb8188611edb565b51613ee9575b600101613ec8565b94613f31600191613f0a613f0061100c8a8a611edb565b61287e8389611edb565b613f14888a611edb565b51613f1f8287611edb565b5282613f2b828d611edb565b526126ff565b959050613ee1565b5096945092509390505f80613e9e565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516613fcc575b5015613f9557565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f613f8d565b916001600160a01b03848116908216101561016f57604080516001600160a01b0392831660208201908152929095169085015260029490940b606084015260559360379361402a81608081016128fe565b5190209060405192733d602d80600a3d3981f3363d3d373d3d3d363d7360601b845260601b60148401526f5af43d82803e903d91602b57fd5bf3ff60801b602884015260601b6038830152604c820152818120606c820152012090565b60b581600160881b811015614132575b80690100000000000000000062010000921015614125575b65010000000000811015614118575b630100000081101561410b575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8080920410900390565b60101c9160081b916140cb565b60201c9160101b916140be565b60401c9160201b916140af565b5068b500000000000000009050608082901c614097565b90805f19048211810215670de0b6b3a7640000021561016f57670de0b6b3a764000091020490565b7812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111670de0b6b3a7640000021582021561016f57670de0b6b3a7640000020490565b90805f19048211810215620f4240021561016f57620f424091020490565b7f1c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71811160090215600a021561016f576009600a91020490565b815f1904811182021583021561016f57020490565b60020b908160ff1d82810118620d89e881116145335763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102600160801b189160028116614517575b600481166144fb575b600881166144df575b601081166144c3575b602081166144a7575b6040811661448b575b6080811661446f575b6101008116614453575b6102008116614437575b610400811661441b575b61080081166143ff575b61100081166143e3575b61200081166143c7575b61400081166143ab575b618000811661438f575b620100008116614373575b620200008116614358575b62040000811661433d575b6208000016614324575b5f1261431c575b0160201c90565b5f1904614315565b6b048a170391f7dc42444e8fa290910260801c9061430e565b6d2216e584f5fa1ea926041bedfe9890920260801c91614304565b916e5d6af8dedb81196699c329225ee6040260801c916142f9565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c916142ee565b916f31be135f97d08fd981231505542fcfa60260801c916142e3565b916f70d869a156d2a1b890bb3df62baf32f70260801c916142d9565b916fa9f746462d870fdf8a65dc1f90e061e50260801c916142cf565b916fd097f3bdfd2022b8845ad8f792aa58250260801c916142c5565b916fe7159475a2c29b7443b29c7fa6e889d90260801c916142bb565b916ff3392b0822b70005940c7a398e4b70f30260801c916142b1565b916ff987a7253ac413176f2b074cf7815e540260801c916142a7565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c9161429d565b916ffe5dee046a99a2a811c461f1969c30530260801c91614293565b916fff2ea16466c96a3843ec78b326b528610260801c9161428a565b916fff973b41fa98c081472e6896dfb254c00260801c91614281565b916fffcb9843d60f6159c9db58835c9266440260801c91614278565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c9161426f565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c91614266565b916ffff97272373d413259a46990580e213a0260801c9161425d565b82614cfd565b94929593905f92808710155f1461455c575050505061026a906001948094614e34565b9296919281871161457657505050908161026a9294614dfa565b909196506001600160801b03869493961161016f57801561463b576145ba6145b36145c7936145c0936002880a04818860011b0303908703614171565b9785614ddf565b86612063565b8095614171565b938585101561460e5761460b92939495614604916001976145f385670de0b6b3a7640000038685614204565b670de0b6b3a7640000019203614204565b8093614e6d565b91565b61026a9294614634915f976146238582614149565b670de0b6b3a7640000039203614204565b8094614dfa565b614d12565b919390916001600160a01b03808616908416116146d6575b6001600160a01b038181169084168111614682575050509061026a9261467d92614ee6565b614ed2565b929391929091906001600160a01b03861611156146c757906146ac6146b2939261026a9683614ee6565b93614ea1565b808210156146c05750614ed2565b9050614ed2565b505061026a9261467d92614ea1565b919391614658565b909160608284031261016f5781516146f58161015e565b926020830151926040810151906001600160401b03821161016f570181601f8201121561016f5780519061472882611628565b926147366040519485610214565b8284526020838301011161016f57815f9260208093018386015e8301015290565b9161477e906080949796959260018060a01b0316845260a0602085015260a0840190610235565b6001600160a01b039687166040840152951660608201520152565b909260406147b361012092602080825183010191016146de565b939096865f146148d35701906147d961100c6147d261100c8551611ea9565b9351611ebb565b935b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116925f91614818908b9086908816613f49565b6148408a8751988997889687956344bc658560e01b875260018060a01b031660048701614757565b03925af192831561077a575f915f946148b0575b501561488a57614877614881916148726133559596613ac786611ea9565b612063565b93610f2d83611ebb565b61308082611ebb565b6148ab906148726148a161335595610f2d86611ea9565b95613ac785611ebb565b614881565b9093506148cc915060403d6040116132c0576132a28183610214565b925f614854565b01906148ef61100c6148e861100c8551611ebb565b9351611ea9565b936147db565b51604051630d34328160e11b815290602090829060049082906001600160a01b03165afa90811561077a575f9161492a575090565b90506020813d602011614957575b8161494560209383610214565b8101031261016f575161026a8161198a565b3d9150614938565b97919492959690935f925b6064841061497f575050505050505050505090565b61499a9086620f42409c939495969798999a9b9c03906141ad565b8a15614a8f576001600160a01b03906149b490898b6152d5565b16906001600160a01b03906149ca90898b61539a565b165b8a15614a72570160011c6001600160a01b03165b6001600160a01b038181169086168110614a045750505050505061026a9450614f53565b9498979695946001600160a01b0383161015614a5f5780828b858b8d614a30614a38978e8e8e88614f78565b959094614fc4565b809b92959195614a505750600101929190989361496a565b9a505050505050505050505090565b50509550509091925061026a9450614f2d565b6001600160a01b039180820160011c9160029108151501166149e0565b906001600160a01b0390614aa490898b615276565b16906001600160a01b0390614aba90898b61533e565b166149cc565b600160ff1b8114612035575f0390565b6001600160a01b039182168152911515602083015260408201929092529116606082015260a06080820181905261026a92910190610235565b92614bc79291604091614b31614b2b610bad610bad855160018060a01b031690565b91614ac0565b8415614c65575f6401000276a4915b614ba96101208601614b9b614b6c6060614b6361100c6139b361100c8751611ea9565b99015160020b90565b89516001600160a01b03938416602082015298909216604089015260029190910b606088015286906080820190565b03601f198101875286610214565b8551630251596160e31b8152978895869485938a3060048701614ad0565b03925af190811561077a57613355925f905f93614c41575b508115614c2c57614bf390613ac786611ea9565b614bfc85611ea9565b5215614c1f576130bf90614c19614c1285611ebb565b5191614ac0565b90612063565b6130bf90613ac784611ebb565b614c3c90614c19614c1287611ea9565b614bf3565b9050614c5d91925060403d6040116132c0576132a28183610214565b91905f614bdf565b5f73fffd8963efd1fc6a506488495d951d5263988d2591614b40565b906014528160345263095ea7b360601b5f5260205f6044601082855af13d1560015f5114171615614cb5575b50505f603452565b604460105f80936020958260345263095ea7b360601b83528238868683865af1506034525af13d1560015f5114171615614cf0575f80614cad565b633e3f8f735f526004601cfd5b6345c3193d60e11b5f5260020b60045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b9290600193925b60018211614d39575050565b909280820481116120355760018416614d59575b80029260011c90614d2d565b80940293614d4d565b614d735f196002600160601b614d26565b90815f19048111612035570290565b905f918015614dd9578080600114614dd157600214614dca5760016101338210166001600b83101617614dbf57614d7391925060025f1991614d26565b6002900a9161203557565b5060049150565b506001925050565b505f9150565b90614dec61026a92614d82565b614df4614d62565b916151f3565b90916001600160801b03821161016f5761026a92614e2191670de0b6b3a764000003614149565b906002614e2c614d62565b910a916151f3565b90916001600160801b03821161016f5761026a92614e5b91670de0b6b3a764000003614149565b6002614e65614d62565b920a906151f3565b9190916001600160801b03811161016f5761026a92614e90916002614e2c614d62565b90670de0b6b3a76400000390614171565b61026a9291906001600160a01b0380831690821611614ecc575b90036001600160a01b03169061511d565b90614ebb565b906001600160801b03821691820361016f57565b61026a92916001600160a01b0380821690831611614f27575b614f156001600160a01b038281169084166151aa565b9190036001600160a01b0316916151f3565b90614eff565b614f4661026a94614f4d92949394620f42400390615438565b838361533e565b9061549a565b614f6c61026a94614f7392949394620f42400390615438565b83836152d5565b61555e565b93949391929115614fa95790614fa38392614f9761026a9584896154fe565b90620f4240039061545b565b9461555e565b93614fbe8295614f97858461026a979661558a565b9461549a565b939497959296909196845f146150ea57888111156150e157614fea8982035b8984614ee6565b90614ff88588018486614ea1565b985b898310998a156150d15761500e8185614171565b9a5b15615076575050509061502661502c9392614ed2565b9161558a565b911561506757620f4240929190828211156150565750035b925b670de0b6b3a76400000310929190565b905061506291506141cb565b615044565b939450620f4240910393615046565b90925061508e91969750615094945099949899614ed2565b916154fe565b92156150a757500392620f424090615046565b9493620f42409290828211156150c05750035b92615046565b90506150cc91506141cb565b6150ba565b6150db8482614171565b9a615010565b614fea5f614fe3565b6150f78482018984614ee6565b90898711156151145761510e8a88035b8486614ea1565b98614ffa565b61510e5f615107565b90606082901b905f19600160601b84099282808510940393808503946151448685116135b8565b146151a3578190600160601b900981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b81810291905f1982820991838084109303928084039384600160601b111561016f57146151ea57600160601b910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f198185099383808610950394808603956152148786116135b8565b1461526e579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b919081156152d0576001600160a01b03909216918183029160609190911b600160601b600160e01b0316908204831482821116156152c35761026a926152be928203916155c6565b6155ef565b63f5c787f15f526004601cfd5b505090565b919081156152d05760601b600160601b600160e01b0316916001600160a01b0316818102828104821461532a575b50801561463b5782049081018091116120355780820615159104016001600160a01b031690565b8301838110615303579150610bad926155c6565b916001600160a01b038111615381576001600160801b039060601b911690811561463b5704905b6001600160a01b03169081019081106120355761026a906155ef565b6001600160801b0361539492169061511d565b90615365565b91906001600160a01b0382116153fb576001600160801b036153bf6153cc9360601b90565b9116808206151591040190565b905b6001600160a01b031690808211156153ee5790036001600160a01b031690565b634323a5555f526004601cfd5b6001600160801b031661541381600160601b846151f3565b91811561463b57600160601b900961542c575b906153ce565b60010180615426575f80fd5b815f19048111820215620f4240021561016f5702620f4240808204910615150190565b7d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98111620f4240021582021561016f57620f42400290808204910615150190565b6001600160a01b03828116908216116154f8575b6001600160a01b0381169283156154ec576154e8926001600160a01b0380821693909103169060601b600160601b600160e01b03166151f3565b0490565b62bfc9215f526004601cfd5b906154ae565b6001600160a01b0382811690821611615558575b6001600160a01b0381169283156154ec5761554c926001600160a01b0380821693909103169060601b600160601b600160e01b03166155c6565b90808206151591040190565b90615512565b61026a926001600160a01b03928316919092160360ff81901d90810118906001600160801b03166151aa565b6001600160a01b0391821691160360ff81901d90810118906001906001600160801b03166155b883826151aa565b928260601b91091515160190565b9291906155d48282866151f3565b93821561463b57096155e257565b9060010190811561016f57565b6001600160a01b0381169190820361560357565b6393dafdf160e01b5f5260045ffdfea264697066735822122032441f6e2a19fdc65eec2f6bae8a2d476038bb0c041afe28e11c4fc7aafe088f64736f6c634300081e0033000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470200000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca80000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a810000000000000000000000001dc7a0f5336f52724b650e39174cfcbbedd67bf1000000000000000000000000d74339e0f10fce96894916b93e5cc7de89c98272
Deployed Bytecode
0x6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c80630686ddd9146101595780630a73e391146101545780631204f5251461014f578063150b7a021461014a5780632e7df039146101455780632fcb4f041461014057806341dc24da1461013b578063452a9320146101365780635c975abb146101315780635f4860df1461012c5780638456cb59146101275780638da5cb5b146101225780638da92e711461011d578063a129568d14610118578063a7310b5814610113578063a89d6dd41461010e578063bc25cf7714610109578063f2fde38b14610104578063fa461e33146100ff5763ffa1ad740361000e57611643565b611514565b6114a0565b6113c7565b61134d565b611277565b610b62565b610a3f565b610a18565b6109cb565b6108af565b610881565b610859565b610544565b6104f1565b610463565b610409565b610381565b610350565b61026d565b6001600160a01b0381160361016f57565b5f80fd5b60c435906101808261015e565b565b35906101808261015e565b90600182811c921680156101bb575b60208310146101a757565b634e487b7160e01b5f52602260045260245ffd5b91607f169161019c565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b038211176101f457604052565b6101c5565b60a081019081106001600160401b038211176101f457604052565b90601f801991011681019081106001600160401b038211176101f457604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90602061026a928181520190610235565b90565b3461016f57602036600319011261016f5760043561028a8161015e565b6001600160a01b03165f90815260046020526040808220905181549092916102b18261018d565b808552916001811690811561032957506001146102e9575b6102e5846102d981860382610214565b60405191829182610259565b0390f35b5f90815260208120939250905b80821061030f575090915081016020016102d9826102c9565b9192600181602092548385880101520191019092916102f6565b60ff191660208087019190915292151560051b850190920192506102d991508390506102c9565b3461016f57602036600319011261016f5760206103776004356103728161015e565b61168a565b6040519015158152f35b3461016f57604036600319011261016f5760206004356103a08161015e565b602435906103ad8261015e565b6001600160a01b039081165f908152600584526040808220938316825260209390935282902054915191168152f35b9181601f8401121561016f578235916001600160401b03831161016f576020838186019501011161016f57565b3461016f57608036600319011261016f5761042560043561015e565b61043060243561015e565b6064356001600160401b03811161016f5761044f9036906004016103dc565b5050604051630a85bd0160e11b8152602090f35b3461016f5761012036600319011261016f576004356104818161015e565b60243561048d8161015e565b6044359160843560643560a4356104a2610173565b9060e4356001600160401b03811161016f576104c29036906004016103dc565b94909361010435986001600160401b038a1161016f576104e96100189a36906004016103dc565b9990986117aa565b3461016f57602036600319011261016f5760043561050e8161015e565b61052260018060a01b035f54163314611945565b600180546001600160a01b0319166001600160a01b0392909216919091179055005b3461016f57604036600319011261016f576004356105618161015e565b602435906001600160401b03821161016f578160040190610160600319843603011261016f5761058f61259e565b6002546001600160a01b031661084057600280546001600160a01b0319166001600160a01b038316179055604051638da5cb5b60e01b81526001600160a01b03821693906020816004815f895af190811561077a578361061161063393610626935f91610811575b506001600160a01b03165f90815260056020526040902090565b9060018060a01b03165f5260205260405f2090565b546001600160a01b031690565b336001600160a01b03909116036108025761065761065361037285611980565b1590565b6107f357610715926106836106fc9360018060a01b03166001600160601b0360a01b6002541617600255565b5f5f908360448101936001600160801b0361069d8661199b565b16158015906107d0575b61077f575b6001600160801b03808261070a6106eb60646106e46106de60246106d76001600160601b039a611980565b9a016119b6565b9b61199b565b9b0161199b565b916040519b8c913360208401611a3b565b03601f1981018c528b610214565b16961694169061282b565b90803b1561016f5760405162b9252f60e41b8152905f90829081838161073f883060048401611b9a565b03925af1801561077a57610760575b600280546001600160a01b0319169055005b8061076e5f61077493610214565b8061084f565b8061074e565b611745565b6001600160801b0393508092506107969150611980565b50836001600160601b03836107c46107bf6107b3602486016119b6565b6001600160601b031690565b61266a565b959094925050506106ac565b506107ec6107e06064840161199b565b6001600160801b031690565b15156106a7565b63ed5f09f160e01b5f5260045ffd5b6317fb43e560e31b5f5260045ffd5b610833915060203d602011610839575b61082b8183610214565b810190611750565b5f6105f7565b503d610821565b63b5dfd9e560e01b5f5260045ffd5b5f91031261016f57565b3461016f575f36600319011261016f576001546040516001600160a01b039091168152602090f35b3461016f575f36600319011261016f57602060ff5f5460a01c166040519015158152f35b8015150361016f57565b3461016f57606036600319011261016f576004356108cc8161015e565b6108d76024356108a5565b6044356001600160401b03811161016f576108f69036906004016103dc565b6002549192916001600160a01b031661084057604051630972932760e21b81523360048201526020816024816001600160a01b037f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59165afa90811561077a575f9161099c575b501561098d578261097291610018940190611bd7565b9790966001600160a01b03928316969395939216903361237e565b630ea8370b60e41b5f5260045ffd5b6109be915060203d6020116109c4575b6109b68183610214565b810190611730565b5f61095c565b503d6109ac565b3461016f575f36600319011261016f576001546001600160a01b03163303610a09576109f561259e565b5f805460ff60a01b1916600160a01b179055005b636570ecab60e11b5f5260045ffd5b3461016f575f36600319011261016f575f546040516001600160a01b039091168152602090f35b3461016f57602036600319011261016f57600435610a5c816108a5565b610a7060018060a01b035f54163314611945565b5f805460ff60a01b191691151560a01b60ff60a01b16919091179055005b90602080835192838152019201905f5b818110610aab5750505090565b82516001600160a01b0316845260209384019390920191600101610a9e565b90602080835192838152019201905f5b818110610ae75750505090565b8251845260209384019390920191600101610ada565b61026a916060610b40610b2e610b1c8551608086526080860190610a8e565b60208601518582036020870152610aca565b60408501518482036040860152610aca565b920151906060818403910152610aca565b90602061026a928181520190610afd565b3461016f57602036600319011261016f576004356001600160401b03811161016f57610b929036906004016103dc565b90610b9b611c55565b50600254610bb9906001600160a01b03165b6001600160a01b031690565b330361126857335f908152600360205260409020610be49190610bdb90611c79565b92810190611d30565b80516001600160a01b03169060e0810193610c0685516001600160401b031690565b6001600160401b03610c2e610c2284516001600160401b031690565b6001600160401b031690565b9116118015611231575b611222576020820193610c5e610c586107b387516001600160601b031690565b85612a63565b91610120830193610c70855151611e63565b93610c886107e060408401516001600160801b031690565b610c9186611ea9565b52610ca96107e060608401516001600160801b031690565b610cb286611ebb565b52610cbd8551611e63565b60a084018051919a9091610cdb90610bad906001600160a01b031681565b94610d0960408b610140880198878a519284519586948593849363c92d78a360e01b85523360048601611fc8565b03915afa90811561077a57610d35915f915f916111ed575b5060020b60a08701525b60020b60c0860152565b610d4460c08601518583612dee565b90610100850192610d596106538486516120bd565b611167578d868d610d77610c22610d7e95516001600160401b031690565b928d612f09565b610d89858c8b6132c7565b610d93858a6133de565b858d6080808301938c6001600160801b0380610db688516001600160801b031690565b160361119057610de4610ddf610dce610df193611ea9565b51610dd887611ea9565b519061203a565b6135bf565b6001600160801b03168652565b01516001600160401b0316936040880151610e0e9062ffffff1690565b94610100840151610e25906001600160401b031690565b8751604084015190918f916001600160a01b031660608601519091906001600160a01b031693610e5484611ea9565b51610e5e89611ea9565b51610e689161203a565b8a516001600160801b03166001600160801b0316610e859161203a565b93610e8f90611ebb565b51610e9989611ebb565b51610ea39161203a565b9a60a08a019b8c51610ebb906001600160801b031690565b6001600160801b0316610ecd9161203a565b610ef99790966001600160a01b039081169516936001600160401b039081169262ffffff1691166135d7565b958c82888b8151610f0990151590565b5f149761065397610f4295610f569a61117657610f336020860151610f2d84611ea9565b51612063565b610f3c83611ea9565b526136c7565b610f4e610bad8a6137f8565b8091526120bd565b61116757610fd0918d8a92610f6b8651151590565b1561112d576107e0610fb9610fc79493610dd8610fb2610fac610fa696610fa66107e0610f978d611ea9565b5192516001600160801b031690565b9061203a565b98611ebb565b5191611ebb565b92516001600160801b031690565b905b858a61392a565b6001600160801b036040610fee60e08601516001600160801b031690565b92015191161061111e57610bad610bad6110199261100c858c613b23565b516001600160a01b031690565b88516001600160601b0316935193813b1561016f575f918391838b611055604051998a968795869463bd6884a360e01b86523360048701612070565b03925af194851561077a5760206110ae838a6110bc966102e59e866110fb9b6110ca9b7ffea7a9a6e25cd0bbbfa80ce0c7646e61ee5e0551b3fdaaff0642e6f6adcc72e29e61110a575b506001600160a01b0316613c21565b920196875190519089613e33565b95516001600160601b031690565b9251604080516001600160601b03909516855260208501919091526001600160a01b03909416933393918291820190565b0390a360405191829182610b51565b8061076e5f61111893610214565b5f61109f565b63bb55fd2760e01b5f5260045ffd5b610fa66107e0610fb961115194610dd861114a6111619899611ea9565b5191611ea9565b91610fa66107e0610f978d611ebb565b90610fc9565b633a8bf65960e01b5f5260045ffd5b6111876020860151610f2d84611ebb565b610f3c83611ebb565b60a085016001600160801b036111b06107e083516001600160801b031690565b146111bd575b5050610df1565b6111d9610ddf6111cf6111e694611ebb565b51610dd888611ebb565b6001600160801b03169052565b8c5f6111b6565b610d2b9250611214915060403d60401161121b575b61120c8183610214565b810190611f05565b9091610d21565b503d611202565b632a9ffab760e21b5f5260045ffd5b506101008201516001600160401b03166001600160401b03611260610c2260208501516001600160401b031690565b911611610c38565b63f3f6425d60e01b5f5260045ffd5b3461016f57602036600319011261016f576004356112948161015e565b6001600160a01b039081165f90815260036020908152604091829020805460019091015483516001600160401b03808416825283861c811694820194909452608083811c85168287015260c093841c60608301529382169381019390935290921c90921660a083015290f35b6040519061018061016083610214565b6040519061018060c083610214565b60405190610180608083610214565b6040519061018060a083610214565b6040519061018061018083610214565b3461016f5760a036600319011261016f57600435608036602319011261016f576113b56102e59160405190611381826101d9565b6024358252604435602083015260643561139a8161015e565b60408301526084356113ab8161015e565b60608301526120bd565b60405190151581529081906020820190565b3461016f57602036600319011261016f576004356113e48161015e565b6113f860018060a01b035f54163314611945565b61140061259e565b6002546001600160a01b0316610840576001600160a01b03168061143857506100185f80808047335af16114326120d6565b90612105565b6040516370a0823160e01b815230600482015290602082602481845afa90811561077a57610018925f9261146f575b503390613f49565b61149291925060203d602011611499575b61148a8183610214565b810190611765565b905f611467565b503d611480565b3461016f57602036600319011261016f576004356114bd8161015e565b5f54906114d4336001600160a01b03841614611945565b60018060a01b031680916001600160601b0360a01b16175f55337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b3461016f57606036600319011261016f576024356004356044356001600160401b03811161016f5761154c60609136906004016103dc565b908092918101031261016f578035926115648461015e565b6115dc60406020840135936115788561015e565b01359461158486611eef565b6001600160a01b039384169593169285847f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f7f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca8613fd9565b336001600160a01b0390911603611619575f831315611602575061001892503390613f49565b9150505f811361160e57005b610018913390613f49565b634b60273560e01b5f5260045ffd5b6001600160401b0381116101f457601f01601f191660200190565b3461016f575f36600319011261016f576102e5604051611664604082610214565b6005815264322e312e3160d81b6020820152604051918291602083526020830190610235565b7f0000000000000000000000001dc7a0f5336f52724b650e39174cfcbbedd67bf16001600160a01b0390811691169081149081156116fd575b81156116cd575090565b7f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b031614919050565b7f000000000000000000000000d74339e0f10fce96894916b93e5cc7de89c982726001600160a01b0316811491506116c3565b9081602091031261016f575161026a816108a5565b6040513d5f823e3d90fd5b9081602091031261016f575161026a8161015e565b9081602091031261016f575190565b92919261178082611628565b9161178e6040519384610214565b82948184528183011161016f578281602093845f960137010152565b989091929394959699979960018060a01b036117cd60025460018060a01b031690565b1661084057604051630972932760e21b81526001600160a01b038b166004820152602081806024810103817f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03165afa90811561077a575f91611926575b501561098d57604051638da5cb5b60e01b8152916001600160a01b038b166020846004815f855af193841561077a575f94611905575b506001600160a01b03841633036118f65760205f91600460405180948193635e34633b60e11b83525af1801561077a576003915f916118d7575b50106118c8576118ba6118c2926101809d3691611774565b993691611774565b9861237e565b63a93eca7960e01b5f5260045ffd5b6118f0915060203d6020116114995761148a8183610214565b5f6118a2565b6312272fd360e11b5f5260045ffd5b61191f91945060203d6020116108395761082b8183610214565b925f611868565b61193f915060203d6020116109c4576109b68183610214565b5f611832565b1561194c57565b60405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606490fd5b3561026a8161015e565b6001600160801b0381160361016f57565b3561026a8161198a565b6001600160601b0381160361016f57565b3561026a816119a5565b3590610180826119a5565b35906101808261198a565b35906001600160401b038216820361016f57565b9035601e198236030181121561016f5701602081359101916001600160401b03821161016f57813603831361016f57565b908060209392818452848401375f828201840152601f01601f1916010190565b61026a929160018060a01b0316815260406020820152611a6e60408201611a6184610182565b6001600160a01b03169052565b611a8d611a7d602084016119c0565b6001600160601b03166060830152565b611aac611a9c604084016119cb565b6001600160801b03166080830152565b611acb611abb606084016119cb565b6001600160801b031660a0830152565b611aea611ada608084016119cb565b6001600160801b031660c0830152565b611b09611af960a084016119cb565b6001600160801b031660e0830152565b60c0820135610100820152611b34611b2360e084016119d6565b6001600160401b0316610120830152565b611b55611b4461010084016119d6565b6001600160401b0316610140830152565b611b87611b7b611b696101208501856119ea565b610160808601526101a0850191611a1b565b926101408101906119ea565b91610180603f1982860301910152611a1b565b6001600160a01b03909116815260406020820181905261026a92910190610235565b9080601f8301121561016f5781602061026a93359101611774565b9190916101008184031261016f578035611bf08161015e565b9260208201359260408301359260608101359260808201359260a0830135611c178161015e565b9260c08101356001600160401b03811161016f5783611c37918301611bbc565b9260e08201356001600160401b03811161016f5761026a9201611bbc565b60405190611c62826101d9565b606080838181528160208201528160408201520152565b9060405160c08101908082106001600160401b038311176101f4576101809160405260a0611d2260018396611d02611cf282546001600160401b03811688526001600160401b038160401c166020890152611cec6001600160401b038260801c1660408a01906001600160401b03169052565b60c01c90565b6001600160401b03166060870152565b01546001600160401b038116608085015260401c6001600160a01b031690565b6001600160a01b0316910152565b919060408382031261016f578235611d478161015e565b926020810135906001600160401b03821161016f57016101608183031261016f57611d70611300565b91611d7a82610182565b8352611d88602083016119c0565b6020840152611d99604083016119cb565b6040840152611daa606083016119cb565b6060840152611dbb608083016119cb565b6080840152611dcc60a083016119cb565b60a084015260c082013560c0840152611de760e083016119d6565b60e0840152611df961010083016119d6565b6101008401526101208201356001600160401b03811161016f5781611e1f918401611bbc565b6101208401526101408201356001600160401b03811161016f57611e439201611bbc565b61014082015290565b6001600160401b0381116101f45760051b60200190565b90611e6d82611e4c565b611e7a6040519182610214565b8281528092611e8b601f1991611e4c565b0190602036910137565b634e487b7160e01b5f52603260045260245ffd5b805115611eb65760200190565b611e95565b805160011015611eb65760400190565b805160021015611eb65760600190565b8051821015611eb65760209160051b010190565b8060020b0361016f57565b519061018082611eef565b919082604091031261016f5760208251611f1e81611eef565b92015161026a81611eef565b80516001600160a01b0316825261026a9190610140906101209060208101516020850152611f646040820151604086019062ffffff169052565b60608181015160020b9085015260808181015160020b9085015260a08181015160020b9085015260c08181015160020b9085015260e0818101516001600160801b031690850152610100810151610100850152015191816101208201520190610a8e565b6001600160a01b0391821681529116602082015260806040820181905261026a939192611ff791840190611f2a565b916060818403910152610235565b634e487b7160e01b5f52601160045260245ffd5b670de0b6b3a76400000390670de0b6b3a7640000821161203557565b612005565b9190820391821161203557565b670de0b6b3a7640000019081670de0b6b3a76400001161203557565b9190820180921161203557565b6001600160a01b039182168152911660208201526001600160601b03909116604082015260a06060820181905261026a9391926120af91840190611f2a565b916080818403910152610235565b8151811191826120cc57505090565b6020015111919050565b3d15612100573d906120e782611628565b916120f56040519384610214565b82523d5f602084013e565b606090565b1561210d5750565b60405162461bcd60e51b815260206004820152908190612131906024830190610235565b0390fd5b90670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561203557565b9061223460a060016101809461218f6001600160401b0386511682906001600160401b03166001600160401b0319825416179055565b60208501518154604080880151606089015160809190911b67ffffffffffffffff60801b169390911b6fffffffffffffffff0000000000000000166001600160401b03909216919091179190911760c09190911b6001600160c01b031916178155019261222661220960808301516001600160401b031690565b855467ffffffffffffffff19166001600160401b03909116178555565b01516001600160a01b031690565b815468010000000000000000600160e01b03191660409190911b68010000000000000000600160e01b0316179055565b601f821161227157505050565b5f5260205f20906020601f840160051c830193106122a9575b601f0160051c01905b81811061229e575050565b5f8155600101612293565b909150819061228a565b91909182516001600160401b0381116101f4576122da816122d4845461018d565b84612264565b6020601f821160011461231957819061230a9394955f9261230e575b50508160011b915f199060031b1c19161790565b9055565b015190505f806122f6565b601f1982169061232c845f5260205f2090565b915f5b8181106123665750958360019596971061234e575b505050811b019055565b01515f1960f88460031b161c191690555f8080612344565b9192602060018192868b01518155019401920161232f565b97989390969594929196670de0b6b3a76400008311801561258d575b801561257c575b801561256b575b611222576124e19561248961249a936124796124c5976124088d8f6123e9906106116001600160401b039a60018060a01b03165f52600560205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b6124698661243a610c2261242f61242a612434610c2261242f61242a8a612047565b612135565b614087565b96612019565b9461245882612447611310565b9c168c906001600160401b03169052565b166001600160401b031660208a0152565b6001600160401b03166040880152565b6001600160401b03166060860152565b166001600160401b03166080830152565b6001600160a01b03851660a08201526001600160a01b0387165f908152600360205260409020612159565b6001600160a01b0385165f9081526004602052604090206122b3565b6001600160a01b031692833b1561016f575f6125129160405180938192632f9c799b60e21b83528760048401611b9a565b038183885af1801561077a57612557575b506001600160a01b0390811691167f343ef5cc595144359c9db657cd7fcef6ecc88d06d17651a8292e553ab73b1c705f80a4565b8061076e5f61256593610214565b5f612523565b50670de0b6b3a764000086116123a8565b50670de0b6b3a764000084116123a1565b50670de0b6b3a7640000821161239a565b60ff5f5460a01c166125ac57565b6313d0ff5960e31b5f5260045ffd5b51906101808261198a565b91908261018091031261016f5781516125de816119a5565b9160208101516125ed8161015e565b9160408201516125fc8161015e565b91606081015161260b8161015e565b91608082015161261a81611eef565b9161262760a08201611efa565b9161263460c08301611efa565b9161264160e082016125bb565b91610100820151916101208101519161026a61016061266361014085016125bb565b93016125bb565b60405163133f757160e31b81526004810191909152610180816024817f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b03165afa801561077a575f915f916126c557509091565b90506126e991506101803d81116126f8575b6126e18183610214565b8101906125c6565b50505050505050509250905091565b503d6126d7565b5f1981146120355760010190565b6040516080919061271e8382610214565b6003815291601f1901366020840137565b604051606091906127408382610214565b6002815291601f1901366020840137565b60405190606082018281106001600160401b038211176101f4576040525f604083606081528260208201520152565b929493916127996127a79260a0865260a0860190610afd565b908482036020860152610afd565b828103604084015260608101948051956060835286518091526020608084019701905f5b81811061280057505050946120af9160408088602061026a999a01516020850152015191015283810360608501526060610235565b825180516001600160a01b03168a52602090810151818b0152604090990198909201916001016127cb565b929461026a95949294600193801515908161297a575b831515908161296a575b61285487611e63565b9861285e88611e63565b9661288d61287461286e8b611e63565b9a611e63565b9a61287e8d611ea9565b6001600160a01b039091169052565b61289688611ea9565b5260016128a289611ea9565b5260026128ae8a611ea9565b5260019361293a575b505061290c575b5050506128c961131f565b9384526020840152604083015260608201526128fe6128e6611c55565b936128ef612751565b60405195869460208601612780565b03601f198101835282610214565b6001926129206129319361287e848b611edb565b61292a8287611edb565b5285611edb565b525f80806128be565b61294a9192935061287e8a611ebb565b61295386611ebb565b52600161295f87611ebb565b526002905f806128b7565b95612974906126ff565b9561284b565b60029550612841565b6040519061014082018281106001600160401b038211176101f4576040526060610120835f81525f60208201525f60408201525f838201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201520152565b519061ffff8216820361016f57565b91908260c091031261016f578151612a048161015e565b916020810151612a1381611eef565b91612a20604083016129de565b91612a2d606082016129de565b9160a0612a3c608084016129de565b92015161026a816108a5565b9081602091031261016f575162ffffff8116810361016f5790565b612aca90929192612a72612983565b6020810185905260405163133f757160e31b8152600481019590955293917f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b03169061018090849081906024820190565b0381845afa91821561077a57612be293612bd59387935f935f935f905f905f905f96612d99575b50612b3a929160c08a612b2a612b3394612b206060612b8d9d9e9f019b60e08501906001600160801b03169052565b60020b60a0830152565b019060020b9052565b60020b8452565b6001600160a01b0316148015612d63575b8015612d2d575b15612cec57612b5f61272f565b6101208901525b612b868361287e6101208b01612b808861287e8351611ea9565b51611ebb565b5160020b90565b917f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f7f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca8613fd9565b6001600160a01b03168352565b815160049060c090612bfe90610bad906001600160a01b031681565b604051633850c7bd60e01b815292839182905afa801561077a575f915f91612cb1575b5060020b60808401525b6001600160a01b039081166101008401528251600491602091612c5091610bad911681565b60405163ddca3f4360e01b815292839182905afa801561077a57610180915f91612c82575b5062ffffff166040840152565b612ca4915060203d602011612caa575b612c9c8183610214565b810190612a48565b5f612c75565b503d612c92565b612c2b9250612cd8915060c03d60c011612ce5575b612cd08183610214565b8101906129ed565b5050505091909190612c21565b503d612cc6565b612d28612cf761270d565b806101208b015261287e7f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8191611ecb565b612b66565b506001600160a01b038381167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8190911614612b52565b506001600160a01b038481167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8190911614612b4b565b612b2a9850612b8d9750612b339650612b3a935060c09250612dca91506101803d81116126f8576126e18183610214565b505050509b9650945095929c919c9b909b9c9b95939450509a999850509192612af1565b9190612ea6612e5d61026a935f6060604051612e09816101d9565b8281528260208201528260408201520152612e57610c226040612e486001600160401b03612e4160608c01516001600160401b031690565b1685614149565b9801516001600160401b031690565b90614149565b91612e85612e7760a0612e7c612e7760c086015160020b90565b614219565b93015160020b90565b92612e8e61131f565b95865260208601526001600160a01b03166040850152565b6001600160a01b03166060830152565b919082604091031261016f576020825192015190565b91909160606001600160801b038160808401958051855260018060a01b036020820151166020860152826040820151166040860152015116910152565b6001600160a01b039283169491929091907f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb24702168581146131085750602084810151604051630ae169a560e41b8152600481019190915295869060249082905f905af194851561077a575f956130e3575b50612f849085614149565b9160038251145f146130115790612fbe91612fb8612fb2612fa888610f2d86611ecb565b95610f2d84611ecb565b91611ecb565b52611ecb565b5261012001516001600160a01b0390612fda9061100c90611ecb565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79080602081015b0390a3565b9092610120019261302561100c8551611ea9565b6001600160a01b039081167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a819091160361309e576130869161308061307a61307088610f2d86611ea9565b95610f2d84611ea9565b91611ea9565b52611ea9565b52516001600160a01b0390612fda9061100c90611ea9565b6130cb916130c56130bf6130b588610f2d86611ebb565b95610f2d84611ebb565b91611ebb565b52611ebb565b52516001600160a01b0390612fda9061100c90611ebb565b612f849195506131019060203d6020116114995761148a8183610214565b9490612f79565b849392955061315e60409160205f97015161312161131f565b9081523060208201526001600160801b03818501526001600160801b036060820152835197888094819363fc6f786560e01b835260048301612ecc565b03925af194851561077a575f945f96613266575b50610bad9361100c936131e46130bf6131db8996866131b46130bf8e6131a16132319f610f2d6101209d611ea9565b6131aa85611ea9565b52610f2d84611ebb565b526131cb6131c2828a614149565b610f2d87611ea9565b6131d486611ea9565b528b614149565b610f2d84611ebb565b52019060018060a01b036131fb61100c8451611ea9565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd790602090a351611ebb565b6040519182529033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd790806020810161300c565b6130bf965061323195509361100c936131e46130bf6131db6131b496610120966132aa610bad9b60403d6040116132c0575b6132a28183610214565b810190612eb6565b9990999d50509650969950505050935093613172565b503d613298565b916001600160a01b03918216917f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470216820361330157505050565b602081810151604051630852cd8d60e31b8152600481019190915292839060249082905f905af191821561077a575f926133bd575b508161334157505050565b825160030361335857509061335590611ecb565b52565b61100c610120613369920151611ea9565b6001600160a01b039081167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81909116036133ad5761307a61335591610f2d84611ea9565b6130bf61335591610f2d84611ebb565b6133d791925060203d6020116114995761148a8183610214565b905f613336565b9060018060a01b037f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470216916134a76040602084019361344761342c60e087519301516001600160801b031690565b61343461132e565b9283526001600160801b03166020830152565b5f8183018190526060820190815242608083019081528351630624e65f60e11b81528351600482015260208401516001600160801b03166024820152604090930151604484015290516064830152516084820152918290819060a4820190565b03815f885af1801561077a5761359a575b50613503604083516134c861131f565b9081523060208201526001600160801b03818301526001600160801b03606082015281518093819263fc6f786560e01b835260048301612ecc565b03815f885af190811561077a5761352f916130bf915f905f92613576575b506131a190610f2d86611ea9565b525190803b1561016f57604051630852cd8d60e31b815260048101929092525f908290602490829084905af1801561077a576135685750565b8061076e5f61018093610214565b6131a19250613594915060403d6040116132c0576132a28183610214565b91613521565b6135b29060403d6040116132c0576132a28183610214565b506134b8565b1561016f57565b600160801b81101561016f576001600160801b031690565b906107e08661367995613629888c8c899c9b9e9d9b6136729a64e8d4a5100061366d9a5f6080604051613609816101f9565b828152826020820152826040820152826060820152015202019386614539565b9b8c82849f9495939c8183885f146136b657506136459161203a565b955b156136a5575061365691612063565b935b6001600160a01b039081169281169116614640565b614149565b9282614149565b9161368c61368561132e565b9515158652565b8260208601526040850152036060830152608082015290565b90506136b09161203a565b93613658565b90506136c191612063565b95613647565b9293949160608601918251156137ef5761012082015180516137d6575091846137468893610fa66107e060a06137376137b69a99610dd8610fb261373161371a6137108c611ea9565b51610dd886611ea9565b610fa66107e060808c01516001600160801b031690565b99611ebb565b9301516001600160801b031690565b916137518451151590565b9461376260408a015162ffffff1690565b9561376c8a6148f5565b6101008b01516001600160a01b03169162ffffff60806137a9606061379a604089015160018060a01b031690565b9701516001600160a01b031690565b965199015199169061495f565b9182156137d0576137ca6101809451151590565b91614b09565b50505050565b9593505050506137e96101809451151590565b91614799565b50505050505050565b51604051633850c7bd60e01b81529060c090829060049082906001600160a01b03165afa90811561077a575f9161382d575090565b613846915060c03d60c011612ce557612cd08183610214565b505050505090565b919082608091031261016f57815191602081015161386b8161198a565b916060604083015192015190565b81516001600160a01b031681526101808101929161018091906101609081906020818101516001600160a01b03169085015260408181015160020b9085015260608181015160020b9085015260808181015160020b9085015260a0818101519085015260c0808201519085015260e080820151908501526101008082015190850152610120808201516001600160a01b031690850152610140818101519085015201516001600160a01b0316910152565b92613a805f929360809261012086019061394a610bad61100c8451611ea9565b7f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b031693906139839082908690614c81565b61399d8285613998610bad61100c8851611ebb565b614c81565b613a366139ba61100c6139b361100c8751611ea9565b9551611ebb565b613a2d8a613a236139cf606083015160020b90565b613a196139ee60a06139e560c087015160020b90565b95015160020b90565b95613a096139fa61133d565b6001600160a01b03909c168c52565b6001600160a01b031660208b0152565b60020b6040890152565b60020b6060870152565b60020b84880152565b60a083015260c082015260e08101859052610100810185905230610120820152426101408201528461016082015260405194858094819363b5007d1f60e01b835260048301613879565b03925af191821561077a57613355926130bf925f5f5f925f94613ae0575b5084613abc60209260e0613acd979801906001600160801b03169052565b0152613ac786611ea9565b5161203a565b613ad685611ea9565b52613ac784611ebb565b613acd945060209350613abc9250613b10915060803d608011613b1c575b613b088183610214565b81019061384e565b95509391925090613a9e565b503d613afe565b906001600160a01b037f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470281169190831690828203613b615750505050565b602001928351833b1561016f5760405163095ea7b360e01b81526001600160a01b039290921660048301526024820152915f908390604490829084905af190811561077a575f93602093613bd793613c0d575b505160405194858094819363140e25ad60e31b8352600483019190602083019252565b03925af1801561077a57613bee575b8080806137d0565b613c069060203d6020116114995761148a8183610214565b505f613be6565b8061076e87613c1b93610214565b5f613bb4565b602086015193969590949093906001600160a01b0316803b1561016f5760405163095ea7b360e01b815233600482015260248101929092525f908290604490829084905af1801561077a57613e1f575b506001955f5b8251811015613e175760018111613e115780613df0576001600160801b03613ca960808401516001600160801b031690565b165b613cbd61100c83610120890151611edb565b90613ccc81613ac78588611edb565b613cd68488611edb565b511015613dc75750613cf6613ceb8386611edb565b51610dd88488611edb565b613d008386611edb565b525b613d0c8285611edb565b51613d95575b90600191613d208287611edb565b51613d74575b613d308287611edb565b5160405190815260a084901b8490039182169189169033907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e05663190602090a401613c77565b613d90613d818388611edb565b5189858060a01b038416613f49565b613d26565b9790613dbe600192613db9613daa8488611edb565b51338d878060a01b0316614c81565b6126ff565b98909150613d12565b613dd581613ac78588611edb565b613ddf8488611edb565b52613dea8386611edb565b52613d02565b6001600160801b03613e0c60a08401516001600160801b031690565b613ca9565b5f613cab565b505050505050565b8061076e5f613e2d93610214565b5f613c71565b9293909193613e40611c55565b50613e4a82611e63565b946001613e5684611e63565b93613e6081611e63565b95613e77613e6d83611e63565b9861287e8b611ea9565b613e8086611ea9565b5281613e8b87611ea9565b526002613e9788611ea9565b5211613ebd575b5050613ea861131f565b93845260208401526040830152606082015290565b9194909395926001935f5b8651811015613f3957613edb8188611edb565b51613ee9575b600101613ec8565b94613f31600191613f0a613f0061100c8a8a611edb565b61287e8389611edb565b613f14888a611edb565b51613f1f8287611edb565b5282613f2b828d611edb565b526126ff565b959050613ee1565b5096945092509390505f80613e9e565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516613fcc575b5015613f9557565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f613f8d565b916001600160a01b03848116908216101561016f57604080516001600160a01b0392831660208201908152929095169085015260029490940b606084015260559360379361402a81608081016128fe565b5190209060405192733d602d80600a3d3981f3363d3d373d3d3d363d7360601b845260601b60148401526f5af43d82803e903d91602b57fd5bf3ff60801b602884015260601b6038830152604c820152818120606c820152012090565b60b581600160881b811015614132575b80690100000000000000000062010000921015614125575b65010000000000811015614118575b630100000081101561410b575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8080920410900390565b60101c9160081b916140cb565b60201c9160101b916140be565b60401c9160201b916140af565b5068b500000000000000009050608082901c614097565b90805f19048211810215670de0b6b3a7640000021561016f57670de0b6b3a764000091020490565b7812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111670de0b6b3a7640000021582021561016f57670de0b6b3a7640000020490565b90805f19048211810215620f4240021561016f57620f424091020490565b7f1c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71811160090215600a021561016f576009600a91020490565b815f1904811182021583021561016f57020490565b60020b908160ff1d82810118620d89e881116145335763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102600160801b189160028116614517575b600481166144fb575b600881166144df575b601081166144c3575b602081166144a7575b6040811661448b575b6080811661446f575b6101008116614453575b6102008116614437575b610400811661441b575b61080081166143ff575b61100081166143e3575b61200081166143c7575b61400081166143ab575b618000811661438f575b620100008116614373575b620200008116614358575b62040000811661433d575b6208000016614324575b5f1261431c575b0160201c90565b5f1904614315565b6b048a170391f7dc42444e8fa290910260801c9061430e565b6d2216e584f5fa1ea926041bedfe9890920260801c91614304565b916e5d6af8dedb81196699c329225ee6040260801c916142f9565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c916142ee565b916f31be135f97d08fd981231505542fcfa60260801c916142e3565b916f70d869a156d2a1b890bb3df62baf32f70260801c916142d9565b916fa9f746462d870fdf8a65dc1f90e061e50260801c916142cf565b916fd097f3bdfd2022b8845ad8f792aa58250260801c916142c5565b916fe7159475a2c29b7443b29c7fa6e889d90260801c916142bb565b916ff3392b0822b70005940c7a398e4b70f30260801c916142b1565b916ff987a7253ac413176f2b074cf7815e540260801c916142a7565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c9161429d565b916ffe5dee046a99a2a811c461f1969c30530260801c91614293565b916fff2ea16466c96a3843ec78b326b528610260801c9161428a565b916fff973b41fa98c081472e6896dfb254c00260801c91614281565b916fffcb9843d60f6159c9db58835c9266440260801c91614278565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c9161426f565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c91614266565b916ffff97272373d413259a46990580e213a0260801c9161425d565b82614cfd565b94929593905f92808710155f1461455c575050505061026a906001948094614e34565b9296919281871161457657505050908161026a9294614dfa565b909196506001600160801b03869493961161016f57801561463b576145ba6145b36145c7936145c0936002880a04818860011b0303908703614171565b9785614ddf565b86612063565b8095614171565b938585101561460e5761460b92939495614604916001976145f385670de0b6b3a7640000038685614204565b670de0b6b3a7640000019203614204565b8093614e6d565b91565b61026a9294614634915f976146238582614149565b670de0b6b3a7640000039203614204565b8094614dfa565b614d12565b919390916001600160a01b03808616908416116146d6575b6001600160a01b038181169084168111614682575050509061026a9261467d92614ee6565b614ed2565b929391929091906001600160a01b03861611156146c757906146ac6146b2939261026a9683614ee6565b93614ea1565b808210156146c05750614ed2565b9050614ed2565b505061026a9261467d92614ea1565b919391614658565b909160608284031261016f5781516146f58161015e565b926020830151926040810151906001600160401b03821161016f570181601f8201121561016f5780519061472882611628565b926147366040519485610214565b8284526020838301011161016f57815f9260208093018386015e8301015290565b9161477e906080949796959260018060a01b0316845260a0602085015260a0840190610235565b6001600160a01b039687166040840152951660608201520152565b909260406147b361012092602080825183010191016146de565b939096865f146148d35701906147d961100c6147d261100c8551611ea9565b9351611ebb565b935b6001600160a01b037f000000000000000000000000354dbba1348985cc952c467b8ddaf5dd075906678116925f91614818908b9086908816613f49565b6148408a8751988997889687956344bc658560e01b875260018060a01b031660048701614757565b03925af192831561077a575f915f946148b0575b501561488a57614877614881916148726133559596613ac786611ea9565b612063565b93610f2d83611ebb565b61308082611ebb565b6148ab906148726148a161335595610f2d86611ea9565b95613ac785611ebb565b614881565b9093506148cc915060403d6040116132c0576132a28183610214565b925f614854565b01906148ef61100c6148e861100c8551611ebb565b9351611ea9565b936147db565b51604051630d34328160e11b815290602090829060049082906001600160a01b03165afa90811561077a575f9161492a575090565b90506020813d602011614957575b8161494560209383610214565b8101031261016f575161026a8161198a565b3d9150614938565b97919492959690935f925b6064841061497f575050505050505050505090565b61499a9086620f42409c939495969798999a9b9c03906141ad565b8a15614a8f576001600160a01b03906149b490898b6152d5565b16906001600160a01b03906149ca90898b61539a565b165b8a15614a72570160011c6001600160a01b03165b6001600160a01b038181169086168110614a045750505050505061026a9450614f53565b9498979695946001600160a01b0383161015614a5f5780828b858b8d614a30614a38978e8e8e88614f78565b959094614fc4565b809b92959195614a505750600101929190989361496a565b9a505050505050505050505090565b50509550509091925061026a9450614f2d565b6001600160a01b039180820160011c9160029108151501166149e0565b906001600160a01b0390614aa490898b615276565b16906001600160a01b0390614aba90898b61533e565b166149cc565b600160ff1b8114612035575f0390565b6001600160a01b039182168152911515602083015260408201929092529116606082015260a06080820181905261026a92910190610235565b92614bc79291604091614b31614b2b610bad610bad855160018060a01b031690565b91614ac0565b8415614c65575f6401000276a4915b614ba96101208601614b9b614b6c6060614b6361100c6139b361100c8751611ea9565b99015160020b90565b89516001600160a01b03938416602082015298909216604089015260029190910b606088015286906080820190565b03601f198101875286610214565b8551630251596160e31b8152978895869485938a3060048701614ad0565b03925af190811561077a57613355925f905f93614c41575b508115614c2c57614bf390613ac786611ea9565b614bfc85611ea9565b5215614c1f576130bf90614c19614c1285611ebb565b5191614ac0565b90612063565b6130bf90613ac784611ebb565b614c3c90614c19614c1287611ea9565b614bf3565b9050614c5d91925060403d6040116132c0576132a28183610214565b91905f614bdf565b5f73fffd8963efd1fc6a506488495d951d5263988d2591614b40565b906014528160345263095ea7b360601b5f5260205f6044601082855af13d1560015f5114171615614cb5575b50505f603452565b604460105f80936020958260345263095ea7b360601b83528238868683865af1506034525af13d1560015f5114171615614cf0575f80614cad565b633e3f8f735f526004601cfd5b6345c3193d60e11b5f5260020b60045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b9290600193925b60018211614d39575050565b909280820481116120355760018416614d59575b80029260011c90614d2d565b80940293614d4d565b614d735f196002600160601b614d26565b90815f19048111612035570290565b905f918015614dd9578080600114614dd157600214614dca5760016101338210166001600b83101617614dbf57614d7391925060025f1991614d26565b6002900a9161203557565b5060049150565b506001925050565b505f9150565b90614dec61026a92614d82565b614df4614d62565b916151f3565b90916001600160801b03821161016f5761026a92614e2191670de0b6b3a764000003614149565b906002614e2c614d62565b910a916151f3565b90916001600160801b03821161016f5761026a92614e5b91670de0b6b3a764000003614149565b6002614e65614d62565b920a906151f3565b9190916001600160801b03811161016f5761026a92614e90916002614e2c614d62565b90670de0b6b3a76400000390614171565b61026a9291906001600160a01b0380831690821611614ecc575b90036001600160a01b03169061511d565b90614ebb565b906001600160801b03821691820361016f57565b61026a92916001600160a01b0380821690831611614f27575b614f156001600160a01b038281169084166151aa565b9190036001600160a01b0316916151f3565b90614eff565b614f4661026a94614f4d92949394620f42400390615438565b838361533e565b9061549a565b614f6c61026a94614f7392949394620f42400390615438565b83836152d5565b61555e565b93949391929115614fa95790614fa38392614f9761026a9584896154fe565b90620f4240039061545b565b9461555e565b93614fbe8295614f97858461026a979661558a565b9461549a565b939497959296909196845f146150ea57888111156150e157614fea8982035b8984614ee6565b90614ff88588018486614ea1565b985b898310998a156150d15761500e8185614171565b9a5b15615076575050509061502661502c9392614ed2565b9161558a565b911561506757620f4240929190828211156150565750035b925b670de0b6b3a76400000310929190565b905061506291506141cb565b615044565b939450620f4240910393615046565b90925061508e91969750615094945099949899614ed2565b916154fe565b92156150a757500392620f424090615046565b9493620f42409290828211156150c05750035b92615046565b90506150cc91506141cb565b6150ba565b6150db8482614171565b9a615010565b614fea5f614fe3565b6150f78482018984614ee6565b90898711156151145761510e8a88035b8486614ea1565b98614ffa565b61510e5f615107565b90606082901b905f19600160601b84099282808510940393808503946151448685116135b8565b146151a3578190600160601b900981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b81810291905f1982820991838084109303928084039384600160601b111561016f57146151ea57600160601b910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f198185099383808610950394808603956152148786116135b8565b1461526e579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b919081156152d0576001600160a01b03909216918183029160609190911b600160601b600160e01b0316908204831482821116156152c35761026a926152be928203916155c6565b6155ef565b63f5c787f15f526004601cfd5b505090565b919081156152d05760601b600160601b600160e01b0316916001600160a01b0316818102828104821461532a575b50801561463b5782049081018091116120355780820615159104016001600160a01b031690565b8301838110615303579150610bad926155c6565b916001600160a01b038111615381576001600160801b039060601b911690811561463b5704905b6001600160a01b03169081019081106120355761026a906155ef565b6001600160801b0361539492169061511d565b90615365565b91906001600160a01b0382116153fb576001600160801b036153bf6153cc9360601b90565b9116808206151591040190565b905b6001600160a01b031690808211156153ee5790036001600160a01b031690565b634323a5555f526004601cfd5b6001600160801b031661541381600160601b846151f3565b91811561463b57600160601b900961542c575b906153ce565b60010180615426575f80fd5b815f19048111820215620f4240021561016f5702620f4240808204910615150190565b7d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98111620f4240021582021561016f57620f42400290808204910615150190565b6001600160a01b03828116908216116154f8575b6001600160a01b0381169283156154ec576154e8926001600160a01b0380821693909103169060601b600160601b600160e01b03166151f3565b0490565b62bfc9215f526004601cfd5b906154ae565b6001600160a01b0382811690821611615558575b6001600160a01b0381169283156154ec5761554c926001600160a01b0380821693909103169060601b600160601b600160e01b03166155c6565b90808206151591040190565b90615512565b61026a926001600160a01b03928316919092160360ff81901d90810118906001600160801b03166151aa565b6001600160a01b0391821691160360ff81901d90810118906001906001600160801b03166155b883826151aa565b928260601b91091515160190565b9291906155d48282866151f3565b93821561463b57096155e257565b9060010190811561016f57565b6001600160a01b0381169190820361560357565b6393dafdf160e01b5f5260045ffdfea264697066735822122032441f6e2a19fdc65eec2f6bae8a2d476038bb0c041afe28e11c4fc7aafe088f64736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470200000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca80000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a810000000000000000000000001dc7a0f5336f52724b650e39174cfcbbedd67bf1000000000000000000000000d74339e0f10fce96894916b93e5cc7de89c98272
-----Decoded View---------------
Arg [0] : owner_ (address): 0xb4d72B1c91e640e4ED7d7397F3244De4D8ACc50B
Arg [1] : arcadiaFactory (address): 0xDa14Fdd72345c4d2511357214c5B89A919768e59
Arg [2] : routerTrampoline (address): 0x354dBBa1348985CC952c467b8ddaF5dD07590667
Arg [3] : positionManager (address): 0x991d5546C4B442B4c5fdc4c8B8b8d131DEB24702
Arg [4] : cLFactory (address): 0x04625B046C69577EfC40e6c0Bb83CDBAfab5a55F
Arg [5] : poolImplementation (address): 0x321f7Dfb9B2eA9131B8C17691CF6e01E5c149cA8
Arg [6] : rewardToken (address): 0x7f9AdFbd38b669F03d1d11000Bc76b9AaEA28A81
Arg [7] : stakedSlipstreamAm (address): 0x1Dc7A0f5336F52724B650E39174cfcbbEdD67bF1
Arg [8] : stakedSlipstreamWrapper (address): 0xD74339e0F10fcE96894916B93E5Cc7dE89C98272
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b
Arg [1] : 000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59
Arg [2] : 000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667
Arg [3] : 000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb24702
Arg [4] : 00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f
Arg [5] : 000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca8
Arg [6] : 0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a81
Arg [7] : 0000000000000000000000001dc7a0f5336f52724b650e39174cfcbbedd67bf1
Arg [8] : 000000000000000000000000d74339e0f10fce96894916b93e5cc7de89c98272
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.