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
Contract Name:
CompounderUniswapV3
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 { UniswapV3 } from "../base/UniswapV3.sol";
/**
* @title Compounder for Uniswap V3 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 CompounderUniswapV3 is Compounder, UniswapV3 {
/* //////////////////////////////////////////////////////////////
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 Uniswap v3 Position Manager.
* @param uniswapV3Factory The contract address of the Uniswap v3 Factory.
*/
constructor(
address owner_,
address arcadiaFactory,
address routerTrampoline,
address positionManager,
address uniswapV3Factory
) Compounder(owner_, arcadiaFactory, routerTrampoline) UniswapV3(positionManager, uniswapV3Factory) { }
}/**
* 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 { IPositionManagerV3 } from "../interfaces/IPositionManagerV3.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 { IUniswapV3Pool } from "../interfaces/IUniswapV3Pool.sol";
import { PoolAddress } from "../../../lib/accounts-v2/src/asset-modules/UniswapV3/libraries/PoolAddress.sol";
import { PositionState } from "../state/PositionState.sol";
import { SafeApprove } from "../../libraries/SafeApprove.sol";
/**
* @title Base implementation for managing Uniswap V3 Liquidity Positions.
*/
abstract contract UniswapV3 is AbstractBase {
using FixedPointMathLib for uint256;
using SafeApprove for ERC20;
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// The contract address of the Uniswap v3 Position Manager.
IPositionManagerV3 internal immutable POSITION_MANAGER;
// The contract address of the Uniswap v3 Factory.
address internal immutable UNISWAP_V3_FACTORY;
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error OnlyPool();
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param positionManager The contract address of the Uniswap v3 Position Manager.
* @param uniswapV3Factory The contract address of the Uniswap v3 Factory.
*/
constructor(address positionManager, address uniswapV3Factory) {
POSITION_MANAGER = IPositionManagerV3(positionManager);
UNISWAP_V3_FACTORY = uniswapV3Factory;
}
/* ///////////////////////////////////////////////////////////////
POSITION VALIDATION
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns if a position manager matches the position manager(s) of Uniswap v3.
* @param positionManager the contract address of the position manager to check.
*/
function isPositionManager(address positionManager) public view virtual override returns (bool) {
return 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, uint256 id)
internal
view
virtual
override
returns (PositionState memory position)
{
// Positions have two underlying tokens.
position.tokens = new address[](2);
// Get data of the Liquidity Position.
position.id = id;
(
,,
position.tokens[0],
position.tokens[1],
position.fee,
position.tickLower,
position.tickUpper,
position.liquidity,,,,
) = POSITION_MANAGER.positions(id);
// Get data of the Liquidity Pool.
position.pool =
PoolAddress.computeAddress(UNISWAP_V3_FACTORY, position.tokens[0], position.tokens[1], position.fee);
(position.sqrtPrice, position.tickCurrent,,,,,) = IUniswapV3Pool(position.pool).slot0();
position.tickSpacing = IUniswapV3Pool(position.pool).tickSpacing();
}
/**
* @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 = IUniswapV3Pool(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,,,,,,) = IUniswapV3Pool(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,
PositionState memory position,
uint256 claimFee
) internal virtual override {
// We assume that the amount of tokens to collect never exceeds type(uint128).max.
(uint256 amount0, uint256 amount1) = POSITION_MANAGER.collect(
IPositionManagerV3.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, PositionState memory) internal virtual override { }
/**
* @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, address, PositionState memory) internal virtual override { }
/* ///////////////////////////////////////////////////////////////
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(
IPositionManagerV3.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(
IPositionManagerV3.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) = IUniswapV3Pool(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.fee)
);
// 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 IUniswapV3Pool.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 IUniswapV3Pool.swap() call.
*/
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external virtual {
// Check that callback came from an actual Uniswap V3 Pool.
(address token0, address token1, uint24 fee) = abi.decode(data, (address, address, uint24));
if (PoolAddress.computeAddress(UNISWAP_V3_FACTORY, token0, token1, fee) != 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(
IPositionManagerV3.MintParams({
token0: position.tokens[0],
token1: position.tokens[1],
fee: position.fee,
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
})
);
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(
IPositionManagerV3.IncreaseLiquidityParams({
tokenId: position.id,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
})
);
balances[0] -= amount0;
balances[1] -= amount1;
}
}/**
* 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 IPositionManagerV3 {
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;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
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,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
function mint(MintParams calldata params)
external
payable
returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
}/**
* 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 IUniswapV3Pool {
function liquidity() external view returns (uint128 liquidity_);
function slot0()
external
view
returns (
uint160 sqrtPrice,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
function tickSpacing() external view returns (int24 tickSpacing_);
}// https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Uniswap V3 factory contract address
/// @param token0 Contract address of token0.
/// @param token1 Contract address of token1.
/// @param fee The fee of the pool.
/// @return pool The contract address of the V3 pool
function computeAddress(address factory, address token0, address token1, uint24 fee)
internal
pure
returns (address pool)
{
require(token0 < token1);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff", factory, keccak256(abi.encode(token0, token1, fee)), POOL_INIT_CODE_HASH
)
)
)
)
);
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: 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":"uniswapV3Factory","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"}]Contract Creation Code
6101003461015957601f6147fe38819003918201601f19168301916001600160401b0383118484101761015d5780849260a0946040528339810103126101595761004881610171565b61005460208301610171565b9161006160408201610171565b610079608061007260608501610171565b9301610171565b5f80546001600160a01b0319166001600160a01b039095169485178155604051959194907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36001600160a01b0390811660805290811660a0521660c05260e05261467890816101868239608051818181611ab8015281816122af0152612469015260a05181818161043e01526134b6015260c05181818161081101528181610b5801528181610fc901528181611011015281816110cf01528181611c5601528181612141015261272d015260e05181818161018c01526108fc0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101595756fe610140806040526004361015610013575f80fd5b5f610120525f3560e01c9081630686ddd914612761575080630a73e3911461270d5780631204f525146126ba578063150b7a02146126645780632fcb4f04146125f15780633d692da1146123e5578063452a9320146123bd5780635c975abb146123995780635f4860df146122435780637d5ad9cd14611b7a5780638456cb5914611ae75780638cffa27714611aa05780638da5cb5b14611a735780638da92e71146119ed578063a129568d14610574578063a7310b58146104ec578063a89d6dd41461046d578063b699b82b14610426578063bc25cf77146102a4578063f2fde38b146102235763fa461e3314610109575f80fd5b3461021c57606036600319011261021c576024356004356044356001600160401b03811161021c576101416060913690600401612932565b908092918101031261021c576101568161286b565b9260406101656020840161286b565b9201359362ffffff8516850361021c576001600160a01b03928316949216916101b09085847f0000000000000000000000000000000000000000000000000000000000000000613691565b336001600160a01b039091160361020757610120518313156101e057506101d992503390613601565b6101205180f35b9150506101205181136101f5575b50506101d9565b610200913390613601565b5f806101ee565b634b60273560e01b6101205152600461012051fd5b6101205180fd5b3461021c57602036600319011261021c5761023c61283f565b610120515490610256336001600160a01b03841614612a2b565b60018060a01b031680916001600160601b0360a01b16176101205155337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06101205161012051a36101205180f35b3461021c57602036600319011261021c576102bd61283f565b610120515460ff906102d9336001600160a01b03831614612a2b565b60a01c16610411576002546001600160a01b03166103fc576001600160a01b03168061037557506101205180808047335af13d1561036d573d9061031c82612a87565b9161032a60405193846128ed565b8252610120513d90602084013e5b1561034557506101205180f35b60405162461bcd60e51b81526020600482015290819061036990602483019061290e565b0390fd5b606090610338565b6040516370a0823160e01b815230600482015290602082602481845afa9081156103ee5761012051916103b4575b6103af92503390613601565b6101d9565b90506020823d6020116103e6575b816103cf602093836128ed565b810103126103e2576103af9151906103a3565b5f80fd5b3d91506103c2565b6040513d61012051823e3d90fd5b63b5dfd9e560e01b6101205152600461012051fd5b6313d0ff5960e31b6101205152600461012051fd5b3461021c576101205136600319011261021c576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461021c5760a036600319011261021c57608036602319011261021c57604051610496816128b7565b602435815260443560208201526064356001600160a01b038116810361021c5760408201526084356001600160a01b038116810361021c57816104e29160606020940152600435612c4b565b6040519015158152f35b3461021c57602036600319011261021c576001600160a01b0361050d61283f565b166101205152600360205260a0604061012051206001600160401b036001825492015416604051916001600160401b03811683526001600160401b038160401c1660208401526001600160401b038160801c16604084015260c01c60608301526080820152f35b3461021c57602036600319011261021c576004356001600160401b03811161021c576105a4903690600401612932565b906105ad612b6b565b506002546001600160a01b031633036119d857336101205152600360205260406101205120916001600160401b036001604051946105ea866128d2565b80548381168752838160401c166020880152838160801c16604088015260c01c60608701520154166080840152604082828101031261021c5761062c8261286b565b6101005260208201356001600160401b03811161021c57610100818401838501031261021c576040519261010084018481106001600160401b038211176114e15760405261067b82820161286b565b845261068b602083830101612b2f565b602085015261069e604083830101612b43565b60408501526106b1606083830101612b43565b60608501526080828201013560808501526106d060a083830101612b57565b60a08501526106e360c083830101612b57565b60c08501526001600160401b0360e083830101351161021c5761070f928101910160e081013501612ad8565b60e082015260018060a01b03815116916001600160401b0360a0830151166001600160401b038251161080156119b5575b6119a0576001600160601b036020830151169160405161014081018181106001600160401b038211176114e1576040526101205181526101205160208201526101205160408201526101205160608201526101205160808201526101205160a08201526101205160c08201526101205160e08201526101205161010082015260606101208201526060936040516107d786826128ed565b60028152601f1986013660208301376101208301526020820181905260405163133f757160e31b81526004810191909152610180816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156103ee57610120519081908190819081908190611953575b6108a89596509062ffffff92916001600160801b036101208a0151961660e08a015260020b60a089015260020b60c088015216604086015261089682612bf9565b6001600160a01b039091169052612bd8565b6001600160a01b03918216905261012082015160049160e0916108ca90612bd8565b511661092060018060a01b036108e4610120870151612bf9565b51169160018060a01b039262ffffff604088015116917f0000000000000000000000000000000000000000000000000000000000000000613691565b1680845260405192838092633850c7bd851b82525afa80156103ee576101205191829161192d575b5060020b60808301526001600160a01b0390811661010083015281516040516334324e9f60e21b81529160209183916004918391165afa9081156103ee5761012051916118f3575b5060020b60608201526109a861012082015151612ba6565b916001600160801b036040820151166109c084612bd8565b526001600160801b036060820151166109d884612bf9565b526109e38351612ba6565b936080820151906040516109f6816128b7565b61012051815261012051602082015261012051604082015287610120519101526001600160401b0360608201511691825f19048111830215670de0b6b3a7640000021561021c576001600160401b03604083015116805f19048211810215670de0b6b3a7640000021561021c57670de0b6b3a7640000610a7c60c088015160020b61382d565b91610a8d60a089015160020b61382d565b938260405197610a9c896128b7565b8202048752020460208501526001600160a01b0390811660408501521687830152610100840151610ace908390612c4b565b15611521576001600160401b0360a08401511660208501519160405192610af4846128b7565b83526001600160801b036020840130815281604086018181528c8701928284526040519763fc6f786560e01b895251600489015260018060a01b03905116602488015251166044860152511660648401526040836084816101205160018060a01b037f0000000000000000000000000000000000000000000000000000000000000000165af180156103ee57610120519384916118be575b50610ba084610b9a8a612bd8565b51612c3e565b610ba989612bd8565b52610bb781610b9a8a612bf9565b610bc089612bf9565b52825f19048411830215670de0b6b3a7640000021561021c57610bf2670de0b6b3a764000084860204610b9a8b612bd8565b610bfb8a612bd8565b52825f19048111830215670de0b6b3a7640000021561021c576001600160401b0393610c38670de0b6b3a7640000608095840204610b9a8c612bf9565b610c418b612bf9565b526101208801516001600160a01b0390610c5a90612bd8565b5116906040519081527ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd760203392a36101208701516001600160a01b0390610ca190612bf9565b5116906040519081527ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd760203392a3015160408581015160c08601516001600160401b031660e052610100870151918501516001600160a01b0390811660a0528a86015162ffffff9092169594929391169116610d31610d2089612bd8565b51610d2a8b612bd8565b5190612c1d565b91610d48610d3e8a612bf9565b51610d2a8c612bf9565b60c052604051610d57816128d2565b61012080518252805160208301528051604083015280518d8301528051608090920191909152518185106116255750600192809661012051506001600160801b03861161021c57815f190464e8d4a51000820260e05101670de0b6b3a76400000311820215670de0b6b3a7640000021561021c57610dfb90610dd76140cb565b90670de0b6b3a76400008464e8d4a5100060028b0a930260e05101820302046142b3565b945b6001600160a01b031690878686156116145750610e1991612c1d565b905b841561160357610e2d8660c051612c3e565b60a0519391828086116115f5575b506001600160a01b0385168111611596575050610e5792614199565b6001600160801b0381169081036103e257925b815f19046001600160801b03851611820215670de0b6b3a7640000021561021c5760e0515f1904861160e0510215670de0b6b3a7640000021561021c57670de0b6b3a764000096600496610f389587958b948f6040519c8d98610ecc8a6128d2565b8415158a526001600160801b0360208b01968360e0518a0204885260408c0160805216020460805152670de0b6b3a764000060e051860204850390880152608087015215155f1461156b57610f26915051610b9a8d612bd8565b610f2f8c612bd8565b525b8a8a6130fc565b8351604051633850c7bd60e01b81529260e09184919082906001600160a01b03165afa80156103ee57610f86926101205191611536575b506001600160a01b03166101008501819052612c4b565b156115215751156114fb57610f9a82612bd8565b51610fb1610fa784612bf9565b51610d2a86612bf9565b905b610120830151610ffb9082906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691610ff490612bd8565b5116613f87565b61012083015161103c9083906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691610ff490612bf9565b6020830151916040519260c084018481106001600160401b038211176114e15760405283526020830191825260408301908152868301610120518152608084019161012051835260a08501934285526040519563219f5d1760e01b87525160048701525160248601525160448501525160648401525160848301525160a4820152848160c4816101205160018060a01b037f0000000000000000000000000000000000000000000000000000000000000000165af180156103ee576101205161012051926101205192611494575b50611143926001600160801b03611130921660e086015261112a86612bd8565b51612c1d565b61113985612bd8565b5261112a84612bf9565b61114c83612bf9565b526001600160801b0360e082015116608051511161147f576020810151853b1561021c5760405163095ea7b360e01b815233600482015260248101919091526101205181604481838a5af180156103ee57611464575b5061012051600195905b83518110156112e0576101208301516001600160a01b03906111cf908390612c09565b5116966111dc8286612c09565b516111e78388612c09565b5110156112b1579061123260019261120d6112028489612c09565b51610d2a858b612c09565b6112178489612c09565b5261122d6112258489612c09565b51338c613f87565b61305b565b975b61123e8288612c09565b5161128d575b61124e8288612c09565b51604051908152838060a01b036101005116907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e05663160203392a4016111ac565b6112ac61129a8389612c09565b51848060a01b03610100511683613601565b611244565b96906001916112c08287612c09565b516112cb8389612c09565b52610120516112da8388612c09565b52611234565b5092509092602082015190610120830151956112fa612b6b565b5061130481612ba6565b9561130e82612ba6565b97600161131a84612ba6565b9361132481612ba6565b968961132f8c612bd8565b526113398c612bd8565b528161134486612bd8565b52600261135088612bd8565b52116113c4575b50509060209392916113c09760405197611370896128b7565b885285880152604087015285015201516040519081527f6c7e7d4cb83a668aef31739dd35dc3fc3d5f31d62b69e438b7b24d35b40dcc6360203392a36040519182916020835260208301906129a1565b0390f35b9296939190956001956101205150610120515b885181101561144e576113ea818a612c09565b516113f8575b6001016113d7565b96600190611446906001600160a01b036114128b8a612c09565b511661141e828b612c09565b526114298a8c612c09565b516114348288612c09565b52826114408289612c09565b5261305b565b9790506113f0565b50949790965093945090929150806113c0611357565b61012051611471916128ed565b6101205161021c57856111a2565b63bb55fd2760e01b6101205152600461012051fd5b925050508481813d83116114da575b6114ad81836128ed565b8101031261021c57806111306114c561114393612f9f565b6020830151604090930151929390915061110a565b503d6114a3565b634e487b7160e01b61012051526041600452602461012051fd5b61151161150783612bd8565b51610d2a85612bd8565b61151a83612bf9565b5190610fb3565b633a8bf65960e01b6101205152600461012051fd5b611558915060e03d60e011611564575b61155081836128ed565b810190613078565b50505050505089610f6f565b503d611546565b50670de0b6b3a76400006115879160e0510204610b9a8d612bf9565b6115908c612bf9565b52610f31565b909290916001600160a01b0381168310156115e95782916115bb916115c19594614199565b93614168565b808210156115e257505b6001600160801b0381169081036103e25792610e6a565b90506115cb565b9150506115cb92614168565b60a05190955092505f610e3b565b61160f8860c051612c1d565b610e2d565b905061161f91612c3e565b90610e1b565b60a05190939085116116525761164c64e8d4a5100060c051980260e0510160c05187614114565b94610dfd565b939592506001600160801b03861161021c576002860a936116738286614003565b60a0518860011b0303937812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f2160a051890311670de0b6b3a7640000021585021561021c576116d56116cd6116be8a61400d565b6116c66140cb565b90856142b3565b60c051612c3e565b947812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f2160c05111670de0b6b3a7640000021586021561021c5780670de0b6b3a764000060a0518b03020486670de0b6b3a764000060c0510204105f1461180f5761179c6117ab916001978b61177364e8d4a51000870260e05101670de0b6b3a76400000364e8d4a51000880260e0510185670de0b6b3a764000060a05186030204613818565b670de0b6b3a76400000192670de0b6b3a7640000838160c05102049260a0519003020403613818565b966117a56140cb565b886142b3565b907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218211670de0b6b3a7640000021564e8d4a51000820260e05101670de0b6b3a764000003021561021c5764e8d4a51000670de0b6b3a7640000910260e05101810391020496610dfd565b9490979550610120519464e8d4a51000890260e051015f190481670de0b6b3a764000060a0518a0302041164e8d4a510008a0260e051010215670de0b6b3a7640000021561021c576118b164e8d4a510009161164c93670de0b6b3a7640000848d0260e0510183828d60a051900302040204670de0b6b3a76400000391670de0b6b3a764000060a0518c03020481670de0b6b3a764000060c051020403613818565b980260e051018887614114565b90506118e391935060403d6040116118ec575b6118db81836128ed565b8101906130e6565b9290928b610b8c565b503d6118d1565b90506020813d602011611925575b8161190e602093836128ed565b8101031261021c5761191f90612f91565b86610990565b3d9150611901565b9050611948915060e03d60e0116115645761155081836128ed565b505050505087610948565b5050505050506108a861198162ffffff926101803d8111611999575b61197981836128ed565b810190612fb3565b50505050965091945091969296939250869550610855565b503d61196f565b632a9ffab760e21b6101205152600461012051fd5b506001600160401b0360c0830151166001600160401b0360208301511610610740565b63f3f6425d60e01b6101205152600461012051fd5b3461021c57602036600319011261021c576004358015159081900361021c5761012051547f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a91602091611a4a336001600160a01b03831614612a2b565b6101205160ff60a01b1990911660a083901b60ff60a01b16179055604051908152a16101205180f35b3461021c576101205136600319011261021c5761012051546040516001600160a01b039091168152602090f35b3461021c576101205136600319011261021c576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461021c576101205136600319011261021c576001546001600160a01b03163303611b6557610120515460ff8160a01c16610411576101205160ff60a01b19909116600160a01b179055604051600181527f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a90602090a16101205180f35b636570ecab60e11b6101205152600461012051fd5b346103e25760403660031901126103e257611b9361283f565b602435906001600160401b0382116103e257816004018236036101006003198201126103e25760ff5f5460a01c1661223457600254926001600160a01b038416612225576001600160a01b03166001600160a01b0319939093168317600255604051638da5cb5b60e01b81526020816004815f885af190811561202e575f916121eb575b506001600160a01b039081165f9081526005602090815260408083208784529091529020541633036121dc57611c4c82612af3565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169116036121cd575f5f60448601956001600160801b03611c9688612b07565b16158015906121b0575b612103575b611cae85612af3565b916024820191611cbd83612b1b565b95611cc78a612b07565b996064830190611cd682612b07565b60408051336020820152808201919091529a9096906001600160a01b0390611cfd9061286b565b1660608c0152611d0c90612b2f565b6001600160601b031660808b0152611d2390612b43565b6001600160801b031660a08a0152611d3a90612b43565b6001600160801b031660c0890152608482013560e0890152611d5e60a48301612b57565b6001600160401b0316610100890152611d7960c48301612b57565b6001600160401b031661012089015260e482013590602219018112156103e25701602460048201359101906001600160401b0381116103e25780360382136103e2576001600160601b0399611e0b6101808a846001600160801b039695879661010061014085015281610160850152848401375f838284010152601f801991011681010301601f1981018b528a6128ed565b1692169160019481151591826120fa575b84151591826120ea575b611e2f88612ba6565b9b8c99611e3b8a612ba6565b98611e57611e51611e4b8d612ba6565b9c612ba6565b9c612bd8565b6001600160a01b03909116905216611e6e88612bd8565b526001611e7a89612bd8565b526002611e868a612bd8565b526001936120b0575b505061207b575b50505060405195611ea6876128b7565b8652602086015260408501526060840152611ebf612b6b565b60405190606082018281106001600160401b038211176120675790611f119592916040526060825260208201905f8252611f2360408401915f835260405198899660a0602089015260c08801906129a1565b868103601f19016040880152906129a1565b91601f19858403016060860152606083019351936060845284518091526020608085019501905f5b81811061203957505050916040611fae9492611fbc979451602084015251910152601f19848203016080850152606051808252806080602084015e5f828201602090810191909152601f909101601f191690910184810360a0860152019061290e565b03601f1981018452836128ed565b803b156103e2576040805162b9252f60e41b81523060048201526024810191909152905f908290818381611ff3604482018961290e565b03925af1801561202e57612019575b600280546001600160a01b03191690556101205180f35b5f612023916128ed565b5f6101205280612002565b6040513d5f823e3d90fd5b825180516001600160a01b03168852602090810151818901528b985060409097019690920191600101611f4b565b634e487b7160e01b5f52604160045260245ffd5b6001926120a79261208c838c612c09565b90858060a01b031690526120a08287612c09565b5285612c09565b52868080611e96565b909192506120bd8b612bf9565b6001600160a01b0390911690526120d386612bf9565b5260016120df87612bf9565b526002908a80611e8f565b966120f49061305b565b96611e26565b60029650611e1c565b91505061210f83612af3565b506001600160601b0361212460248301612b1b565b60405163133f757160e31b815291166004820152610180816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa801561202e575f915f91612182575b509091611ca5565b905061219d91506101803d81116119995761197981836128ed565b505050505050505092509050908761217a565b506001600160801b036121c560648301612b07565b161515611ca0565b63ed5f09f160e01b5f5260045ffd5b6317fb43e560e31b5f5260045ffd5b90506020813d60201161221d575b81612206602093836128ed565b810103126103e25761221790612a73565b85611c17565b3d91506121f9565b63b5dfd9e560e01b5f5260045ffd5b6313d0ff5960e31b5f5260045ffd5b346103e25760603660031901126103e25761225c61283f565b61226461295f565b506044356001600160401b0381116103e257612284903690600401612932565b6002546001600160a01b031661222557604051630972932760e21b81523360048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561202e575f9161235f575b50156123505781019160c0828403126103e2576123038261286b565b9060a08301356001600160401b0381116103e25761234e94612326918501612ad8565b9260808101359260608201359260408301359260200135916001600160a01b03169033612c64565b005b630ea8370b60e41b5f5260045ffd5b90506020813d602011612391575b8161237a602093836128ed565b810103126103e25761238b90612a66565b846122e7565b3d915061236d565b346103e2575f3660031901126103e257602060ff5f5460a01c166040519015158152f35b346103e2575f3660031901126103e2576001546040516001600160a01b039091168152602090f35b346103e25760e03660031901126103e2576123fe61283f565b612406612855565b9060c4356001600160401b0381116103e257612426903690600401612932565b60025491939092916001600160a01b031661222557604051630972932760e21b81526001600160a01b0383811660048301819052949190602090829060249082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561202e575f916125b7575b501561235057604051638da5cb5b60e01b8152936020856004815f855af194851561202e575f9561257b575b506001600160a01b038516330361256c5760205f91600460405180948193635e34633b60e11b83525af1801561202e575f90612539575b600391501061252a5761234e94612514913691612aa2565b9260a43592608435926064359260443592612c64565b63a93eca7960e01b5f5260045ffd5b506020813d602011612564575b81612553602093836128ed565b810103126103e257600390516124fc565b3d9150612546565b6312272fd360e11b5f5260045ffd5b9094506020813d6020116125af575b81612597602093836128ed565b810103126103e2576125a890612a73565b93866124c5565b3d915061258a565b90506020813d6020116125e9575b816125d2602093836128ed565b810103126103e2576125e390612a66565b86612499565b3d91506125c5565b346103e25760203660031901126103e25761260a61283f565b61261e60018060a01b035f54163314612a2b565b600180546001600160a01b0319166001600160a01b03929092169182179055337fa14fc14d8620a708a896fd11392a235647d99385500a295f0d7da2a258b2e9675f80a3005b346103e25760803660031901126103e25761267d61283f565b50612686612855565b506064356001600160401b0381116103e2576126a6903690600401612932565b5050604051630a85bd0160e11b8152602090f35b346103e25760403660031901126103e2576126d361283f565b6126db612855565b6001600160a01b039182165f908152600560209081526040808320938516835292815290829020549151919092168152f35b346103e25760203660031901126103e257602061272861283f565b6040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b039081169216919091148152f35b346103e25760203660031901126103e2576001600160a01b0361278261283f565b165f52600460205260405f205f9080549061279c8261287f565b808552916001811690811561281857506001146127d8575b6113c0846127c4818603826128ed565b60405191829160208352602083019061290e565b5f90815260208120939250905b8082106127fe575090915081016020016127c4826127b4565b9192600181602092548385880101520191019092916127e5565b60ff191660208087019190915292151560051b850190920192506127c491508390506127b4565b600435906001600160a01b03821682036103e257565b602435906001600160a01b03821682036103e257565b35906001600160a01b03821682036103e257565b90600182811c921680156128ad575b602083101461289957565b634e487b7160e01b5f52602260045260245ffd5b91607f169161288e565b608081019081106001600160401b0382111761206757604052565b60a081019081106001600160401b0382111761206757604052565b90601f801991011681019081106001600160401b0382111761206757604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f840112156103e2578235916001600160401b0383116103e257602083818601950101116103e257565b6024359081151582036103e257565b90602080835192838152019201905f5b81811061298b5750505090565b825184526020938401939092019160010161297e565b80516080808452815190840181905260a08401949391602001905f5b818110612a0c5750505060606129f86129e6612a0995966020860151858203602087015261296e565b6040850151848203604086015261296e565b92015190606081840391015261296e565b90565b82516001600160a01b03168752602096870196909201916001016129bd565b15612a3257565b60405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606490fd5b519081151582036103e257565b51906001600160a01b03821682036103e257565b6001600160401b03811161206757601f01601f191660200190565b929192612aae82612a87565b91612abc60405193846128ed565b8294818452818301116103e2578281602093845f960137010152565b9080601f830112156103e257816020612a0993359101612aa2565b356001600160a01b03811681036103e25790565b356001600160801b03811681036103e25790565b356001600160601b03811681036103e25790565b35906001600160601b03821682036103e257565b35906001600160801b03821682036103e257565b35906001600160401b03821682036103e257565b60405190612b78826128b7565b606080838181528160208201528160408201520152565b6001600160401b0381116120675760051b60200190565b90612bb082612b8f565b612bbd60405191826128ed565b8281528092612bce601f1991612b8f565b0190602036910137565b805115612be55760200190565b634e487b7160e01b5f52603260045260245ffd5b805160011015612be55760400190565b8051821015612be55760209160051b010190565b91908203918211612c2a57565b634e487b7160e01b5f52601160045260245ffd5b91908201809211612c2a57565b815181119182612c5a57505090565b6020015111919050565b9196949095929395670de0b6b3a764000085118015612f80575b8015612f6f575b8015612f5e575b612f4f576001600160a01b039081165f9081526005602090815260408083208685168452909152902080546001600160a01b031916989091169788179055670de0b6b3a7640000808201908110612c2a57670de0b6b3a7640000810290808204670de0b6b3a76400001490151715612c2a57612d0f6001600160401b0391613756565b169281670de0b6b3a76400000391670de0b6b3a76400008311612c2a57670de0b6b3a76400008302928304670de0b6b3a76400001490670de0b6b3a7640000141715612c2a576001600160401b039360019385612d6c8195613756565b16908460405198612d7c8a6128d2565b16885284602089019a168a526040880192835260608801918252846080890194168452858060a01b031698895f526003602052848060405f209951161685198954161788555191846fffffffffffffffff00000000000000008954928260801b905160801b16938260c01b905160c01b169460401b16911617171785555116920191166001600160401b0319825416179055815f52600460205260405f20908051906001600160401b038211612067578190612e38845461287f565b601f8111612eff575b50602090601f8311600114612e9c575f92612e91575b50508160011b915f199060031b1c19161790555b7febc70f7c8d6a67b19e15e968cb908d21719e8ff9a778a71171fba931a618d0525f80a3565b015190505f80612e57565b5f8581528281209350601f198516905b818110612ee75750908460019594939210612ecf575b505050811b019055612e6b565b01515f1960f88460031b161c191690555f8080612ec2565b92936020600181928786015181550195019301612eac565b909150835f5260205f20601f840160051c81019160208510612f45575b90601f859493920160051c01905b818110612f375750612e41565b5f8155849350600101612f2a565b9091508190612f1c565b632a9ffab760e21b5f5260045ffd5b50670de0b6b3a76400008411612c8c565b50670de0b6b3a76400008211612c85565b50670de0b6b3a76400008711612c7e565b51908160020b82036103e257565b51906001600160801b03821682036103e257565b9190826101809103126103e25781516001600160601b03811681036103e25791612fdf60208201612a73565b91612fec60408301612a73565b91612ff960608201612a73565b91608082015162ffffff811681036103e2579161301860a08201612f91565b9161302560c08301612f91565b9161303260e08201612f9f565b916101008201519161012081015191612a096101606130546101408501612f9f565b9301612f9f565b5f198114612c2a5760010190565b519061ffff821682036103e257565b908160e09103126103e25780516001600160a01b03811681036103e257916130a260208301612f91565b916130af60408201613069565b916130bc60608301613069565b916130c960808201613069565b9160a082015160ff811681036103e25760c0612a09919301612a66565b91908260409103126103e2576020825192015190565b94909391929360608301938451156135f85760e001518051806133d757505082511515906040860194600462ffffff87511692602060018060a01b038a511660405193848092630d34328160e11b82525afa801561202e578a925f91613396575b506101008a015160408801516060909801516131b9986001600160a01b03918216949082169391909216916131ab90610d2a6131a461319e6115078a612bd8565b98612bf9565b5191612bf9565b95519660808b015198613b5a565b91821561338f576132ad92604092511515945f6131df60018060a01b0383511693613f77565b87821461336e5761326b6101206401000276a4945b0161325d62ffffff60018060a01b0361320d8451612bd8565b5193519316926001600160a01b039061322590612bf9565b5199518b516001600160a01b03958616602082015291909a16909316604084015290971662ffffff1660608201529586906080820190565b03601f1981018752866128ed565b855196879586948593630251596160e31b85523060048601528b6024860152604485015260018060a01b0316606484015260a0608484015260a483019061290e565b03925af190811561202e575f905f92613331575b5061330c9290811561331c576132da9061112a86612bd8565b6132e385612bd8565b521561330f57613306906133006132f985612bf9565b5191613f77565b90612c3e565b91612bf9565b52565b6133069061112a84612bf9565b61332c906133006132f987612bd8565b6132da565b9150506040813d604011613366575b8161334d604093836128ed565b810103126103e25780516020909101519061330c6132c1565b3d9150613340565b61326b61012073fffd8963efd1fc6a506488495d951d5263988d25946131f4565b5050505050565b9250506020823d6020116133cf575b816133b2602093836128ed565b810103126103e2576131b9956133c88b93612f9f565b909661315d565b3d91506133a5565b959392509593505051151592840190606085602084019303126103e25760208501516001600160a01b038116908190036103e2576040860151956060810151906001600160401b0382116103e257019083603f830112156103e25760208201519161344183612a87565b9461344f60405196876128ed565b838652604082850101116103e2576040935f60208561350e96886101209601838b015e8801015287156135c45701935f60018060a01b036134908751612bd8565b5196519616956001600160a01b03906134a890612bf9565b5116955b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169491166134e58b8683613601565b8651978896879586946344bc658560e01b8652600486015260a0602486015260a485019061290e565b60448401929092526001600160a01b03166064830152608482018b905203925af192831561202e575f915f946135a1575b501561357b5761356261356c9161355d61330c959661112a86612bd8565b612c3e565b93610b9a83612bf9565b61357582612bf9565b52612bd8565b61359c9061355d61359261330c95610b9a86612bd8565b9561112a85612bf9565b61356c565b9093506135bd915060403d6040116118ec576118db81836128ed565b925f61353f565b01935f60018060a01b036135d88751612bf9565b5196519616956001600160a01b03906135f090612bd8565b5116956134ac565b50505050505050565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516613684575b501561364d57565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f613645565b9290916001600160a01b0380821690841610156103e257604080516001600160a01b0394851660208201908152929094169084015262ffffff919091166060808401919091528252906136e56080826128ed565b5190209060405191602083019160ff60f81b83526001600160601b03199060601b16602184015260358301527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b546055830152605582526137466075836128ed565b905190206001600160a01b031690565b60b581600160881b811015613801575b806901000000000000000000620100009210156137f4575b650100000000008110156137e7575b63010000008110156137da575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8080920410900390565b60101c9160081b9161379a565b60201c9160101b9161378d565b60401c9160201b9161377e565b5068b500000000000000009050608082901c613766565b815f190481118202158302156103e257020490565b60020b908160ff1d82810118620d89e88111613b475763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102600160801b189160028116613b2b575b60048116613b0f575b60088116613af3575b60108116613ad7575b60208116613abb575b60408116613a9f575b60808116613a83575b6101008116613a67575b6102008116613a4b575b6104008116613a2f575b6108008116613a13575b61100081166139f7575b61200081166139db575b61400081166139bf575b61800081166139a3575b620100008116613987575b62020000811661396c575b620400008116613951575b6208000016613938575b5f12613930575b0160201c90565b5f1904613929565b6b048a170391f7dc42444e8fa290910260801c90613922565b6d2216e584f5fa1ea926041bedfe9890920260801c91613918565b916e5d6af8dedb81196699c329225ee6040260801c9161390d565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c91613902565b916f31be135f97d08fd981231505542fcfa60260801c916138f7565b916f70d869a156d2a1b890bb3df62baf32f70260801c916138ed565b916fa9f746462d870fdf8a65dc1f90e061e50260801c916138e3565b916fd097f3bdfd2022b8845ad8f792aa58250260801c916138d9565b916fe7159475a2c29b7443b29c7fa6e889d90260801c916138cf565b916ff3392b0822b70005940c7a398e4b70f30260801c916138c5565b916ff987a7253ac413176f2b074cf7815e540260801c916138bb565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c916138b1565b916ffe5dee046a99a2a811c461f1969c30530260801c916138a7565b916fff2ea16466c96a3843ec78b326b528610260801c9161389e565b916fff973b41fa98c081472e6896dfb254c00260801c91613895565b916fffcb9843d60f6159c9db58835c9266440260801c9161388c565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c91613883565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c9161387a565b916ffff97272373d413259a46990580e213a0260801c91613871565b826345c3193d60e11b5f5260045260245ffd5b93929597949091965f945b60648610613b7a575050505050505050505090565b9091929394959697989984620f42400397885f19048111890215620f424002156103e257620f4240908902048215613f46576001600160a01b0390613bc0908c8c614392565b16906001600160a01b038111613efa5760601b6001600160801b038b1680820615159104015b6001600160a01b038a169080821115613eed5790036001600160a01b03165b8215613ecf570160011c6001600160a01b0316945b6001600160a01b038681169085168110613c52575050505050505050613c46613c4d91612a0995614462565b8383614392565b61458e565b60018060a09d9c9b9a939495969798999d1b0383161015613ea6578215613e8557613c8790613c828a8a8a61452e565b614485565b99613c9389898961458e565b965b87908c8c838715613e52575050808d1115613e4857613cb8908d035b8883614199565b613cc58a88018387614168565b995b8a82109a8b15613e07577812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218311670de0b6b3a764000002158102156103e25780670de0b6b3a76400008402049b5b15613dc05750506001600160801b0381169081036103e257613d3091856145ba565b908415613db65785919082821115613d785750035b620f4240819c98670de0b6b3a76400000310613d6957506001019492909391613b65565b9a505050505050505050505090565b915050600a60097f1c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c718311021502156103e2576009600a910204613d45565b9b5084039a613d45565b90939e9291506001600160801b0381169081036103e25788613de19261452e565b918515613df25750508a039a613d45565b8c92919d508282115f14613d78575003613d45565b7812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111670de0b6b3a764000002158302156103e25782670de0b6b3a76400008202049b613d0e565b50613cb85f613cb1565b613e5e91018984614199565b9080881115613e7b57613e759088035b8387614168565b99613cc7565b50613e755f613e6e565b613e9490613c828a898b6145ba565b99613ea089888a6144c4565b96613c95565b945050509050612a099650613ec993949550613ec29250614462565b838361440c565b906144c4565b6001600160a01b039180820160011c91600291081515011694613c1a565b634323a5555f526004601cfd5b6001600160801b038b16613f1381600160601b846142b3565b918115613f3257600160601b900915613be65760010180613be6575f80fd5b634e487b7160e01b5f52601260045260245ffd5b906001600160a01b0390613f5b908c8c614333565b16906001600160a01b0390613f71908c8c61440c565b16613c05565b600160ff1b8114612c2a575f0390565b906014528160345263095ea7b360601b5f5260205f6044601082855af13d1560015f5114171615613fbb575b50505f603452565b604460105f80936020958260345263095ea7b360601b83528238868683865af1506034525af13d1560015f5114171615613ff6575f80613fb3565b633e3f8f735f526004601cfd5b8115613f32570490565b5f9080156140c55780806001146140bd576002146140b65760016101338210166001600b831016176140a8579060019060025b6001811161406c5750825f1904821161405857500290565b634e487b7160e01b81526011600452602490fd5b92805f19048111614094576001841661408b575b80029260011c614040565b80920291614080565b634e487b7160e01b82526011600452602482fd5b6002900a9190806140585750565b5050600490565b505050600190565b50505f90565b600160601b600160025b600181116140ec5750815f19048111612c2a570290565b91805f19048111612c2a576001831661410b575b80029160011c6140d5565b80910290614100565b90916001600160801b0382116103e257670de0b6b3a76400000390825f19048211830215670de0b6b3a764000002156103e257670de0b6b3a7640000612a0993600261415e6140cb565b930a9302046142b3565b612a099291906001600160a01b0380831690821611614193575b90036001600160a01b0316906141e0565b90614182565b612a0992916001600160a01b03808216908316116141da575b6141c86001600160a01b0382811690841661426a565b9190036001600160a01b0316916142b3565b906141b2565b90606082901b905f19600160601b8409928280851094039380850394858411156103e25714614263578190600160601b900981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b81810291905f1982820991838084109303928084039384600160601b11156103e257146142aa57600160601b910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f19818509938380861095039480860395868511156103e2571461432b579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b9190811561438d576001600160a01b03909216918183029160609190911b600160601b600160e01b03169082048314828211161561438057612a099261437b928203916145f6565b61461f565b63f5c787f15f526004601cfd5b505090565b9190811561438d5760601b600160601b600160e01b0316916001600160a01b031690808202826143c28383614003565b146143ea575b5061355d6143d69284614003565b80820491061515016001600160a01b031690565b83018381106143c8576001600160a01b0393614408939192506145f6565b1690565b612a099261437b92906001600160a01b038111614449576001600160801b0361443992169060601b614003565b905b6001600160a01b0316612c3e565b6001600160801b0361445c9216906141e0565b9061443b565b815f19048111820215620f424002156103e25702620f4240808204910615150190565b7d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98111620f424002158202156103e257620f42400290808204910615150190565b906001600160a01b0380821690831611614528575b6001600160a01b03821691821561451c57612a0993614517926001600160a01b0380821693909103169060601b600160601b600160e01b03166142b3565b614003565b62bfc9215f526004601cfd5b906144d9565b6001600160a01b0382811690821611614588575b6001600160a01b03811692831561451c5761457c926001600160a01b0380821693909103169060601b600160601b600160e01b03166145f6565b90808206151591040190565b90614542565b612a09926001600160a01b03928316919092160360ff81901d90810118906001600160801b031661426a565b6001600160a01b0391821691160360ff81901d90810118906001906001600160801b03166145e8838261426a565b928260601b91091515160190565b9291906146048282866142b3565b938215613f32570961461257565b906001019081156103e257565b6001600160a01b0381169190820361463357565b6393dafdf160e01b5f5260045ffdfea26469706673582212200dd4c52570472f7b8b8963488f616c558fff507a130f20c1c5bf86d8bf15265d64736f6c634300081e0033000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e90000000000000000000000001f98400000000000000000000000000000000003
Deployed Bytecode
0x610140806040526004361015610013575f80fd5b5f610120525f3560e01c9081630686ddd914612761575080630a73e3911461270d5780631204f525146126ba578063150b7a02146126645780632fcb4f04146125f15780633d692da1146123e5578063452a9320146123bd5780635c975abb146123995780635f4860df146122435780637d5ad9cd14611b7a5780638456cb5914611ae75780638cffa27714611aa05780638da5cb5b14611a735780638da92e71146119ed578063a129568d14610574578063a7310b58146104ec578063a89d6dd41461046d578063b699b82b14610426578063bc25cf77146102a4578063f2fde38b146102235763fa461e3314610109575f80fd5b3461021c57606036600319011261021c576024356004356044356001600160401b03811161021c576101416060913690600401612932565b908092918101031261021c576101568161286b565b9260406101656020840161286b565b9201359362ffffff8516850361021c576001600160a01b03928316949216916101b09085847f0000000000000000000000001f98400000000000000000000000000000000003613691565b336001600160a01b039091160361020757610120518313156101e057506101d992503390613601565b6101205180f35b9150506101205181136101f5575b50506101d9565b610200913390613601565b5f806101ee565b634b60273560e01b6101205152600461012051fd5b6101205180fd5b3461021c57602036600319011261021c5761023c61283f565b610120515490610256336001600160a01b03841614612a2b565b60018060a01b031680916001600160601b0360a01b16176101205155337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06101205161012051a36101205180f35b3461021c57602036600319011261021c576102bd61283f565b610120515460ff906102d9336001600160a01b03831614612a2b565b60a01c16610411576002546001600160a01b03166103fc576001600160a01b03168061037557506101205180808047335af13d1561036d573d9061031c82612a87565b9161032a60405193846128ed565b8252610120513d90602084013e5b1561034557506101205180f35b60405162461bcd60e51b81526020600482015290819061036990602483019061290e565b0390fd5b606090610338565b6040516370a0823160e01b815230600482015290602082602481845afa9081156103ee5761012051916103b4575b6103af92503390613601565b6101d9565b90506020823d6020116103e6575b816103cf602093836128ed565b810103126103e2576103af9151906103a3565b5f80fd5b3d91506103c2565b6040513d61012051823e3d90fd5b63b5dfd9e560e01b6101205152600461012051fd5b6313d0ff5960e31b6101205152600461012051fd5b3461021c576101205136600319011261021c576040517f000000000000000000000000354dbba1348985cc952c467b8ddaf5dd075906676001600160a01b03168152602090f35b3461021c5760a036600319011261021c57608036602319011261021c57604051610496816128b7565b602435815260443560208201526064356001600160a01b038116810361021c5760408201526084356001600160a01b038116810361021c57816104e29160606020940152600435612c4b565b6040519015158152f35b3461021c57602036600319011261021c576001600160a01b0361050d61283f565b166101205152600360205260a0604061012051206001600160401b036001825492015416604051916001600160401b03811683526001600160401b038160401c1660208401526001600160401b038160801c16604084015260c01c60608301526080820152f35b3461021c57602036600319011261021c576004356001600160401b03811161021c576105a4903690600401612932565b906105ad612b6b565b506002546001600160a01b031633036119d857336101205152600360205260406101205120916001600160401b036001604051946105ea866128d2565b80548381168752838160401c166020880152838160801c16604088015260c01c60608701520154166080840152604082828101031261021c5761062c8261286b565b6101005260208201356001600160401b03811161021c57610100818401838501031261021c576040519261010084018481106001600160401b038211176114e15760405261067b82820161286b565b845261068b602083830101612b2f565b602085015261069e604083830101612b43565b60408501526106b1606083830101612b43565b60608501526080828201013560808501526106d060a083830101612b57565b60a08501526106e360c083830101612b57565b60c08501526001600160401b0360e083830101351161021c5761070f928101910160e081013501612ad8565b60e082015260018060a01b03815116916001600160401b0360a0830151166001600160401b038251161080156119b5575b6119a0576001600160601b036020830151169160405161014081018181106001600160401b038211176114e1576040526101205181526101205160208201526101205160408201526101205160608201526101205160808201526101205160a08201526101205160c08201526101205160e08201526101205161010082015260606101208201526060936040516107d786826128ed565b60028152601f1986013660208301376101208301526020820181905260405163133f757160e31b81526004810191909152610180816024817f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e96001600160a01b03165afa80156103ee57610120519081908190819081908190611953575b6108a89596509062ffffff92916001600160801b036101208a0151961660e08a015260020b60a089015260020b60c088015216604086015261089682612bf9565b6001600160a01b039091169052612bd8565b6001600160a01b03918216905261012082015160049160e0916108ca90612bd8565b511661092060018060a01b036108e4610120870151612bf9565b51169160018060a01b039262ffffff604088015116917f0000000000000000000000001f98400000000000000000000000000000000003613691565b1680845260405192838092633850c7bd851b82525afa80156103ee576101205191829161192d575b5060020b60808301526001600160a01b0390811661010083015281516040516334324e9f60e21b81529160209183916004918391165afa9081156103ee5761012051916118f3575b5060020b60608201526109a861012082015151612ba6565b916001600160801b036040820151166109c084612bd8565b526001600160801b036060820151166109d884612bf9565b526109e38351612ba6565b936080820151906040516109f6816128b7565b61012051815261012051602082015261012051604082015287610120519101526001600160401b0360608201511691825f19048111830215670de0b6b3a7640000021561021c576001600160401b03604083015116805f19048211810215670de0b6b3a7640000021561021c57670de0b6b3a7640000610a7c60c088015160020b61382d565b91610a8d60a089015160020b61382d565b938260405197610a9c896128b7565b8202048752020460208501526001600160a01b0390811660408501521687830152610100840151610ace908390612c4b565b15611521576001600160401b0360a08401511660208501519160405192610af4846128b7565b83526001600160801b036020840130815281604086018181528c8701928284526040519763fc6f786560e01b895251600489015260018060a01b03905116602488015251166044860152511660648401526040836084816101205160018060a01b037f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e9165af180156103ee57610120519384916118be575b50610ba084610b9a8a612bd8565b51612c3e565b610ba989612bd8565b52610bb781610b9a8a612bf9565b610bc089612bf9565b52825f19048411830215670de0b6b3a7640000021561021c57610bf2670de0b6b3a764000084860204610b9a8b612bd8565b610bfb8a612bd8565b52825f19048111830215670de0b6b3a7640000021561021c576001600160401b0393610c38670de0b6b3a7640000608095840204610b9a8c612bf9565b610c418b612bf9565b526101208801516001600160a01b0390610c5a90612bd8565b5116906040519081527ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd760203392a36101208701516001600160a01b0390610ca190612bf9565b5116906040519081527ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd760203392a3015160408581015160c08601516001600160401b031660e052610100870151918501516001600160a01b0390811660a0528a86015162ffffff9092169594929391169116610d31610d2089612bd8565b51610d2a8b612bd8565b5190612c1d565b91610d48610d3e8a612bf9565b51610d2a8c612bf9565b60c052604051610d57816128d2565b61012080518252805160208301528051604083015280518d8301528051608090920191909152518185106116255750600192809661012051506001600160801b03861161021c57815f190464e8d4a51000820260e05101670de0b6b3a76400000311820215670de0b6b3a7640000021561021c57610dfb90610dd76140cb565b90670de0b6b3a76400008464e8d4a5100060028b0a930260e05101820302046142b3565b945b6001600160a01b031690878686156116145750610e1991612c1d565b905b841561160357610e2d8660c051612c3e565b60a0519391828086116115f5575b506001600160a01b0385168111611596575050610e5792614199565b6001600160801b0381169081036103e257925b815f19046001600160801b03851611820215670de0b6b3a7640000021561021c5760e0515f1904861160e0510215670de0b6b3a7640000021561021c57670de0b6b3a764000096600496610f389587958b948f6040519c8d98610ecc8a6128d2565b8415158a526001600160801b0360208b01968360e0518a0204885260408c0160805216020460805152670de0b6b3a764000060e051860204850390880152608087015215155f1461156b57610f26915051610b9a8d612bd8565b610f2f8c612bd8565b525b8a8a6130fc565b8351604051633850c7bd60e01b81529260e09184919082906001600160a01b03165afa80156103ee57610f86926101205191611536575b506001600160a01b03166101008501819052612c4b565b156115215751156114fb57610f9a82612bd8565b51610fb1610fa784612bf9565b51610d2a86612bf9565b905b610120830151610ffb9082906001600160a01b037f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e9811691610ff490612bd8565b5116613f87565b61012083015161103c9083906001600160a01b037f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e9811691610ff490612bf9565b6020830151916040519260c084018481106001600160401b038211176114e15760405283526020830191825260408301908152868301610120518152608084019161012051835260a08501934285526040519563219f5d1760e01b87525160048701525160248601525160448501525160648401525160848301525160a4820152848160c4816101205160018060a01b037f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e9165af180156103ee576101205161012051926101205192611494575b50611143926001600160801b03611130921660e086015261112a86612bd8565b51612c1d565b61113985612bd8565b5261112a84612bf9565b61114c83612bf9565b526001600160801b0360e082015116608051511161147f576020810151853b1561021c5760405163095ea7b360e01b815233600482015260248101919091526101205181604481838a5af180156103ee57611464575b5061012051600195905b83518110156112e0576101208301516001600160a01b03906111cf908390612c09565b5116966111dc8286612c09565b516111e78388612c09565b5110156112b1579061123260019261120d6112028489612c09565b51610d2a858b612c09565b6112178489612c09565b5261122d6112258489612c09565b51338c613f87565b61305b565b975b61123e8288612c09565b5161128d575b61124e8288612c09565b51604051908152838060a01b036101005116907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e05663160203392a4016111ac565b6112ac61129a8389612c09565b51848060a01b03610100511683613601565b611244565b96906001916112c08287612c09565b516112cb8389612c09565b52610120516112da8388612c09565b52611234565b5092509092602082015190610120830151956112fa612b6b565b5061130481612ba6565b9561130e82612ba6565b97600161131a84612ba6565b9361132481612ba6565b968961132f8c612bd8565b526113398c612bd8565b528161134486612bd8565b52600261135088612bd8565b52116113c4575b50509060209392916113c09760405197611370896128b7565b885285880152604087015285015201516040519081527f6c7e7d4cb83a668aef31739dd35dc3fc3d5f31d62b69e438b7b24d35b40dcc6360203392a36040519182916020835260208301906129a1565b0390f35b9296939190956001956101205150610120515b885181101561144e576113ea818a612c09565b516113f8575b6001016113d7565b96600190611446906001600160a01b036114128b8a612c09565b511661141e828b612c09565b526114298a8c612c09565b516114348288612c09565b52826114408289612c09565b5261305b565b9790506113f0565b50949790965093945090929150806113c0611357565b61012051611471916128ed565b6101205161021c57856111a2565b63bb55fd2760e01b6101205152600461012051fd5b925050508481813d83116114da575b6114ad81836128ed565b8101031261021c57806111306114c561114393612f9f565b6020830151604090930151929390915061110a565b503d6114a3565b634e487b7160e01b61012051526041600452602461012051fd5b61151161150783612bd8565b51610d2a85612bd8565b61151a83612bf9565b5190610fb3565b633a8bf65960e01b6101205152600461012051fd5b611558915060e03d60e011611564575b61155081836128ed565b810190613078565b50505050505089610f6f565b503d611546565b50670de0b6b3a76400006115879160e0510204610b9a8d612bf9565b6115908c612bf9565b52610f31565b909290916001600160a01b0381168310156115e95782916115bb916115c19594614199565b93614168565b808210156115e257505b6001600160801b0381169081036103e25792610e6a565b90506115cb565b9150506115cb92614168565b60a05190955092505f610e3b565b61160f8860c051612c1d565b610e2d565b905061161f91612c3e565b90610e1b565b60a05190939085116116525761164c64e8d4a5100060c051980260e0510160c05187614114565b94610dfd565b939592506001600160801b03861161021c576002860a936116738286614003565b60a0518860011b0303937812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f2160a051890311670de0b6b3a7640000021585021561021c576116d56116cd6116be8a61400d565b6116c66140cb565b90856142b3565b60c051612c3e565b947812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f2160c05111670de0b6b3a7640000021586021561021c5780670de0b6b3a764000060a0518b03020486670de0b6b3a764000060c0510204105f1461180f5761179c6117ab916001978b61177364e8d4a51000870260e05101670de0b6b3a76400000364e8d4a51000880260e0510185670de0b6b3a764000060a05186030204613818565b670de0b6b3a76400000192670de0b6b3a7640000838160c05102049260a0519003020403613818565b966117a56140cb565b886142b3565b907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218211670de0b6b3a7640000021564e8d4a51000820260e05101670de0b6b3a764000003021561021c5764e8d4a51000670de0b6b3a7640000910260e05101810391020496610dfd565b9490979550610120519464e8d4a51000890260e051015f190481670de0b6b3a764000060a0518a0302041164e8d4a510008a0260e051010215670de0b6b3a7640000021561021c576118b164e8d4a510009161164c93670de0b6b3a7640000848d0260e0510183828d60a051900302040204670de0b6b3a76400000391670de0b6b3a764000060a0518c03020481670de0b6b3a764000060c051020403613818565b980260e051018887614114565b90506118e391935060403d6040116118ec575b6118db81836128ed565b8101906130e6565b9290928b610b8c565b503d6118d1565b90506020813d602011611925575b8161190e602093836128ed565b8101031261021c5761191f90612f91565b86610990565b3d9150611901565b9050611948915060e03d60e0116115645761155081836128ed565b505050505087610948565b5050505050506108a861198162ffffff926101803d8111611999575b61197981836128ed565b810190612fb3565b50505050965091945091969296939250869550610855565b503d61196f565b632a9ffab760e21b6101205152600461012051fd5b506001600160401b0360c0830151166001600160401b0360208301511610610740565b63f3f6425d60e01b6101205152600461012051fd5b3461021c57602036600319011261021c576004358015159081900361021c5761012051547f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a91602091611a4a336001600160a01b03831614612a2b565b6101205160ff60a01b1990911660a083901b60ff60a01b16179055604051908152a16101205180f35b3461021c576101205136600319011261021c5761012051546040516001600160a01b039091168152602090f35b3461021c576101205136600319011261021c576040517f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03168152602090f35b3461021c576101205136600319011261021c576001546001600160a01b03163303611b6557610120515460ff8160a01c16610411576101205160ff60a01b19909116600160a01b179055604051600181527f549bab54c75a364ce0e438a4fbf09df7e6b096bcc83a6f91065a0fc8e410b29a90602090a16101205180f35b636570ecab60e11b6101205152600461012051fd5b346103e25760403660031901126103e257611b9361283f565b602435906001600160401b0382116103e257816004018236036101006003198201126103e25760ff5f5460a01c1661223457600254926001600160a01b038416612225576001600160a01b03166001600160a01b0319939093168317600255604051638da5cb5b60e01b81526020816004815f885af190811561202e575f916121eb575b506001600160a01b039081165f9081526005602090815260408083208784529091529020541633036121dc57611c4c82612af3565b6001600160a01b037f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e981169116036121cd575f5f60448601956001600160801b03611c9688612b07565b16158015906121b0575b612103575b611cae85612af3565b916024820191611cbd83612b1b565b95611cc78a612b07565b996064830190611cd682612b07565b60408051336020820152808201919091529a9096906001600160a01b0390611cfd9061286b565b1660608c0152611d0c90612b2f565b6001600160601b031660808b0152611d2390612b43565b6001600160801b031660a08a0152611d3a90612b43565b6001600160801b031660c0890152608482013560e0890152611d5e60a48301612b57565b6001600160401b0316610100890152611d7960c48301612b57565b6001600160401b031661012089015260e482013590602219018112156103e25701602460048201359101906001600160401b0381116103e25780360382136103e2576001600160601b0399611e0b6101808a846001600160801b039695879661010061014085015281610160850152848401375f838284010152601f801991011681010301601f1981018b528a6128ed565b1692169160019481151591826120fa575b84151591826120ea575b611e2f88612ba6565b9b8c99611e3b8a612ba6565b98611e57611e51611e4b8d612ba6565b9c612ba6565b9c612bd8565b6001600160a01b03909116905216611e6e88612bd8565b526001611e7a89612bd8565b526002611e868a612bd8565b526001936120b0575b505061207b575b50505060405195611ea6876128b7565b8652602086015260408501526060840152611ebf612b6b565b60405190606082018281106001600160401b038211176120675790611f119592916040526060825260208201905f8252611f2360408401915f835260405198899660a0602089015260c08801906129a1565b868103601f19016040880152906129a1565b91601f19858403016060860152606083019351936060845284518091526020608085019501905f5b81811061203957505050916040611fae9492611fbc979451602084015251910152601f19848203016080850152606051808252806080602084015e5f828201602090810191909152601f909101601f191690910184810360a0860152019061290e565b03601f1981018452836128ed565b803b156103e2576040805162b9252f60e41b81523060048201526024810191909152905f908290818381611ff3604482018961290e565b03925af1801561202e57612019575b600280546001600160a01b03191690556101205180f35b5f612023916128ed565b5f6101205280612002565b6040513d5f823e3d90fd5b825180516001600160a01b03168852602090810151818901528b985060409097019690920191600101611f4b565b634e487b7160e01b5f52604160045260245ffd5b6001926120a79261208c838c612c09565b90858060a01b031690526120a08287612c09565b5285612c09565b52868080611e96565b909192506120bd8b612bf9565b6001600160a01b0390911690526120d386612bf9565b5260016120df87612bf9565b526002908a80611e8f565b966120f49061305b565b96611e26565b60029650611e1c565b91505061210f83612af3565b506001600160601b0361212460248301612b1b565b60405163133f757160e31b815291166004820152610180816024817f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e96001600160a01b03165afa801561202e575f915f91612182575b509091611ca5565b905061219d91506101803d81116119995761197981836128ed565b505050505050505092509050908761217a565b506001600160801b036121c560648301612b07565b161515611ca0565b63ed5f09f160e01b5f5260045ffd5b6317fb43e560e31b5f5260045ffd5b90506020813d60201161221d575b81612206602093836128ed565b810103126103e25761221790612a73565b85611c17565b3d91506121f9565b63b5dfd9e560e01b5f5260045ffd5b6313d0ff5960e31b5f5260045ffd5b346103e25760603660031901126103e25761225c61283f565b61226461295f565b506044356001600160401b0381116103e257612284903690600401612932565b6002546001600160a01b031661222557604051630972932760e21b81523360048201526020816024817f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03165afa90811561202e575f9161235f575b50156123505781019160c0828403126103e2576123038261286b565b9060a08301356001600160401b0381116103e25761234e94612326918501612ad8565b9260808101359260608201359260408301359260200135916001600160a01b03169033612c64565b005b630ea8370b60e41b5f5260045ffd5b90506020813d602011612391575b8161237a602093836128ed565b810103126103e25761238b90612a66565b846122e7565b3d915061236d565b346103e2575f3660031901126103e257602060ff5f5460a01c166040519015158152f35b346103e2575f3660031901126103e2576001546040516001600160a01b039091168152602090f35b346103e25760e03660031901126103e2576123fe61283f565b612406612855565b9060c4356001600160401b0381116103e257612426903690600401612932565b60025491939092916001600160a01b031661222557604051630972932760e21b81526001600160a01b0383811660048301819052949190602090829060249082907f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59165afa90811561202e575f916125b7575b501561235057604051638da5cb5b60e01b8152936020856004815f855af194851561202e575f9561257b575b506001600160a01b038516330361256c5760205f91600460405180948193635e34633b60e11b83525af1801561202e575f90612539575b600391501061252a5761234e94612514913691612aa2565b9260a43592608435926064359260443592612c64565b63a93eca7960e01b5f5260045ffd5b506020813d602011612564575b81612553602093836128ed565b810103126103e257600390516124fc565b3d9150612546565b6312272fd360e11b5f5260045ffd5b9094506020813d6020116125af575b81612597602093836128ed565b810103126103e2576125a890612a73565b93866124c5565b3d915061258a565b90506020813d6020116125e9575b816125d2602093836128ed565b810103126103e2576125e390612a66565b86612499565b3d91506125c5565b346103e25760203660031901126103e25761260a61283f565b61261e60018060a01b035f54163314612a2b565b600180546001600160a01b0319166001600160a01b03929092169182179055337fa14fc14d8620a708a896fd11392a235647d99385500a295f0d7da2a258b2e9675f80a3005b346103e25760803660031901126103e25761267d61283f565b50612686612855565b506064356001600160401b0381116103e2576126a6903690600401612932565b5050604051630a85bd0160e11b8152602090f35b346103e25760403660031901126103e2576126d361283f565b6126db612855565b6001600160a01b039182165f908152600560209081526040808320938516835292815290829020549151919092168152f35b346103e25760203660031901126103e257602061272861283f565b6040517f000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e96001600160a01b039081169216919091148152f35b346103e25760203660031901126103e2576001600160a01b0361278261283f565b165f52600460205260405f205f9080549061279c8261287f565b808552916001811690811561281857506001146127d8575b6113c0846127c4818603826128ed565b60405191829160208352602083019061290e565b5f90815260208120939250905b8082106127fe575090915081016020016127c4826127b4565b9192600181602092548385880101520191019092916127e5565b60ff191660208087019190915292151560051b850190920192506127c491508390506127b4565b600435906001600160a01b03821682036103e257565b602435906001600160a01b03821682036103e257565b35906001600160a01b03821682036103e257565b90600182811c921680156128ad575b602083101461289957565b634e487b7160e01b5f52602260045260245ffd5b91607f169161288e565b608081019081106001600160401b0382111761206757604052565b60a081019081106001600160401b0382111761206757604052565b90601f801991011681019081106001600160401b0382111761206757604052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f840112156103e2578235916001600160401b0383116103e257602083818601950101116103e257565b6024359081151582036103e257565b90602080835192838152019201905f5b81811061298b5750505090565b825184526020938401939092019160010161297e565b80516080808452815190840181905260a08401949391602001905f5b818110612a0c5750505060606129f86129e6612a0995966020860151858203602087015261296e565b6040850151848203604086015261296e565b92015190606081840391015261296e565b90565b82516001600160a01b03168752602096870196909201916001016129bd565b15612a3257565b60405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606490fd5b519081151582036103e257565b51906001600160a01b03821682036103e257565b6001600160401b03811161206757601f01601f191660200190565b929192612aae82612a87565b91612abc60405193846128ed565b8294818452818301116103e2578281602093845f960137010152565b9080601f830112156103e257816020612a0993359101612aa2565b356001600160a01b03811681036103e25790565b356001600160801b03811681036103e25790565b356001600160601b03811681036103e25790565b35906001600160601b03821682036103e257565b35906001600160801b03821682036103e257565b35906001600160401b03821682036103e257565b60405190612b78826128b7565b606080838181528160208201528160408201520152565b6001600160401b0381116120675760051b60200190565b90612bb082612b8f565b612bbd60405191826128ed565b8281528092612bce601f1991612b8f565b0190602036910137565b805115612be55760200190565b634e487b7160e01b5f52603260045260245ffd5b805160011015612be55760400190565b8051821015612be55760209160051b010190565b91908203918211612c2a57565b634e487b7160e01b5f52601160045260245ffd5b91908201809211612c2a57565b815181119182612c5a57505090565b6020015111919050565b9196949095929395670de0b6b3a764000085118015612f80575b8015612f6f575b8015612f5e575b612f4f576001600160a01b039081165f9081526005602090815260408083208685168452909152902080546001600160a01b031916989091169788179055670de0b6b3a7640000808201908110612c2a57670de0b6b3a7640000810290808204670de0b6b3a76400001490151715612c2a57612d0f6001600160401b0391613756565b169281670de0b6b3a76400000391670de0b6b3a76400008311612c2a57670de0b6b3a76400008302928304670de0b6b3a76400001490670de0b6b3a7640000141715612c2a576001600160401b039360019385612d6c8195613756565b16908460405198612d7c8a6128d2565b16885284602089019a168a526040880192835260608801918252846080890194168452858060a01b031698895f526003602052848060405f209951161685198954161788555191846fffffffffffffffff00000000000000008954928260801b905160801b16938260c01b905160c01b169460401b16911617171785555116920191166001600160401b0319825416179055815f52600460205260405f20908051906001600160401b038211612067578190612e38845461287f565b601f8111612eff575b50602090601f8311600114612e9c575f92612e91575b50508160011b915f199060031b1c19161790555b7febc70f7c8d6a67b19e15e968cb908d21719e8ff9a778a71171fba931a618d0525f80a3565b015190505f80612e57565b5f8581528281209350601f198516905b818110612ee75750908460019594939210612ecf575b505050811b019055612e6b565b01515f1960f88460031b161c191690555f8080612ec2565b92936020600181928786015181550195019301612eac565b909150835f5260205f20601f840160051c81019160208510612f45575b90601f859493920160051c01905b818110612f375750612e41565b5f8155849350600101612f2a565b9091508190612f1c565b632a9ffab760e21b5f5260045ffd5b50670de0b6b3a76400008411612c8c565b50670de0b6b3a76400008211612c85565b50670de0b6b3a76400008711612c7e565b51908160020b82036103e257565b51906001600160801b03821682036103e257565b9190826101809103126103e25781516001600160601b03811681036103e25791612fdf60208201612a73565b91612fec60408301612a73565b91612ff960608201612a73565b91608082015162ffffff811681036103e2579161301860a08201612f91565b9161302560c08301612f91565b9161303260e08201612f9f565b916101008201519161012081015191612a096101606130546101408501612f9f565b9301612f9f565b5f198114612c2a5760010190565b519061ffff821682036103e257565b908160e09103126103e25780516001600160a01b03811681036103e257916130a260208301612f91565b916130af60408201613069565b916130bc60608301613069565b916130c960808201613069565b9160a082015160ff811681036103e25760c0612a09919301612a66565b91908260409103126103e2576020825192015190565b94909391929360608301938451156135f85760e001518051806133d757505082511515906040860194600462ffffff87511692602060018060a01b038a511660405193848092630d34328160e11b82525afa801561202e578a925f91613396575b506101008a015160408801516060909801516131b9986001600160a01b03918216949082169391909216916131ab90610d2a6131a461319e6115078a612bd8565b98612bf9565b5191612bf9565b95519660808b015198613b5a565b91821561338f576132ad92604092511515945f6131df60018060a01b0383511693613f77565b87821461336e5761326b6101206401000276a4945b0161325d62ffffff60018060a01b0361320d8451612bd8565b5193519316926001600160a01b039061322590612bf9565b5199518b516001600160a01b03958616602082015291909a16909316604084015290971662ffffff1660608201529586906080820190565b03601f1981018752866128ed565b855196879586948593630251596160e31b85523060048601528b6024860152604485015260018060a01b0316606484015260a0608484015260a483019061290e565b03925af190811561202e575f905f92613331575b5061330c9290811561331c576132da9061112a86612bd8565b6132e385612bd8565b521561330f57613306906133006132f985612bf9565b5191613f77565b90612c3e565b91612bf9565b52565b6133069061112a84612bf9565b61332c906133006132f987612bd8565b6132da565b9150506040813d604011613366575b8161334d604093836128ed565b810103126103e25780516020909101519061330c6132c1565b3d9150613340565b61326b61012073fffd8963efd1fc6a506488495d951d5263988d25946131f4565b5050505050565b9250506020823d6020116133cf575b816133b2602093836128ed565b810103126103e2576131b9956133c88b93612f9f565b909661315d565b3d91506133a5565b959392509593505051151592840190606085602084019303126103e25760208501516001600160a01b038116908190036103e2576040860151956060810151906001600160401b0382116103e257019083603f830112156103e25760208201519161344183612a87565b9461344f60405196876128ed565b838652604082850101116103e2576040935f60208561350e96886101209601838b015e8801015287156135c45701935f60018060a01b036134908751612bd8565b5196519616956001600160a01b03906134a890612bf9565b5116955b6001600160a01b037f000000000000000000000000354dbba1348985cc952c467b8ddaf5dd0759066781169491166134e58b8683613601565b8651978896879586946344bc658560e01b8652600486015260a0602486015260a485019061290e565b60448401929092526001600160a01b03166064830152608482018b905203925af192831561202e575f915f946135a1575b501561357b5761356261356c9161355d61330c959661112a86612bd8565b612c3e565b93610b9a83612bf9565b61357582612bf9565b52612bd8565b61359c9061355d61359261330c95610b9a86612bd8565b9561112a85612bf9565b61356c565b9093506135bd915060403d6040116118ec576118db81836128ed565b925f61353f565b01935f60018060a01b036135d88751612bf9565b5196519616956001600160a01b03906135f090612bd8565b5116956134ac565b50505050505050565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516613684575b501561364d57565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f613645565b9290916001600160a01b0380821690841610156103e257604080516001600160a01b0394851660208201908152929094169084015262ffffff919091166060808401919091528252906136e56080826128ed565b5190209060405191602083019160ff60f81b83526001600160601b03199060601b16602184015260358301527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b546055830152605582526137466075836128ed565b905190206001600160a01b031690565b60b581600160881b811015613801575b806901000000000000000000620100009210156137f4575b650100000000008110156137e7575b63010000008110156137da575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8080920410900390565b60101c9160081b9161379a565b60201c9160101b9161378d565b60401c9160201b9161377e565b5068b500000000000000009050608082901c613766565b815f190481118202158302156103e257020490565b60020b908160ff1d82810118620d89e88111613b475763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102600160801b189160028116613b2b575b60048116613b0f575b60088116613af3575b60108116613ad7575b60208116613abb575b60408116613a9f575b60808116613a83575b6101008116613a67575b6102008116613a4b575b6104008116613a2f575b6108008116613a13575b61100081166139f7575b61200081166139db575b61400081166139bf575b61800081166139a3575b620100008116613987575b62020000811661396c575b620400008116613951575b6208000016613938575b5f12613930575b0160201c90565b5f1904613929565b6b048a170391f7dc42444e8fa290910260801c90613922565b6d2216e584f5fa1ea926041bedfe9890920260801c91613918565b916e5d6af8dedb81196699c329225ee6040260801c9161390d565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c91613902565b916f31be135f97d08fd981231505542fcfa60260801c916138f7565b916f70d869a156d2a1b890bb3df62baf32f70260801c916138ed565b916fa9f746462d870fdf8a65dc1f90e061e50260801c916138e3565b916fd097f3bdfd2022b8845ad8f792aa58250260801c916138d9565b916fe7159475a2c29b7443b29c7fa6e889d90260801c916138cf565b916ff3392b0822b70005940c7a398e4b70f30260801c916138c5565b916ff987a7253ac413176f2b074cf7815e540260801c916138bb565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c916138b1565b916ffe5dee046a99a2a811c461f1969c30530260801c916138a7565b916fff2ea16466c96a3843ec78b326b528610260801c9161389e565b916fff973b41fa98c081472e6896dfb254c00260801c91613895565b916fffcb9843d60f6159c9db58835c9266440260801c9161388c565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c91613883565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c9161387a565b916ffff97272373d413259a46990580e213a0260801c91613871565b826345c3193d60e11b5f5260045260245ffd5b93929597949091965f945b60648610613b7a575050505050505050505090565b9091929394959697989984620f42400397885f19048111890215620f424002156103e257620f4240908902048215613f46576001600160a01b0390613bc0908c8c614392565b16906001600160a01b038111613efa5760601b6001600160801b038b1680820615159104015b6001600160a01b038a169080821115613eed5790036001600160a01b03165b8215613ecf570160011c6001600160a01b0316945b6001600160a01b038681169085168110613c52575050505050505050613c46613c4d91612a0995614462565b8383614392565b61458e565b60018060a09d9c9b9a939495969798999d1b0383161015613ea6578215613e8557613c8790613c828a8a8a61452e565b614485565b99613c9389898961458e565b965b87908c8c838715613e52575050808d1115613e4857613cb8908d035b8883614199565b613cc58a88018387614168565b995b8a82109a8b15613e07577812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218311670de0b6b3a764000002158102156103e25780670de0b6b3a76400008402049b5b15613dc05750506001600160801b0381169081036103e257613d3091856145ba565b908415613db65785919082821115613d785750035b620f4240819c98670de0b6b3a76400000310613d6957506001019492909391613b65565b9a505050505050505050505090565b915050600a60097f1c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c71c718311021502156103e2576009600a910204613d45565b9b5084039a613d45565b90939e9291506001600160801b0381169081036103e25788613de19261452e565b918515613df25750508a039a613d45565b8c92919d508282115f14613d78575003613d45565b7812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111670de0b6b3a764000002158302156103e25782670de0b6b3a76400008202049b613d0e565b50613cb85f613cb1565b613e5e91018984614199565b9080881115613e7b57613e759088035b8387614168565b99613cc7565b50613e755f613e6e565b613e9490613c828a898b6145ba565b99613ea089888a6144c4565b96613c95565b945050509050612a099650613ec993949550613ec29250614462565b838361440c565b906144c4565b6001600160a01b039180820160011c91600291081515011694613c1a565b634323a5555f526004601cfd5b6001600160801b038b16613f1381600160601b846142b3565b918115613f3257600160601b900915613be65760010180613be6575f80fd5b634e487b7160e01b5f52601260045260245ffd5b906001600160a01b0390613f5b908c8c614333565b16906001600160a01b0390613f71908c8c61440c565b16613c05565b600160ff1b8114612c2a575f0390565b906014528160345263095ea7b360601b5f5260205f6044601082855af13d1560015f5114171615613fbb575b50505f603452565b604460105f80936020958260345263095ea7b360601b83528238868683865af1506034525af13d1560015f5114171615613ff6575f80613fb3565b633e3f8f735f526004601cfd5b8115613f32570490565b5f9080156140c55780806001146140bd576002146140b65760016101338210166001600b831016176140a8579060019060025b6001811161406c5750825f1904821161405857500290565b634e487b7160e01b81526011600452602490fd5b92805f19048111614094576001841661408b575b80029260011c614040565b80920291614080565b634e487b7160e01b82526011600452602482fd5b6002900a9190806140585750565b5050600490565b505050600190565b50505f90565b600160601b600160025b600181116140ec5750815f19048111612c2a570290565b91805f19048111612c2a576001831661410b575b80029160011c6140d5565b80910290614100565b90916001600160801b0382116103e257670de0b6b3a76400000390825f19048211830215670de0b6b3a764000002156103e257670de0b6b3a7640000612a0993600261415e6140cb565b930a9302046142b3565b612a099291906001600160a01b0380831690821611614193575b90036001600160a01b0316906141e0565b90614182565b612a0992916001600160a01b03808216908316116141da575b6141c86001600160a01b0382811690841661426a565b9190036001600160a01b0316916142b3565b906141b2565b90606082901b905f19600160601b8409928280851094039380850394858411156103e25714614263578190600160601b900981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b81810291905f1982820991838084109303928084039384600160601b11156103e257146142aa57600160601b910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f19818509938380861095039480860395868511156103e2571461432b579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b9190811561438d576001600160a01b03909216918183029160609190911b600160601b600160e01b03169082048314828211161561438057612a099261437b928203916145f6565b61461f565b63f5c787f15f526004601cfd5b505090565b9190811561438d5760601b600160601b600160e01b0316916001600160a01b031690808202826143c28383614003565b146143ea575b5061355d6143d69284614003565b80820491061515016001600160a01b031690565b83018381106143c8576001600160a01b0393614408939192506145f6565b1690565b612a099261437b92906001600160a01b038111614449576001600160801b0361443992169060601b614003565b905b6001600160a01b0316612c3e565b6001600160801b0361445c9216906141e0565b9061443b565b815f19048111820215620f424002156103e25702620f4240808204910615150190565b7d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98111620f424002158202156103e257620f42400290808204910615150190565b906001600160a01b0380821690831611614528575b6001600160a01b03821691821561451c57612a0993614517926001600160a01b0380821693909103169060601b600160601b600160e01b03166142b3565b614003565b62bfc9215f526004601cfd5b906144d9565b6001600160a01b0382811690821611614588575b6001600160a01b03811692831561451c5761457c926001600160a01b0380821693909103169060601b600160601b600160e01b03166145f6565b90808206151591040190565b90614542565b612a09926001600160a01b03928316919092160360ff81901d90810118906001600160801b031661426a565b6001600160a01b0391821691160360ff81901d90810118906001906001600160801b03166145e8838261426a565b928260601b91091515160190565b9291906146048282866142b3565b938215613f32570961461257565b906001019081156103e257565b6001600160a01b0381169190820361463357565b6393dafdf160e01b5f5260045ffdfea26469706673582212200dd4c52570472f7b8b8963488f616c558fff507a130f20c1c5bf86d8bf15265d64736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e90000000000000000000000001f98400000000000000000000000000000000003
-----Decoded View---------------
Arg [0] : owner_ (address): 0xb4d72B1c91e640e4ED7d7397F3244De4D8ACc50B
Arg [1] : arcadiaFactory (address): 0xDa14Fdd72345c4d2511357214c5B89A919768e59
Arg [2] : routerTrampoline (address): 0x354dBBa1348985CC952c467b8ddaF5dD07590667
Arg [3] : positionManager (address): 0x943e6e07a7E8E791dAFC44083e54041D743C46E9
Arg [4] : uniswapV3Factory (address): 0x1F98400000000000000000000000000000000003
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000b4d72b1c91e640e4ed7d7397f3244de4d8acc50b
Arg [1] : 000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59
Arg [2] : 000000000000000000000000354dbba1348985cc952c467b8ddaf5dd07590667
Arg [3] : 000000000000000000000000943e6e07a7e8e791dafc44083e54041d743c46e9
Arg [4] : 0000000000000000000000001f98400000000000000000000000000000000003
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.