| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| no token holdings | ||||
| Type | Age | Block | Details |
|---|---|---|---|
| no cross-chain L1↔L2 transactions for this address | |||
| Transaction Hash | Method | Block | Age | From | To | Type | Item | ||
|---|---|---|---|---|---|---|---|---|---|
| no NFT transfers for this address yet | |||||||||
| Txn Hash | Age | Event | Topics / Data |
|---|---|---|---|
| no event logs emitted by this address | |||
| Transaction Hash | Method | Block | Age | From | To | Amount | Token | ||
|---|---|---|---|---|---|---|---|---|---|
| no token transfers for this address yet | |||||||||
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.26;
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IV4Utils } from "./interfaces/IV4Utils.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { FullMath } from "@uniswap/v4-core/src/libraries/FullMath.sol";
import { Currency, CurrencyLibrary } from "@uniswap/v4-core/src/types/Currency.sol";
import { Actions } from "@uniswap/v4-periphery/src/libraries/Actions.sol";
import { PoolKey } from "@uniswap/v4-core/src/types/PoolKey.sol";
import { PoolIdLibrary } from "@uniswap/v4-core/src/types/PoolId.sol";
import { IPositionManager } from "@uniswap/v4-periphery/src/interfaces/IPositionManager.sol";
import { PositionInfoLibrary, PositionInfo } from "@uniswap/v4-periphery/src/libraries/PositionInfoLibrary.sol";
import { IPoolManager } from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import { StateLibrary } from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import { TickMath } from "@uniswap/v4-core/src/libraries/TickMath.sol";
import { IAllowanceTransfer } from "@uniswap/v4-periphery/lib/permit2/src/interfaces/IAllowanceTransfer.sol";
import { IWETH9 } from "@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol";
import { Context } from "@openzeppelin/contracts/utils/Context.sol";
import { Withdrawable } from "../base/Withdrawable.sol";
import { LiquidityAmounts } from "../libraries/LiquidityAmounts.sol";
import { IPermitForwarder } from "../interfaces/IPermitForwarder.sol";
import { SharedStorage } from "../libraries/Storage.sol";
import { FeeType } from "../libraries/FeeConstants.sol";
contract V4Utils is Context, IERC721Receiver, IV4Utils {
using SafeERC20 for IERC20;
using Address for address;
using StateLibrary for IPoolManager;
using PoolIdLibrary for PoolKey;
/// @dev Q64 constant for fee calculations (2 ** 64)
uint256 internal constant Q64 = 2 ** 64;
/// @notice Execute instruction by pulling approved NFT instead of direct safeTransferFrom call from owner
/// @param tokenId Token to process
/// @param instructions Instructions to execute
function execute(address posm, uint256 tokenId, Instructions calldata instructions) external {
// must be approved beforehand
IERC721(posm).safeTransferFrom(_msgSender(), address(this), tokenId, abi.encode(instructions));
}
/// @notice ERC721 callback function. Called on safeTransferFrom and does manipulation as configured in encoded
/// Instructions parameter.
/// At the end the NFT (and any newly minted NFT) is returned to sender. The leftover tokens are sent to
/// instructions.recipient.
function onERC721Received(address, address from, uint256 tokenId, bytes calldata data)
external
override
returns (bytes4)
{
address posm = msg.sender;
// not allowed to send to itself
require(from != address(this), SelfSend());
Instructions memory instructions = abi.decode(data, (Instructions));
UtilActions action = instructions.action;
bytes memory params = instructions.params;
(PoolKey memory poolKey,) = IPositionManager(posm).getPoolAndPositionInfo(tokenId);
require(!(poolKey.currency0 == poolKey.currency1), SameToken());
if (action == UtilActions.DECREASE_AND_SWAP) {
DecreaseAndSwapParams memory decreaseAndSwapParams = abi.decode(params, (DecreaseAndSwapParams));
_decreaseAndSwap(posm, poolKey, decreaseAndSwapParams, from, tokenId);
} else if (action == UtilActions.ADJUST_RANGE) {
AdjustRangeParams memory adjustParams = abi.decode(params, (AdjustRangeParams));
_adjustRange(posm, poolKey, adjustParams, from, tokenId);
} else if (action == UtilActions.COMPOUND) {
CompoundFeesParams memory compoundParams = abi.decode(params, (CompoundFeesParams));
_compoundFees(posm, poolKey, compoundParams, from, tokenId);
} else {
revert NotSupportedAction();
}
// return token to owner (this line guarantees that token is returned to originating owner)
IERC721(posm).transferFrom(address(this), from, tokenId);
return IERC721Receiver.onERC721Received.selector;
}
function swapAndMint(SwapAndMintParams calldata params) external payable {
address posm = params.posm;
_takeTokens(params.inputTokens);
_takeFees(
posm,
TakeFeesParams({
inputTokens: params.inputTokens,
feeX64: params.protocolFeeX64,
feeType: FeeType.PROTOCOL_FEE,
tokenId: IPositionManager(posm).nextTokenId(),
userAddress: _msgSender()
})
);
_takeFees(
posm,
TakeFeesParams({
inputTokens: params.inputTokens,
feeX64: params.gasFeeX64,
feeType: FeeType.GAS_FEE,
tokenId: IPositionManager(posm).nextTokenId(),
userAddress: _msgSender()
})
);
_swaps(params.swapParams);
(uint256 tokenId, uint256 liquidity, uint256 amount0, uint256 amount1) =
_mint(posm, params.poolKey, params.mintParams, _msgSender(), type(uint256).max, type(uint256).max);
_sweeps(params.sweepTokens, _msgSender());
emit SwapAndMint(posm, tokenId, liquidity, amount0, amount1);
}
function swapAndIncrease(SwapAndIncreaseParams calldata params) external payable {
address posm = params.posm;
require(_isApprovedOrOwner(posm, _msgSender(), params.tokenId), Unauthorized());
_takeTokens(params.inputTokens);
_takeFees(
posm,
TakeFeesParams({
inputTokens: params.inputTokens,
feeX64: params.protocolFeeX64,
feeType: FeeType.PROTOCOL_FEE,
tokenId: IPositionManager(posm).nextTokenId(),
userAddress: _msgSender()
})
);
_takeFees(
posm,
TakeFeesParams({
inputTokens: params.inputTokens,
feeX64: params.gasFeeX64,
feeType: FeeType.GAS_FEE,
tokenId: IPositionManager(posm).nextTokenId(),
userAddress: _msgSender()
})
);
_swaps(params.swapParams);
(PoolKey memory poolKey,) = IPositionManager(posm).getPoolAndPositionInfo(params.tokenId);
(uint256 liquidity, uint256 amount0, uint256 amount1) =
_increase(posm, poolKey, params.tokenId, params.increaseParams, _msgSender());
_sweeps(params.sweepTokens, _msgSender());
emit SwapAndIncrease(posm, params.tokenId, liquidity, amount0, amount1);
}
function _takeToken(Currency token, uint256 amount) internal {
uint256 balanceBefore = token.balanceOf(address(this));
IERC20(Currency.unwrap(token)).safeTransferFrom(_msgSender(), address(this), amount);
uint256 balanceAfter = token.balanceOf(address(this));
require(balanceAfter - balanceBefore == amount, TransferError()); // reverts for fee-on-transfer tokens
}
function _takeTokens(InputTokenParams[] calldata inputTokens) internal {
uint256 totalNative;
for (uint256 i; i < inputTokens.length; i++) {
if (inputTokens[i].token.isAddressZero()) totalNative += inputTokens[i].amount;
else _takeToken(inputTokens[i].token, inputTokens[i].amount);
}
require(msg.value >= totalNative, AmountError());
}
function _swaps(SwapParams[] memory swapParams) internal {
for (uint256 i; i < swapParams.length; i++) {
_swap(
swapParams[i].tokenIn,
swapParams[i].tokenOut,
swapParams[i].amountIn,
swapParams[i].amountOutMin,
swapParams[i].swapData,
i
);
}
}
function _sweeps(Currency[] memory sweepTokens, address recipient) internal {
for (uint256 i; i < sweepTokens.length; i++) {
_sweep(sweepTokens[i], 0, recipient);
}
}
function _sweep(Currency currency, uint256 amount, address recipient) internal {
uint256 bal = currency.balanceOf(address(this));
if (amount == 0 || amount > bal) amount = bal;
if (amount > 0) currency.transfer(recipient, amount);
}
// general swap function which uses external router with off-chain calculated swap instructions
// does slippage check with amountOutMin param
// returns token amounts deltas after swap
function _swap(
Currency tokenIn,
Currency tokenOut,
uint256 amountIn,
uint256 amountOutMin,
bytes memory swapData,
uint256 index
) internal returns (uint256 amountInDelta, uint256 amountOutDelta) {
SharedStorage.Storage storage ss = SharedStorage.getStorage();
address weth = ss.weth;
address swapRouter = ss.swapRouter;
Currency _cWETH = Currency.wrap(weth);
if (tokenIn.isAddressZero() && tokenOut == _cWETH) {
IWETH9(weth).deposit{ value: amountIn }();
amountInDelta = amountIn;
amountOutDelta = amountIn;
} else if (tokenIn == _cWETH && tokenOut.isAddressZero()) {
IWETH9(weth).withdraw(amountIn);
amountInDelta = amountIn;
amountOutDelta = amountIn;
} else if (swapData.length != 0) {
uint256 balanceInBefore = tokenIn.balanceOfSelf();
uint256 balanceOutBefore = tokenOut.balanceOfSelf();
if (amountIn == 0) amountIn = balanceInBefore;
uint256 _value;
if (tokenIn.isAddressZero()) {
_value = amountIn;
} else {
// approve needed amount
_safeResetAndApprove(Currency.unwrap(tokenIn), swapRouter, amountIn, address(0));
}
// execute swap
(bool success,) = swapRouter.call{ value: _value }(swapData);
require(success, SwapFailed(swapData, index));
if (!tokenIn.isAddressZero()) {
// reset approval
_safeApprove(Currency.unwrap(tokenIn), swapRouter, 0, address(0));
}
uint256 balanceInAfter = tokenIn.balanceOfSelf();
uint256 balanceOutAfter = tokenOut.balanceOfSelf();
amountInDelta = balanceInBefore - balanceInAfter;
amountOutDelta = balanceOutAfter - balanceOutBefore;
}
// amountMin slippage check
require(amountOutDelta >= amountOutMin, SwapSlippage());
// event for any swap with exact swapped value
emit Swap(Currency.unwrap(tokenIn), Currency.unwrap(tokenOut), amountInDelta, amountOutDelta);
}
// Mint new position using all balance of currency0 and currency1
function _mint(
address posm,
PoolKey memory poolKey,
MintParams memory params,
address from,
uint256 amount0,
uint256 amount1
) internal returns (uint256 tokenId, uint256 liquidity, uint256 amountAdded0, uint256 amountAdded1) {
uint256 balanceBefore0 = poolKey.currency0.balanceOfSelf();
uint256 balanceBefore1 = poolKey.currency1.balanceOfSelf();
amountAdded0 = amount0 > balanceBefore0 ? balanceBefore0 : amount0;
amountAdded1 = amount1 > balanceBefore1 ? balanceBefore1 : amount1;
{
(uint160 sqrtPriceX96,,,) = IPoolManager(IPositionManager(posm).poolManager()).getSlot0(poolKey.toId());
liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(params.tickLower),
TickMath.getSqrtPriceAtTick(params.tickUpper),
amountAdded0,
amountAdded1
);
require(liquidity >= params.minLiquidity, LiquiditySlippage());
}
// mint new position
bytes memory actions;
bytes[] memory callParams;
bool isNative = poolKey.currency0.isAddressZero();
if (isNative) {
actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR), uint8(Actions.SWEEP));
callParams = new bytes[](3);
} else {
actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR));
callParams = new bytes[](2);
}
bytes memory hookData = params.hookData;
callParams[0] =
abi.encode(poolKey, params.tickLower, params.tickUpper, liquidity, amountAdded0, amountAdded1, from, hookData);
callParams[1] = abi.encode(poolKey.currency0, poolKey.currency1);
if (isNative) callParams[2] = abi.encode(CurrencyLibrary.ADDRESS_ZERO, from);
address permit2 = IPermitForwarder(posm).permit2();
_safeResetAndApprove(Currency.unwrap(poolKey.currency0), posm, type(uint256).max, permit2);
_safeResetAndApprove(Currency.unwrap(poolKey.currency1), posm, type(uint256).max, permit2);
IPositionManager(posm).modifyLiquidities{ value: isNative ? amountAdded0 : 0 }(
abi.encode(actions, callParams), block.timestamp
);
_safeApprove(Currency.unwrap(poolKey.currency0), posm, 0, permit2);
_safeApprove(Currency.unwrap(poolKey.currency1), posm, 0, permit2);
tokenId = IPositionManager(posm).nextTokenId() - 1;
amountAdded0 = balanceBefore0 - poolKey.currency0.balanceOfSelf();
amountAdded1 = balanceBefore1 - poolKey.currency1.balanceOfSelf();
}
function _increase(
address posm,
PoolKey memory poolKey,
uint256 tokenId,
IncreaseLiquidityParams memory params,
address from
) internal returns (uint256 liquidity, uint256 amountAdded0, uint256 amountAdded1) {
amountAdded0 = poolKey.currency0.balanceOfSelf();
amountAdded1 = poolKey.currency1.balanceOfSelf();
(uint160 sqrtPriceX96,,,) = IPoolManager(IPositionManager(posm).poolManager()).getSlot0(poolKey.toId());
PositionInfo positionInfo = IPositionManager(posm).positionInfo(tokenId);
{
liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(positionInfo.tickLower()),
TickMath.getSqrtPriceAtTick(positionInfo.tickUpper()),
amountAdded0,
amountAdded1
);
require(liquidity >= params.minLiquidity, LiquiditySlippage());
}
bytes memory actions;
bytes[] memory callParams;
if (poolKey.currency0.isAddressZero()) {
// closes -> increase -> settle -> sweep
actions = abi.encodePacked(
uint8(Actions.INCREASE_LIQUIDITY),
uint8(Actions.CLOSE_CURRENCY),
uint8(Actions.CLOSE_CURRENCY),
uint8(Actions.SWEEP)
);
callParams = new bytes[](4);
callParams[1] = abi.encode(poolKey.currency0);
callParams[2] = abi.encode(poolKey.currency1);
callParams[3] = abi.encode(CurrencyLibrary.ADDRESS_ZERO, from);
} else {
// closes -> increase -> settle
actions = abi.encodePacked(
uint8(Actions.INCREASE_LIQUIDITY), uint8(Actions.CLOSE_CURRENCY), uint8(Actions.CLOSE_CURRENCY)
);
callParams = new bytes[](3);
callParams[1] = abi.encode(poolKey.currency0);
callParams[2] = abi.encode(poolKey.currency1);
}
callParams[0] = abi.encode(tokenId, liquidity, amountAdded0, amountAdded1, params.hookData);
uint256 value = poolKey.currency0.isAddressZero() ? poolKey.currency0.balanceOfSelf() : 0;
address permit2 = IPermitForwarder(posm).permit2();
_safeResetAndApprove(Currency.unwrap(poolKey.currency0), posm, type(uint256).max, permit2);
_safeResetAndApprove(Currency.unwrap(poolKey.currency1), posm, type(uint256).max, permit2);
IPositionManager(posm).modifyLiquidities{ value: value }(abi.encode(actions, callParams), block.timestamp);
_safeApprove(Currency.unwrap(poolKey.currency0), posm, 0, permit2);
_safeApprove(Currency.unwrap(poolKey.currency1), posm, 0, permit2);
(amountAdded0, amountAdded1) = LiquidityAmounts.getAmountsForLiquidity(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(positionInfo.tickLower()),
TickMath.getSqrtPriceAtTick(positionInfo.tickUpper()),
uint128(liquidity)
);
}
function _decreaseAndSwap(
address posm,
PoolKey memory poolKey,
DecreaseAndSwapParams memory params,
address from,
uint256 tokenId
) internal {
uint256 balanceDestBefore = params.swapDestToken.balanceOfSelf();
_collectPositionFees(
posm,
poolKey,
from,
tokenId,
params.decreaseParams.hookData,
params.performanceFeeX64,
params.decreaseParams.liquidity == 0 ? params.gasFeeX64 : 0
);
_decrease(posm, poolKey, params.decreaseParams, from, tokenId, params.protocolFeeX64, params.gasFeeX64);
_swaps(params.swapParams);
_sweep(params.swapDestToken, type(uint256).max, from);
if (poolKey.currency0.toId() != params.swapDestToken.toId()) _sweep(poolKey.currency0, type(uint256).max, from);
if (poolKey.currency1.toId() != params.swapDestToken.toId()) _sweep(poolKey.currency1, type(uint256).max, from);
emit DecreaseAndSwap(
posm,
tokenId,
params.decreaseParams.liquidity,
params.swapDestToken,
params.swapDestToken.balanceOfSelf() - balanceDestBefore
);
}
function _collectPositionFees(
address posm,
PoolKey memory poolKey,
address from,
uint256 tokenId,
bytes memory hookData,
uint64 performanceFeeX64,
uint64 gasFeeX64
) internal returns (uint256 amount0, uint256 amount1) {
amount0 = poolKey.currency0.balanceOfSelf();
amount1 = poolKey.currency1.balanceOfSelf();
bytes memory actions = abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR));
bytes[] memory callParams = new bytes[](2);
callParams[0] = abi.encode(tokenId, 0, 0, 0, hookData);
callParams[1] = abi.encode(poolKey.currency0, poolKey.currency1, address(this));
IPositionManager(posm).modifyLiquidities(abi.encode(actions, callParams), block.timestamp);
amount0 = poolKey.currency0.balanceOfSelf() - amount0;
amount1 = poolKey.currency1.balanceOfSelf() - amount1;
InputTokenParams[] memory tokensToTakeFees = new InputTokenParams[](2);
tokensToTakeFees[0] = InputTokenParams({ token: poolKey.currency0, amount: amount0 });
tokensToTakeFees[1] = InputTokenParams({ token: poolKey.currency1, amount: amount1 });
uint256[] memory performanceFeeAmountsTaken = _takeFees(
posm,
TakeFeesParams({
inputTokens: tokensToTakeFees,
feeX64: performanceFeeX64,
feeType: FeeType.PERFORMANCE_FEE,
tokenId: tokenId,
userAddress: from
})
);
uint256[] memory gassFeeAmountsTaken = _takeFees(
posm,
TakeFeesParams({
inputTokens: tokensToTakeFees, feeX64: gasFeeX64, feeType: FeeType.GAS_FEE, tokenId: tokenId, userAddress: from
})
);
amount0 -= (performanceFeeAmountsTaken[0] + gassFeeAmountsTaken[0]);
amount1 -= (performanceFeeAmountsTaken[1] + gassFeeAmountsTaken[1]);
}
function _decrease(
address posm,
PoolKey memory poolKey,
DecreaseLiquidityParams memory params,
address from,
uint256 tokenId,
uint64 protocolFeeX64,
uint64 gasFeeX64
) internal returns (uint256 amount0, uint256 amount1) {
if (params.liquidity == 0) return (amount0, amount1);
amount0 = poolKey.currency0.balanceOfSelf();
amount1 = poolKey.currency1.balanceOfSelf();
bytes memory actions = abi.encodePacked(uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR));
bytes[] memory callParams = new bytes[](2);
uint128 liquidity = IPositionManager(posm).getPositionLiquidity(tokenId);
if (params.liquidity < liquidity) liquidity = params.liquidity;
callParams[0] = abi.encode(tokenId, liquidity, params.amount0Min, params.amount1Min, params.hookData);
callParams[1] = abi.encode(poolKey.currency0, poolKey.currency1, address(this));
IPositionManager(posm).modifyLiquidities(abi.encode(actions, callParams), block.timestamp);
amount0 = poolKey.currency0.balanceOfSelf() - amount0;
amount1 = poolKey.currency1.balanceOfSelf() - amount1;
InputTokenParams[] memory tokensToTakeFee = new InputTokenParams[](2);
tokensToTakeFee[0] = InputTokenParams({ token: poolKey.currency0, amount: amount0 });
tokensToTakeFee[1] = InputTokenParams({ token: poolKey.currency1, amount: amount1 });
_takeFees(
posm,
TakeFeesParams({
inputTokens: tokensToTakeFee,
feeX64: protocolFeeX64,
feeType: FeeType.PROTOCOL_FEE,
tokenId: tokenId,
userAddress: from
})
);
_takeFees(
posm,
TakeFeesParams({
inputTokens: tokensToTakeFee, feeX64: gasFeeX64, feeType: FeeType.GAS_FEE, tokenId: tokenId, userAddress: from
})
);
}
function _adjustRange(
address posm,
PoolKey memory poolKey,
AdjustRangeParams memory adjustParams,
address from,
uint256 tokenId
) internal {
(uint256 collected0, uint256 collected1) = _collectPositionFees(
posm, poolKey, from, tokenId, adjustParams.collectFeesHookData, adjustParams.performanceFeeX64, 0
);
_decrease(
posm,
poolKey,
DecreaseLiquidityParams({
liquidity: type(uint128).max, deadline: block.timestamp, amount0Min: 0, amount1Min: 0, hookData: ""
}),
from,
tokenId,
adjustParams.protocolFeeX64,
adjustParams.gasFeeX64
);
_swaps(adjustParams.swapParams);
uint256 amountAdded0 = poolKey.currency0.balanceOfSelf();
uint256 amountAdded1 = poolKey.currency1.balanceOfSelf();
uint256 newTokenId;
uint256 liquidity;
if (!adjustParams.compoundFees) {
// Deduct collected fee in case we don't compound
amountAdded0 -= collected0;
amountAdded1 -= collected1;
}
(newTokenId, liquidity, amountAdded0, amountAdded1) =
_mint(posm, poolKey, adjustParams.mintParams, from, amountAdded0, amountAdded1);
_sweep(poolKey.currency0, type(uint256).max, from);
_sweep(poolKey.currency1, type(uint256).max, from);
emit AdjustRange(posm, tokenId, newTokenId, liquidity, amountAdded0, amountAdded1);
}
function _compoundFees(
address posm,
PoolKey memory poolKey,
CompoundFeesParams memory params,
address from,
uint256 tokenId
) internal {
_collectPositionFees(
posm, poolKey, from, tokenId, params.collectFeesHookData, params.performanceFeeX64, params.gasFeeX64
);
_swaps(params.swapParams);
uint256 amount0Added = poolKey.currency0.balanceOfSelf();
uint256 amount1Added = poolKey.currency1.balanceOfSelf();
_increase(posm, poolKey, tokenId, params.increaseParams, from);
amount0Added -= poolKey.currency0.balanceOfSelf();
amount1Added -= poolKey.currency1.balanceOfSelf();
_sweep(poolKey.currency0, type(uint256).max, from);
_sweep(poolKey.currency1, type(uint256).max, from);
emit CompoundFees(posm, tokenId, IPositionManager(posm).getPositionLiquidity(tokenId), amount0Added, amount1Added);
}
function _takeFees(address posm, TakeFeesParams memory params) internal returns (uint256[] memory feeAmountsTaken) {
// to save gas, we always need to check if fee exists before deductFees
feeAmountsTaken = new uint256[](params.inputTokens.length);
if (params.feeX64 == 0) return feeAmountsTaken;
SharedStorage.Storage storage ss = SharedStorage.getStorage();
address feeTaker = ss.feeTaker;
require(params.feeX64 <= ss.maxFeeX64[uint8(params.feeType)], TooMuchFee());
uint256 inputTokensLength = params.inputTokens.length;
InputTokenParams[] memory feeTokens = new InputTokenParams[](inputTokensLength);
for (uint256 i; i < inputTokensLength;) {
InputTokenParams memory inputToken = params.inputTokens[i];
if (inputToken.amount > 0) {
feeTokens[i].amount = FullMath.mulDiv(inputToken.amount, params.feeX64, Q64);
feeTokens[i].token = inputToken.token;
feeAmountsTaken[i] = feeTokens[i].amount;
if (feeTokens[i].amount > 0) inputToken.token.transfer(feeTaker, feeTokens[i].amount);
}
unchecked {
i++;
}
}
emit TakeFees(posm, params.tokenId, params.userAddress, TakeFeesEventData(feeTokens, params.feeX64, params.feeType));
}
/// @dev some tokens require allowance == 0 to approve new amount
/// but some tokens does not allow approve amount = 0
/// we try to set allowance = 0 before approve new amount. if it revert means that
/// the token not allow to approve 0, which means the following line code will work properly
function _safeResetAndApprove(address token, address _spender, uint256 _value, address _permit2) internal {
if (token != address(0) && _value > 0) {
/// @dev omitted approve(0) result because it might fail and does not break the flow
if (_permit2 != address(0)) {
token.call(abi.encodeWithSelector(IERC20.approve.selector, address(_permit2), 0));
IAllowanceTransfer(_permit2).approve(token, _spender, 0, 0);
} else {
token.call(abi.encodeWithSelector(IERC20.approve.selector, _spender, 0));
}
/// @dev value for approval after reset must greater than 0
_safeApprove(token, _spender, _value, _permit2);
}
}
function _safeApprove(address token, address _spender, uint256 _value, address _permit2) internal {
if (token != address(0)) {
bool success;
bytes memory returnData;
if (_permit2 != address(0)) {
(success, returnData) = token.call(abi.encodeWithSelector(IERC20.approve.selector, address(_permit2), _value));
IAllowanceTransfer(_permit2).approve(token, _spender, uint160(_value), 0);
} else {
(success, returnData) = token.call(abi.encodeWithSelector(IERC20.approve.selector, _spender, _value));
}
if (_value == 0) {
// some token does not allow approve(0) so we skip check for this case
return;
}
require(success && (returnData.length == 0 || abi.decode(returnData, (bool))), "SafeERC20: approve failed");
}
}
function _isApprovedOrOwner(address posm, address spender, uint256 tokenId) internal view returns (bool) {
address owner = IERC721(posm).ownerOf(tokenId);
return
spender == owner || IERC721(posm).getApproved(tokenId) == spender
|| IERC721(posm).isApprovedForAll(owner, spender);
}
receive() external payable { }
}[
{
"name": "AmountError",
"type": "error",
"inputs": []
},
{
"name": "LiquiditySlippage",
"type": "error",
"inputs": []
},
{
"name": "NotSupportedAction",
"type": "error",
"inputs": []
},
{
"name": "SafeERC20FailedOperation",
"type": "error",
"inputs": [
{
"name": "token",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "SameToken",
"type": "error",
"inputs": []
},
{
"name": "SelfSend",
"type": "error",
"inputs": []
},
{
"name": "SwapFailed",
"type": "error",
"inputs": [
{
"name": "swapData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "index",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "SwapSlippage",
"type": "error",
"inputs": []
},
{
"name": "TooMuchFee",
"type": "error",
"inputs": []
},
{
"name": "TransferError",
"type": "error",
"inputs": []
},
{
"name": "Unauthorized",
"type": "error",
"inputs": []
},
{
"name": "ZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "AdjustRange",
"type": "event",
"inputs": [
{
"name": "posm",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "newTokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "newLiquidity",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "token0Added",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "token1Added",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "CompoundFees",
"type": "event",
"inputs": [
{
"name": "posm",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "liquidity",
"type": "uint128",
"indexed": false,
"internalType": "uint128"
},
{
"name": "amount0",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount1",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "DecreaseAndSwap",
"type": "event",
"inputs": [
{
"name": "posm",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "liquidity",
"type": "uint128",
"indexed": false,
"internalType": "uint128"
},
{
"name": "token",
"type": "address",
"indexed": false,
"internalType": "Currency"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "Swap",
"type": "event",
"inputs": [
{
"name": "tokenIn",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amountOut",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "SwapAndIncrease",
"type": "event",
"inputs": [
{
"name": "posm",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "liquidity",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount0",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount1",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "SwapAndMint",
"type": "event",
"inputs": [
{
"name": "posm",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "liquidity",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount0",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount1",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "TakeFees",
"type": "event",
"inputs": [
{
"name": "posm",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "userAddress",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "data",
"type": "tuple",
"indexed": false,
"components": [
{
"name": "feeTokens",
"type": "tuple[]",
"components": [
{
"name": "token",
"type": "address",
"internalType": "Currency"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IV4Utils.InputTokenParams[]"
},
{
"name": "feeX64",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "feeType",
"type": "uint8",
"internalType": "enum FeeType"
}
],
"internalType": "struct IV4Utils.TakeFeesEventData"
}
],
"anonymous": false
},
{
"name": "execute",
"type": "function",
"inputs": [
{
"name": "posm",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "instructions",
"type": "tuple",
"components": [
{
"name": "action",
"type": "uint8",
"internalType": "enum IV4Utils.UtilActions"
},
{
"name": "params",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct IV4Utils.Instructions"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "onERC721Received",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "from",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "data",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "nonpayable"
},
{
"name": "swapAndIncrease",
"type": "function",
"inputs": [
{
"name": "params",
"type": "tuple",
"components": [
{
"name": "posm",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "increaseParams",
"type": "tuple",
"components": [
{
"name": "minLiquidity",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "hookData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "deadline",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IV4Utils.IncreaseLiquidityParams"
},
{
"name": "swapParams",
"type": "tuple[]",
"components": [
{
"name": "tokenIn",
"type": "address",
"internalType": "Currency"
},
{
"name": "amountIn",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "tokenOut",
"type": "address",
"internalType": "Currency"
},
{
"name": "amountOutMin",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "swapData",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct IV4Utils.SwapParams[]"
},
{
"name": "inputTokens",
"type": "tuple[]",
"components": [
{
"name": "token",
"type": "address",
"internalType": "Currency"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IV4Utils.InputTokenParams[]"
},
{
"name": "sweepTokens",
"type": "address[]",
"internalType": "Currency[]"
},
{
"name": "protocolFeeX64",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "performanceFeeX64",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "gasFeeX64",
"type": "uint64",
"internalType": "uint64"
}
],
"internalType": "struct IV4Utils.SwapAndIncreaseParams"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "swapAndMint",
"type": "function",
"inputs": [
{
"name": "params",
"type": "tuple",
"components": [
{
"name": "posm",
"type": "address",
"internalType": "address"
},
{
"name": "poolKey",
"type": "tuple",
"components": [
{
"name": "currency0",
"type": "address",
"internalType": "Currency"
},
{
"name": "currency1",
"type": "address",
"internalType": "Currency"
},
{
"name": "fee",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "tickSpacing",
"type": "int24",
"internalType": "int24"
},
{
"name": "hooks",
"type": "address",
"internalType": "contract IHooks"
}
],
"internalType": "struct PoolKey"
},
{
"name": "mintParams",
"type": "tuple",
"components": [
{
"name": "tickLower",
"type": "int24",
"internalType": "int24"
},
{
"name": "tickUpper",
"type": "int24",
"internalType": "int24"
},
{
"name": "minLiquidity",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "hookData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "deadline",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IV4Utils.MintParams"
},
{
"name": "swapParams",
"type": "tuple[]",
"components": [
{
"name": "tokenIn",
"type": "address",
"internalType": "Currency"
},
{
"name": "amountIn",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "tokenOut",
"type": "address",
"internalType": "Currency"
},
{
"name": "amountOutMin",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "swapData",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct IV4Utils.SwapParams[]"
},
{
"name": "inputTokens",
"type": "tuple[]",
"components": [
{
"name": "token",
"type": "address",
"internalType": "Currency"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IV4Utils.InputTokenParams[]"
},
{
"name": "sweepTokens",
"type": "address[]",
"internalType": "Currency[]"
},
{
"name": "protocolFeeX64",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "performanceFeeX64",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "gasFeeX64",
"type": "uint64",
"internalType": "uint64"
}
],
"internalType": "struct IV4Utils.SwapAndMintParams"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"type": "receive",
"stateMutability": "payable"
}
]0x60a080604052600436101561001c575b50361561001a575f80fd5b005b5f905f3560e01c9081630deff71c1461202057508063150b7a0214610c4a5780638c6d942614610ac35763c29f0a8d0361000f576020600319360112610ac05767ffffffffffffffff60043511610ac05760043536036101a0600319820112610abc5761008d6004356004016125a3565b90610104600435016100ad6100a7826004356004016125b7565b90612c13565b6100bc816004356004016125b7565b91906100cd6101446004350161260b565b604051917f75794a3c0000000000000000000000000000000000000000000000000000000083526020836004816001600160a01b038b165afa928315610ab1578893610a6f575b509167ffffffffffffffff61016b959261013f61015f956040519561013887612620565b36916126cb565b845216602083015260026040830152606082015233608082015285612e53565b506004356004016125b7565b61017a6101846004350161260b565b91604051907f75794a3c0000000000000000000000000000000000000000000000000000000082526020826004816001600160a01b038a165afa918215610896578792610a39575b50610200936101e167ffffffffffffffff926040519561013887612620565b8452166020830152856040830152606082015233608082015283612e53565b5061022761022261021b60e460043501600435600401612778565b369161282e565b613172565b60c460043501357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5d8201811215610a35577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc60a091600435019201126108c5576040519161029483612620565b6102a260246004350161258f565b83526102b260446004350161258f565b60208401526064600435013562ffffff811681036109085760408401526102dd608460043501612b8e565b606084015260a460043501356001600160a01b038116810361090857608084015260a06003198336030112610a35576040519261031984612620565b61032560048401612b8e565b845261033360248401612b8e565b602085015260408401926044810135845260648101359067ffffffffffffffff82116108b65761036b608492600436918401016127e8565b60608701520135608085015261038a6001600160a01b038251166140bf565b936103a16001600160a01b036020840151166140bf565b90855f19115f14610a2e57855b825f19115f14610a265782905b6040517fdc4c90d3000000000000000000000000000000000000000000000000000000008152896020826004816001600160a01b038c165afa8015610a19578361042786926fffffffffffffffffffffffffffffffff9561044d95916109ea575b5060a08b2090614149565b505050610437885160020b614215565b61044760208a015160020b614215565b91614568565b16965187106109c25788918791608061059e886104f56001600160a01b038251161598895f14610957576040517f020000000000000000000000000000000000000000000000000000000000000060208201527f0d0000000000000000000000000000000000000000000000000000000000000060218201527f1400000000000000000000000000000000000000000000000000000000000000602282015261050381602381015b03601f198101835282612690565b9661050c613512565b985b6060830151926020815160020b91015160020b906001600160a01b036040519988828c9a511660208b01528260208201511660408b015262ffffff60408201511660608b0152606081015160020b828b015201511660a088015260c087015260e08601526101008501528661012085015261014084015233610160840152610180808401526101a0830190612b69565b6105a784612e32565b526105b183612e32565b506001600160a01b038751166001600160a01b036020890151166040519160208301526040820152604081526105e8606082612690565b6105f184612e02565b52846105fc84612e02565b5061091f575b604051947f12261ee70000000000000000000000000000000000000000000000000000000086526020866004816001600160a01b038d165afa9586156109145785966108d4575b5061065f868a6001600160a01b038b5116614620565b610677868a6001600160a01b0360208c015116614620565b156108c9576106a290915b61069460405194859260208401613586565b03601f198101845283612690565b6001600160a01b0387163b156108c5576106ee91839160405180809581947fdd46508f0000000000000000000000000000000000000000000000000000000083524290600484016135f8565b03916001600160a01b038b165af180156108ba576108a1575b50508061072261073692866001600160a01b0387511661481c565b846001600160a01b0360208601511661481c565b604051947f75794a3c0000000000000000000000000000000000000000000000000000000086526020866004816001600160a01b0388165afa95861561089657879661085e575b505f198601958611610831576001600160a01b03926107e07f12ee145ee75f0b2423e2fcc34842c9de2cf72f8583360edea90d82a695b2c5a995936107d18660206107d761082b976107d1848951166140bf565b90613ce9565b950151166140bf565b9061080c6108076107fc61012460043501600435600401612778565b9190339236916129a5565b613c94565b6040519485941696846040919493926060820195825260208201520152565b0390a380f35b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9095506020813d60201161088e575b8161087a60209383612690565b8101031261088a5751945f61077d565b5f80fd5b3d915061086d565b6040513d89823e3d90fd5b816108ab91612690565b6108b657865f610707565b8680fd5b6040513d84823e3d90fd5b8280fd5b506106a28391610682565b9095506020813d60201161090c575b816108f060209383612690565b8101031261090857610901906128fd565b945f610649565b8480fd5b3d91506108e3565b6040513d87823e3d90fd5b604080515f6020820152339181019190915261093e81606081016104f5565b61094784612e12565b5261095183612e12565b50610602565b6040517f020000000000000000000000000000000000000000000000000000000000000060208201527f0d0000000000000000000000000000000000000000000000000000000000000060218201526109b381602281016104f5565b966109bc61354c565b9861050e565b6004897fbb0fd130000000000000000000000000000000000000000000000000000000008152fd5b610a0c915060203d602011610a12575b610a048183612690565b8101906134f3565b5f61041c565b503d6109fa565b50604051903d90823e3d90fd5b5f19906103bb565b5f196103ae565b8380fd5b9091506020813d602011610a67575b81610a5560209383612690565b8101031261088a5751906102006101c2565b3d9150610a48565b949192506020853d602011610aa9575b81610a8c60209383612690565b8101031261088a579351909390919067ffffffffffffffff610114565b3d9150610a7f565b6040513d8a823e3d90fd5b5080fd5b80fd5b5034610ac0576060600319360112610ac05780610ade612579565b60443567ffffffffffffffff8111610c3d57803603916040600319840112610c45576001600160a01b0316906040519260208085015281600401356003811015610c4157610b2b81612741565b60408501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd6024830135910181121561090857016024600482013591019067ffffffffffffffff81116109085780360382136109085783601f19601f8360a094610bbb96604060608701528160808701528686013788858286010152011681010301601f198101845283612690565b803b15610c3d57610c1b83929183926040519485809481937fb88d4fde0000000000000000000000000000000000000000000000000000000083523360048401523060248401526024356044840152608060648401526084830190612b69565b03925af180156108ba57610c2c5750f35b81610c3691612690565b610ac05780f35b5050fd5b8580fd5b505050fd5b503461088a57608060031936011261088a57610c64612579565b506024356001600160a01b03811680820361088a5760643567ffffffffffffffff811161088a573660238201121561088a57806004013567ffffffffffffffff811161088a578101602481019136831161088a57308414611ff85760208183031261088a57602481013567ffffffffffffffff811161088a5701906040908290031261088a5760405191610cf783612658565b6024820135600381101561088a57835260448201359167ffffffffffffffff831161088a57610d2992016024016127e8565b90602081019182525190610d3c82612741565b5191604051917f7ba03aad000000000000000000000000000000000000000000000000000000008352604435600484015260c083602481335afa928315611c91575f93611fc6575b506001600160a01b038351166001600160a01b0360208501511614611f9e57610dac81612741565b6001810361110157505081518201906020820192602081840312610c415760208101519067ffffffffffffffff82116108b657019260c084840312610c415760405192610df884612674565b602085015167ffffffffffffffff81116110fd578501602081019160a0919003126108b65760405190610e2a82612620565b610e3381612b4c565b825260208101516020830152604081015160408301526060810151606083015260808101519067ffffffffffffffff82116110f957610e74918491016129f9565b60808201528352604084015167ffffffffffffffff81116108b657840160200190610e9e91612a3f565b6020830152610eaf606084016128fd565b604083018181529390610ec460808301612b2a565b6060850152610ed560a08301612b2a565b916080850192835260c001610ee990612b2a565b60a08501526001600160a01b0316610f00906140bf565b928051906080820151925167ffffffffffffffff1691516fffffffffffffffffffffffffffffffff16155f146020610ff795610f726001600160a01b03966fffffffffffffffffffffffffffffffff9688956110f25767ffffffffffffffff60a088015116915b6044358e8633614bdc565b5050610fa4845167ffffffffffffffff60608701511667ffffffffffffffff60a088015116918d604435918633614f0e565b5050610fb282850151613172565b610fbf8a848b5116614baa565b828151168a848b511682036110e2575b5050015116878488511682036110d2575b505051511693511691610ff2836140bf565b613ce9565b9060405192835260208301526040820152604435907f996e6f7b625f447186ad5bd062f3c5b818c8dae1e686b41eb29a63fb9180ab7060603392a35b81333b15610ac0576040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0392909216602483015260448035908301528160648183335af180156108ba576110bd575b60206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b6110c8828092612690565b610ac05780611093565b6110db91614baa565b5f87610fe0565b6110eb91614baa565b5f8a610fcf565b8d91610f67565b8880fd5b8780fd5b90929061110d81612741565b80611cc957508051810192602084019160208186031261088a5760208101519067ffffffffffffffff821161088a570160e08186031261088a576040519460e0860186811067ffffffffffffffff821117611c9c57604052602082015167ffffffffffffffff811161088a57846020611188928501016129f9565b8652604082015167ffffffffffffffff811161088a578460206111ad92850101612a3f565b9360208701948552606083015167ffffffffffffffff811161088a578301602081019260a09190031261088a57604051916111e783612620565b6111f081612911565b83526111fe60208201612911565b602084015260408101516040840152606081015167ffffffffffffffff811161088a5760809261122f9183016129f9565b6060840152015160808201526040860190815261124e60808301612b2a565b916060870192835261126260a08201612b2a565b9485608089015261128860e061127a60c08501612b2a565b9360a08b0194855201612b3f565b9760c08101988952516112a46001600160a01b038951166140bf565b6112ba6001600160a01b0360208b0151166140bf565b916113cf60405161131c816104f5602082016002907f010000000000000000000000000000000000000000000000000000000000000081527f110000000000000000000000000000000000000000000000000000000000000060018201520190565b6104f561135b61132a61354c565b9460405192839160443560208401525f60408401525f60608401525f608084015260a08084015260c0830190612b69565b61136484612e32565b5261136e83612e32565b508b516020808e0151604080516001600160a01b03948516938101939093529216918101919091523060608201526113a981608081016104f5565b6113b284612e02565b526113bc83612e02565b506104f560405193849260208401613586565b333b1561088a575f61141091604051809381927fdd46508f0000000000000000000000000000000000000000000000000000000083524290600484016135f8565b038183335af18015611c9157611c79575b5090889182516001600160a01b0316611439906140bf565b9061144391613ce9565b9060208301516001600160a01b031661145b906140bf565b9061146591613ce9565b9061146e612db6565b9883516001600160a01b03166040519061148782612658565b81528260208201526114988b612e32565b526114a28a612e32565b5060208401516001600160a01b0316604051906114be82612658565b81528360208201526114cf8b612e02565b526114d98a612e02565b50604051906114e782612620565b8a825267ffffffffffffffff166020820152600160408201526044356060820152608081018990526115199033612e53565b986040519061152782612620565b81528d60208201528d60408201526044356060820152886080820152339061154e91612e53565b906115588a612e32565b5161156283612e32565b5161156c91612bd9565b61157591613ce9565b9861157f90612e02565b519061158a90612e02565b5161159491612bd9565b61159d91613ce9565b92604051906115ab82612620565b6fffffffffffffffffffffffffffffffff82524260208301528c60408301528c60608301526020968d604051906115e28a83612690565b815260808401525167ffffffffffffffff16905167ffffffffffffffff16918c60443591339561161196614f0e565b50505161161d90613172565b85516001600160a01b0316611631906140bf565b9460208701516001600160a01b0316611649906140bf565b975115611c5c575b505051916116686001600160a01b038651166140bf565b9561167f6001600160a01b036020880151166140bf565b9487811115611c56575086905b85811115611c515750845b6040517fdc4c90d30000000000000000000000000000000000000000000000000000000081528481600481335afa8015611c46576116e1918c91611c29575b5060a0892090614149565b505050916fffffffffffffffffffffffffffffffff61171a83836117088a5160020b614215565b96898b0197610447895160020b614215565b169560408101518710611c01579088611863876104f58f9796956001600160a01b0385511615998a5f14611b95576040516117cb816104f58782016003907f020000000000000000000000000000000000000000000000000000000000000081527f0d0000000000000000000000000000000000000000000000000000000000000060018201527f140000000000000000000000000000000000000000000000000000000000000060028201520190565b976117d4613512565b995b6060840151935160020b905160020b906001600160a01b0360806040519a8b9983825116908b01528260208201511660408b015262ffffff60408201511660608b0152606081015160020b828b015201511660a088015260c087015260e08601528d61010086015287610120860152610140850152610160840152610180808401526101a0830190612b69565b61186c84612e32565b5261187683612e32565b506001600160a01b038951166001600160a01b0360208b01511660405191888301526040820152604081526118ac606082612690565b6118b584612e02565b52846118c084612e02565b50611b54575b604051947f12261ee70000000000000000000000000000000000000000000000000000000086528686600481335afa958615610914578596611b1d575b5061191986336001600160a01b038d5116614620565b611932868b6001600160a01b0360203392015116614620565b15611b125761194e90915b610694604051948592898401613586565b333b156108c55761199191839160405180809581947fdd46508f0000000000000000000000000000000000000000000000000000000083524290600484016135f8565b0391335af180156108ba57611afd575b5050806119bc6119d092336001600160a01b0389511661481c565b336001600160a01b0360208801511661481c565b604051947f75794a3c0000000000000000000000000000000000000000000000000000000086528186600481335afa958615610ab1578896611ace575b505f198601958611611aa157866001600160a01b036020611a49611a3a611a5f956107d1858c51166140bf565b976107d184848c0151166140bf565b97611a578484835116614baa565b015116614baa565b60405194855284015260408301526060820152604435907fa6b100d712e4bb5aacbc88f520305e93dc6834efba307027c822bea5baf127ac60803392a3611033565b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9095508181813d8311611af6575b611ae68183612690565b8101031261088a5751945f611a0d565b503d611adc565b81611b0791612690565b6110fd57875f6119a1565b5061194e839161193d565b9095508681813d8311611b4d575b611b358183612690565b8101031261090857611b46906128fd565b945f611903565b503d611b2b565b604080515f8882019081526001600160a01b038e1660208201529091611b7c918391016104f5565b611b8584612e12565b52611b8f83612e12565b506118c6565b604051611bf2816104f58782016002907f020000000000000000000000000000000000000000000000000000000000000081527f0d0000000000000000000000000000000000000000000000000000000000000060018201520190565b97611bfb61354c565b996117d6565b60048c7fbb0fd130000000000000000000000000000000000000000000000000000000008152fd5b611c409150863d8811610a1257610a048183612690565b5f6116d6565b6040513d8d823e3d90fd5b611697565b9061168c565b611c6b90611c71939896613ce9565b94613ce9565b945f80611651565b8992919c505f611c8891612690565b5f9b9091611421565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6002919350611cd781612741565b03611f765781518201602081019260208183031261088a5760208101519067ffffffffffffffff821161088a570160c08183031261088a5760405193611d1c85612674565b602082015167ffffffffffffffff811161088a57816020611d3f928501016129f9565b8552604082015167ffffffffffffffff811161088a57816020611d6492850101612a3f565b9160208601928352606081015167ffffffffffffffff811161088a578101602081019460609190031261088a5760405195611d9e8761263c565b8451875260208501519267ffffffffffffffff841161088a5785611dcc604092611e3096611e3899016129f9565b60208a01520151604088015260408101968752611deb60808301612b2a565b606082015267ffffffffffffffff80611e1860c0611e0b60a08701612b2a565b9586608087015201612b2a565b928360a0820152519216921690604435898833614bdc565b505051613172565b611ea2836001600160a01b036020611e94611e85611e58848851166140bf565b611e7686611e6a87878c0151166140bf565b9a516044358b33613614565b505087516107d19086166140bf565b966107d18484890151166140bf565b94611a578484835116614baa565b604051917f1efeed330000000000000000000000000000000000000000000000000000000083526044356004840152602083602481335afa928315611c91575f93611f3a575b506fffffffffffffffffffffffffffffffff6040519316835260208301526040820152604435907f4a3d983c4891bedb0622c5a26f90b626e9451fe3a832b73ed9c886f1aa489cda60603392a3611033565b9092506020813d602011611f6e575b81611f5660209383612690565b8101031261088a57611f6790612b4c565b915f611ee8565b3d9150611f49565b7fe83deb0f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f201b580a000000000000000000000000000000000000000000000000000000005f5260045ffd5b611fe991935060c03d60c011611ff1575b611fe18183612690565b81019061291f565b50915f610d84565b503d611fd7565b7f27977151000000000000000000000000000000000000000000000000000000005f5260045ffd5b602060031936011261088a576004359067ffffffffffffffff821161088a57816004018236039061012060031983011261088a5761205d816125a3565b936024810135946001600160a01b038116947f6352211e000000000000000000000000000000000000000000000000000000008152866004820152602081602481895afa8015611c91575f90612539575b6001600160a01b039150168033149081156124a8575b811561241c575b50156123f457608482016120e26100a782866125b7565b6120ec81856125b7565b91906120fa60c4860161260b565b604051917f75794a3c0000000000000000000000000000000000000000000000000000000083526020836004818d5afa928315611c91575f936123b2575b509167ffffffffffffffff612183959261215c61217c956040519561013887612620565b845216602083015260026040830152606082015233608082015284612e53565b50846125b7565b612190610104850161260b565b91604051907f75794a3c0000000000000000000000000000000000000000000000000000000082526020826004818c5afa918215611c91575f9261237c575b5061220d936121ee67ffffffffffffffff926040519561013887612620565b84521660208301525f6040830152606082015233608082015282612e53565b5061222161022261021b6064850186612778565b604051937f7ba03aad00000000000000000000000000000000000000000000000000000000855286600486015260c085602481895afa948515611c91575f9561235a575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9d6044840135910181121561088a5782016060600319823603011261088a57604051906122b28261263c565b6004810135825260248101359567ffffffffffffffff871161088a57612328612355946107fc948b610807946044612338976123157f843904b5529aa2c8fb61db0b113d6ae5e139f3dc8b0810fd1046d81294eab3809e600436918401016127e8565b6020860152013560408401523393613614565b93909660a4608051970190612778565b604051938493846040919493926060820195825260208201520152565b0390a3005b61237491955060c03d60c011611ff157611fe18183612690565b509387612265565b9091506020813d6020116123aa575b8161239860209383612690565b8101031261088a57519061220d6121cf565b3d915061238b565b949192506020853d6020116123ec575b816123cf60209383612690565b8101031261088a579351909390919067ffffffffffffffff612138565b3d91506123c2565b7f82b42900000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050604051907fe985e9c50000000000000000000000000000000000000000000000000000000082526004820152336024820152602081604481895afa908115611c91575f9161246e575b50876120cb565b90506020813d6020116124a0575b8161248960209383612690565b8101031261088a5761249a90612b3f565b87612467565b3d915061247c565b90506040517f081812fc0000000000000000000000000000000000000000000000000000000081528760048201526020816024818a5afa908115611c91575f916124ff575b506001600160a01b03163314906120c4565b90506020813d602011612531575b8161251a60209383612690565b8101031261088a5761252b906128fd565b886124ed565b3d915061250d565b506020813d602011612571575b8161255360209383612690565b8101031261088a5761256c6001600160a01b03916128fd565b6120ae565b3d9150612546565b600435906001600160a01b038216820361088a57565b35906001600160a01b038216820361088a57565b356001600160a01b038116810361088a5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561088a570180359067ffffffffffffffff821161088a57602001918160061b3603831361088a57565b3567ffffffffffffffff8116810361088a5790565b60a0810190811067ffffffffffffffff821117611c9c57604052565b6060810190811067ffffffffffffffff821117611c9c57604052565b6040810190811067ffffffffffffffff821117611c9c57604052565b60c0810190811067ffffffffffffffff821117611c9c57604052565b90601f601f19910116810190811067ffffffffffffffff821117611c9c57604052565b67ffffffffffffffff8111611c9c5760051b60200190565b9291926126d7826126b3565b936126e56040519586612690565b602085848152019260061b82019181831161088a57925b8284106127095750505050565b60408483031261088a576020604091825161272381612658565b61272c8761258f565b815282870135838201528152019301926126fc565b6003111561274b57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561088a570180359067ffffffffffffffff821161088a57602001918160051b3603831361088a57565b67ffffffffffffffff8111611c9c57601f01601f191660200190565b81601f8201121561088a578035906127ff826127cc565b9261280d6040519485612690565b8284526020838301011161088a57815f926020809301838601378301015290565b9291909261283b846126b3565b936128496040519586612690565b602085828152019060051b82019183831161088a5780915b83831061286f575050505050565b823567ffffffffffffffff811161088a57820160a08187031261088a576040519161289983612620565b6128a28261258f565b8352602082013560208401526128ba6040830161258f565b60408401526060820135606084015260808201359267ffffffffffffffff841161088a576128ed886020958695016127e8565b6080820152815201920191612861565b51906001600160a01b038216820361088a57565b51908160020b820361088a57565b8092910360c0811261088a5760a01361088a5760405161293e81612620565b612947836128fd565b8152612955602084016128fd565b6020820152604083015162ffffff8116810361088a57604082015261297c60608401612911565b60608201526080830151906001600160a01b038216820361088a5760a091608082015292015190565b9291906129b1816126b3565b936129bf6040519586612690565b602085838152019160051b810192831161088a57905b8282106129e157505050565b602080916129ee8461258f565b8152019101906129d5565b81601f8201121561088a57805190612a10826127cc565b92612a1e6040519485612690565b8284526020838301011161088a57815f9260208093018386015e8301015290565b9080601f8301121561088a57815191612a57836126b3565b92612a656040519485612690565b80845260208085019160051b8301019183831161088a5760208101915b838310612a9157505050505090565b825167ffffffffffffffff811161088a5782019060a0601f19838803011261088a5760405190612ac082612620565b612acc602084016128fd565b825260408301516020830152612ae4606084016128fd565b60408301526080830151606083015260a08301519167ffffffffffffffff831161088a57612b1a886020809695819601016129f9565b6080820152815201920191612a82565b519067ffffffffffffffff8216820361088a57565b5190811515820361088a57565b51906fffffffffffffffffffffffffffffffff8216820361088a57565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b35908160020b820361088a57565b9190811015612bac5760061b0190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91908201809211612be657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b905f915f915b808310612c53575050503410612c2b57565b7f4ff64a9f000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091926001600160a01b03612c71612c6c868587612b9c565b6125a3565b16612c9b57612c916001916020612c89878688612b9c565b013590612bd9565b935b019190612c19565b92612caa612c6c828486612b9c565b6020612cb7838587612b9c565b013590612cc43082615313565b6040517f23b872dd000000000000000000000000000000000000000000000000000000006020828101918252336024840152306044840152606483018690529293926001600160a01b0384169290915f91612d2281608481016104f5565b519082855af115611c91575f513d612dad5750803b155b612d82575090610ff2612d4d923090615313565b03612d5a57600190612c93565b7f4ffddc7c000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b60011415612d39565b60405160609190612dc78382612690565b6002815291601f1901825f5b828110612ddf57505050565b602090604051612dee81612658565b5f81525f8382015282828501015201612dd3565b805160011015612bac5760400190565b805160021015612bac5760600190565b805160031015612bac5760800190565b805115612bac5760200190565b8051821015612bac5760209160051b010190565b919080515190601f19612e7e612e68846126b3565b93612e766040519586612690565b8085526126b3565b013660208401378193602082019267ffffffffffffffff84511690811561316a57506001600160a01b037f8a22373512790c48b83a1fe1c0048e02673acc5986e87a2b0a6a8dd60bb3b4c6541691604084019160ff8351612ede81612741565b612ee781612741565b165f527f8a22373512790c48b83a1fe1c0048e02673acc5986e87a2b0a6a8dd60bb3b4ca60205267ffffffffffffffff60405f2054161061314257835151612f2e816126b3565b93612f3c6040519586612690565b818552601f19612f4b836126b3565b015f5b81811061311f5750505f5b8281106130625750505067ffffffffffffffff6001600160a01b0360806060870151960151169551169151612f8d81612741565b60405193612f9a8561263c565b8452602084019283526040840190612fb181612741565b8152604051926020845260808401945194606060208601528551809152602060a086019601905f5b8181106130375750505083927f5ace6df0caae4da0c692ffe53a9d07a9ce50a3cedca8a3275522166d3d96e9e8949267ffffffffffffffff6001600160a01b0393511660408501525161302b81612741565b606084015216930390a4565b825180516001600160a01b03168952602090810151818a015260409098019790920191600101612fd9565b806130706001928951612e3f565b51602081015180613084575b505001612f59565b61309a9067ffffffffffffffff8c511690613cf6565b60206130a6848b612e3f565b5101526001600160a01b038151166130be838a612e3f565b51528b6130da8360206130d1828d612e3f565b51015192612e3f565b5260206130e7838a612e3f565b5101516130f5575b8061307c565b6001600160a01b03613119915116846020613110858c612e3f565b51015191613ebe565b5f6130ef565b60209060405161312e81612658565b5f81525f8382015282828a01015201612f4e565b7f9a334005000000000000000000000000000000000000000000000000000000005f5260045ffd5b955050505050565b905f5b82518110156134ee576001600160a01b036131908285612e3f565b515116906001600160a01b0360406131a88387612e3f565b5101511660206131b88387612e3f565b5101519260606131c88488612e3f565b5101519360806131d88589612e3f565b510151815f80916001600160a01b037f8a22373512790c48b83a1fe1c0048e02673acc5986e87a2b0a6a8dd60bb3b4c85416936001600160a01b037f8a22373512790c48b83a1fe1c0048e02673acc5986e87a2b0a6a8dd60bb3b4c754169087159081806134e5575b1561330957505050505050803b1561088a575f82916004604051809481937fd0e30db00000000000000000000000000000000000000000000000000000000083525af18015611c91576132f9575b5080945b85106132d15760407ffa2dda1cc1b86e41239702756b13effbc1a092b5c57e3ad320fbe4f3b13fe2359160019682519182526020820152a301613175565b7fe378141d000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f61330391612690565b5f61328f565b868914806134dd575b1561337e57505050505050803b1561088a575f80916024604051809481937f2e1a7d4d0000000000000000000000000000000000000000000000000000000083528760048401525af18015611c915761336e575b508094613293565b5f61337891612690565b5f613366565b809192939497959b965051613398575b5050505050613293565b91939550919398506133a9866140bf565b946133b3886140bf565b94156134d6575b5f80918185831461345f57505b8351906020850190875af16133da614090565b5015613424575090613409929115613414575b506134036133fa856140bf565b611c6b876140bf565b92613ce9565b945f8080808061338e565b61341e90856147a6565b5f6133ed565b879061345b6040519283927fa9901528000000000000000000000000000000000000000000000000000000008452600484016135f8565b0390fd5b908061346c575b506133c7565b826134d09181808d8b8260405160208101927f095ea7b30000000000000000000000000000000000000000000000000000000084526024820152816044820152604481526134bb606482612690565b51925af1506134c8614090565b50888c614963565b5f613466565b50846133ba565b508915613312565b50868a14613241565b509050565b9081602091031261088a57516001600160a01b038116810361088a5790565b604051608091906135238382612690565b6003815291601f1901825f5b82811061353b57505050565b80606060208093850101520161352f565b6040516060919061355d8382612690565b6002815291601f1901825f5b82811061357557505050565b806060602080938501015201613569565b9061359990604083526040830190612b69565b906020818303910152815180825260208201916020808360051b8301019401925f915b8383106135cb57505050505090565b90919293946020806135e983601f1986600196030187528951612b69565b970193019301919392906135bc565b92919061360f602091604086526040860190612b69565b930152565b9091935f6080528251916136316001600160a01b035f94166140bf565b956136486001600160a01b036020870151166140bf565b966001600160a01b038316916040517fdc4c90d3000000000000000000000000000000000000000000000000000000008152602081600481875afa8015611c915761369e915f91613c7b575060a0892090614149565b50505098604051987f89097a6a000000000000000000000000000000000000000000000000000000008a528260048b015260208a602481885afa998a15611c91578b905f9b613c43575b5061371f83866fffffffffffffffffffffffffffffffff938e6104476137138260081c60020b614215565b9160201c60020b614215565b16608052805160805110613c1b57886001600160a01b03815116155f14613b3657506040515f60208201527f120000000000000000000000000000000000000000000000000000000000000060218201527f120000000000000000000000000000000000000000000000000000000000000060228201527f14000000000000000000000000000000000000000000000000000000000000006023820152600481526137cb602482612690565b966040519060a06137dc8184612690565b600483528b9796959493929190601f19015f5b818110613b205750506104f5926138a7836104f56138986020956001600160a01b03876138d99c9b999f828151166040519083820152828152613833604082612690565b61383c88612e02565b5261384687612e02565b500151166040519088820152878152613860604082612690565b61386985612e12565b5261387384612e12565b5060405192839188830191909160206001600160a01b0360408301945f845216910152565b6138a182612e22565b52612e22565b505b015190604051958694602086015260805160408601526060850152608084015260a08084015260c0830190612b69565b6138e283612e32565b526138ec82612e32565b5085516001600160a01b031680613b1857613906906140bf565b915b604051947f12261ee7000000000000000000000000000000000000000000000000000000008652602086600481865afa958615611c91575f96613ad9575b5061398a9061396087876001600160a01b038c5116614620565b61397887876001600160a01b0360208d015116614620565b6104f560405193849260208401613586565b813b1561088a575f916139ce916040519485809481937fdd46508f0000000000000000000000000000000000000000000000000000000083524290600484016135f8565b03925af18015611c9157613aba575b506001600160a01b036020856139fb858585613a03999a511661481c565b01511661481c565b613a21613a158360081c60020b614215565b9260201c60020b614215565b928391839281946001600160a01b0387166001600160a01b03821611613aaf575b50506001600160a01b038281169084168111613a6f57505050613a69919260805191615458565b905b9091565b919350906001600160a01b0385161115613aa05750613a96613a6b92936080519085615458565b926080519161541e565b92613a6b92506080519161541e565b955092505f80613a42565b613a0393505f613ac991612690565b5f92506001600160a01b036139dd565b9095506020813d602011613b10575b81613af560209383612690565b8101031261088a57613b0961398a916128fd565b9590613946565b3d9150613ae8565b505f91613908565b606060208583018101919091528e9a50016137ef565b9493929096506104f56138d99260206040515f828201527f120000000000000000000000000000000000000000000000000000000000000060218201527f1200000000000000000000000000000000000000000000000000000000000000602282015260038152613ba8602382612690565b996001600160a01b0382613bba613512565b9a828151166040519083820152828152613bd5604082612690565b613bde8d612e02565b52613be88c612e02565b500151166040519083820152828152613c02604082612690565b613c0b8a612e12565b52613c1589612e12565b506138a9565b7fbb0fd130000000000000000000000000000000000000000000000000000000005f5260045ffd5b9a505060208a3d602011613c73575b81613c5f60209383612690565b8101031261088a579851988a61371f6136e8565b3d9150613c52565b611c40915060203d602011610a1257610a048183612690565b91905f5b8351811015613ce357806001600160a01b03613cb660019387612e3f565b5116613cc23082615313565b8481613cd2575b50505001613c98565b613cdb92613ebe565b5f8084613cc9565b50509050565b91908203918211612be657565b90808202915f198282099183808410930392808403938468010000000000000000111561088a5714613d405768010000000000000000910990828211900360c01b910360401c1790565b50505060401c90565b908160601b905f196c0100000000000000000000000084099282808510940393808503948584111561088a5714613ddc576c0100000000000000000000000082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5091500490565b90808202915f19828209918380841093039280840393846c01000000000000000000000000111561088a5714613e35576c01000000000000000000000000910990828211900360a01b910360601c1790565b50505060601c90565b91818302915f198185099383808610950394808603958685111561088a5714613eb6579082910981805f03168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b505091500490565b9091906001600160a01b0381169081613f785750505f80808093855af115613ee35750565b601f19601f3d01166001600160a01b03604051927f90bfb8650000000000000000000000000000000000000000000000000000000084521660048301525f6024830152608060448301528060a00160648301523d60848301523d5f60a484013e7ff4b3b1bc0000000000000000000000000000000000000000000000000000000060c4828401600460a4820152015260e40190fd5b60205f6044819496826040956001600160a01b03988751998a947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af13d15601f3d11600185511416171692828152826020820152015215613fe45750565b601f19601f3d0116604051917f90bfb86500000000000000000000000000000000000000000000000000000000835260048301527fa9059cbb000000000000000000000000000000000000000000000000000000006024830152608060448301528060a00160648301523d60848301523d5f60a484013e7ff27f64e40000000000000000000000000000000000000000000000000000000060c4828401600460a4820152015260e40190fd5b3d156140ba573d906140a1826127cc565b916140af6040519384612690565b82523d5f602084013e565b606090565b6001600160a01b0316806140d257504790565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115611c91575f9161411a575090565b90506020813d602011614141575b8161413560209383612690565b8101031261088a575190565b3d9150614128565b60209060246001600160a01b0394936040518481019182526006604082015260408152614177606082612690565b51902060405195869384927f1e2eaeaf0000000000000000000000000000000000000000000000000000000084526004840152165afa918215611c91575f926141e1575b506001600160a01b038216918060a01c60020b9162ffffff808360b81c169260d01c1690565b9091506020813d60201161420d575b816141fd60209383612690565b8101031261088a5751905f6141bb565b3d91506141f0565b60020b908160ff1d82810118620d89e8811161453c5763ffffffff9192600182167001fffcb933bd6fad37aa2d162d1a59400102700100000000000000000000000000000000189160028116614520575b60048116614504575b600881166144e8575b601081166144cc575b602081166144b0575b60408116614494575b60808116614478575b610100811661445c575b6102008116614440575b6104008116614424575b6108008116614408575b61100081166143ec575b61200081166143d0575b61400081166143b4575b6180008116614398575b62010000811661437c575b620200008116614361575b620400008116614346575b620800001661432d575b5f12614325575b0160201c90565b5f190461431e565b6b048a170391f7dc42444e8fa290910260801c90614317565b6d2216e584f5fa1ea926041bedfe9890920260801c9161430d565b916e5d6af8dedb81196699c329225ee6040260801c91614302565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c916142f7565b916f31be135f97d08fd981231505542fcfa60260801c916142ec565b916f70d869a156d2a1b890bb3df62baf32f70260801c916142e2565b916fa9f746462d870fdf8a65dc1f90e061e50260801c916142d8565b916fd097f3bdfd2022b8845ad8f792aa58250260801c916142ce565b916fe7159475a2c29b7443b29c7fa6e889d90260801c916142c4565b916ff3392b0822b70005940c7a398e4b70f30260801c916142ba565b916ff987a7253ac413176f2b074cf7815e540260801c916142b0565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c916142a6565b916ffe5dee046a99a2a811c461f1969c30530260801c9161429c565b916fff2ea16466c96a3843ec78b326b528610260801c91614293565b916fff973b41fa98c081472e6896dfb254c00260801c9161428a565b916fffcb9843d60f6159c9db58835c9266440260801c91614281565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c91614278565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c9161426f565b916ffff97272373d413259a46990580e213a0260801c91614266565b827f8b86327a000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b936001600160a01b0383166001600160a01b03831611614618575b6001600160a01b038581169590831686116145a85750506145a493506153ce565b905b565b92909391946001600160a01b038216115f1461460c5782916145ce916145d495946153ce565b93615398565b6fffffffffffffffffffffffffffffffff81166fffffffffffffffffffffffffffffffff8316105f14614605575090565b90506145a4565b9150506145a492615398565b909190614583565b6001600160a01b03811692918315158061479e575b614640575b50505050565b6001600160a01b038316938415614735575f8060405160208101907f095ea7b300000000000000000000000000000000000000000000000000000000825288602482015282604482015260448152614699606482612690565b519082875af1506146a8614090565b50843b1561088a575f946084869260405197889384927f87517c4500000000000000000000000000000000000000000000000000000000845260048401526001600160a01b03871660248401528160448401528160648401525af1918215611c915761471c945f1993614725575b50614963565b5f80808061463a565b5f61472f91612690565b5f614716565b5061471c93505f19915f8060405160208101907f095ea7b30000000000000000000000000000000000000000000000000000000082526001600160a01b03861660248201528260448201526044815261478f606482612690565b519082855af150614716614090565b506001614635565b906001600160a01b0382166147b9575050565b5f918291826040516001600160a01b0360208201937f095ea7b30000000000000000000000000000000000000000000000000000000085521660248201528160448201526044815261480c606482612690565b51925af1614818614090565b5050565b91906001600160a01b03831691826148345750505050565b6001600160a01b031692831561490d575f80916040518260208201917f095ea7b30000000000000000000000000000000000000000000000000000000083528860248201528160448201526044815261488e606482612690565b51925af161489a614090565b5050823b1561088a5760845f92836001600160a01b039560405196879586947f87517c4500000000000000000000000000000000000000000000000000000000865260048601521660248401528160448401528160648401525af18015611c9157614903575b50565b5f6145a691612690565b905f8094508093506040516001600160a01b0360208201937f095ea7b30000000000000000000000000000000000000000000000000000000085521660248201528160448201526044815261480c606482612690565b9092915f926001600160a01b0383169283614981575b505050505050565b6001600160a01b038216918215614b3b575f918291826040516149e9816104f58a60208301967f095ea7b300000000000000000000000000000000000000000000000000000000885260248401602090939291936001600160a01b0360408201951681520152565b51925af1926149f6614090565b9395823b1561088a5760845f92836001600160a01b039560405196879586947f87517c4500000000000000000000000000000000000000000000000000000000865260048601521660248401526001600160a01b03881660448401528160648401525af18015611c9157614b26575b505b15614b215782614ae2575b505015614a84575f8080808080614979565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f5361666545524332303a20617070726f7665206661696c6564000000000000006044820152fd5b8091925051918215928315614afc575b5050505f80614a72565b829350916020919281010312610ac057506020614b199101612b3f565b5f8080614af2565b505050565b614b339193505f90612690565b5f915f614a65565b5090505f925082919482604051614b97816104f58a60208301967f095ea7b300000000000000000000000000000000000000000000000000000000885260248401602090939291936001600160a01b0360408201951681520152565b51925af1614ba3614090565b9092614a67565b905f1990614bb83084615313565b808311614bd4575b5081614bcb57505050565b6145a692613ebe565b91505f614bc0565b92969596949091948251614bf96001600160a01b035f92166140bf565b966020850197614c126001600160a01b038a51166140bf565b97604051614c71816104f5602082016002907f010000000000000000000000000000000000000000000000000000000000000081527f110000000000000000000000000000000000000000000000000000000000000060018201520190565b6104f5614cae614c7f61354c565b936040519283918a60208401525f60408401525f60608401525f608084015260a08084015260c0830190612b69565b614cb783612e32565b52614cc182612e32565b5087518b51604080516001600160a01b0393841660208201529290911690820152306060820152614cf581608081016104f5565b614cfe83612e02565b52614d0882612e02565b50614d296001600160a01b038a169161069460405194859260208401613586565b803b1561088a57614d6f5f929183926040519485809481937fdd46508f0000000000000000000000000000000000000000000000000000000083524290600484016135f8565b03925af18015611c9157614ef9575b5085516001600160a01b0316614d93906140bf565b90614d9d91613ce9565b9688516001600160a01b0316614db2906140bf565b90614dbc91613ce9565b97614dc5612db6565b95516001600160a01b031660405190614ddd82612658565b8152886020820152614dee87612e32565b52614df886612e32565b50516001600160a01b031660405190614e1082612658565b8152886020820152614e2186612e02565b52614e2b85612e02565b5060405193614e3985612620565b85855267ffffffffffffffff16602085015260408401600190528260608501526001600160a01b031692836080820152614e739086612e53565b9860405194614e8186612620565b855267ffffffffffffffff166020850152604084015260608301526080820152614eaa91612e53565b90614eb485612e32565b51614ebe83612e32565b51614ec891612bd9565b614ed191613ce9565b93614edb90612e02565b5190614ee690612e02565b51614ef091612bd9565b6145a491613ce9565b614f069193505f90612690565b5f915f614d7e565b93909295969491965f906fffffffffffffffffffffffffffffffff8951161561530557614f446001600160a01b038651166140bf565b976020860198614f5d6001600160a01b038b51166140bf565b9a604051614fbc816104f5602082016002907f010000000000000000000000000000000000000000000000000000000000000081527f110000000000000000000000000000000000000000000000000000000000000060018201520190565b614fc461354c565b916001600160a01b038b16916040517f1efeed33000000000000000000000000000000000000000000000000000000008152896004820152602081602481875afa908115611c91578f918b8e925f926152ae575b506150e89261510e96836001600160a01b0394938594916fffffffffffffffffffffffffffffffff80855116911681106152a4575b506104f58360406150a3949501519060806060820151910151916fffffffffffffffffffffffffffffffff60405197889660208801521660408601526060850152608084015260a08084015260c0830190612b69565b6150ac8a612e32565b526150b689612e32565b50519351604080516001600160a01b0393909616831660208701529216169083015230606083015281608081016104f5565b6150f185612e02565b526150fb84612e02565b5061069460405194859260208401613586565b803b1561088a576151545f929183926040519485809481937fdd46508f0000000000000000000000000000000000000000000000000000000083524290600484016135f8565b03925af18015611c915761528f575b5086516001600160a01b0316615178906140bf565b9061518291613ce9565b9989516001600160a01b0316615197906140bf565b906151a191613ce9565b986151aa612db6565b96516001600160a01b0316604051906151c282612658565b81528b60208201526151d388612e32565b526151dd87612e32565b50516001600160a01b0316604051906151f582612658565b815289602082015261520687612e02565b5261521086612e02565b506040519461521e86612620565b86865267ffffffffffffffff16602086015260408501600290528360608601526001600160a01b0316938460808201526152589087612e53565b506040519461526686612620565b855267ffffffffffffffff16602085015260408401526060830152608082015261490091612e53565b61529c9194505f90612690565b5f925f615163565b91506104f561504d565b93505050506020813d6020116152fd575b816152cc60209383612690565b8101031261088a576150e88f916001600160a01b038e61510e968e6152f18495612b4c565b94509250965092615018565b3d91506152bf565b505f97508796505050505050565b6001600160a01b03168061532657503190565b906001600160a01b03602460209260405194859384927f70a082310000000000000000000000000000000000000000000000000000000084521660048301525afa908115611c91575f9161411a575090565b906001600160a01b03809116911603906001600160a01b038211612be657565b916001600160a01b036153bc6145a4946153c394838116848316116153c857615378565b1690613d49565b6154e8565b90615378565b916153c3916145a4936001600160a01b0382166001600160a01b03821611615418575b6154116001600160a01b039161540b838516848316613de3565b93615378565b1691613e3e565b906153f1565b6001600160a01b036154506fffffffffffffffffffffffffffffffff926145a49594838116848316116153c857615378565b169116613de3565b916154a8906001600160a01b0392838116848616116154e2575b7bffffffffffffffffffffffffffffffff000000000000000000000000848061549b8885615378565b1692169260601b16613e3e565b91169081156154b5570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b93615472565b906fffffffffffffffffffffffffffffffff821691820361550557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6c6971756964697479206f766572666c6f7700000000000000000000000000006044820152fdfea164736f6c634300081a000a
| Transaction Hash | Method ? | Block | Age | From | To | Amount | Txn Fee ? | ||
|---|---|---|---|---|---|---|---|---|---|
| No direct transactions — this address is only ever reached via internal calls (common for a contract only invoked through a router or proxy). View Internal Transactions → | |||||||||
| Block | Age | Parent Transaction Hash | Type | Method | From | To | Value | |
|---|---|---|---|---|---|---|---|---|
| 39,083,266 | 1 min agoMon, 17 Aug 2026 19:20:19 UTC | 0xc2b118…305a6b | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 1.6 ETH |
| 39,082,175 | 3 mins agoMon, 17 Aug 2026 19:18:29 UTC | 0xdbf93c…fe7c85 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.0013 ETH |
| 39,079,933 | 7 mins agoMon, 17 Aug 2026 19:14:45 UTC | 0x4142ac…1aae32 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.3 ETH |
| 39,070,884 | 22 mins agoMon, 17 Aug 2026 18:59:37 UTC | 0x60fd1e…585f99 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.004 ETH |
| 39,061,869 | 37 mins agoMon, 17 Aug 2026 18:44:32 UTC | 0x662527…e6eda5 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.0158 ETH |
| 39,061,305 | 38 mins agoMon, 17 Aug 2026 18:43:37 UTC | 0x52c299…78c7ef | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.0158 ETH |
| 39,056,962 | 45 mins agoMon, 17 Aug 2026 18:36:21 UTC | 0x65a4fd…cd7012 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.00035 ETH |
| 39,050,982 | 55 mins agoMon, 17 Aug 2026 18:26:21 UTC | 0x51faae…10f789 | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.00115 ETH |
| 39,049,093 | 58 mins agoMon, 17 Aug 2026 18:23:12 UTC | 0x180ea6…c91435 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.962 ETH |
| 39,047,046 | 1 hr agoMon, 17 Aug 2026 18:19:46 UTC | 0xd18870…35d29f | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.2 ETH |
| 39,045,641 | 1 hr agoMon, 17 Aug 2026 18:17:26 UTC | 0xf277ba…fafa65 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.035 ETH |
| 39,031,963 | 1 hr agoMon, 17 Aug 2026 17:54:33 UTC | 0xbeca71…8ad31d | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.00006 ETH |
| 39,025,362 | 1 hr agoMon, 17 Aug 2026 17:43:32 UTC | 0x7cb114…500420 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 1.17950 ETH |
| 39,024,022 | 1 hr agoMon, 17 Aug 2026 17:41:17 UTC | 0x10d2d5…71b7db | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.008 ETH |
| 39,015,997 | 1 hr agoMon, 17 Aug 2026 17:27:53 UTC | 0x054f10…763793 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.006 ETH |
| 39,014,072 | 1 hr agoMon, 17 Aug 2026 17:24:40 UTC | 0x4c8ca1…19248e | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.03 ETH |
| 39,013,758 | 1 hr agoMon, 17 Aug 2026 17:24:08 UTC | 0x8f7abc…a7cde2 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.18 ETH |
| 38,296,857 | 21 hrs agoSun, 16 Aug 2026 21:25:27 UTC | 0x78f9d1…30610b | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.0015 ETH |
| 38,295,898 | 21 hrs agoSun, 16 Aug 2026 21:23:52 UTC | 0xa23f79…95aa76 | DELEGATECALL | multicall | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.021 ETH |
| 38,295,712 | 21 hrs agoSun, 16 Aug 2026 21:23:33 UTC | 0xde8612…65caa2 | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.00032 ETH |
| 38,287,763 | 22 hrs agoSun, 16 Aug 2026 21:10:16 UTC | 0x7a6325…f4ba9c | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.00157 ETH |
| 38,287,361 | 22 hrs agoSun, 16 Aug 2026 21:09:36 UTC | 0xb27881…d4a60e | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.001 ETH |
| 38,287,244 | 22 hrs agoSun, 16 Aug 2026 21:09:24 UTC | 0xdef7b3…87fbac | DELEGATECALL | executeMulticallWithAgentAllowance | 0xcb3d…d129 | IN | 0xfd13…a293 | 0.001 ETH |
| 38,283,555 | 22 hrs agoSun, 16 Aug 2026 21:03:15 UTC | 0x62b502…f2532d | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.136 ETH |
| 38,282,466 | 22 hrs agoSun, 16 Aug 2026 21:01:25 UTC | 0xf92d09…874559 | DELEGATECALL | execute | 0x5422…83ce | IN | 0xfd13…a293 | 0.1 ETH |