Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
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:
CompounderSlipstream
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 { Compounder } from "./Compounder.sol";
import { Slipstream } from "../base/Slipstream.sol";
/**
* @title Compounder for Slipstream Liquidity Positions.
* @author Pragma Labs
* @notice The Compounder will act as an Asset Manager for Arcadia Accounts.
* It will allow third parties (initiators) to trigger the compounding functionality for a Liquidity Position in the Account.
* The Arcadia Account owner must set a specific initiator that will be permissioned to compound the positions in their Account.
* Compounding can only be triggered if certain conditions are met and the initiator will get a small fee for the service provided.
* The compounding will collect the fees earned by a position and increase the liquidity of the position by those fees.
* Depending on current tick of the pool and the position range, fees will be deposited in appropriate ratio.
* @dev The initiator will provide a trusted sqrtPrice input at the time of compounding to mitigate frontrunning risks.
* This input serves as a reference point for calculating the maximum allowed deviation during the compounding process,
* ensuring that the execution remains within a controlled price range.
*/
contract CompounderSlipstream is Compounder, Slipstream {
/* //////////////////////////////////////////////////////////////
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
)
Compounder(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 { 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 { TickMath } from "../../../lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/libraries/TickMath.sol";
/**
* @title Abstract Compounder for Concentrated Liquidity Positions.
* @author Pragma Labs
* @notice The Compounder will act as an Asset Manager for Arcadia Accounts.
* It will allow third parties (initiators) to trigger the compounding functionality for a Liquidity Position in the Account.
* The Arcadia Account owner must set a specific initiator that will be permissioned to compound the positions in their Account.
* Compounding can only be triggered if certain conditions are met and the initiator will get a small fee for the service provided.
* The compounding will collect the fees earned by a position and increase the liquidity of the position by those fees.
* Depending on current tick of the pool and the position range, fees will be deposited in appropriate ratio.
* @dev The initiator will provide a trusted sqrtPrice input at the time of compounding to mitigate frontrunning risks.
* This input serves as a reference point for calculating the maximum allowed deviation during the compounding process,
* ensuring that the execution remains within a controlled price range.
*/
abstract contract Compounder is IActionBase, AbstractBase, Guardian {
using FixedPointMathLib for uint256;
using SafeApprove for ERC20;
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The contract address of the Arcadia Factory.
IArcadiaFactory public immutable ARCADIA_FACTORY;
// The contract address of the Router Trampoline.
IRouterTrampoline public 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;
}
// A struct with the initiator parameters.
struct InitiatorParams {
// The contract address of the position manager.
address positionManager;
// The id of the position.
uint96 id;
// The amount of token0 withdrawn from the account.
uint128 amount0;
// The amount of token1 withdrawn from the account.
uint128 amount1;
// 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;
}
// 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);
event Compound(address indexed account, address indexed positionManager, uint256 id);
/* //////////////////////////////////////////////////////////////
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,
bytes memory metaData_
) = abi.decode(data, (address, uint256, uint256, uint256, uint256, bytes));
_setAccountInfo(
msg.sender, accountOwner, initiator, maxClaimFee, maxSwapFee, maxTolerance, minLiquidityRatio, 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 metaData_ Custom metadata to be stored with the account.
*/
function setAccountInfo(
address account_,
address initiator,
uint256 maxClaimFee,
uint256 maxSwapFee,
uint256 maxTolerance,
uint256 minLiquidityRatio,
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, 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 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,
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)
});
metaData[account_] = metaData_;
emit AccountInfoSet(account_, initiator);
}
/* ///////////////////////////////////////////////////////////////
COMPOUND LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Compounds 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.
*/
function compound(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();
// If leftovers have to be withdrawn from account, get token0 and token1.
address token0;
address token1;
if (initiatorParams.amount0 > 0 || initiatorParams.amount1 > 0) {
(token0, token1) = _getUnderlyingTokens(initiatorParams.positionManager, initiatorParams.id);
}
// Encode data for the flash-action.
bytes memory actionData = ArcadiaLogic._encodeAction(
initiatorParams.positionManager,
initiatorParams.id,
token0,
token1,
initiatorParams.amount0,
initiatorParams.amount1,
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.id);
// Compounder has withdrawn the underlying tokens from the Account.
uint256[] memory balances = new uint256[](position.tokens.length);
balances[0] = initiatorParams.amount0;
balances[1] = initiatorParams.amount1;
uint256[] memory fees = new uint256[](balances.length);
// 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);
// Get the rebalance parameters, based on a hypothetical swap through the pool itself without slippage.
RebalanceParams memory rebalanceParams = RebalanceLogic._getRebalanceParams(
accountInfo_.minLiquidityRatio,
position.fee,
initiatorParams.swapFee,
position.sqrtPrice,
cache.sqrtRatioLower,
cache.sqrtRatioUpper,
balances[0] - fees[0],
balances[1] - fees[1]
);
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.
// Increase liquidity, update balances and delta liquidity.
(uint256 amount0Desired, uint256 amount1Desired) =
rebalanceParams.zeroToOne ? (balances[0], balances[1] - fees[1]) : (balances[0] - fees[0], balances[1]);
// Increase liquidity, update balances and liquidity
_increaseLiquidity(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 was staked, stake it.
_stake(balances, positionManager, position);
// 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, positionManager, position);
// Encode deposit data for the flash-action.
depositData = ArcadiaLogic._encodeDeposit(positionManager, position.id, position.tokens, balances, count);
emit Compound(msg.sender, positionManager, 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 Compounder 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 Compounder.
* @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 amountOut = RebalanceOptimizationMath._getAmountOutWithSlippage(
rebalanceParams.zeroToOne,
position.fee,
_getPoolLiquidity(position),
uint160(position.sqrtPrice),
cache.sqrtRatioLower,
cache.sqrtRatioUpper,
balances[0] - fees[0],
balances[1] - fees[1],
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 Compounder.
* @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 Compounder.
* @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,
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 the leftovers.
address token;
count = 1;
for (uint256 i; i < balances.length; i++) {
token = position.tokens[i];
// If there are leftovers, deposit them back into the Account.
if (balances[i] > fees[i]) {
balances[i] = balances[i] - fees[i];
ERC20(token).safeApproveWithRetry(msg.sender, balances[i]);
count++;
} else {
fees[i] = balances[i];
balances[i] = 0;
}
// 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();
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event GuardianChanged(address indexed user, address indexed newGuardian);
event PauseFlagsUpdated(bool pauseUpdate);
/* //////////////////////////////////////////////////////////////
MODIFIERS
////////////////////////////////////////////////////////////// */
/**
* @dev Only guardians can call functions with this modifier.
*/
modifier onlyGuardian() {
if (msg.sender != guardian) revert OnlyGuardian();
_;
}
/**
* @dev Throws if the Asset Manager is paused.
*/
modifier whenNotPaused() {
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 {
emit GuardianChanged(msg.sender, guardian = guardian_);
}
/* //////////////////////////////////////////////////////////////
PAUSING LOGIC
////////////////////////////////////////////////////////////// */
/**
* @notice Pauses the Asset Manager.
*/
function pause() external onlyGuardian whenNotPaused {
emit PauseFlagsUpdated(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 {
emit PauseFlagsUpdated(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: 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: 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 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"}],"name":"AccountInfoSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"positionManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Compound","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":"newGuardian","type":"address"}],"name":"GuardianChanged","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":false,"internalType":"bool","name":"pauseUpdate","type":"bool"}],"name":"PauseFlagsUpdated","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":"ARCADIA_FACTORY","outputs":[{"internalType":"contract IArcadiaFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROUTER_TRAMPOLINE","outputs":[{"internalType":"contract IRouterTrampoline","name":"","type":"address"}],"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"}],"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":"address","name":"account_","type":"address"},{"components":[{"internalType":"address","name":"positionManager","type":"address"},{"internalType":"uint96","name":"id","type":"uint96"},{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","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":"struct Compounder.InitiatorParams","name":"initiatorParams","type":"tuple"}],"name":"compound","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 Compounder.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"},{"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":"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
610180346101e157601f614ed338819003918201601f19168301916001600160401b038311848410176101e557808492610120946040528339810103126101e157610049816101f9565b610055602083016101f9565b91610062604082016101f9565b61006e606083016101f9565b61007a608084016101f9565b61008660a085016101f9565b9161009360c086016101f9565b936100ad6101006100a660e089016101f9565b97016101f9565b5f80546001600160a01b0319166001600160a01b039099169889178155604051999198907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36001600160a01b0390811660805290811660a0521660e05260c05261010052610120526101405261016052614cc5908161020e823960805181818161056d015281816107dd0152610bbf015260a0518181816113120152613e5b015260c05181818161150601526125cd015260e0518181816115dc015281816120b7015281816124cf0152818161291c01528181612d0f015281816130b1015261321101526101005181818161152701526125ee01526101205181818161274001528181612778015281816127ae01528181612a3f0152612daf015261014051816115990152610160518161160c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101e15756fe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c80630686ddd9146101695780630a73e391146101645780631204f5251461015f578063150b7a021461015a5780632fcb4f04146101555780633d692da114610150578063452a93201461014b5780635c975abb146101465780635f4860df146101415780637d5ad9cd1461013c5780638456cb59146101375780638cffa277146101325780638da5cb5b1461012d5780638da92e7114610128578063a129568d14610123578063a7310b581461011e578063a89d6dd414610119578063b699b82b14610114578063bc25cf771461010f578063f2fde38b1461010a5763fa461e330361000e57611483565b61140f565b611341565b6112fd565b611283565b6111c3565b610d58565b610c15565b610bee565b610baa565b610b2e565b610858565b610760565b610732565b61070a565b6104dd565b610466565b61040c565b610384565b610353565b610270565b6001600160a01b0381160361017f57565b5f80fd5b359061018e8261016e565b565b90600182811c921680156101be575b60208310146101aa57565b634e487b7160e01b5f52602260045260245ffd5b91607f169161019f565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b038211176101f757604052565b6101c8565b60a081019081106001600160401b038211176101f757604052565b90601f801991011681019081106001600160401b038211176101f757604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90602061026d928181520190610238565b90565b3461017f57602036600319011261017f5760043561028d8161016e565b6001600160a01b03165f90815260046020526040808220905181549092916102b482610190565b808552916001811690811561032c57506001146102ec575b6102e8846102dc81860382610217565b6040519182918261025c565b0390f35b5f90815260208120939250905b808210610312575090915081016020016102dc826102cc565b9192600181602092548385880101520191019092916102f9565b60ff191660208087019190915292151560051b850190920192506102dc91508390506102cc565b3461017f57602036600319011261017f57602061037a6004356103758161016e565b611597565b6040519015158152f35b3461017f57604036600319011261017f5760206004356103a38161016e565b602435906103b08261016e565b6001600160a01b039081165f908152600584526040808220938316825260209390935282902054915191168152f35b9181601f8401121561017f578235916001600160401b03831161017f576020838186019501011161017f57565b3461017f57608036600319011261017f5761042860043561016e565b61043360243561016e565b6064356001600160401b03811161017f576104529036906004016103df565b5050604051630a85bd0160e11b8152602090f35b3461017f57602036600319011261017f576004356104838161016e565b61049760018060a01b035f5416331461163d565b600180546001600160a01b0319166001600160a01b03929092169182179055337fa14fc14d8620a708a896fd11392a235647d99385500a295f0d7da2a258b2e9675f80a3005b3461017f5760e036600319011261017f576004356104fa8161016e565b602435906105078261016e565b60a43560843560643560443560c4356001600160401b03811161017f576105329036906004016103df565b6002549197916001600160a01b03166106f157604051630972932760e21b81526001600160a01b0388166004820152602081806024810103817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561066e575f916106c2575b50156106b357604051638da5cb5b60e01b8152906001600160a01b0388166020836004815f855af192831561066e575f93610682575b506001600160a01b03831633036106735760205f91600460405180948193635e34633b60e11b83525af1801561066e576003915f9161063f575b5010610630576100189861062a9136916116d7565b96611e28565b63a93eca7960e01b5f5260045ffd5b610661915060203d602011610667575b6106598183610217565b8101906116ad565b5f610615565b503d61064f565b61168d565b6312272fd360e11b5f5260045ffd5b6106a591935060203d6020116106ac575b61069d8183610217565b810190611698565b915f6105db565b503d610693565b630ea8370b60e41b5f5260045ffd5b6106e4915060203d6020116106ea575b6106dc8183610217565b810190611678565b5f6105a5565b503d6106d2565b63b5dfd9e560e01b5f5260045ffd5b5f91031261017f57565b3461017f575f36600319011261017f576001546040516001600160a01b039091168152602090f35b3461017f575f36600319011261017f57602060ff5f5460a01c166040519015158152f35b8015150361017f57565b3461017f57606036600319011261017f5760043561077d8161016e565b610788602435610756565b6044356001600160401b03811161017f576107a79036906004016103df565b6002549192916001600160a01b03166106f157604051630972932760e21b81523360048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa90811561066e575f91610839575b50156106b3578261082391610018940190611728565b9590949193916001600160a01b03169033611e28565b610852915060203d6020116106ea576106dc8183610217565b5f61080d565b3461017f57604036600319011261017f576004356108758161016e565b6024356001600160401b03811161017f5780600401610100600319833603011261017f5760ff5f5460a01c16610b1f576002546001600160a01b03166106f157600280546001600160a01b0319166001600160a01b038516179055604051638da5cb5b60e01b81526001600160a01b038416936020826004815f895af190811561066e5761092561093a92610947945f91610b00575b506001600160a01b03165f90815260056020526040902090565b9060018060a01b03165f5260205260405f2090565b546001600160a01b031690565b336001600160a01b0390911603610af15761096b61096761037583611774565b1590565b610ae257610a09916109f0915f5f908360448101936001600160801b036109918661178f565b1615801590610abf575b610a6e575b6001600160801b0380826109fe6109df60646109d86109d260246109cb6001600160601b039a611774565b9a016117aa565b9b61178f565b9b0161178f565b916040519b8c91336020840161182f565b03601f1981018c528b610217565b169616941690612268565b90803b1561017f5760405162b9252f60e41b8152905f908290818381610a3388306004840161192b565b03925af1801561066e57610a54575b600280546001600160a01b0319169055005b80610a625f610a6893610217565b80610700565b80610a42565b6001600160801b039350809250610a859150611774565b50836001600160601b0383610ab3610aae610aa2602486016117aa565b6001600160601b031690565b612099565b959094925050506109a0565b50610adb610acf6064840161178f565b6001600160801b031690565b151561099b565b63ed5f09f160e01b5f5260045ffd5b6317fb43e560e31b5f5260045ffd5b610b19915060203d6020116106ac5761069d8183610217565b5f61090b565b6313d0ff5960e31b5f5260045ffd5b3461017f575f36600319011261017f576001546001600160a01b03163303610b9b575f5460ff8160a01c16610b1f5760ff60a01b1916600160a01b175f55604051600181527f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a90602090a1005b636570ecab60e11b5f5260045ffd5b3461017f575f36600319011261017f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461017f575f36600319011261017f575f546040516001600160a01b039091168152602090f35b3461017f57602036600319011261017f577f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a6020600435610c5581610756565b5f5490610c6c336001600160a01b0384161461163d565b15159060ff60a01b8260a01b169060ff60a01b1916175f55604051908152a1005b90602080835192838152019201905f5b818110610caa5750505090565b8251845260209384019390920191600101610c9d565b80516080808452815190840181905260a08401949391602001905f5b818110610d28575050506060610d17610d0561026d959660208601518582036020870152610c8d565b60408501518482036040860152610c8d565b920151906060818403910152610c8d565b82516001600160a01b0316875260209687019690920191600101610cdc565b90602061026d928181520190610cc0565b3461017f57602036600319011261017f576004356001600160401b03811161017f57610d889036906004016103df565b90610d9161194d565b50600254610daf906001600160a01b03165b6001600160a01b031690565b33036111b457335f908152600360205260409020610dda9190610dd190611971565b928101906119ba565b8051909291906001600160a01b03169060a08401610dff81516001600160401b031690565b6001600160401b03610e27610e1b87516001600160401b031690565b6001600160401b031690565b911611801561117e575b61116f57610e55610e4f610aa260208801516001600160601b031690565b846124a0565b90610120820194610e67865151611aa1565b91610e7f610acf60408a01516001600160801b031690565b610e8884611ae7565b52610ea0610acf60608a01516001600160801b031690565b610ea984611af9565b52610eb48351611aa1565b97610ec46080820151868561282b565b610100860193610ed8610967838751611b98565b611140578a8988809651610ef2906001600160401b031690565b6001600160401b031691610f06938a612909565b610f11848a88612d00565b608001516001600160401b0316604084015162ffffff1660c08401518c906001600160401b0316875160408601519091906001600160a01b031660608701519092906001600160a01b031694610f668c611ae7565b51610f7086611ae7565b51610f7a91611b62565b94610f848d611af9565b5190610f8f90611af9565b51610f9991611b62565b610fc59790966001600160a01b039081169516936001600160401b039081169262ffffff169116612e17565b938a82868051610fd490151590565b5f14966109679661100f946110239961114f57610ffe6020850151610ff883611ae7565b51611b8b565b61100782611ae7565b525b8b612f07565b61101b610da388613008565b809152611b98565b611140578051156111175761106161103a83611ae7565b5161105861104785611af9565b516110518b611af9565b5190611b62565b905b8585613082565b6001600160801b03604061107f60e08601516001600160801b031690565b9201519116106111085760206110b183866102e899856110bf986110a38585613206565b6001600160a01b0316613304565b9201948551905190856134a2565b91516040519081526001600160a01b03919091169033907f6c7e7d4cb83a668aef31739dd35dc3fc3d5f31d62b69e438b7b24d35b40dcc6390602090a360405191829182610d47565b63bb55fd2760e01b5f5260045ffd5b61106161113061112684611ae7565b516110518a611ae7565b61113984611af9565b519061105a565b633a8bf65960e01b5f5260045ffd5b6111606020850151610ff883611af9565b61116982611af9565b52611009565b632a9ffab760e21b5f5260045ffd5b5060c08501516001600160401b03166001600160401b036111ac610e1b60208801516001600160401b031690565b911611610e31565b63f3f6425d60e01b5f5260045ffd5b3461017f57602036600319011261017f576004356111e08161016e565b6001600160a01b03165f90815260036020908152604091829020805460019091015483516001600160401b03808416825283861c811694820194909452608083811c85169582019590955260c09290921c6060830152919091169181019190915260a090f35b6040519061018e61010083610217565b6040519061018e60a083610217565b6040519061018e608083610217565b6040519061018e60c083610217565b3461017f5760a036600319011261017f57600435608036602319011261017f576112eb6102e891604051906112b7826101dc565b602435825260443560208301526064356112d08161016e565b60408301526084356112e18161016e565b6060830152611b98565b60405190151581529081906020820190565b3461017f575f36600319011261017f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461017f57602036600319011261017f5760043561135e8161016e565b5f5460ff90611377336001600160a01b0383161461163d565b60a01c16610b1f576002546001600160a01b03166106f1576001600160a01b0316806113b757506100185f80808047335af16113b1611bb1565b90611be0565b6040516370a0823160e01b815230600482015290602082602481845afa90811561066e57610018925f926113ee575b5033906135b8565b61140891925060203d602011610667576106598183610217565b905f6113e6565b3461017f57602036600319011261017f5760043561142c8161016e565b5f5490611443336001600160a01b0384161461163d565b60018060a01b031680916001600160601b0360a01b16175f55337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b3461017f57606036600319011261017f576024356004356044356001600160401b03811161017f576114bb60609136906004016103df565b908092918101031261017f578035926114d38461016e565b61154b60406020840135936114e78561016e565b0135946114f386611c10565b6001600160a01b039384169593169285847f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061364f565b336001600160a01b0390911603611588575f8313156115715750610018925033906135b8565b9150505f811361157d57005b6100189133906135b8565b634b60273560e01b5f5260045ffd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03908116911690811490811561160a575b81156115da575090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614919050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316811491506115d0565b1561164457565b60405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606490fd5b9081602091031261017f575161026d81610756565b6040513d5f823e3d90fd5b9081602091031261017f575161026d8161016e565b9081602091031261017f575190565b6001600160401b0381116101f757601f01601f191660200190565b9291926116e3826116bc565b916116f16040519384610217565b82948184528183011161017f578281602093845f960137010152565b9080601f8301121561017f5781602061026d933591016116d7565b9160c08383031261017f57823561173e8161016e565b9260208101359260408201359260608301359260808101359260a08201356001600160401b03811161017f5761026d920161170d565b3561026d8161016e565b6001600160801b0381160361017f57565b3561026d8161177e565b6001600160601b0381160361017f57565b3561026d81611799565b359061018e82611799565b359061018e8261177e565b35906001600160401b038216820361017f57565b9035601e198236030181121561017f5701602081359101916001600160401b03821161017f57813603831361017f57565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b0390911681526040602082015261026d916101409061191890803561185a8161016e565b6001600160a01b03166040850152611887611877602083016117b4565b6001600160601b03166060860152565b6118a6611896604083016117bf565b6001600160801b03166080860152565b6118c56118b5606083016117bf565b6001600160801b031660a0860152565b608081013560c08501526118ee6118de60a083016117ca565b6001600160401b031660e0860152565b61190e6118fd60c083016117ca565b6001600160401b0316610100860152565b60e08101906117de565b919092610100610120820152019161180f565b6001600160a01b03909116815260406020820181905261026d92910190610238565b6040519061195a826101dc565b606080838181528160208201528160408201520152565b9060405161197e816101fc565b60806001600160401b03806001849680548381168752838160401c1660208801528381871c16604088015260c01c606087015201541616910152565b919060408382031261017f5782356119d18161016e565b926020810135906001600160401b03821161017f57016101008183031261017f576119fa611246565b91611a0482610183565b8352611a12602083016117b4565b6020840152611a23604083016117bf565b6040840152611a34606083016117bf565b606084015260808201356080840152611a4f60a083016117ca565b60a0840152611a6060c083016117ca565b60c084015260e08201356001600160401b03811161017f57611a82920161170d565b60e082015290565b6001600160401b0381116101f75760051b60200190565b90611aab82611a8a565b611ab86040519182610217565b8281528092611ac9601f1991611a8a565b0190602036910137565b634e487b7160e01b5f52603260045260245ffd5b805115611af45760200190565b611ad3565b805160011015611af45760400190565b805160021015611af45760600190565b8051821015611af45760209160051b010190565b634e487b7160e01b5f52601160045260245ffd5b670de0b6b3a76400000390670de0b6b3a76400008211611b5d57565b611b2d565b91908203918211611b5d57565b670de0b6b3a7640000019081670de0b6b3a764000011611b5d57565b91908201809211611b5d57565b815181119182611ba757505090565b6020015111919050565b3d15611bdb573d90611bc2826116bc565b91611bd06040519384610217565b82523d5f602084013e565b606090565b15611be85750565b60405162461bcd60e51b815260206004820152908190611c0c906024830190610238565b0390fd5b8060020b0361017f57565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715611b5d57565b6001611cf1608061018e94611c746001600160401b0382511686906001600160401b03166001600160401b0319825416179055565b60208101518554604080840151606085015160c089811b8a900377ffffffffffffffffffffffffffffffff0000000000000000199095169590931b6fffffffffffffffff000000000000000016949094176001600160401b03918216871b179290921692901b6001600160c01b0319169190911786559101511690565b9101906001600160401b03166001600160401b0319825416179055565b601f8211611d1b57505050565b5f5260205f20906020601f840160051c83019310611d53575b601f0160051c01905b818110611d48575050565b5f8155600101611d3d565b9091508190611d34565b91909182516001600160401b0381116101f757611d8481611d7e8454610190565b84611d0e565b6020601f8211600114611dc3578190611db49394955f92611db8575b50508160011b915f199060031b1c19161790565b9055565b015190505f80611da0565b601f19821690611dd6845f5260205f2090565b915f5b818110611e1057509583600195969710611df8575b505050811b019055565b01515f1960f88460031b161c191690555f8080611dee565b9192602060018192868b015181550194019201611dd9565b9693909594929195670de0b6b3a764000083118015611fce575b8015611fbd575b8015611fac575b61116f57611f7995611f30611f4193611f20611f5d97611eaf8c611e908f6109256001600160401b039a60018060a01b03165f52600560205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b611f1086611ee1610e1b611ed6611ed1611edb610e1b611ed6611ed18a611b6f565b611c1b565b6136fd565b96611b41565b94611eff82611eee611256565b9c168c906001600160401b03169052565b166001600160401b031660208a0152565b6001600160401b03166040880152565b6001600160401b03166060860152565b166001600160401b03166080830152565b6001600160a01b0386165f908152600360205260409020611c3f565b6001600160a01b0384165f908152600460205260409020611d5d565b6001600160a01b0390811691167febc70f7c8d6a67b19e15e968cb908d21719e8ff9a778a71171fba931a618d0525f80a3565b50670de0b6b3a76400008611611e50565b50670de0b6b3a76400008411611e49565b50670de0b6b3a76400008211611e42565b519061018e82611c10565b519061018e8261177e565b91908261018091031261017f57815161200d81611799565b91602081015161201c8161016e565b91604082015161202b8161016e565b91606081015161203a8161016e565b91608082015161204981611c10565b9161205660a08201611fdf565b9161206360c08301611fdf565b9161207060e08201611fea565b91610100820151916101208101519161026d6101606120926101408501611fea565b9301611fea565b60405163133f757160e31b81526004810191909152610180816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa801561066e575f915f916120f457509091565b905061211891506101803d8111612127575b6121108183610217565b810190611ff5565b50505050505050509250905091565b503d612106565b5f198114611b5d5760010190565b6040516080919061214d8382610217565b6003815291601f1901366020840137565b6040516060919061216f8382610217565b6002815291601f1901366020840137565b60405190606082018281106001600160401b038211176101f7576040525f604083606081528260208201520152565b929493916121c86121d69260a0865260a0860190610cc0565b908482036020860152610cc0565b828103604084015260608101948051956060835286518091526020608084019701905f5b81811061223d575050509461222f9160408088602061026d999a01516020850152015191015283810360608501526060610238565b916080818403910152610238565b825180516001600160a01b03168a52602090810151818b0152604090990198909201916001016121fa565b929461026d9594929460019380151590816123b7575b83151590816123a7575b61229187611aa1565b9861229b88611aa1565b966122ca6122b16122ab8b611aa1565b9a611aa1565b9a6122bb8d611ae7565b6001600160a01b039091169052565b6122d388611ae7565b5260016122df89611ae7565b5260026122eb8a611ae7565b52600193612377575b5050612349575b505050612306611265565b93845260208401526040830152606082015261233b61232361194d565b9361232c612180565b604051958694602086016121af565b03601f198101835282610217565b60019261235d61236e936122bb848b611b19565b6123678287611b19565b5285611b19565b525f80806122fb565b612387919293506122bb8a611af9565b61239086611af9565b52600161239c87611af9565b526002905f806122f4565b956123b19061212e565b95612288565b6002955061227e565b6040519061014082018281106001600160401b038211176101f7576040526060610120835f81525f60208201525f60408201525f838201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201520152565b519061ffff8216820361017f57565b91908260c091031261017f5781516124418161016e565b91602081015161245081611c10565b9161245d6040830161241b565b9161246a6060820161241b565b9160a06124796080840161241b565b92015161026d81610756565b9081602091031261017f575162ffffff8116810361017f5790565b612507909291926124af6123c0565b6020810185905260405163133f757160e31b8152600481019590955293917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169061018090849081906024820190565b0381845afa91821561066e5761261f936126129387935f935f935f905f905f905f966127d6575b50612577929160c08a6125676125709461255d60606125ca9d9e9f019b60e08501906001600160801b03169052565b60020b60a0830152565b019060020b9052565b60020b8452565b6001600160a01b03161480156127a0575b801561276a575b156127295761259c61215e565b6101208901525b6125c3836122bb6101208b016125bd886122bb8351611ae7565b51611af9565b5160020b90565b917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061364f565b6001600160a01b03168352565b815160049060c09061263b90610da3906001600160a01b031681565b604051633850c7bd60e01b815292839182905afa801561066e575f915f916126ee575b5060020b60808401525b6001600160a01b03908116610100840152825160049160209161268d91610da3911681565b60405163ddca3f4360e01b815292839182905afa801561066e5761018e915f916126bf575b5062ffffff166040840152565b6126e1915060203d6020116126e7575b6126d98183610217565b810190612485565b5f6126b2565b503d6126cf565b6126689250612715915060c03d60c011612722575b61270d8183610217565b81019061242a565b505050509190919061265e565b503d612703565b61276561273461213c565b806101208b01526122bb7f000000000000000000000000000000000000000000000000000000000000000091611b09565b6125a3565b506001600160a01b038381167f00000000000000000000000000000000000000000000000000000000000000009091161461258f565b506001600160a01b038481167f000000000000000000000000000000000000000000000000000000000000000090911614612588565b61256798506125ca97506125709650612577935060c0925061280791506101803d8111612127576121108183610217565b505050509b9650945095929c919c9b909b9c9b95939450509a99985050919261252e565b91906128e361289a61026d935f6060604051612846816101dc565b8281528260208201528260408201520152612894610e1b60406128856001600160401b0361287e60608c01516001600160401b031690565b16856137bf565b9801516001600160401b031690565b906137bf565b916128c26128b460a06128b96128b460c086015160020b90565b61388f565b93015160020b90565b926128cb611265565b95865260208601526001600160a01b03166040850152565b6001600160a01b03166060830152565b919082604091031261017f576020825192015190565b6001600160a01b039283169491929091907f000000000000000000000000000000000000000000000000000000000000000016858114612b155750602084810151604051630ae169a560e41b8152600481019190915295869060249082905f905af194851561066e575f95612af0575b5061298490856137bf565b9160038251145f14612a1e57906129be916129b86129b26129a888610ff886611b09565b95610ff884611b09565b91611b09565b52611b09565b5261012001516001600160a01b03906129e7906129da90611b09565b516001600160a01b031690565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79080602081015b0390a3565b90926101200192612a326129da8551611ae7565b6001600160a01b039081167f000000000000000000000000000000000000000000000000000000000000000090911603612aab57612a9391612a8d612a87612a7d88610ff886611ae7565b95610ff884611ae7565b91611ae7565b52611ae7565b52516001600160a01b03906129e7906129da90611ae7565b612ad891612ad2612acc612ac288610ff886611af9565b95610ff884611af9565b91611af9565b52611af9565b52516001600160a01b03906129e7906129da90611af9565b612984919550612b0e9060203d602011610667576106598183610217565b9490612979565b8493929550612b9760409160205f970151612b2e611265565b90815230602082019081526001600160801b0385830181905260608301818152865163fc6f786560e01b81528451600482015292516001600160a01b03166024840152604090930151811660448301529151909116606482015296879283919082906084820190565b03925af194851561066e575f945f96612c9f575b50610da3936129da93612c1d612acc612c14899686612bed612acc8e612bda612c6a9f610ff86101209d611ae7565b612be385611ae7565b52610ff884611af9565b52612c04612bfb828a6137bf565b610ff887611ae7565b612c0d86611ae7565b528b6137bf565b610ff884611af9565b52019060018060a01b03612c346129da8451611ae7565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd790602090a351611af9565b6040519182529033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd7908060208101612a19565b612acc9650612c6a9550936129da93612c1d612acc612c14612bed9661012096612ce3610da39b60403d604011612cf9575b612cdb8183610217565b8101906128f3565b9990999d50509650969950505050935093612bab565b503d612cd1565b916001600160a01b03918216917f0000000000000000000000000000000000000000000000000000000000000000168203612d3a57505050565b602081810151604051630852cd8d60e31b8152600481019190915292839060249082905f905af191821561066e575f92612df6575b5081612d7a57505050565b8251600303612d91575090612d8e90611b09565b52565b6129da610120612da2920151611ae7565b6001600160a01b039081167f000000000000000000000000000000000000000000000000000000000000000090911603612de657612a87612d8e91610ff884611ae7565b612acc612d8e91610ff884611af9565b612e1091925060203d602011610667576106598183610217565b905f612d6f565b90610acf86612eb995612e69888c8c899c9b9e9d9b612eb29a64e8d4a51000612ead9a5f6080604051612e49816101fc565b828152826020820152826040820152826060820152015202019386613baf565b9b8c82849f9495939c8183885f14612ef65750612e8591611b62565b955b15612ee55750612e9691611b8b565b935b6001600160a01b039081169281169116613cb6565b6137bf565b92826137bf565b91612ecc612ec5611256565b9515158652565b8260208601526040850152036060830152608082015290565b9050612ef091611b62565b93612e98565b9050612f0191611b8b565b95612e87565b92939491906060860191825115612fff5760e001518051612fe75750612fc79291848792612f358451151590565b94612f4660408a015162ffffff1690565b95612f508a613f6b565b6101008b01516001600160a01b03169162ffffff6080612fba612f906060612f8160408a015160018060a01b031690565b9801516001600160a01b031690565b97611051612fb3612fad612fa38d611ae7565b5161105185611ae7565b9b611af9565b5191611af9565b9851990151991690613fd5565b918215612fe157612fdb61018e9451151590565b9161417f565b50505050565b9492505050612ff961018e9451151590565b91613e0f565b50505050505050565b51604051633850c7bd60e01b81529060c090829060049082906001600160a01b03165afa90811561066e575f9161303d575090565b613056915060c03d60c0116127225761270d8183610217565b505050505090565b9081606091031261017f5780516130748161177e565b916040602083015192015190565b9261316c835f93946060936130ee8161012089016130e9610da36129da6130af610da36129da8651611ae7565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316998a95916125bd919087906142fe565b6142fe565b6020870151916130fc611274565b928352602083019081526040808401928352868401889052608084018881524260a08601908152915163219f5d1760e01b8152855160048201529251602484015292516044830152606090930151606482015290516084820152905160a4820152938492839190829060c4820190565b03925af191821561066e57612d8e92612acc925f5f915f936131c6575b506131a49060e06131b3949501906001600160801b03169052565b6131ad86611ae7565b51611b62565b6131bc85611ae7565b526131ad84611af9565b6131b3935060e092506131a491506131f59060603d6060116131ff575b6131ed8183610217565b81019061305e565b9450925090613189565b503d6131e3565b906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691908316908282036132445750505050565b602001928351833b1561017f5760405163095ea7b360e01b81526001600160a01b039290921660048301526024820152915f908390604490829084905af190811561066e575f936020936132ba936132f0575b505160405194858094819363140e25ad60e31b8352600483019190602083019252565b03925af1801561066e576132d1575b808080612fe1565b6132e99060203d602011610667576106598183610217565b505f6132c9565b80610a62876132fe93610217565b5f613297565b602085015194959490936001600160a01b0316803b1561017f5760405163095ea7b360e01b815233600482015260248101929092525f908290604490829084905af1801561066e5761348e575b506001945f5b8251811015613487576133726129da82610120850151611b19565b9661337d8285611b19565b516133888387611b19565b51101561345b57906133da6001926133ae6133a38488611b19565b51611051858a611b19565b6133b88488611b19565b526133d56133c68488611b19565b5133868060a01b038d166142fe565b61212e565b975b6133e68287611b19565b5161343a575b6133f68287611b19565b5160405190815260a084901b8490039182169188169033907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e05663190602090a401613357565b6134566134478388611b19565b5188858060a01b0384166135b8565b6133ec565b969060019161346a8286611b19565b516134758388611b19565b525f6134818387611b19565b526133dc565b5050505050565b80610a625f61349c93610217565b5f613351565b92939091936134af61194d565b506134b982611aa1565b9460016134c584611aa1565b936134cf81611aa1565b956134e66134dc83611aa1565b986122bb8b611ae7565b6134ef86611ae7565b52816134fa87611ae7565b52600261350688611ae7565b521161352c575b5050613517611265565b93845260208401526040830152606082015290565b9194909395926001935f5b86518110156135a85761354a8188611b19565b51613558575b600101613537565b946135a060019161357961356f6129da8a8a611b19565b6122bb8389611b19565b613583888a611b19565b5161358e8287611b19565b528261359a828d611b19565b5261212e565b959050613550565b5096945092509390505f8061350d565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f511416151661363b575b501561360457565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f6135fc565b1561017f57565b916001600160a01b03848116908216101561017f57604080516001600160a01b0392831660208201908152929095169085015260029490940b60608401526055936037936136a0816080810161233b565b5190209060405192733d602d80600a3d3981f3363d3d373d3d3d363d7360601b845260601b60148401526f5af43d82803e903d91602b57fd5bf3ff60801b602884015260601b6038830152604c820152818120606c820152012090565b60b581600160881b8110156137a8575b8069010000000000000000006201000092101561379b575b6501000000000081101561378e575b6301000000811015613781575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8080920410900390565b60101c9160081b91613741565b60201c9160101b91613734565b60401c9160201b91613725565b5068b500000000000000009050608082901c61370d565b90805f19048211810215670de0b6b3a7640000021561017f57670de0b6b3a764000091020490565b7812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111670de0b6b3a7640000021582021561017f57670de0b6b3a7640000020490565b90805f19048211810215620f4240021561017f57620f424091020490565b7f1c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71811160090215600a021561017f576009600a91020490565b815f1904811182021583021561017f57020490565b60020b908160ff1d82810118620d89e88111613ba95763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102600160801b189160028116613b8d575b60048116613b71575b60088116613b55575b60108116613b39575b60208116613b1d575b60408116613b01575b60808116613ae5575b6101008116613ac9575b6102008116613aad575b6104008116613a91575b6108008116613a75575b6110008116613a59575b6120008116613a3d575b6140008116613a21575b6180008116613a05575b6201000081166139e9575b6202000081166139ce575b6204000081166139b3575b620800001661399a575b5f12613992575b0160201c90565b5f190461398b565b6b048a170391f7dc42444e8fa290910260801c90613984565b6d2216e584f5fa1ea926041bedfe9890920260801c9161397a565b916e5d6af8dedb81196699c329225ee6040260801c9161396f565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c91613964565b916f31be135f97d08fd981231505542fcfa60260801c91613959565b916f70d869a156d2a1b890bb3df62baf32f70260801c9161394f565b916fa9f746462d870fdf8a65dc1f90e061e50260801c91613945565b916fd097f3bdfd2022b8845ad8f792aa58250260801c9161393b565b916fe7159475a2c29b7443b29c7fa6e889d90260801c91613931565b916ff3392b0822b70005940c7a398e4b70f30260801c91613927565b916ff987a7253ac413176f2b074cf7815e540260801c9161391d565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c91613913565b916ffe5dee046a99a2a811c461f1969c30530260801c91613909565b916fff2ea16466c96a3843ec78b326b528610260801c91613900565b916fff973b41fa98c081472e6896dfb254c00260801c916138f7565b916fffcb9843d60f6159c9db58835c9266440260801c916138ee565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c916138e5565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c916138dc565b916ffff97272373d413259a46990580e213a0260801c916138d3565b8261437a565b94929593905f92808710155f14613bd2575050505061026d9060019480946144b1565b92969192818711613bec57505050908161026d9294614477565b909196506001600160801b03869493961161017f578015613cb157613c30613c29613c3d93613c36936002880a04818860011b03039087036137e7565b978561445c565b86611b8b565b80956137e7565b9385851015613c8457613c8192939495613c7a91600197613c6985670de0b6b3a764000003868561387a565b670de0b6b3a764000001920361387a565b80936144ea565b91565b61026d9294613caa915f97613c9985826137bf565b670de0b6b3a764000003920361387a565b8094614477565b61438f565b919390916001600160a01b0380861690841611613d4c575b6001600160a01b038181169084168111613cf8575050509061026d92613cf392614563565b61454f565b929391929091906001600160a01b0386161115613d3d5790613d22613d28939261026d9683614563565b9361451e565b80821015613d36575061454f565b905061454f565b505061026d92613cf39261451e565b919391613cce565b909160608284031261017f578151613d6b8161016e565b926020830151926040810151906001600160401b03821161017f570181601f8201121561017f57805190613d9e826116bc565b92613dac6040519485610217565b8284526020838301011161017f57815f9260208093018386015e8301015290565b91613df4906080949796959260018060a01b0316845260a0602085015260a0840190610238565b6001600160a01b039687166040840152951660608201520152565b90926040613e296101209260208082518301019101613d54565b939096865f14613f49570190613e4f6129da613e486129da8551611ae7565b9351611af9565b935b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116925f91613e8e908b90869088166135b8565b613eb68a8751988997889687956344bc658560e01b875260018060a01b031660048701613dcd565b03925af192831561066e575f915f94613f26575b5015613f0057613eed613ef791613ee8612d8e95966131ad86611ae7565b611b8b565b93610ff883611af9565b612a8d82611af9565b613f2190613ee8613f17612d8e95610ff886611ae7565b956131ad85611af9565b613ef7565b909350613f42915060403d604011612cf957612cdb8183610217565b925f613eca565b0190613f656129da613f5e6129da8551611af9565b9351611ae7565b93613e51565b51604051630d34328160e11b815290602090829060049082906001600160a01b03165afa90811561066e575f91613fa0575090565b90506020813d602011613fcd575b81613fbb60209383610217565b8101031261017f575161026d8161177e565b3d9150613fae565b97919492959690935f925b60648410613ff5575050505050505050505090565b6140109086620f42409c939495969798999a9b9c0390613823565b8a15614105576001600160a01b039061402a90898b614952565b16906001600160a01b039061404090898b614a17565b165b8a156140e8570160011c6001600160a01b03165b6001600160a01b03818116908616811061407a5750505050505061026d94506145d0565b9498979695946001600160a01b03831610156140d55780828b858b8d6140a66140ae978e8e8e886145f5565b959094614641565b809b929591956140c657506001019291909893613fe0565b9a505050505050505050505090565b50509550509091925061026d94506145aa565b6001600160a01b039180820160011c916002910815150116614056565b906001600160a01b039061411a90898b6148f3565b16906001600160a01b039061413090898b6149bb565b16614042565b600160ff1b8114611b5d575f0390565b6001600160a01b039182168152911515602083015260408201929092529116606082015260a06080820181905261026d92910190610238565b9261424492916040916141a76141a1610da3610da3855160018060a01b031690565b91614136565b84156142e2575f6401000276a4915b61422661012086016142186141e960606141e06129da6141d96129da8751611ae7565b9551611af9565b99015160020b90565b89516001600160a01b03938416602082015298909216604089015260029190910b606088015286906080820190565b03601f198101875286610217565b8551630251596160e31b8152978895869485938a3060048701614146565b03925af190811561066e57612d8e925f905f936142be575b5081156142a957614270906131ad86611ae7565b61427985611ae7565b521561429c57612acc9061429661428f85611af9565b5191614136565b90611b8b565b612acc906131ad84611af9565b6142b99061429661428f87611ae7565b614270565b90506142da91925060403d604011612cf957612cdb8183610217565b91905f61425c565b5f73fffd8963efd1fc6a506488495d951d5263988d25916141b6565b906014528160345263095ea7b360601b5f5260205f6044601082855af13d1560015f5114171615614332575b50505f603452565b604460105f80936020958260345263095ea7b360601b83528238868683865af1506034525af13d1560015f511417161561436d575f8061432a565b633e3f8f735f526004601cfd5b6345c3193d60e11b5f5260020b60045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b9290600193925b600182116143b6575050565b90928082048111611b5d57600184166143d6575b80029260011c906143aa565b809402936143ca565b6143f05f196002600160601b6143a3565b90815f19048111611b5d570290565b905f91801561445657808060011461444e576002146144475760016101338210166001600b8310161761443c576143f091925060025f19916143a3565b6002900a91611b5d57565b5060049150565b506001925050565b505f9150565b9061446961026d926143ff565b6144716143df565b91614870565b90916001600160801b03821161017f5761026d9261449e91670de0b6b3a7640000036137bf565b9060026144a96143df565b910a91614870565b90916001600160801b03821161017f5761026d926144d891670de0b6b3a7640000036137bf565b60026144e26143df565b920a90614870565b9190916001600160801b03811161017f5761026d9261450d9160026144a96143df565b90670de0b6b3a764000003906137e7565b61026d9291906001600160a01b0380831690821611614549575b90036001600160a01b03169061479a565b90614538565b906001600160801b03821691820361017f57565b61026d92916001600160a01b03808216908316116145a4575b6145926001600160a01b03828116908416614827565b9190036001600160a01b031691614870565b9061457c565b6145c361026d946145ca92949394620f42400390614ab5565b83836149bb565b90614b17565b6145e961026d946145f092949394620f42400390614ab5565b8383614952565b614bdb565b939493919291156146265790614620839261461461026d958489614b7b565b90620f42400390614ad8565b94614bdb565b9361463b8295614614858461026d9796614c07565b94614b17565b939497959296909196845f14614767578881111561475e576146678982035b8984614563565b90614675858801848661451e565b985b898310998a1561474e5761468b81856137e7565b9a5b156146f357505050906146a36146a9939261454f565b91614c07565b91156146e457620f4240929190828211156146d35750035b925b670de0b6b3a76400000310929190565b90506146df9150613841565b6146c1565b939450620f42409103936146c3565b90925061470b9196975061471194509994989961454f565b91614b7b565b921561472457500392620f4240906146c3565b9493620f424092908282111561473d5750035b926146c3565b90506147499150613841565b614737565b61475884826137e7565b9a61468d565b6146675f614660565b6147748482018984614563565b90898711156147915761478b8a88035b848661451e565b98614677565b61478b5f614784565b90606082901b905f19600160601b84099282808510940393808503946147c1868511613648565b14614820578190600160601b900981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b81810291905f1982820991838084109303928084039384600160601b111561017f571461486757600160601b910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f19818509938380861095039480860395614891878611613648565b146148eb579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b9190811561494d576001600160a01b03909216918183029160609190911b600160601b600160e01b0316908204831482821116156149405761026d9261493b92820391614c43565b614c6c565b63f5c787f15f526004601cfd5b505090565b9190811561494d5760601b600160601b600160e01b0316916001600160a01b031681810282810482146149a7575b508015613cb1578204908101809111611b5d5780820615159104016001600160a01b031690565b8301838110614980579150610da392614c43565b916001600160a01b0381116149fe576001600160801b039060601b9116908115613cb15704905b6001600160a01b0316908101908110611b5d5761026d90614c6c565b6001600160801b03614a1192169061479a565b906149e2565b91906001600160a01b038211614a78576001600160801b03614a3c614a499360601b90565b9116808206151591040190565b905b6001600160a01b03169080821115614a6b5790036001600160a01b031690565b634323a5555f526004601cfd5b6001600160801b0316614a9081600160601b84614870565b918115613cb157600160601b9009614aa9575b90614a4b565b60010180614aa3575f80fd5b815f19048111820215620f4240021561017f5702620f4240808204910615150190565b7d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98111620f4240021582021561017f57620f42400290808204910615150190565b6001600160a01b0382811690821611614b75575b6001600160a01b038116928315614b6957614b65926001600160a01b0380821693909103169060601b600160601b600160e01b0316614870565b0490565b62bfc9215f526004601cfd5b90614b2b565b6001600160a01b0382811690821611614bd5575b6001600160a01b038116928315614b6957614bc9926001600160a01b0380821693909103169060601b600160601b600160e01b0316614c43565b90808206151591040190565b90614b8f565b61026d926001600160a01b03928316919092160360ff81901d90810118906001600160801b0316614827565b6001600160a01b0391821691160360ff81901d90810118906001906001600160801b0316614c358382614827565b928260601b91091515160190565b929190614c51828286614870565b938215613cb15709614c5f57565b9060010190811561017f57565b6001600160a01b03811691908203614c8057565b6393dafdf160e01b5f5260045ffdfea2646970667358221220f14670242a92f44dbb020f4d9cf32142f187ce6704228d21852e54269864ac3864736f6c634300081e0033000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470200000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca80000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a810000000000000000000000001dc7a0f5336f52724b650e39174cfcbbedd67bf1000000000000000000000000d74339e0f10fce96894916b93e5cc7de89c98272
Deployed Bytecode
0x6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c80630686ddd9146101695780630a73e391146101645780631204f5251461015f578063150b7a021461015a5780632fcb4f04146101555780633d692da114610150578063452a93201461014b5780635c975abb146101465780635f4860df146101415780637d5ad9cd1461013c5780638456cb59146101375780638cffa277146101325780638da5cb5b1461012d5780638da92e7114610128578063a129568d14610123578063a7310b581461011e578063a89d6dd414610119578063b699b82b14610114578063bc25cf771461010f578063f2fde38b1461010a5763fa461e330361000e57611483565b61140f565b611341565b6112fd565b611283565b6111c3565b610d58565b610c15565b610bee565b610baa565b610b2e565b610858565b610760565b610732565b61070a565b6104dd565b610466565b61040c565b610384565b610353565b610270565b6001600160a01b0381160361017f57565b5f80fd5b359061018e8261016e565b565b90600182811c921680156101be575b60208310146101aa57565b634e487b7160e01b5f52602260045260245ffd5b91607f169161019f565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b038211176101f757604052565b6101c8565b60a081019081106001600160401b038211176101f757604052565b90601f801991011681019081106001600160401b038211176101f757604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90602061026d928181520190610238565b90565b3461017f57602036600319011261017f5760043561028d8161016e565b6001600160a01b03165f90815260046020526040808220905181549092916102b482610190565b808552916001811690811561032c57506001146102ec575b6102e8846102dc81860382610217565b6040519182918261025c565b0390f35b5f90815260208120939250905b808210610312575090915081016020016102dc826102cc565b9192600181602092548385880101520191019092916102f9565b60ff191660208087019190915292151560051b850190920192506102dc91508390506102cc565b3461017f57602036600319011261017f57602061037a6004356103758161016e565b611597565b6040519015158152f35b3461017f57604036600319011261017f5760206004356103a38161016e565b602435906103b08261016e565b6001600160a01b039081165f908152600584526040808220938316825260209390935282902054915191168152f35b9181601f8401121561017f578235916001600160401b03831161017f576020838186019501011161017f57565b3461017f57608036600319011261017f5761042860043561016e565b61043360243561016e565b6064356001600160401b03811161017f576104529036906004016103df565b5050604051630a85bd0160e11b8152602090f35b3461017f57602036600319011261017f576004356104838161016e565b61049760018060a01b035f5416331461163d565b600180546001600160a01b0319166001600160a01b03929092169182179055337fa14fc14d8620a708a896fd11392a235647d99385500a295f0d7da2a258b2e9675f80a3005b3461017f5760e036600319011261017f576004356104fa8161016e565b602435906105078261016e565b60a43560843560643560443560c4356001600160401b03811161017f576105329036906004016103df565b6002549197916001600160a01b03166106f157604051630972932760e21b81526001600160a01b0388166004820152602081806024810103817f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03165afa90811561066e575f916106c2575b50156106b357604051638da5cb5b60e01b8152906001600160a01b0388166020836004815f855af192831561066e575f93610682575b506001600160a01b03831633036106735760205f91600460405180948193635e34633b60e11b83525af1801561066e576003915f9161063f575b5010610630576100189861062a9136916116d7565b96611e28565b63a93eca7960e01b5f5260045ffd5b610661915060203d602011610667575b6106598183610217565b8101906116ad565b5f610615565b503d61064f565b61168d565b6312272fd360e11b5f5260045ffd5b6106a591935060203d6020116106ac575b61069d8183610217565b810190611698565b915f6105db565b503d610693565b630ea8370b60e41b5f5260045ffd5b6106e4915060203d6020116106ea575b6106dc8183610217565b810190611678565b5f6105a5565b503d6106d2565b63b5dfd9e560e01b5f5260045ffd5b5f91031261017f57565b3461017f575f36600319011261017f576001546040516001600160a01b039091168152602090f35b3461017f575f36600319011261017f57602060ff5f5460a01c166040519015158152f35b8015150361017f57565b3461017f57606036600319011261017f5760043561077d8161016e565b610788602435610756565b6044356001600160401b03811161017f576107a79036906004016103df565b6002549192916001600160a01b03166106f157604051630972932760e21b81523360048201526020816024816001600160a01b037f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59165afa90811561066e575f91610839575b50156106b3578261082391610018940190611728565b9590949193916001600160a01b03169033611e28565b610852915060203d6020116106ea576106dc8183610217565b5f61080d565b3461017f57604036600319011261017f576004356108758161016e565b6024356001600160401b03811161017f5780600401610100600319833603011261017f5760ff5f5460a01c16610b1f576002546001600160a01b03166106f157600280546001600160a01b0319166001600160a01b038516179055604051638da5cb5b60e01b81526001600160a01b038416936020826004815f895af190811561066e5761092561093a92610947945f91610b00575b506001600160a01b03165f90815260056020526040902090565b9060018060a01b03165f5260205260405f2090565b546001600160a01b031690565b336001600160a01b0390911603610af15761096b61096761037583611774565b1590565b610ae257610a09916109f0915f5f908360448101936001600160801b036109918661178f565b1615801590610abf575b610a6e575b6001600160801b0380826109fe6109df60646109d86109d260246109cb6001600160601b039a611774565b9a016117aa565b9b61178f565b9b0161178f565b916040519b8c91336020840161182f565b03601f1981018c528b610217565b169616941690612268565b90803b1561017f5760405162b9252f60e41b8152905f908290818381610a3388306004840161192b565b03925af1801561066e57610a54575b600280546001600160a01b0319169055005b80610a625f610a6893610217565b80610700565b80610a42565b6001600160801b039350809250610a859150611774565b50836001600160601b0383610ab3610aae610aa2602486016117aa565b6001600160601b031690565b612099565b959094925050506109a0565b50610adb610acf6064840161178f565b6001600160801b031690565b151561099b565b63ed5f09f160e01b5f5260045ffd5b6317fb43e560e31b5f5260045ffd5b610b19915060203d6020116106ac5761069d8183610217565b5f61090b565b6313d0ff5960e31b5f5260045ffd5b3461017f575f36600319011261017f576001546001600160a01b03163303610b9b575f5460ff8160a01c16610b1f5760ff60a01b1916600160a01b175f55604051600181527f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a90602090a1005b636570ecab60e11b5f5260045ffd5b3461017f575f36600319011261017f576040517f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03168152602090f35b3461017f575f36600319011261017f575f546040516001600160a01b039091168152602090f35b3461017f57602036600319011261017f577f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a6020600435610c5581610756565b5f5490610c6c336001600160a01b0384161461163d565b15159060ff60a01b8260a01b169060ff60a01b1916175f55604051908152a1005b90602080835192838152019201905f5b818110610caa5750505090565b8251845260209384019390920191600101610c9d565b80516080808452815190840181905260a08401949391602001905f5b818110610d28575050506060610d17610d0561026d959660208601518582036020870152610c8d565b60408501518482036040860152610c8d565b920151906060818403910152610c8d565b82516001600160a01b0316875260209687019690920191600101610cdc565b90602061026d928181520190610cc0565b3461017f57602036600319011261017f576004356001600160401b03811161017f57610d889036906004016103df565b90610d9161194d565b50600254610daf906001600160a01b03165b6001600160a01b031690565b33036111b457335f908152600360205260409020610dda9190610dd190611971565b928101906119ba565b8051909291906001600160a01b03169060a08401610dff81516001600160401b031690565b6001600160401b03610e27610e1b87516001600160401b031690565b6001600160401b031690565b911611801561117e575b61116f57610e55610e4f610aa260208801516001600160601b031690565b846124a0565b90610120820194610e67865151611aa1565b91610e7f610acf60408a01516001600160801b031690565b610e8884611ae7565b52610ea0610acf60608a01516001600160801b031690565b610ea984611af9565b52610eb48351611aa1565b97610ec46080820151868561282b565b610100860193610ed8610967838751611b98565b611140578a8988809651610ef2906001600160401b031690565b6001600160401b031691610f06938a612909565b610f11848a88612d00565b608001516001600160401b0316604084015162ffffff1660c08401518c906001600160401b0316875160408601519091906001600160a01b031660608701519092906001600160a01b031694610f668c611ae7565b51610f7086611ae7565b51610f7a91611b62565b94610f848d611af9565b5190610f8f90611af9565b51610f9991611b62565b610fc59790966001600160a01b039081169516936001600160401b039081169262ffffff169116612e17565b938a82868051610fd490151590565b5f14966109679661100f946110239961114f57610ffe6020850151610ff883611ae7565b51611b8b565b61100782611ae7565b525b8b612f07565b61101b610da388613008565b809152611b98565b611140578051156111175761106161103a83611ae7565b5161105861104785611af9565b516110518b611af9565b5190611b62565b905b8585613082565b6001600160801b03604061107f60e08601516001600160801b031690565b9201519116106111085760206110b183866102e899856110bf986110a38585613206565b6001600160a01b0316613304565b9201948551905190856134a2565b91516040519081526001600160a01b03919091169033907f6c7e7d4cb83a668aef31739dd35dc3fc3d5f31d62b69e438b7b24d35b40dcc6390602090a360405191829182610d47565b63bb55fd2760e01b5f5260045ffd5b61106161113061112684611ae7565b516110518a611ae7565b61113984611af9565b519061105a565b633a8bf65960e01b5f5260045ffd5b6111606020850151610ff883611af9565b61116982611af9565b52611009565b632a9ffab760e21b5f5260045ffd5b5060c08501516001600160401b03166001600160401b036111ac610e1b60208801516001600160401b031690565b911611610e31565b63f3f6425d60e01b5f5260045ffd5b3461017f57602036600319011261017f576004356111e08161016e565b6001600160a01b03165f90815260036020908152604091829020805460019091015483516001600160401b03808416825283861c811694820194909452608083811c85169582019590955260c09290921c6060830152919091169181019190915260a090f35b6040519061018e61010083610217565b6040519061018e60a083610217565b6040519061018e608083610217565b6040519061018e60c083610217565b3461017f5760a036600319011261017f57600435608036602319011261017f576112eb6102e891604051906112b7826101dc565b602435825260443560208301526064356112d08161016e565b60408301526084356112e18161016e565b6060830152611b98565b60405190151581529081906020820190565b3461017f575f36600319011261017f576040517f000000000000000000000000354dbba1348985cc952c467b8ddaf5dd075906676001600160a01b03168152602090f35b3461017f57602036600319011261017f5760043561135e8161016e565b5f5460ff90611377336001600160a01b0383161461163d565b60a01c16610b1f576002546001600160a01b03166106f1576001600160a01b0316806113b757506100185f80808047335af16113b1611bb1565b90611be0565b6040516370a0823160e01b815230600482015290602082602481845afa90811561066e57610018925f926113ee575b5033906135b8565b61140891925060203d602011610667576106598183610217565b905f6113e6565b3461017f57602036600319011261017f5760043561142c8161016e565b5f5490611443336001600160a01b0384161461163d565b60018060a01b031680916001600160601b0360a01b16175f55337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b3461017f57606036600319011261017f576024356004356044356001600160401b03811161017f576114bb60609136906004016103df565b908092918101031261017f578035926114d38461016e565b61154b60406020840135936114e78561016e565b0135946114f386611c10565b6001600160a01b039384169593169285847f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f7f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca861364f565b336001600160a01b0390911603611588575f8313156115715750610018925033906135b8565b9150505f811361157d57005b6100189133906135b8565b634b60273560e01b5f5260045ffd5b7f0000000000000000000000001dc7a0f5336f52724b650e39174cfcbbedd67bf16001600160a01b03908116911690811490811561160a575b81156115da575090565b7f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b031614919050565b7f000000000000000000000000d74339e0f10fce96894916b93e5cc7de89c982726001600160a01b0316811491506115d0565b1561164457565b60405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606490fd5b9081602091031261017f575161026d81610756565b6040513d5f823e3d90fd5b9081602091031261017f575161026d8161016e565b9081602091031261017f575190565b6001600160401b0381116101f757601f01601f191660200190565b9291926116e3826116bc565b916116f16040519384610217565b82948184528183011161017f578281602093845f960137010152565b9080601f8301121561017f5781602061026d933591016116d7565b9160c08383031261017f57823561173e8161016e565b9260208101359260408201359260608301359260808101359260a08201356001600160401b03811161017f5761026d920161170d565b3561026d8161016e565b6001600160801b0381160361017f57565b3561026d8161177e565b6001600160601b0381160361017f57565b3561026d81611799565b359061018e82611799565b359061018e8261177e565b35906001600160401b038216820361017f57565b9035601e198236030181121561017f5701602081359101916001600160401b03821161017f57813603831361017f57565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b0390911681526040602082015261026d916101409061191890803561185a8161016e565b6001600160a01b03166040850152611887611877602083016117b4565b6001600160601b03166060860152565b6118a6611896604083016117bf565b6001600160801b03166080860152565b6118c56118b5606083016117bf565b6001600160801b031660a0860152565b608081013560c08501526118ee6118de60a083016117ca565b6001600160401b031660e0860152565b61190e6118fd60c083016117ca565b6001600160401b0316610100860152565b60e08101906117de565b919092610100610120820152019161180f565b6001600160a01b03909116815260406020820181905261026d92910190610238565b6040519061195a826101dc565b606080838181528160208201528160408201520152565b9060405161197e816101fc565b60806001600160401b03806001849680548381168752838160401c1660208801528381871c16604088015260c01c606087015201541616910152565b919060408382031261017f5782356119d18161016e565b926020810135906001600160401b03821161017f57016101008183031261017f576119fa611246565b91611a0482610183565b8352611a12602083016117b4565b6020840152611a23604083016117bf565b6040840152611a34606083016117bf565b606084015260808201356080840152611a4f60a083016117ca565b60a0840152611a6060c083016117ca565b60c084015260e08201356001600160401b03811161017f57611a82920161170d565b60e082015290565b6001600160401b0381116101f75760051b60200190565b90611aab82611a8a565b611ab86040519182610217565b8281528092611ac9601f1991611a8a565b0190602036910137565b634e487b7160e01b5f52603260045260245ffd5b805115611af45760200190565b611ad3565b805160011015611af45760400190565b805160021015611af45760600190565b8051821015611af45760209160051b010190565b634e487b7160e01b5f52601160045260245ffd5b670de0b6b3a76400000390670de0b6b3a76400008211611b5d57565b611b2d565b91908203918211611b5d57565b670de0b6b3a7640000019081670de0b6b3a764000011611b5d57565b91908201809211611b5d57565b815181119182611ba757505090565b6020015111919050565b3d15611bdb573d90611bc2826116bc565b91611bd06040519384610217565b82523d5f602084013e565b606090565b15611be85750565b60405162461bcd60e51b815260206004820152908190611c0c906024830190610238565b0390fd5b8060020b0361017f57565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715611b5d57565b6001611cf1608061018e94611c746001600160401b0382511686906001600160401b03166001600160401b0319825416179055565b60208101518554604080840151606085015160c089811b8a900377ffffffffffffffffffffffffffffffff0000000000000000199095169590931b6fffffffffffffffff000000000000000016949094176001600160401b03918216871b179290921692901b6001600160c01b0319169190911786559101511690565b9101906001600160401b03166001600160401b0319825416179055565b601f8211611d1b57505050565b5f5260205f20906020601f840160051c83019310611d53575b601f0160051c01905b818110611d48575050565b5f8155600101611d3d565b9091508190611d34565b91909182516001600160401b0381116101f757611d8481611d7e8454610190565b84611d0e565b6020601f8211600114611dc3578190611db49394955f92611db8575b50508160011b915f199060031b1c19161790565b9055565b015190505f80611da0565b601f19821690611dd6845f5260205f2090565b915f5b818110611e1057509583600195969710611df8575b505050811b019055565b01515f1960f88460031b161c191690555f8080611dee565b9192602060018192868b015181550194019201611dd9565b9693909594929195670de0b6b3a764000083118015611fce575b8015611fbd575b8015611fac575b61116f57611f7995611f30611f4193611f20611f5d97611eaf8c611e908f6109256001600160401b039a60018060a01b03165f52600560205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b611f1086611ee1610e1b611ed6611ed1611edb610e1b611ed6611ed18a611b6f565b611c1b565b6136fd565b96611b41565b94611eff82611eee611256565b9c168c906001600160401b03169052565b166001600160401b031660208a0152565b6001600160401b03166040880152565b6001600160401b03166060860152565b166001600160401b03166080830152565b6001600160a01b0386165f908152600360205260409020611c3f565b6001600160a01b0384165f908152600460205260409020611d5d565b6001600160a01b0390811691167febc70f7c8d6a67b19e15e968cb908d21719e8ff9a778a71171fba931a618d0525f80a3565b50670de0b6b3a76400008611611e50565b50670de0b6b3a76400008411611e49565b50670de0b6b3a76400008211611e42565b519061018e82611c10565b519061018e8261177e565b91908261018091031261017f57815161200d81611799565b91602081015161201c8161016e565b91604082015161202b8161016e565b91606081015161203a8161016e565b91608082015161204981611c10565b9161205660a08201611fdf565b9161206360c08301611fdf565b9161207060e08201611fea565b91610100820151916101208101519161026d6101606120926101408501611fea565b9301611fea565b60405163133f757160e31b81526004810191909152610180816024817f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b03165afa801561066e575f915f916120f457509091565b905061211891506101803d8111612127575b6121108183610217565b810190611ff5565b50505050505050509250905091565b503d612106565b5f198114611b5d5760010190565b6040516080919061214d8382610217565b6003815291601f1901366020840137565b6040516060919061216f8382610217565b6002815291601f1901366020840137565b60405190606082018281106001600160401b038211176101f7576040525f604083606081528260208201520152565b929493916121c86121d69260a0865260a0860190610cc0565b908482036020860152610cc0565b828103604084015260608101948051956060835286518091526020608084019701905f5b81811061223d575050509461222f9160408088602061026d999a01516020850152015191015283810360608501526060610238565b916080818403910152610238565b825180516001600160a01b03168a52602090810151818b0152604090990198909201916001016121fa565b929461026d9594929460019380151590816123b7575b83151590816123a7575b61229187611aa1565b9861229b88611aa1565b966122ca6122b16122ab8b611aa1565b9a611aa1565b9a6122bb8d611ae7565b6001600160a01b039091169052565b6122d388611ae7565b5260016122df89611ae7565b5260026122eb8a611ae7565b52600193612377575b5050612349575b505050612306611265565b93845260208401526040830152606082015261233b61232361194d565b9361232c612180565b604051958694602086016121af565b03601f198101835282610217565b60019261235d61236e936122bb848b611b19565b6123678287611b19565b5285611b19565b525f80806122fb565b612387919293506122bb8a611af9565b61239086611af9565b52600161239c87611af9565b526002905f806122f4565b956123b19061212e565b95612288565b6002955061227e565b6040519061014082018281106001600160401b038211176101f7576040526060610120835f81525f60208201525f60408201525f838201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201520152565b519061ffff8216820361017f57565b91908260c091031261017f5781516124418161016e565b91602081015161245081611c10565b9161245d6040830161241b565b9161246a6060820161241b565b9160a06124796080840161241b565b92015161026d81610756565b9081602091031261017f575162ffffff8116810361017f5790565b612507909291926124af6123c0565b6020810185905260405163133f757160e31b8152600481019590955293917f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b03169061018090849081906024820190565b0381845afa91821561066e5761261f936126129387935f935f935f905f905f905f966127d6575b50612577929160c08a6125676125709461255d60606125ca9d9e9f019b60e08501906001600160801b03169052565b60020b60a0830152565b019060020b9052565b60020b8452565b6001600160a01b03161480156127a0575b801561276a575b156127295761259c61215e565b6101208901525b6125c3836122bb6101208b016125bd886122bb8351611ae7565b51611af9565b5160020b90565b917f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f7f000000000000000000000000321f7dfb9b2ea9131b8c17691cf6e01e5c149ca861364f565b6001600160a01b03168352565b815160049060c09061263b90610da3906001600160a01b031681565b604051633850c7bd60e01b815292839182905afa801561066e575f915f916126ee575b5060020b60808401525b6001600160a01b03908116610100840152825160049160209161268d91610da3911681565b60405163ddca3f4360e01b815292839182905afa801561066e5761018e915f916126bf575b5062ffffff166040840152565b6126e1915060203d6020116126e7575b6126d98183610217565b810190612485565b5f6126b2565b503d6126cf565b6126689250612715915060c03d60c011612722575b61270d8183610217565b81019061242a565b505050509190919061265e565b503d612703565b61276561273461213c565b806101208b01526122bb7f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8191611b09565b6125a3565b506001600160a01b038381167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a819091161461258f565b506001600160a01b038481167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8190911614612588565b61256798506125ca97506125709650612577935060c0925061280791506101803d8111612127576121108183610217565b505050509b9650945095929c919c9b909b9c9b95939450509a99985050919261252e565b91906128e361289a61026d935f6060604051612846816101dc565b8281528260208201528260408201520152612894610e1b60406128856001600160401b0361287e60608c01516001600160401b031690565b16856137bf565b9801516001600160401b031690565b906137bf565b916128c26128b460a06128b96128b460c086015160020b90565b61388f565b93015160020b90565b926128cb611265565b95865260208601526001600160a01b03166040850152565b6001600160a01b03166060830152565b919082604091031261017f576020825192015190565b6001600160a01b039283169491929091907f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb2470216858114612b155750602084810151604051630ae169a560e41b8152600481019190915295869060249082905f905af194851561066e575f95612af0575b5061298490856137bf565b9160038251145f14612a1e57906129be916129b86129b26129a888610ff886611b09565b95610ff884611b09565b91611b09565b52611b09565b5261012001516001600160a01b03906129e7906129da90611b09565b516001600160a01b031690565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79080602081015b0390a3565b90926101200192612a326129da8551611ae7565b6001600160a01b039081167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8190911603612aab57612a9391612a8d612a87612a7d88610ff886611ae7565b95610ff884611ae7565b91611ae7565b52611ae7565b52516001600160a01b03906129e7906129da90611ae7565b612ad891612ad2612acc612ac288610ff886611af9565b95610ff884611af9565b91611af9565b52611af9565b52516001600160a01b03906129e7906129da90611af9565b612984919550612b0e9060203d602011610667576106598183610217565b9490612979565b8493929550612b9760409160205f970151612b2e611265565b90815230602082019081526001600160801b0385830181905260608301818152865163fc6f786560e01b81528451600482015292516001600160a01b03166024840152604090930151811660448301529151909116606482015296879283919082906084820190565b03925af194851561066e575f945f96612c9f575b50610da3936129da93612c1d612acc612c14899686612bed612acc8e612bda612c6a9f610ff86101209d611ae7565b612be385611ae7565b52610ff884611af9565b52612c04612bfb828a6137bf565b610ff887611ae7565b612c0d86611ae7565b528b6137bf565b610ff884611af9565b52019060018060a01b03612c346129da8451611ae7565b604051928352169033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd790602090a351611af9565b6040519182529033907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd7908060208101612a19565b612acc9650612c6a9550936129da93612c1d612acc612c14612bed9661012096612ce3610da39b60403d604011612cf9575b612cdb8183610217565b8101906128f3565b9990999d50509650969950505050935093612bab565b503d612cd1565b916001600160a01b03918216917f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb24702168203612d3a57505050565b602081810151604051630852cd8d60e31b8152600481019190915292839060249082905f905af191821561066e575f92612df6575b5081612d7a57505050565b8251600303612d91575090612d8e90611b09565b52565b6129da610120612da2920151611ae7565b6001600160a01b039081167f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8190911603612de657612a87612d8e91610ff884611ae7565b612acc612d8e91610ff884611af9565b612e1091925060203d602011610667576106598183610217565b905f612d6f565b90610acf86612eb995612e69888c8c899c9b9e9d9b612eb29a64e8d4a51000612ead9a5f6080604051612e49816101fc565b828152826020820152826040820152826060820152015202019386613baf565b9b8c82849f9495939c8183885f14612ef65750612e8591611b62565b955b15612ee55750612e9691611b8b565b935b6001600160a01b039081169281169116613cb6565b6137bf565b92826137bf565b91612ecc612ec5611256565b9515158652565b8260208601526040850152036060830152608082015290565b9050612ef091611b62565b93612e98565b9050612f0191611b8b565b95612e87565b92939491906060860191825115612fff5760e001518051612fe75750612fc79291848792612f358451151590565b94612f4660408a015162ffffff1690565b95612f508a613f6b565b6101008b01516001600160a01b03169162ffffff6080612fba612f906060612f8160408a015160018060a01b031690565b9801516001600160a01b031690565b97611051612fb3612fad612fa38d611ae7565b5161105185611ae7565b9b611af9565b5191611af9565b9851990151991690613fd5565b918215612fe157612fdb61018e9451151590565b9161417f565b50505050565b9492505050612ff961018e9451151590565b91613e0f565b50505050505050565b51604051633850c7bd60e01b81529060c090829060049082906001600160a01b03165afa90811561066e575f9161303d575090565b613056915060c03d60c0116127225761270d8183610217565b505050505090565b9081606091031261017f5780516130748161177e565b916040602083015192015190565b9261316c835f93946060936130ee8161012089016130e9610da36129da6130af610da36129da8651611ae7565b7f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb247026001600160a01b0316998a95916125bd919087906142fe565b6142fe565b6020870151916130fc611274565b928352602083019081526040808401928352868401889052608084018881524260a08601908152915163219f5d1760e01b8152855160048201529251602484015292516044830152606090930151606482015290516084820152905160a4820152938492839190829060c4820190565b03925af191821561066e57612d8e92612acc925f5f915f936131c6575b506131a49060e06131b3949501906001600160801b03169052565b6131ad86611ae7565b51611b62565b6131bc85611ae7565b526131ad84611af9565b6131b3935060e092506131a491506131f59060603d6060116131ff575b6131ed8183610217565b81019061305e565b9450925090613189565b503d6131e3565b906001600160a01b037f000000000000000000000000991d5546c4b442b4c5fdc4c8b8b8d131deb24702811691908316908282036132445750505050565b602001928351833b1561017f5760405163095ea7b360e01b81526001600160a01b039290921660048301526024820152915f908390604490829084905af190811561066e575f936020936132ba936132f0575b505160405194858094819363140e25ad60e31b8352600483019190602083019252565b03925af1801561066e576132d1575b808080612fe1565b6132e99060203d602011610667576106598183610217565b505f6132c9565b80610a62876132fe93610217565b5f613297565b602085015194959490936001600160a01b0316803b1561017f5760405163095ea7b360e01b815233600482015260248101929092525f908290604490829084905af1801561066e5761348e575b506001945f5b8251811015613487576133726129da82610120850151611b19565b9661337d8285611b19565b516133888387611b19565b51101561345b57906133da6001926133ae6133a38488611b19565b51611051858a611b19565b6133b88488611b19565b526133d56133c68488611b19565b5133868060a01b038d166142fe565b61212e565b975b6133e68287611b19565b5161343a575b6133f68287611b19565b5160405190815260a084901b8490039182169188169033907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e05663190602090a401613357565b6134566134478388611b19565b5188858060a01b0384166135b8565b6133ec565b969060019161346a8286611b19565b516134758388611b19565b525f6134818387611b19565b526133dc565b5050505050565b80610a625f61349c93610217565b5f613351565b92939091936134af61194d565b506134b982611aa1565b9460016134c584611aa1565b936134cf81611aa1565b956134e66134dc83611aa1565b986122bb8b611ae7565b6134ef86611ae7565b52816134fa87611ae7565b52600261350688611ae7565b521161352c575b5050613517611265565b93845260208401526040830152606082015290565b9194909395926001935f5b86518110156135a85761354a8188611b19565b51613558575b600101613537565b946135a060019161357961356f6129da8a8a611b19565b6122bb8389611b19565b613583888a611b19565b5161358e8287611b19565b528261359a828d611b19565b5261212e565b959050613550565b5096945092509390505f8061350d565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f511416151661363b575b501561360457565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f6135fc565b1561017f57565b916001600160a01b03848116908216101561017f57604080516001600160a01b0392831660208201908152929095169085015260029490940b60608401526055936037936136a0816080810161233b565b5190209060405192733d602d80600a3d3981f3363d3d373d3d3d363d7360601b845260601b60148401526f5af43d82803e903d91602b57fd5bf3ff60801b602884015260601b6038830152604c820152818120606c820152012090565b60b581600160881b8110156137a8575b8069010000000000000000006201000092101561379b575b6501000000000081101561378e575b6301000000811015613781575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8080920410900390565b60101c9160081b91613741565b60201c9160101b91613734565b60401c9160201b91613725565b5068b500000000000000009050608082901c61370d565b90805f19048211810215670de0b6b3a7640000021561017f57670de0b6b3a764000091020490565b7812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111670de0b6b3a7640000021582021561017f57670de0b6b3a7640000020490565b90805f19048211810215620f4240021561017f57620f424091020490565b7f1c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71811160090215600a021561017f576009600a91020490565b815f1904811182021583021561017f57020490565b60020b908160ff1d82810118620d89e88111613ba95763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102600160801b189160028116613b8d575b60048116613b71575b60088116613b55575b60108116613b39575b60208116613b1d575b60408116613b01575b60808116613ae5575b6101008116613ac9575b6102008116613aad575b6104008116613a91575b6108008116613a75575b6110008116613a59575b6120008116613a3d575b6140008116613a21575b6180008116613a05575b6201000081166139e9575b6202000081166139ce575b6204000081166139b3575b620800001661399a575b5f12613992575b0160201c90565b5f190461398b565b6b048a170391f7dc42444e8fa290910260801c90613984565b6d2216e584f5fa1ea926041bedfe9890920260801c9161397a565b916e5d6af8dedb81196699c329225ee6040260801c9161396f565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c91613964565b916f31be135f97d08fd981231505542fcfa60260801c91613959565b916f70d869a156d2a1b890bb3df62baf32f70260801c9161394f565b916fa9f746462d870fdf8a65dc1f90e061e50260801c91613945565b916fd097f3bdfd2022b8845ad8f792aa58250260801c9161393b565b916fe7159475a2c29b7443b29c7fa6e889d90260801c91613931565b916ff3392b0822b70005940c7a398e4b70f30260801c91613927565b916ff987a7253ac413176f2b074cf7815e540260801c9161391d565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c91613913565b916ffe5dee046a99a2a811c461f1969c30530260801c91613909565b916fff2ea16466c96a3843ec78b326b528610260801c91613900565b916fff973b41fa98c081472e6896dfb254c00260801c916138f7565b916fffcb9843d60f6159c9db58835c9266440260801c916138ee565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c916138e5565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c916138dc565b916ffff97272373d413259a46990580e213a0260801c916138d3565b8261437a565b94929593905f92808710155f14613bd2575050505061026d9060019480946144b1565b92969192818711613bec57505050908161026d9294614477565b909196506001600160801b03869493961161017f578015613cb157613c30613c29613c3d93613c36936002880a04818860011b03039087036137e7565b978561445c565b86611b8b565b80956137e7565b9385851015613c8457613c8192939495613c7a91600197613c6985670de0b6b3a764000003868561387a565b670de0b6b3a764000001920361387a565b80936144ea565b91565b61026d9294613caa915f97613c9985826137bf565b670de0b6b3a764000003920361387a565b8094614477565b61438f565b919390916001600160a01b0380861690841611613d4c575b6001600160a01b038181169084168111613cf8575050509061026d92613cf392614563565b61454f565b929391929091906001600160a01b0386161115613d3d5790613d22613d28939261026d9683614563565b9361451e565b80821015613d36575061454f565b905061454f565b505061026d92613cf39261451e565b919391613cce565b909160608284031261017f578151613d6b8161016e565b926020830151926040810151906001600160401b03821161017f570181601f8201121561017f57805190613d9e826116bc565b92613dac6040519485610217565b8284526020838301011161017f57815f9260208093018386015e8301015290565b91613df4906080949796959260018060a01b0316845260a0602085015260a0840190610238565b6001600160a01b039687166040840152951660608201520152565b90926040613e296101209260208082518301019101613d54565b939096865f14613f49570190613e4f6129da613e486129da8551611ae7565b9351611af9565b935b6001600160a01b037f000000000000000000000000354dbba1348985cc952c467b8ddaf5dd075906678116925f91613e8e908b90869088166135b8565b613eb68a8751988997889687956344bc658560e01b875260018060a01b031660048701613dcd565b03925af192831561066e575f915f94613f26575b5015613f0057613eed613ef791613ee8612d8e95966131ad86611ae7565b611b8b565b93610ff883611af9565b612a8d82611af9565b613f2190613ee8613f17612d8e95610ff886611ae7565b956131ad85611af9565b613ef7565b909350613f42915060403d604011612cf957612cdb8183610217565b925f613eca565b0190613f656129da613f5e6129da8551611af9565b9351611ae7565b93613e51565b51604051630d34328160e11b815290602090829060049082906001600160a01b03165afa90811561066e575f91613fa0575090565b90506020813d602011613fcd575b81613fbb60209383610217565b8101031261017f575161026d8161177e565b3d9150613fae565b97919492959690935f925b60648410613ff5575050505050505050505090565b6140109086620f42409c939495969798999a9b9c0390613823565b8a15614105576001600160a01b039061402a90898b614952565b16906001600160a01b039061404090898b614a17565b165b8a156140e8570160011c6001600160a01b03165b6001600160a01b03818116908616811061407a5750505050505061026d94506145d0565b9498979695946001600160a01b03831610156140d55780828b858b8d6140a66140ae978e8e8e886145f5565b959094614641565b809b929591956140c657506001019291909893613fe0565b9a505050505050505050505090565b50509550509091925061026d94506145aa565b6001600160a01b039180820160011c916002910815150116614056565b906001600160a01b039061411a90898b6148f3565b16906001600160a01b039061413090898b6149bb565b16614042565b600160ff1b8114611b5d575f0390565b6001600160a01b039182168152911515602083015260408201929092529116606082015260a06080820181905261026d92910190610238565b9261424492916040916141a76141a1610da3610da3855160018060a01b031690565b91614136565b84156142e2575f6401000276a4915b61422661012086016142186141e960606141e06129da6141d96129da8751611ae7565b9551611af9565b99015160020b90565b89516001600160a01b03938416602082015298909216604089015260029190910b606088015286906080820190565b03601f198101875286610217565b8551630251596160e31b8152978895869485938a3060048701614146565b03925af190811561066e57612d8e925f905f936142be575b5081156142a957614270906131ad86611ae7565b61427985611ae7565b521561429c57612acc9061429661428f85611af9565b5191614136565b90611b8b565b612acc906131ad84611af9565b6142b99061429661428f87611ae7565b614270565b90506142da91925060403d604011612cf957612cdb8183610217565b91905f61425c565b5f73fffd8963efd1fc6a506488495d951d5263988d25916141b6565b906014528160345263095ea7b360601b5f5260205f6044601082855af13d1560015f5114171615614332575b50505f603452565b604460105f80936020958260345263095ea7b360601b83528238868683865af1506034525af13d1560015f511417161561436d575f8061432a565b633e3f8f735f526004601cfd5b6345c3193d60e11b5f5260020b60045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b9290600193925b600182116143b6575050565b90928082048111611b5d57600184166143d6575b80029260011c906143aa565b809402936143ca565b6143f05f196002600160601b6143a3565b90815f19048111611b5d570290565b905f91801561445657808060011461444e576002146144475760016101338210166001600b8310161761443c576143f091925060025f19916143a3565b6002900a91611b5d57565b5060049150565b506001925050565b505f9150565b9061446961026d926143ff565b6144716143df565b91614870565b90916001600160801b03821161017f5761026d9261449e91670de0b6b3a7640000036137bf565b9060026144a96143df565b910a91614870565b90916001600160801b03821161017f5761026d926144d891670de0b6b3a7640000036137bf565b60026144e26143df565b920a90614870565b9190916001600160801b03811161017f5761026d9261450d9160026144a96143df565b90670de0b6b3a764000003906137e7565b61026d9291906001600160a01b0380831690821611614549575b90036001600160a01b03169061479a565b90614538565b906001600160801b03821691820361017f57565b61026d92916001600160a01b03808216908316116145a4575b6145926001600160a01b03828116908416614827565b9190036001600160a01b031691614870565b9061457c565b6145c361026d946145ca92949394620f42400390614ab5565b83836149bb565b90614b17565b6145e961026d946145f092949394620f42400390614ab5565b8383614952565b614bdb565b939493919291156146265790614620839261461461026d958489614b7b565b90620f42400390614ad8565b94614bdb565b9361463b8295614614858461026d9796614c07565b94614b17565b939497959296909196845f14614767578881111561475e576146678982035b8984614563565b90614675858801848661451e565b985b898310998a1561474e5761468b81856137e7565b9a5b156146f357505050906146a36146a9939261454f565b91614c07565b91156146e457620f4240929190828211156146d35750035b925b670de0b6b3a76400000310929190565b90506146df9150613841565b6146c1565b939450620f42409103936146c3565b90925061470b9196975061471194509994989961454f565b91614b7b565b921561472457500392620f4240906146c3565b9493620f424092908282111561473d5750035b926146c3565b90506147499150613841565b614737565b61475884826137e7565b9a61468d565b6146675f614660565b6147748482018984614563565b90898711156147915761478b8a88035b848661451e565b98614677565b61478b5f614784565b90606082901b905f19600160601b84099282808510940393808503946147c1868511613648565b14614820578190600160601b900981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b81810291905f1982820991838084109303928084039384600160601b111561017f571461486757600160601b910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f19818509938380861095039480860395614891878611613648565b146148eb579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b9190811561494d576001600160a01b03909216918183029160609190911b600160601b600160e01b0316908204831482821116156149405761026d9261493b92820391614c43565b614c6c565b63f5c787f15f526004601cfd5b505090565b9190811561494d5760601b600160601b600160e01b0316916001600160a01b031681810282810482146149a7575b508015613cb1578204908101809111611b5d5780820615159104016001600160a01b031690565b8301838110614980579150610da392614c43565b916001600160a01b0381116149fe576001600160801b039060601b9116908115613cb15704905b6001600160a01b0316908101908110611b5d5761026d90614c6c565b6001600160801b03614a1192169061479a565b906149e2565b91906001600160a01b038211614a78576001600160801b03614a3c614a499360601b90565b9116808206151591040190565b905b6001600160a01b03169080821115614a6b5790036001600160a01b031690565b634323a5555f526004601cfd5b6001600160801b0316614a9081600160601b84614870565b918115613cb157600160601b9009614aa9575b90614a4b565b60010180614aa3575f80fd5b815f19048111820215620f4240021561017f5702620f4240808204910615150190565b7d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98111620f4240021582021561017f57620f42400290808204910615150190565b6001600160a01b0382811690821611614b75575b6001600160a01b038116928315614b6957614b65926001600160a01b0380821693909103169060601b600160601b600160e01b0316614870565b0490565b62bfc9215f526004601cfd5b90614b2b565b6001600160a01b0382811690821611614bd5575b6001600160a01b038116928315614b6957614bc9926001600160a01b0380821693909103169060601b600160601b600160e01b0316614c43565b90808206151591040190565b90614b8f565b61026d926001600160a01b03928316919092160360ff81901d90810118906001600160801b0316614827565b6001600160a01b0391821691160360ff81901d90810118906001906001600160801b0316614c358382614827565b928260601b91091515160190565b929190614c51828286614870565b938215613cb15709614c5f57565b9060010190811561017f57565b6001600160a01b03811691908203614c8057565b6393dafdf160e01b5f5260045ffdfea2646970667358221220f14670242a92f44dbb020f4d9cf32142f187ce6704228d21852e54269864ac3864736f6c634300081e0033
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
$24.22
Net Worth in ETH
0.008387
Token Allocations
WETH
38.96%
USDC
23.44%
CBBTC
22.60%
Others
15.01%
Multichain Portfolio | 35 Chains
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.