Source Code
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
RouterLogic
Compiler Version
v0.8.26+commit.8a97fa7a
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {FeeAdapter} from "./FeeAdapter.sol";
import {RouterAdapter} from "./RouterAdapter.sol";
import {IRouterLogic} from "./interfaces/IRouterLogic.sol";
import {Flags} from "./libraries/Flags.sol";
import {PackedRoute} from "./libraries/PackedRoute.sol";
import {RouterLib} from "./libraries/RouterLib.sol";
import {TokenLib} from "./libraries/TokenLib.sol";
/**
* @title RouterLogic
* @notice Router logic contract for swapping tokens using a route.
* The route must follow the PackedRoute format.
*/
contract RouterLogic is FeeAdapter, RouterAdapter, IRouterLogic {
struct InternalSwapParams {
uint256 feePtr;
uint256 ptr;
uint256 nbTokens;
uint256 nbSwaps;
address feeToken;
address allocatee;
uint256 feePercent;
address recipient;
}
address private immutable ROUTER;
/**
* @dev Constructor for the RouterLogic contract.
*
* Requirements:
* - The router address must be a contract with code.
* - The protocolFeeReceiver address must not be the zero address.
* - The protocolFeeShare must be less than or equal to 10_000 (100%).
*/
constructor(
address router,
address routerV2_0,
address uniswapV4Manager,
address wnative,
address protocolFeeReceiver,
uint96 protocolFeeShare
) RouterAdapter(routerV2_0, uniswapV4Manager, wnative) FeeAdapter(protocolFeeReceiver, protocolFeeShare) {
if (router.code.length == 0) revert RouterLogic__InvalidRouter();
ROUTER = router;
}
/**
* @dev Swaps an exact amount of tokenIn for as much tokenOut as possible.
*
* Requirements:
* - The caller must be the router.
* - The route must be a valid route, following the PackedRoute format.
* - The route must have at least two tokens.
* - The route must have at least one swap.
* - The tokenIn must be the first token in the route.
* - The tokenOut must be the last token in the route.
* - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
* - The entire balance of all tokens must have been swapped to the last token.
* - The actual amountOut must be greater than or equal to the amountOutMin.
* - If the route has a fee, it should be the first route and the data must use the valid format:
* `(allocatee, feePercent, Flags.FEE_ID, 0, 0)`
*/
function swapExactIn(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address from,
address to,
bytes calldata route
) external override returns (uint256, uint256) {
InternalSwapParams memory params;
(params.feePtr, params.ptr, params.nbTokens, params.nbSwaps) = _startAndVerify(route, tokenIn, tokenOut);
(params.feeToken, params.allocatee, params.feePercent) = _getFeePercent(route, params.feePtr, params.nbTokens);
uint256 amountInWithoutFee = amountIn;
if (params.feeToken == tokenIn) {
unchecked {
uint256 feeAmount = (amountInWithoutFee * params.feePercent) / BPS;
amountInWithoutFee -= feeAmount;
_sendFee(tokenIn, from, params.allocatee, feeAmount);
}
}
uint256 amountOut = _swapExactIn(
amountInWithoutFee,
params.nbTokens,
from,
params.feeToken == tokenOut ? address(this) : to,
params.ptr,
params.nbSwaps,
route
);
if (params.feeToken == tokenOut) {
unchecked {
uint256 feeAmount = (amountOut * params.feePercent) / BPS;
amountOut -= feeAmount;
_sendFee(tokenOut, address(this), params.allocatee, feeAmount);
TokenLib.transfer(tokenOut, to, amountOut);
}
}
if (amountOut < amountOutMin) revert RouterLogic__InsufficientAmountOut(amountOut, amountOutMin);
return (amountIn, amountOut);
}
/**
* @dev Swaps an exact amount of tokenOut for as little tokenIn as possible.
* Due to roundings, the actual amountOut might actually be greater than the amountOut.
*
* Requirements:
* - The caller must be the router.
* - The route must be a valid route, following the PackedRoute format.
* - The route must have at least two tokens.
* - The route must have at least one swap.
* - The tokenIn must be the first token in the route.
* - The tokenOut must be the last token in the route.
* - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
* - The entire balance of all tokens must have been used to calculate the amountIn.
* (due to potential rounding, some dust might be left in the contract after the swap)
* - The actual amountIn must be less than or equal to the amountInMax.
* - If the route has a fee, it should be the first route and the data must use the valid format:
* `(feeRecipient, feePercent, Flags.FEE_ID, 0, 0)`
*/
function swapExactOut(
address tokenIn,
address tokenOut,
uint256 amountInMax,
uint256 amountOut,
address from,
address to,
bytes calldata route
) external override returns (uint256 totalIn, uint256 totalOut) {
InternalSwapParams memory params;
(params.feePtr, params.ptr, params.nbTokens, params.nbSwaps) = _startAndVerify(route, tokenIn, tokenOut);
(params.feeToken, params.allocatee, params.feePercent) = _getFeePercent(route, params.feePtr, params.nbTokens);
if (PackedRoute.isTransferTax(route)) revert RouterLogic__TransferTaxNotSupported();
address recipient;
uint256 amountOutWithFee = amountOut;
if (params.feeToken == tokenOut) {
recipient = address(this);
unchecked {
amountOutWithFee = amountOutWithFee * BPS / (BPS - params.feePercent);
}
} else {
recipient = to;
}
(uint256 amountInWithFee, uint256[] memory amountsIn) =
_getAmountsIn(amountOutWithFee, params.nbTokens, params.nbSwaps, params.ptr, route);
if (params.feeToken == tokenIn) {
unchecked {
uint256 feeAmount = amountInWithFee * params.feePercent / (BPS - params.feePercent);
amountInWithFee += feeAmount;
_sendFee(params.feeToken, from, params.allocatee, feeAmount);
}
}
if (amountInWithFee > amountInMax) revert RouterLogic__ExceedsMaxAmountIn(amountInWithFee, amountInMax);
_swapExactOut(from, recipient, amountsIn, params.ptr, params.nbTokens, params.nbSwaps, route);
if (params.feeToken == tokenOut) {
unchecked {
uint256 feeAmount = amountOutWithFee - amountOut;
_sendFee(tokenOut, address(this), params.allocatee, feeAmount);
TokenLib.transfer(tokenOut, to, amountOut);
}
}
return (amountInWithFee, amountOut);
}
/**
* @dev Sweeps tokens from the contract to the recipient.
*
* Requirements:
* - The caller must be the router owner.
*/
function sweep(address token, address to, uint256 amount) external override {
_checkSender();
token == address(0) ? TokenLib.transferNative(to, amount) : TokenLib.transfer(token, to, amount);
}
/**
* @dev Checks if the sender is the router's owner.
*
* Requirements:
* - The sender must be the router's owner.
*/
function _checkSender() internal view override {
if (msg.sender != Ownable(ROUTER).owner()) revert RouterLogic__OnlyRouterOwner();
}
/**
* @dev Helper function to check if the amount is valid.
*
* Requirements:
* - The amount must be greater than zero and less than 2^128.
*/
function _checkAmount(uint256 amount) private pure {
if (amount == 0 || amount > type(uint128).max) revert RouterLogic__InvalidAmount();
}
/**
* @dev Helper function to start and verify the route.
*
* Requirements:
* - The caller must be the router.
* - The route must have at least two tokens.
* - The route must have at least one swap.
* - The tokenIn must be the first token in the route.
* - The tokenOut must be the last token in the route.
*/
function _startAndVerify(bytes calldata route, address tokenIn, address tokenOut)
private
view
returns (uint256 feePtr, uint256 ptr, uint256 nbTokens, uint256 nbSwaps)
{
if (msg.sender != ROUTER) revert RouterLogic__OnlyRouter();
(ptr, nbTokens, nbSwaps) = PackedRoute.start(route);
if (nbTokens < 2) revert RouterLogic__InsufficientTokens();
(uint256 nextPtr, bytes32 value) = PackedRoute.next(route, ptr);
if (Flags.id(PackedRoute.flags(value)) == Flags.FEE_ID) {
if (nbSwaps < 2) revert RouterLogic__ZeroSwap();
unchecked {
--nbSwaps;
}
feePtr = ptr;
ptr = nextPtr;
} else {
if (nbSwaps == 0) revert RouterLogic__ZeroSwap();
}
if (PackedRoute.token(route, 0) != tokenIn) revert RouterLogic__InvalidTokenIn();
if (PackedRoute.token(route, nbTokens - 1) != tokenOut) revert RouterLogic__InvalidTokenOut();
}
/**
* @dev Returns the fee amount added on the swap.
* The fee is calculated as follows:
* - if `isSwapExactIn`, the fee is calculated as `(amountIn * feePercent) / BPS`
* else, the fee is calculated as `(amountIn * feePercent) / (BPS - feePercent)`
*
* Requirements:
* - The data must use the valid format:
* - If the fee is in tokenIn, `(allocatee, feePercent, Flags.FEE_ID, 0, 0)`
* - If the fee is in tokenOut, `(allocatee, feePercent, Flags.FEE_ID, nbTokens - 1, nbTokens - 1)`
* - The feePercent must be greater than 0 and less than BPS.
*/
function _getFeePercent(bytes calldata route, uint256 feePtr, uint256 nbTokens)
private
pure
returns (address feeToken, address allocatee, uint256 feePercent)
{
if (feePtr > 0) {
(, bytes32 value) = PackedRoute.next(route, feePtr);
// The fee route use the pair field as the allocatee
allocatee = PackedRoute.pair(value);
feePercent = PackedRoute.percent(value);
uint256 feeTokenId = PackedRoute.tokenInId(value);
feeToken = PackedRoute.token(route, feeTokenId);
if (
(PackedRoute.flags(value) | (feeTokenId ^ PackedRoute.tokenOutId(value))) != 0
|| (feeTokenId != 0 && feeTokenId != nbTokens - 1)
) revert RouterLogic__InvalidFeeData();
if (feePercent == 0 || feePercent >= BPS) revert RouterLogic__InvalidFeePercent();
}
}
/**
* @dev Helper function to return the amountIn for each swap in the route and the amountIn of the first token.
* The function will most likely revert if the same pair is used twice, or if the output of a pair is changed
* between the calculation and the actual swap (for example, before swap hooks).
*
* Requirements:
* - The route must be a valid route, following the PackedRoute format.
* - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
* - The entire balance of all tokens must have been used to calculate the amountIn.
*/
function _getAmountsIn(uint256 amountOut, uint256 nbTokens, uint256 nbSwaps, uint256 ptr, bytes calldata route)
private
returns (uint256 amountIn, uint256[] memory)
{
ptr = PackedRoute.endPtr(ptr, nbSwaps);
uint256[] memory amountsIn = new uint256[](nbSwaps);
uint256[] memory balances = new uint256[](nbTokens);
balances[nbTokens - 1] = amountOut;
uint256 total = amountOut;
bytes32 value;
for (uint256 i = nbSwaps; i > 0;) {
(ptr, value) = PackedRoute.previous(route, ptr);
// Reversing tokenInId and tokenOutId to match the reverse iteration
uint256 tokenInId = PackedRoute.tokenOutId(value);
uint256 tokenOutId = PackedRoute.tokenInId(value);
uint256 amount = balances[tokenInId] * PackedRoute.percent(value) / BPS;
balances[tokenInId] -= amount;
_checkAmount(amount);
amountIn = _getAmountIn(route, value, amount);
balances[tokenOutId] += amountIn;
_checkAmount(amountIn);
amountsIn[--i] = amountIn;
unchecked {
total += amountIn - amount;
}
}
amountIn = balances[0];
if (total != amountIn) revert RouterLogic__ExcessBalanceUnused();
return (amountIn, amountsIn);
}
/**
* @dev Helper function to swap an exact amount of tokenIn for as much tokenOut as possible.
* The function will most likely revert if the same pair is used twice, or if the output of a pair is changed
* between the calculation and the actual swap (for example, before swap hooks).
*
* Requirements:
* - The route must be a valid route, following the PackedRoute format.
* - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
* - The entire balance of all tokens must have been used to calculate the amountIn.
*/
function _swapExactIn(
uint256 amountIn,
uint256 nbTokens,
address from,
address recipient,
uint256 ptr,
uint256 nbSwaps,
bytes calldata route
) internal returns (uint256 amountOut) {
unchecked {
uint256[] memory balances = new uint256[](nbTokens);
uint256 lastTokenId = nbTokens - 1;
balances[0] = amountIn;
bytes32 value;
for (uint256 i; i < nbSwaps; i++) {
(ptr, value) = PackedRoute.next(route, ptr);
amountIn += _swapExactInSingle(lastTokenId, route, balances, from, recipient, value);
}
amountOut = balances[lastTokenId];
if (amountIn != amountOut) revert RouterLogic__ExcessBalanceUnused();
}
}
/**
* @dev Helper function to swap an exact amount of tokenOut for as little tokenIn as possible.
* The function will most likely revert if the same pair is used twice, or if the output of a pair is changed
* between the calculation and the actual swap (for example, before swap hooks).
*
* Requirements:
* - The route must be a valid route, following the PackedRoute format.
* - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
*/
function _swapExactOut(
address from,
address recipient,
uint256[] memory amountsIn,
uint256 ptr,
uint256 nbTokens,
uint256 nbSwaps,
bytes calldata route
) internal {
bytes32 value;
for (uint256 i; i < nbSwaps; i++) {
(ptr, value) = PackedRoute.next(route, ptr);
_swapExactOutSingle(route, nbTokens, from, recipient, value, amountsIn[i]);
}
}
/**
* @dev Helper function to swap an exact amount of tokenIn for as much tokenOut as possible.
*
* Requirements:
* - The route must be a valid route, following the PackedRoute format.
* - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
*/
function _swapExactInSingle(
uint256 lastTokenId,
bytes calldata route,
uint256[] memory balances,
address from,
address to,
bytes32 value
) private returns (uint256) {
uint256 flags = PackedRoute.flags(value);
uint256 tokenInId = PackedRoute.tokenInId(value);
uint256 tokenOutId = PackedRoute.tokenOutId(value);
address recipient = tokenOutId == lastTokenId ? to : address(this);
uint256 amountIn = balances[tokenInId] * PackedRoute.percent(value) / BPS;
balances[tokenInId] -= amountIn;
(address tokenIn, uint256 actualAmountIn) = _transferFromTokenId(
route, tokenInId, from, Flags.callback(flags) ? address(this) : PackedRoute.pair(value), amountIn
);
_checkAmount(actualAmountIn);
uint256 amountOut = _swap(route, value, tokenIn, actualAmountIn, recipient, flags);
_checkAmount(amountOut);
balances[tokenOutId] += amountOut;
unchecked {
return amountOut - amountIn;
}
}
/**
* @dev Helper function to swap an exact amount of tokenOut for as little tokenIn as possible.
*
* Requirements:
* - The route must be a valid route, following the PackedRoute format.
*/
function _swapExactOutSingle(
bytes calldata route,
uint256 nbTokens,
address from,
address to,
bytes32 value,
uint256 amountIn
) private {
address pair = PackedRoute.pair(value);
uint256 flags = PackedRoute.flags(value);
(address tokenIn, uint256 actualAmountIn) = _transferFromTokenId(
route, PackedRoute.tokenInId(value), from, Flags.callback(flags) ? address(this) : pair, amountIn
);
address recipient = PackedRoute.tokenOutId(value) == nbTokens - 1 ? to : address(this);
_swap(route, value, tokenIn, actualAmountIn, recipient, flags);
}
/**
* @dev Helper function to transfer tokens.
* If the token is the first token of the route, it will transfer the token from the user to the recipient using
* the transfer function of the router. If the token is flagged as a transfer tax, it will return the actual amount
* received by the recipient.
* Else, it will transfer the token from this contract to the recipient, unless the recipient is this contract.
*
* Requirements:
* - The route must be a valid route, following the PackedRoute format.
*/
function _transferFromTokenId(bytes calldata route, uint256 tokenId, address from, address to, uint256 amount)
private
returns (address, uint256)
{
address token = PackedRoute.token(route, tokenId);
if (tokenId == 0) {
bool isTransferTax = PackedRoute.isTransferTax(route);
uint256 balance = isTransferTax ? TokenLib.balanceOf(token, to) : 0;
RouterLib.transfer(ROUTER, token, from, to, amount);
amount = isTransferTax ? TokenLib.balanceOf(token, to) - balance : amount;
} else if (to != address(this)) {
TokenLib.transfer(token, to, amount);
}
return (token, amount);
}
/**
* @dev Helper function to transfer the fee.
* If from is this contract, it will transfer the fee to the recipient directly.
* Else, it will transfer the fee from the router to the recipient.
*/
function _transferFee(address token, address from, address to, uint256 amount) internal override {
if (from == address(this)) TokenLib.transfer(token, to, amount);
else RouterLib.transfer(ROUTER, token, from, to, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IFeeAdapter} from "./interfaces/IFeeAdapter.sol";
import {TokenLib} from "./libraries/TokenLib.sol";
/**
* @title FeeAdapter
* @notice This contract handles the fee sharing logic for the router.
* It allows setting the protocol fee parameters and sending fees to the protocol fee recipient.
* When sending fees, it splits the fee between the protocol fee recipient and the fee recipient
* based on the protocol fee share. E.g. if the protocol fee share is 10% and the fee amount is 100,
* then 10 will be sent to the protocol fee recipient and 90 will be sent to the fee recipient.
*/
abstract contract FeeAdapter is IFeeAdapter {
uint256 internal constant BPS = 10_000;
address private _protocolFeeRecipient;
uint96 private _protocolFeeShare;
constructor(address feeReceiver, uint96 feeShare) {
_setProtocolFeeParameters(feeReceiver, feeShare);
}
/**
* @dev Returns the protocol fee recipient address.
*/
function getProtocolFeeRecipient() external view override returns (address) {
return _protocolFeeRecipient;
}
/**
* @dev Returns the protocol fee share.
*/
function getProtocolFeeShare() external view override returns (uint256) {
return _protocolFeeShare;
}
/**
* @dev Sets the protocol fee parameters.
*
* Requirements:
* - The caller must be authorized.
* - The fee receiver address must not be zero.
* - The fee share must be less than or equal to 10_000 (100%).
*/
function setProtocolFeeParameters(address feeReceiver, uint96 feeShare) external override {
_checkSender();
_setProtocolFeeParameters(feeReceiver, feeShare);
}
/**
* @dev Internal function to set the protocol fee parameters.
*
* Requirements:
* - The fee receiver address must not be zero.
* - The fee share must be less than or equal to 10_000 (100%).
*/
function _setProtocolFeeParameters(address protocolFeeReceiver, uint96 protocolFeeShare) internal {
if (protocolFeeReceiver == address(0)) revert FeeAdapter__InvalidProtocolFeeReceiver();
if (protocolFeeShare > BPS) revert FeeAdapter__InvalidProtocolFeeShare();
_protocolFeeRecipient = protocolFeeReceiver;
_protocolFeeShare = protocolFeeShare;
emit ProtocolFeeParametersSet(msg.sender, protocolFeeReceiver, protocolFeeShare);
}
/**
* @dev Internal function to send the fee to the fee recipient.
* The user parameter is only used for event logging.
*
* Requirements:
* - The fee recipient address must not be zero.
* - The fee amount must be greater than zero.
*/
function _sendFee(address token, address payer, address allocatee, uint256 feeAmount) internal {
if (feeAmount > 0) {
if (allocatee == address(0)) revert FeeAdapter__InvalidFeeReceiver();
uint256 protocolFeeAmount = (feeAmount * _protocolFeeShare) / BPS;
uint256 remainingFeeAmount = feeAmount - protocolFeeAmount;
if (remainingFeeAmount > 0) _transferFee(token, payer, allocatee, remainingFeeAmount);
if (protocolFeeAmount > 0) _transferFee(token, payer, _protocolFeeRecipient, protocolFeeAmount);
emit FeeSent(token, allocatee, feeAmount, protocolFeeAmount);
}
}
/**
* @dev Internal function to transfer tokens from the contract to the recipient.
*
* Requirements:
* - The sender must be the contract itself.
*/
function _transferFee(address token, address from, address to, uint256 amount) internal virtual {
if (from != address(this)) revert FeeAdapter__InvalidFrom();
TokenLib.transfer(token, to, amount);
}
/**
* @dev Internal function to check if the sender is authorized.
* Must be implemented in the derived contract.
*/
function _checkSender() internal view virtual;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Flags} from "./libraries/Flags.sol";
import {PackedRoute} from "./libraries/PackedRoute.sol";
import {PairInteraction} from "./libraries/PairInteraction.sol";
import {TokenLib} from "./libraries/TokenLib.sol";
/**
* @title RouterAdapter
* @notice Router adapter contract for interacting with different types of pairs.
* Currently supports Uniswap V2, LFJ Legacy LB, LFJ LB, Uniswap V3 pairs and LFJ Token Mill.
*/
abstract contract RouterAdapter {
error RouterAdapter__InvalidId();
error RouterAdapter__InsufficientLBLiquidity();
error RouterAdapter__InsufficientTMLiquidity();
error RouterAdapter__InsufficientTMV2Liquidity();
error RouterAdapter__UnexpectedCallback();
error RouterAdapter__UnexpectedAmountIn();
error RouterAdapter__OnlyWnative();
address private immutable ROUTER_V2_0;
address private immutable UNISWAP_V4_MANAGER;
address private immutable WNATIVE;
uint256 private _callbackData = 0xdead;
/**
* @dev Constructor for the RouterAdapter contract.
*/
constructor(address routerV2_0, address uniswapV4Manager, address wnative) {
ROUTER_V2_0 = routerV2_0;
WNATIVE = wnative;
UNISWAP_V4_MANAGER = uniswapV4Manager;
}
/**
* @dev Allows the contract to receive native tokens and wrap them, unless it's from the wrapped native
* token contract itself, then it just accepts the native tokens.
*/
receive() external payable {
if (msg.sender != WNATIVE) TokenLib.wrap(WNATIVE, msg.value);
}
/**
* @dev Fallback function to handle callbacks from pairs.
*
* Requirements:
* - The callback data must have been set to `pair << 96 | PAIR_ID` before the callback, otherwise revert with
* `RouterAdapter__UnexpectedCallback()`.
*/
fallback(bytes calldata data) external returns (bytes memory) {
uint256 callbackData = _callbackData;
uint256 id = Flags.id(callbackData);
// forge-lint: disable-next-line(unsafe-typecast)
address account = address(uint160(callbackData >> 96));
if (id == Flags.UNISWAP_V3_ID && msg.sender == account) {
return _uniswapV3SwapCallback(data);
} else if (id == Flags.UNISWAP_V4_ID && msg.sender == UNISWAP_V4_MANAGER) {
return _uniswapV4UnlockCallback(data, account);
} else if (id == Flags.POE_ID && msg.sender == account) {
return _poeSwapCallback(data);
}
assembly ("memory-safe") {
calldatacopy(0, 0, calldatasize())
revert(0, calldatasize())
}
}
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the pair.
*
* Requirements:
* - The id of the flags must be valid and not the FEE_ID.
*/
function _getAmountIn(bytes calldata, bytes32 value, uint256 amountOut) internal returns (uint256 amountIn) {
address pair = PackedRoute.pair(value);
uint256 flags = PackedRoute.flags(value);
uint256 id = Flags.id(flags);
if (id == Flags.UNISWAP_V2_ID) amountIn = _getAmountInUV2(pair, flags, amountOut);
else if (id == Flags.LFJ_LEGACY_LIQUIDITY_BOOK_ID) amountIn = _getAmountInLegacyLB(pair, flags, amountOut);
else if (id == Flags.LFJ_LIQUIDITY_BOOK_ID) amountIn = _getAmountInLB(pair, flags, amountOut);
else if (id == Flags.UNISWAP_V3_ID) amountIn = _getAmountInUV3(pair, flags, amountOut);
else if (id == Flags.LFJ_TOKEN_MILL_ID) amountIn = _getAmountInTM(pair, flags, amountOut);
else if (id == Flags.LFJ_TOKEN_MILL_V2_ID) amountIn = _getAmountInTMV2(pair, flags, amountOut);
// else if (id == Flags.UNISWAP_V4_ID) amountIn = _getAmountInUV4(route, value, pair, flags, amountOut);
// Not supported yet because of a rounding issue in the getAmountIn vs getAmountOut functions
// else if (id == Flags.BYREAL_ID) amountIn = _getSwapInByReal(route, pair, amountOut, value);
else revert RouterAdapter__InvalidId();
}
/**
* @dev Swaps tokens from the sender to the recipient.
*
* Requirements:
* - The id of the flags must be valid and not the FEE_ID.
*/
function _swap(bytes calldata, bytes32 value, address tokenIn, uint256 amountIn, address recipient, uint256 flags)
internal
returns (uint256 amountOut)
{
address pair = PackedRoute.pair(value);
uint256 id = Flags.id(flags);
if (id == Flags.UNISWAP_V2_ID) amountOut = _swapUV2(pair, flags, amountIn, recipient);
else if (id == Flags.LFJ_LEGACY_LIQUIDITY_BOOK_ID) amountOut = _swapLegacyLB(pair, flags, recipient);
else if (id == Flags.LFJ_LIQUIDITY_BOOK_ID) amountOut = _swapLB(pair, flags, recipient);
else if (id == Flags.UNISWAP_V3_ID) amountOut = _swapUV3(pair, flags, recipient, amountIn, tokenIn);
else if (id == Flags.LFJ_TOKEN_MILL_ID) amountOut = _swapTM(pair, flags, recipient, amountIn);
else if (id == Flags.LFJ_TOKEN_MILL_V2_ID) amountOut = _swapTMV2(pair, flags, recipient, amountIn);
// else if (id == Flags.UNISWAP_V4_ID) amountOut = _swapUV4(route, value, pair, flags, recipient, amountIn);
else if (id == Flags.BYREAL_ID) amountOut = _swapByReal(pair, recipient, amountIn, tokenIn);
else if (id == Flags.POE_ID) amountOut = _swapPoe(pair, flags, recipient, amountIn, tokenIn);
else revert RouterAdapter__InvalidId();
}
/* Uniswap V2 */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the Uniswap V2 pair.
*/
function _getAmountInUV2(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
(uint256 reserveIn, uint256 reserveOut) = PairInteraction.getReservesUV2(pair, Flags.zeroForOne(flags));
return (reserveIn * amountOut * 1000 - 1) / ((reserveOut - amountOut) * 997) + 1;
}
/**
* @dev Swaps tokens from the sender to the recipient using the Uniswap V2 pair.
*/
function _swapUV2(address pair, uint256 flags, uint256 amountIn, address recipient)
internal
returns (uint256 amountOut)
{
bool ordered = Flags.zeroForOne(flags);
(uint256 reserveIn, uint256 reserveOut) = PairInteraction.getReservesUV2(pair, ordered);
uint256 amountInWithFee = amountIn * 997;
amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);
(uint256 amount0, uint256 amount1) = ordered ? (uint256(0), amountOut) : (amountOut, uint256(0));
PairInteraction.swapUV2(pair, amount0, amount1, recipient);
}
/* Legacy LB v2.0 */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the LFJ Legacy LB pair.
*/
function _getAmountInLegacyLB(address pair, uint256 flags, uint256 amountOut)
internal
view
returns (uint256 amountIn)
{
return PairInteraction.getSwapInLegacyLB(ROUTER_V2_0, pair, amountOut, Flags.zeroForOne(flags));
}
/**
* @dev Swaps tokens from the sender to the recipient using the LFJ Legacy LB pair.
*/
function _swapLegacyLB(address pair, uint256 flags, address recipient) internal returns (uint256 amountOut) {
return PairInteraction.swapLegacyLB(pair, Flags.zeroForOne(flags), recipient);
}
/* LB v2.1 and v2.2 */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the LFJ LB pair.
*/
function _getAmountInLB(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
(uint256 amountIn, uint256 amountLeft) = PairInteraction.getSwapInLB(pair, amountOut, Flags.zeroForOne(flags));
if (amountLeft != 0) revert RouterAdapter__InsufficientLBLiquidity();
return amountIn;
}
/**
* @dev Swaps tokens from the sender to the recipient using the LFJ LB pair.
*/
function _swapLB(address pair, uint256 flags, address recipient) internal returns (uint256 amountOut) {
return PairInteraction.swapLB(pair, Flags.zeroForOne(flags), recipient);
}
/* Uniswap V3 */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the Uniswap V3 pair.
*/
function _getAmountInUV3(address pair, uint256 flags, uint256 amountOut) internal returns (uint256 amountIn) {
return PairInteraction.getSwapInUV3(pair, Flags.zeroForOne(flags), amountOut);
}
/**
* @dev Swaps tokens from the sender to the recipient using the Uniswap V3 pair.
* Will set the callback address to the pair.
*/
function _swapUV3(address pair, uint256 flags, address recipient, uint256 amountIn, address tokenIn)
internal
returns (uint256)
{
_callbackData = (uint256(uint160(pair)) << 96) | Flags.UNISWAP_V3_ID;
(uint256 amountOut, uint256 actualAmountIn, uint256 hash) =
PairInteraction.swapUV3(pair, recipient, Flags.zeroForOne(flags), amountIn, tokenIn);
if (_callbackData != hash) revert RouterAdapter__UnexpectedCallback();
if (actualAmountIn != amountIn) revert RouterAdapter__UnexpectedAmountIn();
_callbackData = 0xdead;
return amountOut;
}
/**
* @dev Callback function for Uniswap V3 swaps.
*
* Requirements:
* - The caller must be the callback address.
*/
function _uniswapV3SwapCallback(bytes calldata data) internal returns (bytes memory) {
(int256 amount0Delta, int256 amount1Delta, address token) = PairInteraction.decodeUV3CallbackData(data);
_callbackData = PairInteraction.hashUV3(amount0Delta, amount1Delta, token);
TokenLib.transfer(token, msg.sender, uint256(amount0Delta > 0 ? amount0Delta : amount1Delta));
return new bytes(0);
}
/* Token Mill */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the LFJ Token Mill pair.
*/
function _getAmountInTM(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
(uint256 amountIn, uint256 actualAmountOut) =
PairInteraction.getSwapInTM(pair, amountOut, Flags.zeroForOne(flags));
if (actualAmountOut != amountOut) revert RouterAdapter__InsufficientTMLiquidity();
return amountIn;
}
/**
* @dev Swaps tokens from the sender to the recipient using the LFJ Token Mill pair.
*/
function _swapTM(address pair, uint256 flags, address recipient, uint256 amountIn) internal returns (uint256) {
(uint256 amountOut, uint256 actualAmountIn) =
PairInteraction.swapTM(pair, recipient, amountIn, Flags.zeroForOne(flags));
if (actualAmountIn != amountIn) revert RouterAdapter__InsufficientTMLiquidity();
return amountOut;
}
/* Token Mill V2 */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the LFJ Token Mill V2 pair.
*/
function _getAmountInTMV2(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
(uint256 amountIn, uint256 actualAmountOut) =
PairInteraction.getSwapInTMV2(pair, amountOut, Flags.zeroForOne(flags));
if (actualAmountOut != amountOut) revert RouterAdapter__InsufficientTMV2Liquidity();
return amountIn;
}
/**
* @dev Swaps tokens from the sender to the recipient using the LFJ Token Mill V2 pair.
*/
function _swapTMV2(address pair, uint256 flags, address recipient, uint256 amountIn) internal returns (uint256) {
(uint256 amountOut, uint256 actualAmountIn) =
PairInteraction.swapTMV2(pair, recipient, amountIn, Flags.zeroForOne(flags));
if (actualAmountIn != amountIn) revert RouterAdapter__InsufficientTMV2Liquidity();
return amountOut;
}
/* Uniswap V4 */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the Uniswap V4 pair.
* Will set the callback address to the Uniswap V4 manager.
*/
function _getAmountInUV4(bytes calldata route, bytes32 value, address pair, uint256 flags, uint256 amountOut)
internal
returns (uint256)
{
_callbackData = Flags.UNISWAP_V4_ID;
// Use pair as the dataOffset
return PairInteraction.getSwapInUV4(route, value, UNISWAP_V4_MANAGER, pair, Flags.zeroForOne(flags), amountOut);
}
/**
* @dev Swaps tokens from the sender to the recipient using the Uniswap V4 pair.
* Will set the callback address to the recipient.
*/
function _swapUV4(
bytes calldata route,
bytes32 value,
address pair,
uint256 flags,
address recipient,
uint256 amountIn
) internal returns (uint256) {
_callbackData = (uint256(uint160(recipient)) << 96) | Flags.UNISWAP_V4_ID;
(uint256 amountOut, uint256 actualAmountIn) =
PairInteraction.swapUV4(route, value, UNISWAP_V4_MANAGER, pair, Flags.zeroForOne(flags), amountIn);
_callbackData = 0xdead;
if (actualAmountIn != amountIn) revert RouterAdapter__UnexpectedAmountIn();
return amountOut;
}
/**
* @dev Callback function for Uniswap V4 unlocks.
*
* Requirements:
* - The caller must be the callback address.
*/
function _uniswapV4UnlockCallback(bytes calldata data, address recipient) internal returns (bytes memory) {
(int256 delta0, int256 delta1) = PairInteraction.swapUV4Callback(data, recipient, WNATIVE);
return abi.encode(0x20, 0x40, delta0, delta1);
}
/* ByReal */
/**
* @dev Returns the amount of tokenIn needed to get amountOut from the ByReal pair.
*/
function _getSwapInByReal(bytes calldata route, address pair, uint256 amountOut, bytes32 value)
internal
view
returns (uint256)
{
address tokenOut = PackedRoute.token(route, PackedRoute.tokenOutId(value));
return PairInteraction.getSwapInByReal(pair, amountOut, tokenOut);
}
/**
* @dev Swaps tokens from the sender to the recipient using the ByReal pair.
*/
function _swapByReal(address pair, address recipient, uint256 amountIn, address tokenIn)
internal
returns (uint256 amountOut)
{
TokenLib.forceApprove(tokenIn, pair, amountIn);
return PairInteraction.swapByReal(pair, recipient, amountIn, tokenIn);
}
/* Poe */
/**
* @dev Swaps tokens from the sender to the recipient using the Poe pair.
*/
function _swapPoe(address pair, uint256 flags, address recipient, uint256 amountIn, address tokenIn)
internal
returns (uint256)
{
_callbackData = (uint256(uint160(pair)) << 96) | Flags.POE_ID;
(uint256 amountOut, uint256 actualAmountIn, uint256 hash) =
PairInteraction.swapPOE(pair, recipient, amountIn, Flags.zeroForOne(flags), tokenIn);
if (_callbackData != hash) revert RouterAdapter__UnexpectedCallback();
if (actualAmountIn != amountIn) revert RouterAdapter__UnexpectedAmountIn();
_callbackData = 0xdead;
return amountOut;
}
/**
* @dev Callback function for Poe swaps.
*
* Requirements:
* - The caller must be the callback address.
*/
function _poeSwapCallback(bytes calldata data) internal returns (bytes memory) {
(int256 amount0Delta, int256 amount1Delta, address token) = PairInteraction.decodeUV3CallbackData(data);
_callbackData = PairInteraction.hashUV3(amount0Delta, amount1Delta, token);
TokenLib.transfer(token, msg.sender, uint256(amount0Delta > 0 ? amount0Delta : amount1Delta));
return abi.encode(PairInteraction.POE_SWAP_CALLBACK_SELECTOR);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IFeeAdapter} from "./IFeeAdapter.sol";
interface IRouterLogic is IFeeAdapter {
error RouterLogic__OnlyRouter();
error RouterLogic__InvalidTokenIn();
error RouterLogic__InvalidTokenOut();
error RouterLogic__InvalidRouter();
error RouterLogic__ExcessBalanceUnused();
error RouterLogic__InvalidAmount();
error RouterLogic__ZeroSwap();
error RouterLogic__InsufficientTokens();
error RouterLogic__ExceedsMaxAmountIn(uint256 amountIn, uint256 amountInMax);
error RouterLogic__InsufficientAmountOut(uint256 amountOut, uint256 amountOutMin);
error RouterLogic__TransferTaxNotSupported();
error RouterLogic__OnlyRouterOwner();
error RouterLogic__InvalidFeeData();
error RouterLogic__InvalidFeePercent();
function swapExactIn(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address from,
address to,
bytes calldata route
) external returns (uint256 totalIn, uint256 totalOut);
function swapExactOut(
address tokenIn,
address tokenOut,
uint256 amountInMax,
uint256 amountOut,
address from,
address to,
bytes calldata route
) external returns (uint256 totalIn, uint256 totalOut);
function sweep(address token, address to, uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title Flags
* @dev Helper library for parsing flags received from a packed route
* The flags are a uint16 variable that contains the following information:
* - zeroForOne: The first bit of the flags variable (0: false, 1: true)
* - callback: The second bit of the flags variable (0: false, 1: true)
* - id: The last 8 bits of the flags variable (1-255)
* Note that the bits 2-7 are unused for now, and might be used in the future
* The following ids must set the callback flag in order to work properly:
* - Uniswap V3 (UNISWAP_V3_ID)
* - Uniswap V4 (UNISWAP_V4_ID)
* - ByReal (BYREAL_ID)
*/
library Flags {
uint256 internal constant ONE_FOR_ZERO = 0;
uint256 internal constant ZERO_FOR_ONE = 1;
uint256 internal constant CALLBACK = 2;
uint256 internal constant ID_OFFSET = 8;
uint256 internal constant ID_MASK = 0xff00;
/// forge-lint: disable-start(incorrect-shift)
uint256 internal constant FEE_ID = 0 << ID_OFFSET; // 0 is reserved for the fee id
uint256 internal constant UNISWAP_V2_ID = 1 << ID_OFFSET;
uint256 internal constant LFJ_LEGACY_LIQUIDITY_BOOK_ID = 2 << ID_OFFSET;
uint256 internal constant LFJ_LIQUIDITY_BOOK_ID = 3 << ID_OFFSET; // v2.1 and v2.2 have the same ABI for swaps
uint256 internal constant UNISWAP_V3_ID = 4 << ID_OFFSET;
uint256 internal constant LFJ_TOKEN_MILL_ID = 5 << ID_OFFSET;
uint256 internal constant LFJ_TOKEN_MILL_V2_ID = 6 << ID_OFFSET;
uint256 internal constant UNISWAP_V4_ID = 7 << ID_OFFSET;
uint256 internal constant BYREAL_ID = 8 << ID_OFFSET;
uint256 internal constant POE_ID = 9 << ID_OFFSET;
/// forge-lint: disable-end(incorrect-shift)
/**
* @dev Returns the id of the flags variable
*/
function id(uint256 flags) internal pure returns (uint256 idx) {
return flags & ID_MASK;
}
/**
* @dev Returns whether the zeroForOne flag is set
*/
function zeroForOne(uint256 flags) internal pure returns (bool) {
return flags & ZERO_FOR_ONE == ZERO_FOR_ONE;
}
/**
* @dev Returns whether the callback flag is set
*/
function callback(uint256 flags) internal pure returns (bool) {
return flags & CALLBACK == CALLBACK;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title PackedRoute
* @dev Helper library to decode packed route data
* Route data is a byte array following the format:
* [nb tokens]
* [isTransferTax, token0, token1, token2, ..., tokenN-1, tokenN]
* [{pairAB, percentAB, flagsAB, tokenA_id, tokenB_id}, {pairBC, percentBC, flagsBC, tokenB_id, tokenC_id}, ...]
* [extraData][extraDataLength] (optional)
*
* The number of tokens is encoded on the first byte, it must be less or equal to 255.
* The isTransferTax is a boolean flag that indicates if the token0 is a transfer tax token.
* The tokens are encoded as 20 bytes addresses.
* The token0 must be the tokenIn and the tokenN must be the tokenOut.
* The pairs are encoded as 20 bytes addresses.
* The percent is a 16 bits unsigned integer, it must be less or equal to 10 000 (100%).
* It represents the percentage of the remaining amount use for the swap. Therefore, the last swap must be 100%.
* Thus, the sum of all percents will exceed 100% if there are more than 1 swap.
* The flags are encoded as 16 bits unsigned integer. They contain the dex id and information for the swap. See the
* Flags library for more information.
* The token ids are encoded as 8 bits unsigned integer. They must match the id of the token in the token list.
* All the values are packed in a bytes array each time using the least amount of bytes possible (in solidity,
* use abi.encodePacked).
* The extraData is an optional field that can be used to store additional data for the route. It must be at the end of
* the route and **MUST** have an even length (multiple of 2 bytes).
* This is to avoid the extra data to be taken as a route.
* Currently, only Uniswap V4 requires extra data for its swaps, they must be encoded as follows:
* [fee: 3][tickSpacing: 3][nativeFlag: 1][hooks: 20][hookData length: 3][hookData: variable]
* The hookData length **MUST** be even (multiple of 2 bytes).
* The extraDataLength is a 3 bytes unsigned integer that indicates the length of the extra data. It is appended at the
* end of the route. It is used to know where the extra data starts and ends. If there is no extra data,
* this field should be omitted.
* Example 1, swapExactIn:
* User wants to swap X WETH to USDT using the following route:
*
* WETH
* 0.8 | 0.2
* ------------------
* | |
* UNIV3-WETH/WAVAX LB2.1-WETH/USDC
* | |
* WAVAX |
* 0.3 | 0.7 |
* ----------------- |
* | | |
* | UNIV2-WAVAX/USDC |
* | | |
* | ---------
* | |
* | USDC
* LB2.0-WAVAX/USDT 0.4 | 0.6
* | --------------
* | | |
* | | UNIV3-BTC/USDC
* | | |
* | | BTC
* | LB2.2-USDC/USDT |
* | | UNIV2-BTC/USDT
* | | |
* -----------------------------
* |
* USDT
*
* Encoding:
* 0 1 2 3 4
* [5][false, WETH, WAVAX, USDC, BTC, USDT] // 5 tokens, WETH is not a transfer tax token
* {UNIV3-WETH/WAVAX, 8000, UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
* {LB2.1-WETH/USDC, 2000, LB2_1_ID | ZERO_FOR_ONE, 0, 2}
* {UNIV2-WAVAX/USDC, 7000, UNIV2_ID | ZERO_FOR_ONE, 1, 2}
* {UNIV3-BTC/USDC, 6000, UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
* {UNIV2-BTC/USDT, 10000, UNIV2_ID | ZERO_FOR_ONE, 3, 4}
* {LB2.0-WAVAX/USDT, 3000, LB2_0_ID | ZERO_FOR_ONE, 1, 4}
* {LB2.2-USDC/USDT, 4000, LB2_2_ID | ZERO_FOR_ONE, 2, 4}
*
* Now we have to recalculate the percents, as the amountIn is calculated using the percent of the remaining token
* balance.
*
* UNIV3-WETH/WAVAX = 0.8
* LB2.1-WETH/USDC = 0.2 / (1 - 0.8) = 1.0 (we force it to 1 as it's the last swap from WETH)
* UNIV2-WAVAX/USDC = 0.7
* UNIV3-BTC/USDC = 0.6
* UNIV2-BTC/USDT = 1.0
* LB2.0-WAVAX/USDT = 0.3 / (1 - 0.7) = 1.0 (we force it to 1 as it's the last swap to USDT)
* LB2.2-USDC/USDT = 0.4 / (1 - 0.6) = 1.0 (we force it to 1 as it's the last swap to USDT)
*
* Final encoding:
*
* 0 1 2 3 4
* [5][false, WETH, WAVAX, USDC, BTC, USDT] // 5 tokens, WETH is not a transfer tax token
* {UNIV3-WETH/WAVAX, 8000, UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
* {LB2.1-WETH/USDC, 10000, LB2_1_ID | ZERO_FOR_ONE, 0, 2}
* {UNIV2-WAVAX/USDC, 7000, UNIV2_ID | ZERO_FOR_ONE, 1, 2}
* {UNIV3-BTC/USDC, 6000, UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
* {UNIV2-BTC/USDT, 10000, UNIV2_ID | ZERO_FOR_ONE, 3, 4}
* {LB2.0-WAVAX/USDT, 10000, LB2_0_ID | ZERO_FOR_ONE, 1, 4}
* {LB2.2-USDC/USDT, 10000, LB2_2_ID | ZERO_FOR_ONE, 2, 4}
*
* Example 2, swapExactOut:
* User wants to swap WETH to X USDT using the same route, we need to calculate the weights in the opposite direction:
*
* UNIV2-BTC/USDT = (0.2 + 0.8 * 0.7) * 0.6 ~= 0.46
* LB2.2-USDC/USDT = (0.2 + 0.8 * 0.7) * 0.4 ~= 0.3
* LB2.0-WAVAX/USDT = 0.8 * 0.3 ~= 0.24
* UNIV3-BTC/USDC = 1.0 (single route)
* UNIV2-WAVAX/USDC = 0.8 * 0.7 ~= 0.56
* LB2.1-WETH/USDC = 0.2
* UNIV3-WETH/WAVAX = 1.0 (single route)
*
* Then we have to normalize the nodes where the sum of its edges doesn't equal 1:
*
* UNIV2-WAVAX/USDC = 0.56 / 0.76 ~= 0.74
* LB2.1-WETH/USDC = 0.2 / 0.76 ~= 0.26
*
* WETH
* |
* ------------------
* | |
* UNIV3-WETH/WAVAX LB2.1-WETH/USDC
* | |
* WAVAX |
* | |
* ----------------- |
* | | |
* | UNIV2-WAVAX/USDC |
* | | |
* | ---------
* | 0.74 | 0.26
* | USDC
* LB2.0-WAVAX/USDT |
* | --------------
* | | |
* | | UNIV3-BTC/USDC
* | | |
* | | BTC
* | LB2.2-USDC/USDT |
* | | UNIV2-BTC/USDT
* | | |
* -----------------------------
* 0.24 0.30 0.46
* |
* USDT
*
* Encoding:
* Note that even if the route is now inverted, the tokens are still in the same order, as the user still wants to
* swap WETH to USDT, just instead of swapping a specific number of WETH and receiving the maximum amount of USDT,
* the user wants to swap the minimum amount of WETH to receive a specific number of USDT.
*
* 0 1 2 3 4
* [5][false, WETH, WAVAX, USDC, BTC, USDT] // 5 tokens, WETH is not a transfer tax token
* {UNIV3-WETH/WAVAX, 10000, UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
* {LB2.1-WETH/USDC, 2600, LB2_1_ID | ZERO_FOR_ONE, 0, 2}
* {UNIV2-WAVAX/USDC, 7400, UNIV2_ID | ZERO_FOR_ONE, 1, 2}
* {UNIV3-BTC/USDC, 10000, UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
* {UNIV2-BTC/USDT, 4600, UNIV2_ID | ZERO_FOR_ONE, 3, 4}
* {LB2.2-USDC/USDT, 3000, LB2_2_ID | ZERO_FOR_ONE, 2, 4}
* {LB2.0-WAVAX/USDT, 2400, LB2_0_ID | ZERO_FOR_ONE, 1, 4}
*
* Now we have to recalculate the percents, as the amountIn is calculated using the percent of the remaining token
* balance.
* We start from the last token, USDT, and up to the first token, WETH.
*
* LB2.0-WAVAX/USDT = 0.24
* LB2.2-USDC/USDT = 0.3 / (1 - 0.24) ~= 0.3947
* UNIV2-BTC/USDT = 0.46 / ((1 - 0.24) * (1 - 0.3947)) = 1.0 (we force it to 1 as it's the last swap from USDT)
* UNIV3-BTC/USDC = 1.0 (single route)
* UNIV2-WAVAX/USDC = 0.74
* LB2.1-WETH/USDC = 0.26 / (1 - 0.74) = 1.0 (we force it to 1 as it's the last swap from USDC)
* UNIV3-WETH/WAVAX = 1.0 (single route)
*
* Final encoding:
*
* 0 1 2 3 4
* [5][false, WETH, WAVAX, USDC, BTC, USDT]
* {UNIV3-WETH/WAVAX, 10000, UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
* {LB2.1-WETH/USDC, 10000, LB2_1_ID | ZERO_FOR_ONE, 0, 2}
* {UNIV2-WAVAX/USDC, 7400, UNIV2_ID | ZERO_FOR_ONE, 1, 2}
* {UNIV3-BTC/USDC, 10000, UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
* {UNIV2-BTC/USDT, 10000, UNIV2_ID | ZERO_FOR_ONE, 3, 4}
* {LB2.2-USDC/USDT, 3947, LB2_2_ID | ZERO_FOR_ONE, 2, 4}
* {LB2.0-WAVAX/USDT, 2400, LB2_0_ID | ZERO_FOR_ONE, 1, 4}
*/
library PackedRoute {
error PackedRoute__InvalidLength();
error PackedRoute__InvalidExtraDataLength();
uint256 internal constant IS_TRANSFER_TAX_OFFSET = 1;
uint256 internal constant TOKENS_OFFSET = 2;
uint256 internal constant ROUTE_SIZE = 26;
uint256 internal constant ADDRESS_SIZE = 20;
// Extra data must be at the end of the route and have an even length.
uint256 internal constant EXTRA_DATA_LENGTH_SIZE = 3; // Up to 16777215 bytes of extra data.
uint256 internal constant EXTRA_DATA_LENGTH_SHIFT = 232; // 256 - EXTRA_DATA_LENGTH_SIZE * 8
uint256 internal constant IS_TRANSFER_TAX_SHIFT = 248;
uint256 internal constant ADDRESS_SHIFT = 96;
uint256 internal constant PERCENT_SHIFT = 80;
uint256 internal constant FLAGS_SHIFT = 64;
uint256 internal constant TOKEN_IN_SHIFT = 56;
uint256 internal constant TOKEN_OUT_SHIFT = 48;
uint256 internal constant UINT16_MASK = 0xffff;
uint256 internal constant UINT8_MASK = 0xff;
/**
* @dev Returns whether the tokenIn is a transfer tax token.
* If route is not of the correct length, the function won't revert and might return an incorrect value.
* Always use `start` to validate the route length before calling this function.
*/
function isTransferTax(bytes calldata route) internal pure returns (bool b) {
assembly ("memory-safe") {
b := iszero(iszero(shr(IS_TRANSFER_TAX_SHIFT, calldataload(add(route.offset, IS_TRANSFER_TAX_OFFSET)))))
}
}
/**
* @dev Returns the token address at the given id.
* If route is not of the correct length, the function won't revert and might return an incorrect value.
* Always use `start` to validate the route length before calling this function.
*/
function token(bytes calldata route, uint256 id) internal pure returns (address t) {
assembly ("memory-safe") {
t := shr(ADDRESS_SHIFT, calldataload(add(route.offset, add(TOKENS_OFFSET, mul(id, ADDRESS_SIZE)))))
}
}
/**
* @dev Returns the number of tokens, the number of swaps, and the offset to the first swap.
* If the route is not of the correct length, the function will revert with `PackedRoute__InvalidLength`.
* For the route to be valid, its length must equal
* `TOKENS_OFFSET + nbTokens * ADDRESS_SIZE + nbSwaps * ROUTE_SIZE`.
*/
function start(bytes calldata route) internal pure returns (uint256 ptr, uint256 nbTokens, uint256 nbSwaps) {
assembly ("memory-safe") {
nbTokens := shr(248, calldataload(route.offset))
}
unchecked {
ptr = TOKENS_OFFSET + nbTokens * ADDRESS_SIZE;
uint256 swapLength = route.length - ptr;
if (route.length < ptr) revert PackedRoute__InvalidLength();
if (swapLength % ROUTE_SIZE != 0) {
// Might overflow if swapLength < 3, but `type(uint256).max - {0, 1, 2}` is never multiple of ROUTE_SIZE
// so it will be caught by the modulo check.
swapLength -= extraDataLength(route, swapLength) + EXTRA_DATA_LENGTH_SIZE;
if (swapLength % ROUTE_SIZE != 0) revert PackedRoute__InvalidLength();
}
nbSwaps = swapLength / ROUTE_SIZE;
}
}
/**
* @dev Returns the end pointer of the swaps section of the route.
* If the route is not of the correct length, the function will revert with `PackedRoute__InvalidLength`.
* Always use `start` to validate the route length before calling this function.
*/
function endPtr(uint256 startPtr, uint256 nbSwaps) internal pure returns (uint256 endPtr_) {
unchecked {
endPtr_ = startPtr + nbSwaps * ROUTE_SIZE;
}
}
/**
* @dev Returns the next swap pointer and the swap value.
* If the route is not of the correct length, the function won't revert and might return an incorrect value.
* Always use `start` to validate the route length before calling this function.
*/
function next(bytes calldata route, uint256 ptr) internal pure returns (uint256 nextPtr, bytes32 value) {
assembly ("memory-safe") {
value := calldataload(add(route.offset, ptr))
nextPtr := add(ptr, ROUTE_SIZE)
}
}
/**
* @dev Returns the previous swap pointer and the swap value.
* If the route is not of the correct length, the function won't revert and might return an incorrect value.
* Always use `start` to validate the route length before calling this function.
*/
function previous(bytes calldata route, uint256 ptr) internal pure returns (uint256 previousPtr, bytes32 value) {
assembly ("memory-safe") {
previousPtr := sub(ptr, ROUTE_SIZE)
value := calldataload(add(route.offset, previousPtr))
}
}
/**
* @dev Returns the pair address from the swap value.
*/
function pair(bytes32 value) internal pure returns (address pair_) {
assembly ("memory-safe") {
pair_ := shr(ADDRESS_SHIFT, value)
}
}
/**
* @dev Returns the percent value from the swap value.
*/
function percent(bytes32 value) internal pure returns (uint256 percent_) {
assembly ("memory-safe") {
percent_ := and(shr(PERCENT_SHIFT, value), UINT16_MASK)
}
}
/**
* @dev Returns the flags value from the swap value.
*/
function flags(bytes32 value) internal pure returns (uint256 flags_) {
assembly ("memory-safe") {
flags_ := and(shr(FLAGS_SHIFT, value), UINT16_MASK)
}
}
/**
* @dev Returns the tokenInId from the swap value.
*/
function tokenInId(bytes32 value) internal pure returns (uint256 tokenInId_) {
assembly ("memory-safe") {
tokenInId_ := and(shr(TOKEN_IN_SHIFT, value), UINT8_MASK)
}
}
/**
* @dev Returns the tokenOutId from the swap value.
*/
function tokenOutId(bytes32 value) internal pure returns (uint256 tokenOutId_) {
assembly ("memory-safe") {
tokenOutId_ := and(shr(TOKEN_OUT_SHIFT, value), UINT8_MASK)
}
}
/**
* @dev Returns the length of the extra data at the end of the route.
* If there is no extra data or if the swapLength is less than or equal to EXTRA_DATA_LENGTH_SIZE, returns 0.
* If the route is not of the correct length, the function won't revert and might return an incorrect value.
* Always use `start` to validate the route length before calling this function.
* Reverts if the extra data length is not even.
*/
function extraDataLength(bytes calldata route, uint256 swapLength) internal pure returns (uint256 length) {
assembly ("memory-safe") {
length := mul(
gt(swapLength, EXTRA_DATA_LENGTH_SIZE),
shr(EXTRA_DATA_LENGTH_SHIFT, calldataload(sub(add(route.offset, route.length), EXTRA_DATA_LENGTH_SIZE)))
)
}
if (length & 1 == 1) revert PackedRoute__InvalidExtraDataLength();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {TokenLib} from "./TokenLib.sol";
/**
* @title RouterLib
* @dev Helper library for router operations, such as validateAndTransfer, transfer, and swap.
* The router must implement a fallback function that uses `validateAndTransfer` to validate the allowance
* and transfer the tokens and functions that uses `swap` to call the router logic to swap tokens.
* The router logic must implement the `swapExactIn` and `swapExactOut` functions to swap tokens and
* use the `transfer` function to transfer tokens from the router according to the route selected.
*/
library RouterLib {
error RouterLib__ZeroAmount();
error RouterLib__InsufficientAllowance(uint256 allowance, uint256 amount);
/**
* @dev Returns the slot for the allowance of a token for a sender from an address.
*/
function getAllowanceSlot(
mapping(bytes32 key => uint256) storage allowances,
address token,
address sender,
address from
) internal pure returns (bytes32 s) {
assembly ("memory-safe") {
mstore(0, shl(96, token))
mstore(20, shl(96, sender))
// Overwrite the last 8 bytes of the free memory pointer with zero,
//which should always be zeros
mstore(40, shl(96, from))
let key := keccak256(0, 60)
mstore(0, key)
mstore(32, allowances.slot)
s := keccak256(0, 64)
}
}
/**
* @dev Validates the allowance of a token for a sender from an address, and transfers the token.
*
* Requirements:
* - The allowance must be greater than or equal to the amount.
* - The amount must be greater than zero.
* - If from is not the router, the token must have been approved for the router.
*/
function validateAndTransfer(mapping(bytes32 key => uint256) storage allowances) internal {
address token;
address from;
address to;
uint256 amount;
uint256 allowance;
uint256 success;
assembly ("memory-safe") {
token := shr(96, calldataload(4))
from := shr(96, calldataload(24))
to := shr(96, calldataload(44))
amount := calldataload(64)
}
bytes32 allowanceSlot = getAllowanceSlot(allowances, token, msg.sender, from);
assembly ("memory-safe") {
allowance := sload(allowanceSlot)
if iszero(lt(allowance, amount)) {
success := 1
sstore(allowanceSlot, sub(allowance, amount))
}
}
if (amount == 0) revert RouterLib__ZeroAmount(); // Also prevent calldata <= 64
if (success == 0) revert RouterLib__InsufficientAllowance(allowance, amount);
from == address(this) ? TokenLib.transfer(token, to, amount) : TokenLib.transferFrom(token, from, to, amount);
}
/**
* @dev Calls the router to transfer tokens from an account to another account.
*
* Requirements:
* - The call must succeed.
* - The target contract must use `validateAndTransfer` inside its fallback function to validate the allowance
* and transfer the tokens accordingly.
*/
function transfer(address router, address token, address from, address to, uint256 amount) internal {
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, shr(32, shl(96, token)))
mstore(24, shl(96, from))
mstore(44, shl(96, to))
mstore(64, amount)
if iszero(call(gas(), router, 0, 0, 96, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
mstore(0x40, m0x40)
}
}
/**
* @dev Swaps tokens using the router logic.
* It will also set the allowance for the logic contract to spend the token from the sender and reset it
* after the swap is done.
*
* Requirements:
* - The logic contract must not be the zero address.
* - The call must succeed.
* - The logic contract must call this contract's fallback function to validate the allowance and transfer the
* tokens.
*/
function swap(
mapping(bytes32 key => uint256) storage allowances,
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
address from,
address to,
bytes calldata route,
bool exactIn,
address logic
) internal returns (uint256 totalIn, uint256 totalOut) {
bytes32 allowanceSlot = getAllowanceSlot(allowances, tokenIn, logic, from);
uint256 length = 256 + route.length; // 32 * 6 + 32 + 32 + route.length
bytes memory data = new bytes(length);
assembly ("memory-safe") {
sstore(allowanceSlot, amountIn)
switch exactIn
// swapExactIn(tokenIn, tokenOut, amountIn, amountOut, from, to, route)
// swapExactOut(tokenIn, tokenOut, amountOut, amountIn, from, to, route)
case 1 { mstore(data, 0xbd084435) }
default { mstore(data, 0xcb7e0007) }
mstore(add(data, 32), tokenIn)
mstore(add(data, 64), tokenOut)
mstore(add(data, 96), amountIn)
mstore(add(data, 128), amountOut)
mstore(add(data, 160), from)
mstore(add(data, 192), to)
mstore(add(data, 224), 224) // 32 * 6 + 32
mstore(add(data, 256), route.length)
calldatacopy(add(data, 288), route.offset, route.length)
if iszero(call(gas(), logic, 0, add(data, 28), add(length, 4), 0, 64)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
totalIn := mload(0)
totalOut := mload(32)
sstore(allowanceSlot, 0)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title TokenLib
* @dev Helper library for token operations, such as balanceOf, transfer, transferFrom, wrap, and unwrap.
*/
library TokenLib {
error TokenLib__BalanceOfFailed();
error TokenLib__WrapFailed();
error TokenLib__UnwrapFailed();
error TokenLib__NativeTransferFailed();
error TokenLib__TransferFromFailed();
error TokenLib__TransferFailed();
error TokenLib__ApproveFailed();
/**
* @dev Returns the balance of a token for an account.
*
* Requirements:
* - The call must succeed.
* - The target contract must return at least 32 bytes.
*/
function balanceOf(address token, address account) internal view returns (uint256 amount) {
uint256 success;
uint256 returnDataSize;
assembly ("memory-safe") {
mstore(0, 0x70a08231) // balanceOf(address)
mstore(32, account)
success := staticcall(gas(), token, 28, 36, 0, 32)
returnDataSize := returndatasize()
amount := mload(0)
}
if (success == 0) _tryRevertWithReason();
// If call failed, and it didn't already bubble up the revert reason, then the return data size must be 0,
// which will revert here with a generic error message
if (returnDataSize < 32) revert TokenLib__BalanceOfFailed();
}
/**
* @dev Returns the balance of a token for an account, or the native balance of the account if the token is the
* native token.
*
* Requirements:
* - The call must succeed (if the token is not the native token).
* - The target contract must return at least 32 bytes (if the token is not the native token).
*/
function universalBalanceOf(address token, address account) internal view returns (uint256 amount) {
return token == address(0) ? account.balance : balanceOf(token, account);
}
/**
* @dev Transfers native tokens to an account.
*
* Requirements:
* - The call must succeed.
*/
function transferNative(address to, uint256 amount) internal {
uint256 success;
assembly ("memory-safe") {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
if (success == 0) {
_tryRevertWithReason();
revert TokenLib__NativeTransferFailed();
}
}
/**
* @dev Transfers tokens from an account to another account.
* This function does not check if the target contract has code, this should be done before calling this function
*
* Requirements:
* - The call must succeed.
*/
function wrap(address wnative, uint256 amount) internal {
uint256 success;
assembly ("memory-safe") {
mstore(0, 0xd0e30db0) // deposit()
success := call(gas(), wnative, amount, 28, 4, 0, 0)
}
if (success == 0) {
_tryRevertWithReason();
revert TokenLib__WrapFailed();
}
}
/**
* @dev Transfers tokens from an account to another account.
* This function does not check if the target contract has code, this should be done before calling this function
*
* Requirements:
* - The call must succeed.
*/
function unwrap(address wnative, uint256 amount) internal {
uint256 success;
assembly ("memory-safe") {
mstore(0, 0x2e1a7d4d) // withdraw(uint256)
mstore(32, amount)
success := call(gas(), wnative, 0, 28, 36, 0, 0)
}
if (success == 0) {
_tryRevertWithReason();
revert TokenLib__UnwrapFailed();
}
}
/**
* @dev Transfers tokens from an account to another account.
*
* Requirements:
* - The call must succeed
* - The target contract must either return true or no value.
* - The target contract must have code.
*/
function transfer(address token, address to, uint256 amount) internal {
uint256 success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, 0xa9059cbb) // transfer(address,uint256)
mstore(32, to)
mstore(64, amount)
success := call(gas(), token, 0, 28, 68, 0, 32)
returnSize := returndatasize()
returnValue := mload(0)
mstore(0x40, m0x40)
}
if (success == 0) {
_tryRevertWithReason();
revert TokenLib__TransferFailed();
}
if (returnSize == 0 ? token.code.length == 0 : returnValue != 1) revert TokenLib__TransferFailed();
}
/**
* @dev Transfers tokens from an account to another account.
*
* Requirements:
* - The call must succeed.
* - The target contract must either return true or no value.
* - The target contract must have code.
*/
function transferFrom(address token, address from, address to, uint256 amount) internal {
uint256 success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
let m0x60 := mload(0x60)
mstore(0, 0x23b872dd) // transferFrom(address,address,uint256)
mstore(32, from)
mstore(64, to)
mstore(96, amount)
success := call(gas(), token, 0, 28, 100, 0, 32)
returnSize := returndatasize()
returnValue := mload(0)
mstore(0x40, m0x40)
mstore(0x60, m0x60)
}
if (success == 0) {
_tryRevertWithReason();
revert TokenLib__TransferFromFailed();
}
if (returnSize == 0 ? token.code.length == 0 : returnValue != 1) revert TokenLib__TransferFromFailed();
}
/**
* @dev Approves an account to spend tokens on behalf of the caller.
*
* Requirements:
* - The call must succeed.
* - The target contract must either return true or no value.
* - The target contract must have code.
*/
function forceApprove(address token, address spender, uint256 amount) internal {
uint256 success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
function approve(token_, amount_) -> success_ {
mstore(64, amount_)
success_ := call(gas(), token_, 0, 28, 68, 64, 32)
}
let m0x40 := mload(0x40)
mstore(0, 0x095ea7b3) // approve(address,uint256)
mstore(32, spender)
success := approve(token, amount)
if iszero(and(eq(mload(64), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
pop(approve(token, 0))
success := approve(token, amount)
}
}
returnSize := returndatasize()
returnValue := mload(64)
mstore(0x40, m0x40)
}
if (success == 0) {
_tryRevertWithReason();
revert TokenLib__ApproveFailed();
}
if (returnSize == 0 ? token.code.length == 0 : returnValue != 1) revert TokenLib__ApproveFailed();
}
/**
* @dev Tries to bubble up the revert reason.
* This function needs to be called only if the call has failed, and will revert if there is a revert reason.
* This function might no revert if there is no revert reason, always use it in conjunction with a revert.
*/
function _tryRevertWithReason() private pure {
assembly ("memory-safe") {
if returndatasize() {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IFeeAdapter {
error FeeAdapter__InvalidProtocolFeeReceiver();
error FeeAdapter__InvalidProtocolFeeShare();
error FeeAdapter__InvalidFeeReceiver();
error FeeAdapter__InvalidFrom();
event ProtocolFeeParametersSet(
address indexed sender, address indexed protocolFeeReceiver, uint96 protocolFeeShare
);
event FeeSent(address indexed token, address indexed allocatee, uint256 feeAmount, uint256 protocolFeeAmount);
function getProtocolFeeRecipient() external view returns (address);
function getProtocolFeeShare() external view returns (uint256);
function setProtocolFeeParameters(address feeReceiver, uint96 feeShare) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PackedRoute} from "./PackedRoute.sol";
import {SafeCast} from "./SafeCast.sol";
import {TokenLib} from "./TokenLib.sol";
/**
* @title PairInteraction
* @dev Library for interacting with Uniswap V2, LFJ, and Uniswap V3 pairs.
*/
library PairInteraction {
using SafeCast for uint256;
using SafeCast for int256;
error PairInteraction__InvalidReturnData();
error PairInteraction__CallFailed();
error PairInteraction__InvalidState();
uint256 internal constant MASK_UINT112 = 0xffffffffffffffffffffffffffff;
uint256 internal constant MIN_SWAP_SQRT_RATIO = 4295128739 + 1;
uint256 internal constant MAX_SWAP_SQRT_RATIO = 1461446703485210103287273052203988822378723970342 - 1;
bytes4 internal constant POE_SWAP_CALLBACK_SELECTOR = 0xfa483e72;
/**
* @dev Returns the ordered reserves of a Uniswap V2 pair.
* If ordered is true, the reserves are returned as (reserve0, reserve1), otherwise as (reserve1, reserve0).
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function getReservesUV2(address pair, bool ordered) internal view returns (uint256 reserveIn, uint256 reserveOut) {
uint256 returnDataSize;
assembly ("memory-safe") {
mstore(0, 0x0902f1ac) // getReserves()
if staticcall(gas(), pair, 28, 4, 0, 64) { returnDataSize := returndatasize() }
switch ordered
case 0 {
reserveIn := and(mload(32), MASK_UINT112)
reserveOut := and(mload(0), MASK_UINT112)
}
default {
reserveIn := and(mload(0), MASK_UINT112)
reserveOut := and(mload(32), MASK_UINT112)
}
}
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a Uniswap V2 pair.
* The function doesn't check that the pair has any code, `getReservesUV2` should be called first to ensure that.
*
* Requirements:
* - The call must succeed.
*/
function swapUV2(address pair, uint256 amount0, uint256 amount1, address recipient) internal {
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0x022c0d9f) // swap(uint256,uint256,address,bytes)
mstore(add(ptr, 32), amount0)
mstore(add(ptr, 64), amount1)
mstore(add(ptr, 96), recipient)
mstore(add(ptr, 128), 128)
mstore(add(ptr, 160), 0)
mstore(0x40, add(ptr, 160)) // update free memory pointer to 160 because 160:192 is 0
success := call(gas(), pair, 0, add(ptr, 28), 164, 0, 0)
}
_bubbleRevert(success);
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a LFJ Legacy LB pair.
* It uses the router v2.0 helper function `getSwapIn` to get the amount required.
*
* Requirements:
* - The call must succeed.
* - The router must have code.
* - The return data must be at least 32 bytes.
*/
function getSwapInLegacyLB(address router, address pair, uint256 amountOut, bool swapForY)
internal
view
returns (uint256 amountIn)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0x5bdd4b7c) // getSwapIn(address,uint256,bool)
mstore(add(ptr, 32), pair)
mstore(add(ptr, 64), amountOut)
mstore(add(ptr, 96), swapForY)
mstore(0x40, add(ptr, 128))
success := staticcall(gas(), router, add(ptr, 28), 100, 0, 32)
returnDataSize := returndatasize()
amountIn := mload(0)
}
_bubbleRevert(success);
if (returnDataSize < 32) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Swaps tokenIn for tokenOut in a LFJ Legacy LB pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function swapLegacyLB(address pair, bool swapForY, address recipient) internal returns (uint256 amountOut) {
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, 0x53c059a0) // swap(bool,address)
mstore(32, swapForY)
mstore(64, recipient)
success := call(gas(), pair, 0, 28, 68, 0, 64)
returnDataSize := returndatasize()
switch swapForY
case 0 { amountOut := mload(0) }
default { amountOut := mload(32) }
mstore(0x40, m0x40)
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a LFJ LB pair (v2.0 and v2.1).
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function getSwapInLB(address pair, uint256 amountOut, bool swapForY)
internal
view
returns (uint256 amountIn, uint256 amountLeft)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, 0xabcd7830) // getSwapIn(uint128,bool)
mstore(32, amountOut)
mstore(64, swapForY)
success := staticcall(gas(), pair, 28, 68, 0, 64)
returnDataSize := returndatasize()
amountIn := mload(0)
amountLeft := mload(32)
mstore(0x40, m0x40)
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Swaps tokenIn for tokenOut in a LFJ LB pair (v2.0 and v2.1).
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 32 bytes.
*/
function swapLB(address pair, bool swapForY, address recipient) internal returns (uint256 amountOut) {
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, 0x53c059a0) // swap(bool,address)
mstore(32, swapForY)
mstore(64, recipient)
success := call(gas(), pair, 0, 28, 68, 0, 32)
returnDataSize := returndatasize()
switch swapForY
case 0 { amountOut := shr(128, mload(16)) }
default { amountOut := shr(128, mload(0)) }
mstore(0x40, m0x40)
}
_bubbleRevert(success);
if (returnDataSize < 32) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a Uniswap V3 pair.
* The function actually tries to swap token but revert before having to send any token.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must match the expected format, which is to revert with a
* `UniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta)` error.
*/
function getSwapInUV3(address pair, bool zeroForOne, uint256 amountOut) internal returns (uint256 amountIn) {
(uint256 success, uint256 ptr) = callSwapUV3(pair, address(this), zeroForOne, -amountOut.toInt256(), address(0));
assembly ("memory-safe") {
// data = [selector: 4][amount0: 32][amount1: 32][data_ptr: 32][data_length: 32][data_value: 32]
switch and(iszero(success), eq(returndatasize(), 164))
case 0 {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
default {
switch zeroForOne
case 0 { amountIn := mload(add(ptr, 36)) }
default { amountIn := mload(add(ptr, 4)) }
}
}
}
/**
* @dev Swaps tokenIn for tokenOut in a Uniswap V3 pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function swapUV3(address pair, address recipient, bool zeroForOne, uint256 amountIn, address tokenIn)
internal
returns (uint256 actualAmountOut, uint256 actualAmountIn, uint256 expectedHash)
{
(uint256 success, uint256 ptr) = callSwapUV3(pair, recipient, zeroForOne, amountIn.toInt256(), tokenIn);
_bubbleRevert(success);
uint256 returnDataSize;
assembly ("memory-safe") {
returnDataSize := returndatasize()
mstore(add(ptr, 64), tokenIn)
expectedHash := keccak256(ptr, 96)
switch zeroForOne
case 0 {
actualAmountOut := mload(ptr)
actualAmountIn := mload(add(ptr, 32))
}
default {
actualAmountIn := mload(ptr)
actualAmountOut := mload(add(ptr, 32))
}
actualAmountOut := sub(0, actualAmountOut) // Invert the sign
}
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Returns the hash of the amount deltas and token address for a Uniswap V3 pair.
* The hash is used to check that the callback contains the expected data.
*/
function hashUV3(int256 amount0Delta, int256 amount1Delta, address token) internal pure returns (uint256 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0, amount0Delta)
mstore(32, amount1Delta)
mstore(64, token)
hash := keccak256(0, 96)
mstore(0x40, ptr)
}
}
/**
* @dev Decodes the amount deltas and token address from the callback data of a Uniswap V3 pair.
*
* Requirements:
* - The data must be exactly 164 bytes.
*/
function decodeUV3CallbackData(bytes calldata data)
internal
pure
returns (int256 amount0Delta, int256 amount1Delta, address token)
{
// data = [selector: 4][amount0: 32][amount1: 32][data_ptr: 32][data_length: 32][data_value: 32]
if (data.length != 164) revert PairInteraction__InvalidReturnData();
assembly ("memory-safe") {
amount0Delta := calldataload(4)
amount1Delta := calldataload(36)
token := calldataload(132)
}
}
/**
* @dev Calls the `swap` function of a Uniswap V3 pair.
* This function doesn't revert on failure, it returns a success flag instead.
* It also returns the pointer to the return data.
*
* Requirements:
* - The call must succeed.
*/
function callSwapUV3(address pair, address recipient, bool zeroForOne, int256 deltaAmount, address tokenIn)
internal
returns (uint256 success, uint256 ptr)
{
uint256 priceLimit = zeroForOne ? MIN_SWAP_SQRT_RATIO : MAX_SWAP_SQRT_RATIO;
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(ptr, 0x128acb08) // swap(address,bool,int256,uint160,bytes)
mstore(add(ptr, 32), recipient)
mstore(add(ptr, 64), zeroForOne)
mstore(add(ptr, 96), deltaAmount)
mstore(add(ptr, 128), priceLimit)
mstore(add(ptr, 160), 160)
mstore(add(ptr, 192), 32)
mstore(add(ptr, 224), tokenIn)
mstore(0x40, add(ptr, 256))
success := call(gas(), pair, 0, add(ptr, 28), 228, ptr, 68)
}
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a LFJ Token Mill pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 32 bytes.
*/
function getSwapInTM(address pair, uint256 amountOut, bool swapForY)
internal
view
returns (uint256 amountIn, uint256 actualAmountOut)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, 0xcd56aadc) // getDeltaAmounts(int256,bool)
mstore(32, sub(0, amountOut))
mstore(64, swapForY)
success := staticcall(gas(), pair, 28, 68, 0, 64)
returnDataSize := returndatasize()
switch swapForY
case 0 {
amountIn := mload(32)
actualAmountOut := mload(0)
}
default {
amountIn := mload(0)
actualAmountOut := mload(32)
}
amountIn := add(amountIn, 1) // Add 1 wei to account for rounding errors
actualAmountOut := sub(0, actualAmountOut) // Invert the sign
mstore(0x40, m0x40)
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Swaps tokenIn for tokenOut in a LFJ Token Mill pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function swapTM(address pair, address recipient, uint256 amountIn, bool swapForY)
internal
returns (uint256 amountOut, uint256 actualAmountIn)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0xdc35ff77) // swap(address,int256,bool,bytes,address)
mstore(add(ptr, 32), recipient)
mstore(add(ptr, 64), amountIn)
mstore(add(ptr, 96), swapForY)
mstore(add(ptr, 128), 160)
mstore(add(ptr, 160), 0)
mstore(add(ptr, 192), 0)
mstore(0x40, add(ptr, 160)) // update free memory pointer to 160 because 160:224 is 0
success := call(gas(), pair, 0, add(ptr, 28), 196, 0, 64)
returnDataSize := returndatasize()
switch swapForY
case 0 {
actualAmountIn := mload(32)
amountOut := mload(0)
}
default {
actualAmountIn := mload(0)
amountOut := mload(32)
}
amountOut := sub(0, amountOut) // Invert the sign
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Returns the limit price bounds of a LFJ Token Mill V2 pair depending on the swap direction.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 96 bytes.
*/
function getSqrtLimitPriceInTMV2(address pair, bool swapForY) internal view returns (uint256 sqrtLimitPriceX96) {
uint256 returnDataSize;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0, 0xa0b6ea01) // getSqrtRatiosBounds()
if staticcall(gas(), pair, 28, 4, 0, 96) { returnDataSize := returndatasize() }
switch swapForY
case 0 { sqrtLimitPriceX96 := mload(64) }
default { sqrtLimitPriceX96 := mload(0) }
mstore(0x40, ptr)
}
if (returnDataSize < 96) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a LFJ Token Mill V2 pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function getSwapInTMV2(address pair, uint256 amountOut, bool swapForY)
internal
view
returns (uint256 amountIn, uint256 actualAmountOut)
{
uint256 sqrtLimitPriceX96 = getSqrtLimitPriceInTMV2(pair, swapForY);
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0x3419341c) // getDeltaAmounts(bool,int256,uint256)
mstore(add(ptr, 32), swapForY)
mstore(add(ptr, 64), sub(0, amountOut))
mstore(add(ptr, 96), sqrtLimitPriceX96)
mstore(0x40, add(ptr, 128))
success := staticcall(gas(), pair, add(ptr, 28), 100, 0, 64)
returnDataSize := returndatasize()
switch swapForY
case 0 {
amountIn := mload(32)
actualAmountOut := mload(0)
}
default {
amountIn := mload(0)
actualAmountOut := mload(32)
}
actualAmountOut := sub(0, actualAmountOut) // Invert the sign
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Swaps tokenIn for tokenOut in a LFJ Token Mill V2 pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function swapTMV2(address pair, address recipient, uint256 amountIn, bool swapForY)
internal
returns (uint256 amountOut, uint256 actualAmountIn)
{
uint256 sqrtLimitPriceX96 = getSqrtLimitPriceInTMV2(pair, swapForY);
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0xabb1db2a) // swap(address,bool,int256,uint256)
mstore(add(ptr, 32), recipient)
mstore(add(ptr, 64), swapForY)
mstore(add(ptr, 96), amountIn)
mstore(add(ptr, 128), sqrtLimitPriceX96)
mstore(0x40, add(ptr, 160))
success := call(gas(), pair, 0, add(ptr, 28), 132, 0, 64)
returnDataSize := returndatasize()
switch swapForY
case 0 {
actualAmountIn := mload(32)
amountOut := mload(0)
}
default {
actualAmountIn := mload(0)
amountOut := mload(32)
}
amountOut := sub(0, amountOut) // Invert the sign
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Decodes the extra data for a Uniswap V4 pool from the route.
* The dataOffset is the offset in the route where the extra data starts in the route.
* The extraData must have an even length.
* Requirements:
* - The extra data must be formatted as follows:
* [fee: 3][tickSpacing: 3][nativeFlag: 1][hooks: 20][hookData length: 3][hookData: variable]
* (30 bytes + hookData length)
* - The fee is the fee of the pool in hundredths of a bip, i.e. 1e-6. If the highest bit is 1,
* the pool has a dynamic fee and must be exactly equal to 0x800000
* - The tickSpacing is the pool tick spacing.
* - The nativeFlag indicates if one of the tokens is native
* (1: tokenIn is native, 2: tokenOut is native, otherwise both are ERC20).
* - The hooks is the address of the hooks contract.
* - The hookData length is the length of the hookData in bytes (must be an even number for safety)
* - The hookData is the data to be passed to the hooks contract (can be empty).
*/
function prepareDataUV4(
bytes calldata route,
bytes32 value,
uint256 dataOffset,
bool zeroForOne,
int256 deltaAmount
) internal pure returns (bytes memory data) {
unchecked {
uint256 nativeFlag;
uint256 extraDataOffset;
assembly ("memory-safe") {
extraDataOffset := add(route.offset, dataOffset)
nativeFlag := shr(248, calldataload(add(extraDataOffset, 6)))
}
// 1 -> tokenIn is native, tokenOut is ERC20
// 2 -> tokenIn is ERC20, tokenOut is native
// any other value -> both tokens are ERC20
address token0 = nativeFlag == 1 ? address(0) : PackedRoute.token(route, PackedRoute.tokenInId(value));
address token1 = nativeFlag == 2 ? address(0) : PackedRoute.token(route, PackedRoute.tokenOutId(value));
(token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0);
uint256 priceLimit = zeroForOne ? MIN_SWAP_SQRT_RATIO : MAX_SWAP_SQRT_RATIO;
// data = [offset: 32][length: 32]
// {PoolKey: [token0: 32][token1: 32][fee: 32][tickSpacing: 32][hooks: 32]}
// {SwapParams: [zeroForOne: 32][amountSpecified: 32][priceLimit: 32]}
// {HookData: [offset: 32][length: 32][data: variable]}
// Total length = 32 * 2 + 32 * 5 + 32 * 3 + 32 * 2 + hookData length = 64 + 320 + hookData length
assembly ("memory-safe") {
let hookDataLength := shr(232, calldataload(add(extraDataOffset, 27)))
// Round up to the next complete word (320 + 31 = 351)
let dataLength := shl(5, shr(5, add(hookDataLength, 351)))
data := mload(0x40)
mstore(0x40, add(data, add(96, dataLength))) // update free memory pointer
mstore(data, add(64, dataLength)) // length
mstore(add(data, 32), 0x20) // offset
mstore(add(data, 64), dataLength) // length
mstore(add(data, 96), token0) // PoolKey.token0
mstore(add(data, 128), token1) // PoolKey.token1
mstore(add(data, 160), shr(232, calldataload(extraDataOffset))) // PoolKey.fee
mstore(add(data, 192), sar(232, calldataload(add(extraDataOffset, 3)))) // PoolKey.tickSpacing
mstore(add(data, 224), shr(96, calldataload(add(extraDataOffset, 7)))) // PoolKey.hooks
mstore(add(data, 256), zeroForOne) // SwapParams.zeroForOne
mstore(add(data, 288), deltaAmount) // SwapParams.amountSpecified
mstore(add(data, 320), priceLimit) // SwapParams.priceLimit
mstore(add(data, 352), 288) // HookData.offset
mstore(add(data, 384), hookDataLength) // HookData.length
calldatacopy(add(data, 416), add(extraDataOffset, 30), hookDataLength) // HookData.data
}
}
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a Uniswap V4 pool.
* The function actually tries to swap token but revert before having to send any token.
*
* Requirements:
* - The call must revert with `(int256 amount0Delta, int256 amount1Delta)` data.
*/
function getSwapInUV4(
bytes calldata route,
bytes32 value,
address manager,
address dataOffset,
bool zeroForOne,
uint256 amountOut
) internal returns (uint256 amountIn) {
(uint256 success, int256 deltaIn,) =
callSwapUV4(route, value, manager, dataOffset, zeroForOne, amountOut.toInt256());
if (success != 0) revert PairInteraction__InvalidState(); // Revert if the call succeeded (invalid state)
unchecked {
return (-deltaIn).toUint256(); // Invert the sign
}
}
/**
* @dev Swaps tokenIn for tokenOut in a Uniswap V4 pool.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
*/
function swapUV4(
bytes calldata route,
bytes32 value,
address manager,
address dataOffset,
bool zeroForOne,
uint256 amountIn
) internal returns (uint256 amountOut, uint256 actualAmountIn) {
(uint256 success, int256 deltaIn, int256 deltaOut) =
callSwapUV4(route, value, manager, dataOffset, zeroForOne, -amountIn.toInt256());
_bubbleRevert(success);
unchecked {
return (deltaOut.toUint256(), (-deltaIn).toUint256()); // Invert the sign of deltaIn
}
}
/**
* @dev Calls the `unlock` function of a Uniswap V4 pool.
* This function doesn't revert on failure, it returns a success flag instead.
* It also returns the actual amount in and out of the swap.
* Requirements:
* - The call must return exactly 64 bytes of data.
*/
function callSwapUV4(
bytes calldata route,
bytes32 value,
address manager,
address dataOffset,
bool zeroForOne,
int256 deltaAmount
) private returns (uint256 success, int256 deltaIn, int256 deltaOut) {
bytes memory data = prepareDataUV4(route, value, uint256(uint160(dataOffset)), zeroForOne, deltaAmount);
uint256 returnDataSize;
assembly ("memory-safe") {
let length := mload(data)
mstore(data, 0x48c89491) // unlock(bytes)
success := call(gas(), manager, 0, add(data, 28), add(length, 4), data, 128)
returnDataSize := returndatasize()
switch zeroForOne
case 0 {
deltaIn := mload(add(data, 96))
deltaOut := mload(add(data, 64))
}
default {
deltaIn := mload(add(data, 64))
deltaOut := mload(add(data, 96))
}
}
if (returnDataSize != 128) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Callback function for Uniswap V4 swaps.
* This function should be called after the `unlock` callback from the Uniswap V4 manager.
* This function will then call the swap function and settle the amounts.
*
* Requirements:
* - The call must succeed.
* - The caller must be the Uniswap V4 manager.
*/
function swapUV4Callback(bytes calldata data, address recipient, address wnative)
internal
returns (int256 amount0, int256 amount1)
{
address token0;
address token1;
uint256 success;
uint256 returnDataSize;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0xf3cd914c) // swap((address,address,uint24,int24,address),(bool,int256,uint160),bytes)
calldatacopy(add(ptr, 32), add(data.offset, 68), sub(data.length, 68))
mstore(0x40, shl(5, shr(5, sub(add(ptr, data.length), 37)))) // Round up to the next complete word
success := call(gas(), caller(), 0, add(ptr, 28), sub(data.length, 64), 0, 32)
returnDataSize := returndatasize()
let amounts := mload(0)
amount0 := sar(128, amounts)
amount1 := signextend(15, amounts)
// Revert with amounts to decode them within getSwapInUV4
if iszero(recipient) {
mstore(0, 0x20)
mstore(32, 0x40)
mstore(64, amount0)
mstore(96, amount1)
revert(0, 128)
}
token0 := mload(add(ptr, 32))
token1 := mload(add(ptr, 64))
}
_bubbleRevert(success);
if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
settleOrTakeUV4(token0, recipient, amount0, wnative);
settleOrTakeUV4(token1, recipient, amount1, wnative);
}
/**
* @dev Settles or takes the amount for a token in a Uniswap V4 swap.
* If delta is negative, it means we need to settle the amount (send it to the pair).
* If delta is positive, it means we need to take the amount (receive it from the pair).
*
* Requirements:
* - The call must succeed.
* - The caller must be the Uniswap V4 manager.
*/
function settleOrTakeUV4(address token, address recipient, int256 delta, address wnative) internal {
if (delta < 0) {
uint256 success;
// Settle the amount by calling sync, then transferring the tokens and finally calling settle
assembly ("memory-safe") {
delta := sub(0, delta)
mstore(0, 0xa5841194) // sync(address)
mstore(32, token)
success := call(gas(), caller(), 0, 28, 64, 0, 0)
}
_bubbleRevert(success);
uint256 nativeValue;
if (token == address(0)) {
// The token is native, unwrap wnative and send it
TokenLib.unwrap(wnative, (nativeValue = delta.toUint256()));
} else {
TokenLib.transfer(token, msg.sender, delta.toUint256());
}
uint256 amount;
uint256 returnDataSize;
assembly ("memory-safe") {
mstore(0, 0x11da60b4) // settle()
success := call(gas(), caller(), nativeValue, 28, 4, 0, 32)
returnDataSize := returndatasize()
amount := mload(0)
}
_bubbleRevert(success);
if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
} else if (delta > 0) {
// Take the amount by calling take, if token is native it will be wrapped after being received
// (within the receive function)
address to = token == address(0) ? address(this) : recipient;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0x0b0d9c09) // take(address,address,uint256)
mstore(add(ptr, 32), token)
mstore(add(ptr, 64), to)
mstore(add(ptr, 96), delta)
success := call(gas(), caller(), 0, add(ptr, 28), 100, 0, 0)
mstore(0x40, add(ptr, 128))
}
_bubbleRevert(success);
if (to != recipient) {
// If the token is native, use this as a temporary address to receive the native token
// then wrap it, and send it to the actual recipient
TokenLib.transfer(wnative, recipient, delta.toUint256());
}
}
}
/**
* @dev Returns the amount of tokenIn required to get amountOut from a ByReal pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be exactly 32 bytes.
*/
function getSwapInByReal(address pair, uint256 amountIn, address tokenIn)
internal
view
returns (uint256 amountOut)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let m0x40 := mload(0x40)
mstore(0, 0x1125f13f) // getAmountIn(uint256,address)
mstore(32, amountIn)
mstore(64, tokenIn)
success := staticcall(gas(), pair, 28, 68, 0, 32)
returnDataSize := returndatasize()
amountOut := mload(0)
mstore(0x40, m0x40)
}
_bubbleRevert(success);
if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Swaps tokenIn for tokenOut in a ByReal pair.
* The function doesn't check that the pair has any code, `getSwapInOrOutByReal` should be called first to ensure
* that.
*
* Requirements:
* - The call must succeed.
*/
function swapByReal(address pair, address recipient, uint256 amountIn, address tokenIn)
internal
returns (uint256 amountOut)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0xf946c2a2) // swap(address,bool,uint256,address)
mstore(add(ptr, 32), tokenIn)
mstore(add(ptr, 64), 1) // always givenIn = true
mstore(add(ptr, 96), amountIn)
mstore(add(ptr, 128), recipient)
success := call(gas(), pair, 0, add(ptr, 28), 132, 0, 32)
returnDataSize := returndatasize()
amountOut := mload(0)
mstore(0x40, add(ptr, 160))
}
_bubbleRevert(success);
if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Swaps tokenIn for tokenOut in a POE pair.
*
* Requirements:
* - The call must succeed.
* - The pair must have code.
* - The return data must be at least 64 bytes.
*/
function swapPOE(address pair, address recipient, uint256 amountIn, bool swapForY, address tokenIn)
internal
returns (uint256 actualAmountOut, uint256 actualAmountIn, uint256 expectedHash)
{
uint256 returnDataSize;
uint256 success;
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, 0x80c2c6ef) // swap(address,bool,uint256,bytes)
mstore(add(ptr, 32), recipient)
mstore(add(ptr, 64), swapForY)
mstore(add(ptr, 96), amountIn)
mstore(add(ptr, 128), 128)
mstore(add(ptr, 160), 32)
mstore(add(ptr, 192), tokenIn)
success := call(gas(), pair, 0, add(ptr, 28), 196, ptr, 64)
returnDataSize := returndatasize()
mstore(add(ptr, 64), tokenIn)
expectedHash := keccak256(ptr, 96)
switch swapForY
case 0 {
actualAmountOut := mload(ptr)
actualAmountIn := mload(add(ptr, 32))
}
case 1 {
actualAmountIn := mload(ptr)
actualAmountOut := mload(add(ptr, 32))
}
actualAmountOut := sub(0, actualAmountOut) // Invert the sign
}
_bubbleRevert(success);
if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
}
/**
* @dev Bubbles up a revert if the success flag is false.
* It copies the return data to memory and reverts with it.
* If there is no return data, it reverts with a `PairInteraction__CallFailed` error.
*/
function _bubbleRevert(uint256 success) private pure {
assembly ("memory-safe") {
if iszero(success) {
if returndatasize() {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
mstore(0, 0x824d2235) // PairInteraction__CallFailed()
revert(0x1c, 4)
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SafeCast
* @dev Library for safely casting between different integer types.
*/
library SafeCast {
error SafeCast__Overflow();
/**
* @dev Converts a uint256 to int256, reverting on overflow.
* Requirements:
* - The input must be less than or equal to type(int256).max.
*/
function toInt256(uint256 x) internal pure returns (int256) {
// forge-lint: disable-next-line(unsafe-typecast)
if (int256(x) >= 0) return int256(x);
revert SafeCast__Overflow();
}
/**
* @dev Converts an int256 to uint256, reverting on underflow.
* Requirements:
* - The input must be greater than or equal to 0.
*/
function toUint256(int256 x) internal pure returns (uint256) {
// forge-lint: disable-next-line(unsafe-typecast)
if (x >= 0) return uint256(x);
revert SafeCast__Overflow();
}
}{
"remappings": [
"@forge-std/contracts/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 1000000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"routerV2_0","type":"address"},{"internalType":"address","name":"uniswapV4Manager","type":"address"},{"internalType":"address","name":"wnative","type":"address"},{"internalType":"address","name":"protocolFeeReceiver","type":"address"},{"internalType":"uint96","name":"protocolFeeShare","type":"uint96"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FeeAdapter__InvalidFeeReceiver","type":"error"},{"inputs":[],"name":"FeeAdapter__InvalidFrom","type":"error"},{"inputs":[],"name":"FeeAdapter__InvalidProtocolFeeReceiver","type":"error"},{"inputs":[],"name":"FeeAdapter__InvalidProtocolFeeShare","type":"error"},{"inputs":[],"name":"PackedRoute__InvalidExtraDataLength","type":"error"},{"inputs":[],"name":"PackedRoute__InvalidLength","type":"error"},{"inputs":[],"name":"PairInteraction__InvalidReturnData","type":"error"},{"inputs":[],"name":"RouterAdapter__InsufficientLBLiquidity","type":"error"},{"inputs":[],"name":"RouterAdapter__InsufficientTMLiquidity","type":"error"},{"inputs":[],"name":"RouterAdapter__InsufficientTMV2Liquidity","type":"error"},{"inputs":[],"name":"RouterAdapter__InvalidId","type":"error"},{"inputs":[],"name":"RouterAdapter__OnlyWnative","type":"error"},{"inputs":[],"name":"RouterAdapter__UnexpectedAmountIn","type":"error"},{"inputs":[],"name":"RouterAdapter__UnexpectedCallback","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"}],"name":"RouterLogic__ExceedsMaxAmountIn","type":"error"},{"inputs":[],"name":"RouterLogic__ExcessBalanceUnused","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"RouterLogic__InsufficientAmountOut","type":"error"},{"inputs":[],"name":"RouterLogic__InsufficientTokens","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidAmount","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidFeeData","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidFeePercent","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidRouter","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidTokenIn","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidTokenOut","type":"error"},{"inputs":[],"name":"RouterLogic__OnlyRouter","type":"error"},{"inputs":[],"name":"RouterLogic__OnlyRouterOwner","type":"error"},{"inputs":[],"name":"RouterLogic__TransferTaxNotSupported","type":"error"},{"inputs":[],"name":"RouterLogic__ZeroSwap","type":"error"},{"inputs":[],"name":"SafeCast__Overflow","type":"error"},{"inputs":[],"name":"TokenLib__ApproveFailed","type":"error"},{"inputs":[],"name":"TokenLib__BalanceOfFailed","type":"error"},{"inputs":[],"name":"TokenLib__NativeTransferFailed","type":"error"},{"inputs":[],"name":"TokenLib__TransferFailed","type":"error"},{"inputs":[],"name":"TokenLib__UnwrapFailed","type":"error"},{"inputs":[],"name":"TokenLib__WrapFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"allocatee","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeAmount","type":"uint256"}],"name":"FeeSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"protocolFeeReceiver","type":"address"},{"indexed":false,"internalType":"uint96","name":"protocolFeeShare","type":"uint96"}],"name":"ProtocolFeeParametersSet","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"getProtocolFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeeShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"feeReceiver","type":"address"},{"internalType":"uint96","name":"feeShare","type":"uint96"}],"name":"setProtocolFeeParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"route","type":"bytes"}],"name":"swapExactIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"route","type":"bytes"}],"name":"swapExactOut","outputs":[{"internalType":"uint256","name":"totalIn","type":"uint256"},{"internalType":"uint256","name":"totalOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
61010060405261dead60015534801561001757600080fd5b5060405161378038038061378083398101604081905261003691610164565b8484848484610045828261009b565b50506001600160a01b03928316608052821660c052811660a05286163b600003610082576040516320ee910f60e21b815260040160405180910390fd5b5050506001600160a01b0390921660e052506101e99050565b6001600160a01b0382166100c25760405163ec049e3760e01b815260040160405180910390fd5b612710816001600160601b031611156100ee576040516318fd95d160e11b815260040160405180910390fd5b6001600160a01b038216600160a01b6001600160601b038316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b80516001600160a01b038116811461015f57600080fd5b919050565b60008060008060008060c0878903121561017d57600080fd5b61018687610148565b955061019460208801610148565b94506101a260408801610148565b93506101b060608801610148565b92506101be60808801610148565b60a08801519092506001600160601b03811681146101db57600080fd5b809150509295509295509295565b60805160a05160c05160e05161353d61024360003960008181610cc801528181610e2601528181611a780152611ff601526000818160860152818160b001526103bc0152600061015a015260006122b3015261353d6000f3fe6080604052600436106100695760003560e01c8063bd08443511610043578063bd08443514610274578063c60bc203146102a9578063cb7e0007146102c9576100d7565b8063192bcad3146101d657806362c067671461021f57806372c8fc0e1461023f576100d7565b366100d7573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100d5576100d57f0000000000000000000000000000000000000000000000000000000000000000346102e9565b005b3480156100e357600080fd5b50600154600090369060609061ff00811681831c6104008214801561011d57503373ffffffffffffffffffffffffffffffffffffffff8216145b156101365761012c8686610347565b93505050506101cb565b6107008214801561017c57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b1561018c5761012c8686836103af565b610900821480156101b257503373ffffffffffffffffffffffffffffffffffffffff8216145b156101c15761012c8686610422565b3660008037366000fd5b915050805190602001f35b3480156101e257600080fd5b506000547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166040519081526020015b60405180910390f35b34801561022b57600080fd5b506100d561023a3660046131ed565b6104ad565b34801561024b57600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610216565b34801561028057600080fd5b5061029461028f36600461322e565b6104e6565b60408051928352602083019190915201610216565b3480156102b557600080fd5b506100d56102c43660046132ff565b61073c565b3480156102d557600080fd5b506102946102e436600461322e565b610752565b600063d0e30db06000526000806004601c85875af190508060000361034257610310610a42565b6040517fdc0c8cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b606060008060006103588686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561039481336000861361038d5784610aa6565b855b610aa6565b50506040805160008152602081019091529150505b92915050565b60606000806103e08686867f0000000000000000000000000000000000000000000000000000000000000000610b85565b60408051602081810152808201919091526060810183905260808101829052919350915060a001604051602081830303815290604052925050505b9392505050565b606060008060006104338686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561046881336000861361038d5784610aa6565b604080517ffa483e7200000000000000000000000000000000000000000000000000000000602082015201604051602081830303815290604052935050505092915050565b6104b5610cc6565b73ffffffffffffffffffffffffffffffffffffffff8316156104dc57610342838383610aa6565b6103428282610db9565b60008061057360405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b61057f85858d8d610e09565b60608501526040840181905260208401919091528183526105a491879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152908116608083018190528a918d16900361060d5760006127108360c001518302816105f3576105f3613378565b049050808203915061060b8d8a8560a001518461117b565b505b60006106698284604001518b8f73ffffffffffffffffffffffffffffffffffffffff16876080015173ffffffffffffffffffffffffffffffffffffffff1614610656578b610658565b305b876020015188606001518d8d6112d8565b90508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036106e05760006127108460c001518302816106bb576106bb613378565b04905080820391506106d38d308660a001518461117b565b6106de8d8a84610aa6565b505b89811015610729576040517fc18a3e5800000000000000000000000000000000000000000000000000000000815260048101829052602481018b90526044015b60405180910390fd5b999c999b50989950505050505050505050565b610744610cc6565b61074e82826113de565b5050565b6000806107df60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6107eb85858d8d610e09565b606085015260408401819052602084019190915281835261081091879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152166080820152600185013560f81c15610877576040517fe4c5b0d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808990508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036108da573091508260c00151612710036127108202816108d2576108d2613378565b0490506108de565b8791505b6000806108fb838660400151876060015188602001518d8d6114f2565b915091508e73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16036109735760008560c00151612710038660c0015184028161095557610955613378565b049050808301925061097186608001518d8860a001518461117b565b505b8c8211156109b7576040517f20cd355100000000000000000000000000000000000000000000000000000000815260048101839052602481018e9052604401610720565b6109d38b8583886020015189604001518a606001518f8f61175f565b8d73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff1603610a2e5760008c84039050610a218f308860a001518461117b565b610a2c8f8c8f610aa6565b505b509d999c50989a5050505050505050505050565b3d15610a52573d6000803e3d6000fd5b565b6000808060a48414610a92576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505060043593602435935060843592509050565b600080600060405163a9059cbb6000528560205284604052602060006044601c60008b5af193503d92506000519150806040525082600003610b1c57610aea610a42565b6040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115610b2c578060011415610b46565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b60008060008060008060405163f3cd914c815260448a0360448c0160208301377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb828c010116604052602060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08c01601c840182335af1600051608081901d9850600f0b965092503d915088610c4f5760206000526040602052866040528560605260806000fd5b602081015194506040810151935050610c67826117b7565b80602014610ca1576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cad8489888a6117dd565b610cb98389878a6117dd565b5050505094509492505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5591906133a7565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a52576040517f303693ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600085875af190508060000361034257610dd7610a42565b6040517fa01b460600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080803373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610e7d576040517f4b2fd34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e878888611974565b919450925090506002821015610ec9576040517f7c741b7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a830188840135604081901c61ff0016610f49576002831015610f19576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b939450927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101908390610f83565b82600003610f83576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028a013560601c73ffffffffffffffffffffffffffffffffffffffff891614610fd9576040517f80e4580000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87166110138b8b6110006001896133f3565b601402919091016002013560601c919050565b73ffffffffffffffffffffffffffffffffffffffff1614611060576040517ffc37688f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050945094509450949050565b6000808084156111715786850135606081901c92506110908160501c61ffff1690565b915060006110a18260381c60ff1690565b90506002601482028a01013560601c94506110bf8260301c60ff1690565b81186110cf8360401c61ffff1690565b171515806110f1575080158015906110f157506110ed6001876133f3565b8114155b15611128576040517fee393ce900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158061113757506127108310155b1561116e576040517fa4c2399e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b9450945094915050565b80156112d25773ffffffffffffffffffffffffffffffffffffffff82166111ce576040517fae0edfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805461271090611206907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1684613406565b611210919061341d565b9050600061121e82846133f3565b905080156112325761123286868684611a46565b811561125f5760005461125f908790879073ffffffffffffffffffffffffffffffffffffffff1685611a46565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f77c197a1ae17318af19ae49d654709f9fe13e90a7fb70e1bdd5f665f6b9bce8285856040516112c7929190918252602082015260400190565b60405180910390a350505b50505050565b6000808867ffffffffffffffff8111156112f4576112f4613349565b60405190808252806020026020018201604052801561131d578160200160208202803683370190505b509050600060018a0390508a8260008151811061133c5761133c613458565b6020026020010181815250506000805b8781101561137957601a890198870135915061136d838888878f8f88611aa0565b909c019b60010161134c565b5082828151811061138c5761138c613458565b60200260200101519350838c146113cf576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661142b576040517fec049e3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710816bffffffffffffffffffffffff161115611475576040517f31fb2ba200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216740100000000000000000000000000000000000000006bffffffffffffffffffffffff8316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b60006060601a86028501945060008667ffffffffffffffff81111561151957611519613349565b604051908082528060200260200182016040528015611542578160200160208202803683370190505b50905060008867ffffffffffffffff81111561156057611560613349565b604051908082528060200260200182016040528015611589578160200160208202803683370190505b509050898161159960018c6133f3565b815181106115a9576115a9613458565b6020908102919091010152896000895b80156116f7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe68a81019a8a0101359150600060ff603084901c16905060006116058460381c60ff1690565b9050600061271061161a8660501c61ffff1690565b88858151811061162c5761162c613458565b602002602001015161163e9190613406565b611648919061341d565b90508087848151811061165d5761165d613458565b6020026020010181815161167191906133f3565b90525061167d81611be9565b6116898c8c8784611c3c565b99508987838151811061169e5761169e613458565b602002602001018181516116b29190613487565b9052506116be8a611be9565b89886116c98661349a565b955085815181106116dc576116dc613458565b602002602001018181525050808a03860195505050506115b9565b508260008151811061170b5761170b613458565b6020026020010151955085821461174e576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509192505050965096945050505050565b6000805b848110156117ab57601a87018785013580935081985050506117a38484888d8d878e888151811061179657611796613458565b6020026020010151611dd2565b600101611763565b50505050505050505050565b806117da573d156117cc573d6000803e3d6000fd5b63824d22356000526004601cfd5b50565b60008212156118c357600082600003925063a5841194600052846020526000806040601c6000335af19050611811816117b7565b600073ffffffffffffffffffffffffffffffffffffffff8616611848576118438361183b86611e6c565b925082611eab565b611856565b611856863361038f87611e6c565b6000806311da60b4600052602060006004601c86335af193503d90506000519150611880846117b7565b806020146118ba576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050506112d2565b60008213156112d257600073ffffffffffffffffffffffffffffffffffffffff8516156118f057836118f2565b305b90506000604051630b0d9c0981528660208201528260408201528460608201526000806064601c84016000335af191506080810160405250611933816117b7565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610b7d57610b7d838661038f87611e6c565b813560f81c6014810260028101919060009084037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01838510156119e4576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a810615611a385760036119fa878784611f09565b019003601a810615611a38576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81049150509250925092565b3073ffffffffffffffffffffffffffffffffffffffff841603611a7357611a6e848383610aa6565b6112d2565b6112d27f000000000000000000000000000000000000000000000000000000000000000085858585611f74565b600080611ab18360401c61ffff1690565b90506000611ac28460381c60ff1690565b90506000611ad38560301c60ff1690565b905060008b8214611ae45730611ae6565b865b90506000612710611afb8860501c61ffff1690565b8b8681518110611b0d57611b0d613458565b6020026020010151611b1f9190613406565b611b29919061341d565b9050808a8581518110611b3e57611b3e613458565b60200260200101818151611b5291906133f3565b905250600080611b808e8e888e6002808d1614611b7857611b738e60601c90565b611b7a565b305b88611fb6565b91509150611b8d81611be9565b6000611b9e8f8f8c86868a8e61207d565b9050611ba981611be9565b808d8781518110611bbc57611bbc613458565b60200260200101818151611bd09190613487565b905250929092039e9d5050505050505050505050505050565b801580611c0557506fffffffffffffffffffffffffffffffff81115b156117da576040517f5e2ab1b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611c498460601c90565b90506000611c5b8560401c61ffff1690565b905061ff0081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008101611c9b57611c9483838761223d565b9350611dc7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe008101611ccd57611c948383876122ac565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd008101611cff57611c948383876122e7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101611d3157611c94838387612342565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101611d6357611c94838387612354565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101611d9557611c948383876123a5565b6040517f0886b18e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b6000611dde8360601c90565b90506000611df08460401c61ffff1690565b9050600080611e208b8b611e078960381c60ff1690565b8b600280891614611e185788611e1a565b305b8a611fb6565b90925090506000611e3260018b6133f3565b611e3f8860301c60ff1690565b14611e4a5730611e4c565b875b9050611e5d8c8c898686868a61207d565b50505050505050505050505050565b6000808212611e79575090565b6040517f20e4c52f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000632e1a7d4d600052816020526000806024601c6000875af190508060000361034257611ed7610a42565b6040517fa39163c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd848401013560e81c026001808216900361041b576040517faecb172600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60201c6000528360601b6018528260601b602c528160405260008060606000808a5af1611fac573d6000803e3d6000fd5b6040525050505050565b6000806014860288016002013560601c86820361204757600189013560f81c1515600081611fe5576000611fef565b611fef83886123f6565b905061201e7f0000000000000000000000000000000000000000000000000000000000000000848a8a8a611f74565b81612029578561203e565b8061203484896123f6565b61203e91906133f3565b9550505061206f565b73ffffffffffffffffffffffffffffffffffffffff8516301461206f5761206f818686610aa6565b989297509195505050505050565b60008061208a8760601c90565b905061ff0083167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081016120cb576120c48285888861246f565b9250612230565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0081016120fd576120c48285876124f7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00810161212f576120c4828587612509565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101612163576120c4828587898b61251b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101612196576120c4828587896125f3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0081016121c9576120c48285878961264f565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80081016121fc576120c48286888a6126a1565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7008101611d95576120c4828587898b6126c3565b5050979650505050505050565b600080806122508660018088161461270f565b909250905061225f84826133f3565b61226b906103e5613406565b60016122778685613406565b612283906103e8613406565b61228d91906133f3565b612297919061341d565b6122a2906001613487565b9695505050505050565b60006122df7f000000000000000000000000000000000000000000000000000000000000000085846001808816146127d4565b949350505050565b600080806122fb8685600180891614612862565b9150915080600014612339576040517f48614bfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60006122df84600180861614846128e8565b600080806123688685600180891614612954565b91509150838114612339576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080806123b986856001808916146129bc565b91509150838114612339576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006370a0823160005283602052602060006024601c885afa91503d905060005192508160000361242c5761242c610a42565b6020811015612467576040517f07e05a0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b60006001848116148180612483888461270f565b90925090506000612496876103e5613406565b9050806124a5846103e8613406565b6124af9190613487565b6124b98383613406565b6124c3919061341d565b9450600080856124d5578660006124d9565b6000875b915091506124e98b83838b612a82565b505050505050949350505050565b60006122df8460018086161484612ad5565b60006122df8460018086161484612b67565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1661040017600155600080808061256789886125608b60019081161490565b8989612bf6565b92509250925080600154146125a8576040517f8430856b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8582146125e1576040517f594caabc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061dead6001559695505050505050565b600080806126088786866001808b1614612ca1565b91509150838114612645576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b600080806126648786866001808b1614612d6d565b91509150838114612645576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006126ae828685612e39565b6126ba85858585612f58565b95945050505050565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b166109001760015560008080806125678988886127098c60019081161490565b89612fe4565b6000806000630902f1ac600052604060006004601c885afa1561272f57503d5b838015612765576dffffffffffffffffffffffffffff6000511693506dffffffffffffffffffffffffffff602051169250612790565b6dffffffffffffffffffffffffffff6020511693506dffffffffffffffffffffffffffff6000511692505b5060408110156127cc576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509250929050565b6000806000604051635bdd4b7c815286602082015285604082015284606082015260808101604052602060006064601c84018b5afa9150503d9150600051925061281d816117b7565b6020821015612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b60008060008060405163abcd78306000528660205285604052604060006044601c8b5afa91503d92506000519450602051935080604052506128a3816117b7565b60408210156128de576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050935093915050565b600080600061290c8630876128fc88611e6c565b612905906134cf565b60006130c5565b9150915060a43d148215166000811461294057858015612932576004830151945061293a565b602483015194505b5061294a565b3d6000803e3d6000fd5b5050509392505050565b60008060008060405163cd56aadc6000528660000360205285604052604060006044601c8b5afa3d9350915085801561299657600051955060205194506129a1565b602051955060005194505b5060018501945083600003935080604052506128a3816117b7565b60008060006129cb868561314c565b9050600080604051633419341c815286602082015287600003604082015283606082015260808101604052604060006064601c84018c5afa9150503d91508560008114612a215760005195506020519450612a2c565b602051955060005194505b50836000039350612a3c816117b7565b6040821015612a77576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050935093915050565b600060405163022c0d9f8152846020820152836040820152826060820152608080820152600060a082015260a0810160405260008060a4601c840160008a5af1915050612ace816117b7565b5050505050565b60008060006040516353c059a06000528560205284604052604060006044601c60008b5af13d93509150858015612b10576020519450612b16565b60005194505b50604052612b23816117b7565b6040821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509392505050565b60008060006040516353c059a06000528560205284604052602060006044601c60008b5af13d93509150858015612ba55760005160801c9450612bae565b60105160801c94505b50604052612bbb816117b7565b6020821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806000612c138a8a8a612c0d8b611e6c565b8a6130c5565b91509150612c20826117b7565b604081018690526060812092503d888015612c45578251955060208301519650612c51565b82519650602083015195505b508560000395506040811015612c93576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050955095509592505050565b60008060008060405163dc35ff77815287602082015286604082015285606082015260a06080820152600060a0820152600060c082015260a081016040526040600060c4601c840160008d5af19150503d91508460008114612d0c5760005193506020519450612d17565b602051935060005194505b50836000039350612d27816117b7565b6040821015612d62576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505094509492505050565b6000806000612d7c878561314c565b905060008060405163abb1db2a815288602082015286604082015287606082015283608082015260a08101604052604060006084601c840160008e5af19150503d91508560008114612dd75760005194506020519550612de2565b602051945060005195505b50846000039450612df2816117b7565b6040821015612e2d576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505094509492505050565b6000806000612e5d565b816040526000602060406044601c6000865af19392505050565b60405163095ea7b360005285602052612e768588612e43565b93508360016040511416612ea857833d883b151710612ea857612e9a600088612e43565b50612ea58588612e43565b93505b3d92506040519150806040525082600003612ef757612ec5610a42565b6040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612f07578060011415612f21565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600060405163f946c2a2815284602082015260016040820152856060820152866080820152602060006084601c840160008c5af191503d9250600051935060a0810160405250612faa816117b7565b81602014612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008060006040516380c2c6ef8152896020820152876040820152886060820152608080820152602060a08201528660c082015260408160c4601c840160008f5af191503d9250866040820152606081209350876000811461305057600181146130605761306c565b815196506020820151955061306c565b81519550602082015196505b505084600003945061307d816117b7565b60408210156130b8576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050955095509592505050565b6000806000856130e95773fffd8963efd1fc6a506488495d951d5263988d256130f0565b6401000276a45b9050604051915063128acb08825286602083015285604083015284606083015280608083015260a080830152602060c08301528360e0830152610100820160405260448260e4601c850160008c5af19250509550959350505050565b60008060405163a0b6ea01600052606060006004601c885afa1561316e573d91505b83801561317f576000519350613185565b60405193505b5060405260608110156131c4576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146117da57600080fd5b60008060006060848603121561320257600080fd5b833561320d816131cb565b9250602084013561321d816131cb565b929592945050506040919091013590565b60008060008060008060008060e0898b03121561324a57600080fd5b8835613255816131cb565b97506020890135613265816131cb565b965060408901359550606089013594506080890135613283816131cb565b935060a0890135613293816131cb565b925060c089013567ffffffffffffffff8111156132af57600080fd5b8901601f81018b136132c057600080fd5b803567ffffffffffffffff8111156132d757600080fd5b8b60208284010111156132e957600080fd5b989b979a50959850939692959194602001935050565b6000806040838503121561331257600080fd5b823561331d816131cb565b915060208301356bffffffffffffffffffffffff8116811461333e57600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000602082840312156133b957600080fd5b815161041b816131cb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103a9576103a96133c4565b80820281158282048414176103a9576103a96133c4565b600082613453577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808201808211156103a9576103a96133c4565b6000816134a9576134a96133c4565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007f80000000000000000000000000000000000000000000000000000000000000008203613500576135006133c4565b506000039056fea26469706673582212208b73127399dde8946da9be162faea1fd4faab80441131109f005eacf4d92804964736f6c634300081a003300000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a000000000000000000000000da7305883a97a97f4d4851f7f0e6fb041da65e5400000000000000000000000000000000000000000000000000000000000005dc
Deployed Bytecode
0x6080604052600436106100695760003560e01c8063bd08443511610043578063bd08443514610274578063c60bc203146102a9578063cb7e0007146102c9576100d7565b8063192bcad3146101d657806362c067671461021f57806372c8fc0e1461023f576100d7565b366100d7573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a16146100d5576100d57f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a346102e9565b005b3480156100e357600080fd5b50600154600090369060609061ff00811681831c6104008214801561011d57503373ffffffffffffffffffffffffffffffffffffffff8216145b156101365761012c8686610347565b93505050506101cb565b6107008214801561017c57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e16145b1561018c5761012c8686836103af565b610900821480156101b257503373ffffffffffffffffffffffffffffffffffffffff8216145b156101c15761012c8686610422565b3660008037366000fd5b915050805190602001f35b3480156101e257600080fd5b506000547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166040519081526020015b60405180910390f35b34801561022b57600080fd5b506100d561023a3660046131ed565b6104ad565b34801561024b57600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610216565b34801561028057600080fd5b5061029461028f36600461322e565b6104e6565b60408051928352602083019190915201610216565b3480156102b557600080fd5b506100d56102c43660046132ff565b61073c565b3480156102d557600080fd5b506102946102e436600461322e565b610752565b600063d0e30db06000526000806004601c85875af190508060000361034257610310610a42565b6040517fdc0c8cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b606060008060006103588686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561039481336000861361038d5784610aa6565b855b610aa6565b50506040805160008152602081019091529150505b92915050565b60606000806103e08686867f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a610b85565b60408051602081810152808201919091526060810183905260808101829052919350915060a001604051602081830303815290604052925050505b9392505050565b606060008060006104338686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561046881336000861361038d5784610aa6565b604080517ffa483e7200000000000000000000000000000000000000000000000000000000602082015201604051602081830303815290604052935050505092915050565b6104b5610cc6565b73ffffffffffffffffffffffffffffffffffffffff8316156104dc57610342838383610aa6565b6103428282610db9565b60008061057360405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b61057f85858d8d610e09565b60608501526040840181905260208401919091528183526105a491879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152908116608083018190528a918d16900361060d5760006127108360c001518302816105f3576105f3613378565b049050808203915061060b8d8a8560a001518461117b565b505b60006106698284604001518b8f73ffffffffffffffffffffffffffffffffffffffff16876080015173ffffffffffffffffffffffffffffffffffffffff1614610656578b610658565b305b876020015188606001518d8d6112d8565b90508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036106e05760006127108460c001518302816106bb576106bb613378565b04905080820391506106d38d308660a001518461117b565b6106de8d8a84610aa6565b505b89811015610729576040517fc18a3e5800000000000000000000000000000000000000000000000000000000815260048101829052602481018b90526044015b60405180910390fd5b999c999b50989950505050505050505050565b610744610cc6565b61074e82826113de565b5050565b6000806107df60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6107eb85858d8d610e09565b606085015260408401819052602084019190915281835261081091879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152166080820152600185013560f81c15610877576040517fe4c5b0d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808990508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036108da573091508260c00151612710036127108202816108d2576108d2613378565b0490506108de565b8791505b6000806108fb838660400151876060015188602001518d8d6114f2565b915091508e73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16036109735760008560c00151612710038660c0015184028161095557610955613378565b049050808301925061097186608001518d8860a001518461117b565b505b8c8211156109b7576040517f20cd355100000000000000000000000000000000000000000000000000000000815260048101839052602481018e9052604401610720565b6109d38b8583886020015189604001518a606001518f8f61175f565b8d73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff1603610a2e5760008c84039050610a218f308860a001518461117b565b610a2c8f8c8f610aa6565b505b509d999c50989a5050505050505050505050565b3d15610a52573d6000803e3d6000fd5b565b6000808060a48414610a92576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505060043593602435935060843592509050565b600080600060405163a9059cbb6000528560205284604052602060006044601c60008b5af193503d92506000519150806040525082600003610b1c57610aea610a42565b6040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115610b2c578060011415610b46565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b60008060008060008060405163f3cd914c815260448a0360448c0160208301377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb828c010116604052602060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08c01601c840182335af1600051608081901d9850600f0b965092503d915088610c4f5760206000526040602052866040528560605260806000fd5b602081015194506040810151935050610c67826117b7565b80602014610ca1576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cad8489888a6117dd565b610cb98389878a6117dd565b5050505094509492505050565b7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5591906133a7565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a52576040517f303693ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600085875af190508060000361034257610dd7610a42565b6040517fa01b460600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080803373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b1614610e7d576040517f4b2fd34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e878888611974565b919450925090506002821015610ec9576040517f7c741b7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a830188840135604081901c61ff0016610f49576002831015610f19576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b939450927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101908390610f83565b82600003610f83576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028a013560601c73ffffffffffffffffffffffffffffffffffffffff891614610fd9576040517f80e4580000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87166110138b8b6110006001896133f3565b601402919091016002013560601c919050565b73ffffffffffffffffffffffffffffffffffffffff1614611060576040517ffc37688f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050945094509450949050565b6000808084156111715786850135606081901c92506110908160501c61ffff1690565b915060006110a18260381c60ff1690565b90506002601482028a01013560601c94506110bf8260301c60ff1690565b81186110cf8360401c61ffff1690565b171515806110f1575080158015906110f157506110ed6001876133f3565b8114155b15611128576040517fee393ce900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158061113757506127108310155b1561116e576040517fa4c2399e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b9450945094915050565b80156112d25773ffffffffffffffffffffffffffffffffffffffff82166111ce576040517fae0edfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805461271090611206907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1684613406565b611210919061341d565b9050600061121e82846133f3565b905080156112325761123286868684611a46565b811561125f5760005461125f908790879073ffffffffffffffffffffffffffffffffffffffff1685611a46565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f77c197a1ae17318af19ae49d654709f9fe13e90a7fb70e1bdd5f665f6b9bce8285856040516112c7929190918252602082015260400190565b60405180910390a350505b50505050565b6000808867ffffffffffffffff8111156112f4576112f4613349565b60405190808252806020026020018201604052801561131d578160200160208202803683370190505b509050600060018a0390508a8260008151811061133c5761133c613458565b6020026020010181815250506000805b8781101561137957601a890198870135915061136d838888878f8f88611aa0565b909c019b60010161134c565b5082828151811061138c5761138c613458565b60200260200101519350838c146113cf576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661142b576040517fec049e3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710816bffffffffffffffffffffffff161115611475576040517f31fb2ba200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216740100000000000000000000000000000000000000006bffffffffffffffffffffffff8316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b60006060601a86028501945060008667ffffffffffffffff81111561151957611519613349565b604051908082528060200260200182016040528015611542578160200160208202803683370190505b50905060008867ffffffffffffffff81111561156057611560613349565b604051908082528060200260200182016040528015611589578160200160208202803683370190505b509050898161159960018c6133f3565b815181106115a9576115a9613458565b6020908102919091010152896000895b80156116f7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe68a81019a8a0101359150600060ff603084901c16905060006116058460381c60ff1690565b9050600061271061161a8660501c61ffff1690565b88858151811061162c5761162c613458565b602002602001015161163e9190613406565b611648919061341d565b90508087848151811061165d5761165d613458565b6020026020010181815161167191906133f3565b90525061167d81611be9565b6116898c8c8784611c3c565b99508987838151811061169e5761169e613458565b602002602001018181516116b29190613487565b9052506116be8a611be9565b89886116c98661349a565b955085815181106116dc576116dc613458565b602002602001018181525050808a03860195505050506115b9565b508260008151811061170b5761170b613458565b6020026020010151955085821461174e576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509192505050965096945050505050565b6000805b848110156117ab57601a87018785013580935081985050506117a38484888d8d878e888151811061179657611796613458565b6020026020010151611dd2565b600101611763565b50505050505050505050565b806117da573d156117cc573d6000803e3d6000fd5b63824d22356000526004601cfd5b50565b60008212156118c357600082600003925063a5841194600052846020526000806040601c6000335af19050611811816117b7565b600073ffffffffffffffffffffffffffffffffffffffff8616611848576118438361183b86611e6c565b925082611eab565b611856565b611856863361038f87611e6c565b6000806311da60b4600052602060006004601c86335af193503d90506000519150611880846117b7565b806020146118ba576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050506112d2565b60008213156112d257600073ffffffffffffffffffffffffffffffffffffffff8516156118f057836118f2565b305b90506000604051630b0d9c0981528660208201528260408201528460608201526000806064601c84016000335af191506080810160405250611933816117b7565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610b7d57610b7d838661038f87611e6c565b813560f81c6014810260028101919060009084037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01838510156119e4576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a810615611a385760036119fa878784611f09565b019003601a810615611a38576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81049150509250925092565b3073ffffffffffffffffffffffffffffffffffffffff841603611a7357611a6e848383610aa6565b6112d2565b6112d27f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b85858585611f74565b600080611ab18360401c61ffff1690565b90506000611ac28460381c60ff1690565b90506000611ad38560301c60ff1690565b905060008b8214611ae45730611ae6565b865b90506000612710611afb8860501c61ffff1690565b8b8681518110611b0d57611b0d613458565b6020026020010151611b1f9190613406565b611b29919061341d565b9050808a8581518110611b3e57611b3e613458565b60200260200101818151611b5291906133f3565b905250600080611b808e8e888e6002808d1614611b7857611b738e60601c90565b611b7a565b305b88611fb6565b91509150611b8d81611be9565b6000611b9e8f8f8c86868a8e61207d565b9050611ba981611be9565b808d8781518110611bbc57611bbc613458565b60200260200101818151611bd09190613487565b905250929092039e9d5050505050505050505050505050565b801580611c0557506fffffffffffffffffffffffffffffffff81115b156117da576040517f5e2ab1b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611c498460601c90565b90506000611c5b8560401c61ffff1690565b905061ff0081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008101611c9b57611c9483838761223d565b9350611dc7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe008101611ccd57611c948383876122ac565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd008101611cff57611c948383876122e7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101611d3157611c94838387612342565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101611d6357611c94838387612354565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101611d9557611c948383876123a5565b6040517f0886b18e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b6000611dde8360601c90565b90506000611df08460401c61ffff1690565b9050600080611e208b8b611e078960381c60ff1690565b8b600280891614611e185788611e1a565b305b8a611fb6565b90925090506000611e3260018b6133f3565b611e3f8860301c60ff1690565b14611e4a5730611e4c565b875b9050611e5d8c8c898686868a61207d565b50505050505050505050505050565b6000808212611e79575090565b6040517f20e4c52f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000632e1a7d4d600052816020526000806024601c6000875af190508060000361034257611ed7610a42565b6040517fa39163c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd848401013560e81c026001808216900361041b576040517faecb172600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60201c6000528360601b6018528260601b602c528160405260008060606000808a5af1611fac573d6000803e3d6000fd5b6040525050505050565b6000806014860288016002013560601c86820361204757600189013560f81c1515600081611fe5576000611fef565b611fef83886123f6565b905061201e7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b848a8a8a611f74565b81612029578561203e565b8061203484896123f6565b61203e91906133f3565b9550505061206f565b73ffffffffffffffffffffffffffffffffffffffff8516301461206f5761206f818686610aa6565b989297509195505050505050565b60008061208a8760601c90565b905061ff0083167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081016120cb576120c48285888861246f565b9250612230565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0081016120fd576120c48285876124f7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00810161212f576120c4828587612509565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101612163576120c4828587898b61251b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101612196576120c4828587896125f3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0081016121c9576120c48285878961264f565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80081016121fc576120c48286888a6126a1565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7008101611d95576120c4828587898b6126c3565b5050979650505050505050565b600080806122508660018088161461270f565b909250905061225f84826133f3565b61226b906103e5613406565b60016122778685613406565b612283906103e8613406565b61228d91906133f3565b612297919061341d565b6122a2906001613487565b9695505050505050565b60006122df7f000000000000000000000000000000000000000000000000000000000000000085846001808816146127d4565b949350505050565b600080806122fb8685600180891614612862565b9150915080600014612339576040517f48614bfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60006122df84600180861614846128e8565b600080806123688685600180891614612954565b91509150838114612339576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080806123b986856001808916146129bc565b91509150838114612339576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006370a0823160005283602052602060006024601c885afa91503d905060005192508160000361242c5761242c610a42565b6020811015612467576040517f07e05a0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b60006001848116148180612483888461270f565b90925090506000612496876103e5613406565b9050806124a5846103e8613406565b6124af9190613487565b6124b98383613406565b6124c3919061341d565b9450600080856124d5578660006124d9565b6000875b915091506124e98b83838b612a82565b505050505050949350505050565b60006122df8460018086161484612ad5565b60006122df8460018086161484612b67565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1661040017600155600080808061256789886125608b60019081161490565b8989612bf6565b92509250925080600154146125a8576040517f8430856b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8582146125e1576040517f594caabc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061dead6001559695505050505050565b600080806126088786866001808b1614612ca1565b91509150838114612645576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b600080806126648786866001808b1614612d6d565b91509150838114612645576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006126ae828685612e39565b6126ba85858585612f58565b95945050505050565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b166109001760015560008080806125678988886127098c60019081161490565b89612fe4565b6000806000630902f1ac600052604060006004601c885afa1561272f57503d5b838015612765576dffffffffffffffffffffffffffff6000511693506dffffffffffffffffffffffffffff602051169250612790565b6dffffffffffffffffffffffffffff6020511693506dffffffffffffffffffffffffffff6000511692505b5060408110156127cc576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509250929050565b6000806000604051635bdd4b7c815286602082015285604082015284606082015260808101604052602060006064601c84018b5afa9150503d9150600051925061281d816117b7565b6020821015612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b60008060008060405163abcd78306000528660205285604052604060006044601c8b5afa91503d92506000519450602051935080604052506128a3816117b7565b60408210156128de576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050935093915050565b600080600061290c8630876128fc88611e6c565b612905906134cf565b60006130c5565b9150915060a43d148215166000811461294057858015612932576004830151945061293a565b602483015194505b5061294a565b3d6000803e3d6000fd5b5050509392505050565b60008060008060405163cd56aadc6000528660000360205285604052604060006044601c8b5afa3d9350915085801561299657600051955060205194506129a1565b602051955060005194505b5060018501945083600003935080604052506128a3816117b7565b60008060006129cb868561314c565b9050600080604051633419341c815286602082015287600003604082015283606082015260808101604052604060006064601c84018c5afa9150503d91508560008114612a215760005195506020519450612a2c565b602051955060005194505b50836000039350612a3c816117b7565b6040821015612a77576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050935093915050565b600060405163022c0d9f8152846020820152836040820152826060820152608080820152600060a082015260a0810160405260008060a4601c840160008a5af1915050612ace816117b7565b5050505050565b60008060006040516353c059a06000528560205284604052604060006044601c60008b5af13d93509150858015612b10576020519450612b16565b60005194505b50604052612b23816117b7565b6040821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509392505050565b60008060006040516353c059a06000528560205284604052602060006044601c60008b5af13d93509150858015612ba55760005160801c9450612bae565b60105160801c94505b50604052612bbb816117b7565b6020821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806000612c138a8a8a612c0d8b611e6c565b8a6130c5565b91509150612c20826117b7565b604081018690526060812092503d888015612c45578251955060208301519650612c51565b82519650602083015195505b508560000395506040811015612c93576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050955095509592505050565b60008060008060405163dc35ff77815287602082015286604082015285606082015260a06080820152600060a0820152600060c082015260a081016040526040600060c4601c840160008d5af19150503d91508460008114612d0c5760005193506020519450612d17565b602051935060005194505b50836000039350612d27816117b7565b6040821015612d62576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505094509492505050565b6000806000612d7c878561314c565b905060008060405163abb1db2a815288602082015286604082015287606082015283608082015260a08101604052604060006084601c840160008e5af19150503d91508560008114612dd75760005194506020519550612de2565b602051945060005195505b50846000039450612df2816117b7565b6040821015612e2d576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505094509492505050565b6000806000612e5d565b816040526000602060406044601c6000865af19392505050565b60405163095ea7b360005285602052612e768588612e43565b93508360016040511416612ea857833d883b151710612ea857612e9a600088612e43565b50612ea58588612e43565b93505b3d92506040519150806040525082600003612ef757612ec5610a42565b6040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612f07578060011415612f21565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600060405163f946c2a2815284602082015260016040820152856060820152866080820152602060006084601c840160008c5af191503d9250600051935060a0810160405250612faa816117b7565b81602014612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008060006040516380c2c6ef8152896020820152876040820152886060820152608080820152602060a08201528660c082015260408160c4601c840160008f5af191503d9250866040820152606081209350876000811461305057600181146130605761306c565b815196506020820151955061306c565b81519550602082015196505b505084600003945061307d816117b7565b60408210156130b8576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050955095509592505050565b6000806000856130e95773fffd8963efd1fc6a506488495d951d5263988d256130f0565b6401000276a45b9050604051915063128acb08825286602083015285604083015284606083015280608083015260a080830152602060c08301528360e0830152610100820160405260448260e4601c850160008c5af19250509550959350505050565b60008060405163a0b6ea01600052606060006004601c885afa1561316e573d91505b83801561317f576000519350613185565b60405193505b5060405260608110156131c4576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146117da57600080fd5b60008060006060848603121561320257600080fd5b833561320d816131cb565b9250602084013561321d816131cb565b929592945050506040919091013590565b60008060008060008060008060e0898b03121561324a57600080fd5b8835613255816131cb565b97506020890135613265816131cb565b965060408901359550606089013594506080890135613283816131cb565b935060a0890135613293816131cb565b925060c089013567ffffffffffffffff8111156132af57600080fd5b8901601f81018b136132c057600080fd5b803567ffffffffffffffff8111156132d757600080fd5b8b60208284010111156132e957600080fd5b989b979a50959850939692959194602001935050565b6000806040838503121561331257600080fd5b823561331d816131cb565b915060208301356bffffffffffffffffffffffff8116811461333e57600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000602082840312156133b957600080fd5b815161041b816131cb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103a9576103a96133c4565b80820281158282048414176103a9576103a96133c4565b600082613453577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808201808211156103a9576103a96133c4565b6000816134a9576134a96133c4565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007f80000000000000000000000000000000000000000000000000000000000000008203613500576135006133c4565b506000039056fea26469706673582212208b73127399dde8946da9be162faea1fd4faab80441131109f005eacf4d92804964736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a000000000000000000000000da7305883a97a97f4d4851f7f0e6fb041da65e5400000000000000000000000000000000000000000000000000000000000005dc
-----Decoded View---------------
Arg [0] : router (address): 0x45A62B090DF48243F12A21897e7ed91863E2c86b
Arg [1] : routerV2_0 (address): 0x0000000000000000000000000000000000000000
Arg [2] : uniswapV4Manager (address): 0x188d586Ddcf52439676Ca21A244753fA19F9Ea8e
Arg [3] : wnative (address): 0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A
Arg [4] : protocolFeeReceiver (address): 0xDA7305883a97a97F4d4851f7F0e6fB041da65e54
Arg [5] : protocolFeeShare (uint96): 1500
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e
Arg [3] : 0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a
Arg [4] : 000000000000000000000000da7305883a97a97f4d4851f7f0e6fb041da65e54
Arg [5] : 00000000000000000000000000000000000000000000000000000000000005dc
Deployed Bytecode Sourcemap
637:18921:4:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1535:10:3;:21;1549:7;1535:21;;1531:60;;1558:33;1572:7;1581:9;1558:13;:33::i;:::-;637:18921:4;;;;;;;;;;;-1:-1:-1;1966:13:3;;637:18921:4;;;;1919:12:3;;911:6:7;1884:15;;2126:18:3;;;1358:14:7;2161:25:3;;:50;;;;-1:-1:-1;2190:10:3;:21;;;;2161:50;2157:369;;;2234:28;2257:4;;2234:22;:28::i;:::-;2227:35;;;;;;;2157:369;1555:14:7;2283:25:3;;:61;;;;-1:-1:-1;2312:10:3;:32;2326:18;2312:32;;2283:61;2279:247;;;2367:39;2392:4;;2398:7;2367:24;:39::i;2279:247::-;1668:14:7;2427:18:3;;:43;;;;-1:-1:-1;2449:10:3;:21;;;;2427:43;2423:103;;;2493:22;2510:4;;2493:16;:22::i;2423:103::-;2594:14;2591:1;;2575:34;2594:14;2591:1;2622:25;1871:792;;;;637:18921:4;;;;;;1216:113:2;;;;;;;;;;-1:-1:-1;1279:7:2;1305:17;;;;;;1216:113;;160:25:13;;;148:2;133:18;1216:113:2;;;;;;;;7448:214:4;;;;;;;;;;-1:-1:-1;7448:214:4;;;;;:::i;:::-;;:::i;1029:121:2:-;;;;;;;;;;-1:-1:-1;1096:7:2;1122:21;1029:121;;1122:21;;;;1014:74:13;;1002:2;987:18;1029:121:2;868:226:13;2571:1634:4;;;;;;;;;;-1:-1:-1;2571:1634:4;;;;;:::i;:::-;;:::i;:::-;;;;2666:25:13;;;2722:2;2707:18;;2700:34;;;;2639:18;2571:1634:4;2492:248:13;1585:179:2;;;;;;;;;;-1:-1:-1;1585:179:2;;;;;:::i;:::-;;:::i;5267:2023:4:-;;;;;;;;;;-1:-1:-1;5267:2023:4;;;;;:::i;:::-;;:::i;2680:365:12:-;2746:15;2821:10;2818:1;2811:21;2909:1;2906;2903;2899:2;2891:6;2882:7;2875:5;2870:41;2859:52;;2935:7;2946:1;2935:12;2931:108;;2963:22;:20;:22::i;:::-;3006;;;;;;;;;;;;;;2931:108;2736:309;2680:365;;:::o;9432:423:3:-;9503:12;9528:19;9549;9570:13;9587:43;9625:4;;9587:37;:43::i;:::-;9980:4:9;9974:11;;9900:12;9999:23;;;10042:2;10035:24;;;10072:17;;;10124:2;10111:16;;10141:17;;9527:103:3;;-1:-1:-1;9527:103:3;;-1:-1:-1;9527:103:3;-1:-1:-1;9641:13:3;:74;9726:93;9744:5;9751:10;9786:1;9771:16;;:46;;9805:12;9726:17;:93::i;9771:46::-;9790:12;9771:46;9726:17;:93::i;:::-;-1:-1:-1;;9836:12:3;;;9846:1;9836:12;;;;;;;;;-1:-1:-1;;9432:423:3;;;;;:::o;13345:268::-;13437:12;13462:13;13477;13494:57;13526:4;;13532:9;13543:7;13494:31;:57::i;:::-;13585:4;13568:38;;13579:4;13568:38;;;3615:36:13;3667:18;;;3660:45;;;;3721:18;;;3714:34;;;3764:18;;;3757:34;;;13461:90:3;;-1:-1:-1;13461:90:3;-1:-1:-1;3587:19:13;;13568:38:3;;;;;;;;;;;;13561:45;;;;13345:268;;;;;;:::o;15331:460::-;15396:12;15421:19;15442;15463:13;15480:43;15518:4;;15480:37;:43::i;:::-;9980:4:9;9974:11;;9900:12;9999:23;;;10042:2;10035:24;;;10072:17;;;10124:2;10111:16;;10141:17;;15420:103:3;;-1:-1:-1;15420:103:3;;-1:-1:-1;15420:103:3;-1:-1:-1;15534:13:3;:74;15619:93;15637:5;15644:10;15679:1;15664:16;;:46;;15698:12;9726:17;:93::i;15619:::-;15730:54;;;15741:42;15730:54;;;3946:98:13;3919:18;15730:54:3;;;;;;;;;;;;15723:61;;;;;15331:460;;;;:::o;7448:214:4:-;7534:14;:12;:14::i;:::-;7559:19;;;;:96;;7619:36;7637:5;7644:2;7648:6;7619:17;:36::i;7559:96::-;7581:35;7605:2;7609:6;7581:23;:35::i;2571:1634::-;2804:7;2813;2832:32;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2832:32:4;2937:41;2953:5;;2960:7;2969:8;2937:15;:41::i;:::-;2919:14;;;2874:104;2902:15;;;2874:104;;;2890:10;;;2874:104;;;;;;;3046:53;;3061:5;;;;2874:104;3046:14;:53::i;:::-;3025:17;;;2989:110;;;;;3007:16;;;2989:110;;;;2990:15;;;2989:110;;;3139:8;;3161:26;;;;3157:284;;3231:17;741:6:2;3273::4;:17;;;3252:18;:38;3251:46;;;;;:::i;:::-;;3231:66;;3337:9;3315:31;;;;3364:52;3373:7;3382:4;3388:6;:16;;;3406:9;3364:8;:52::i;:::-;3203:228;3157:284;3451:17;3471:234;3497:18;3529:6;:15;;;3558:4;3595:8;3576:27;;:6;:15;;;:27;;;:48;;3622:2;3576:48;;;3614:4;3576:48;3638:6;:10;;;3662:6;:14;;;3690:5;;3471:12;:234::i;:::-;3451:254;;3739:8;3720:27;;:6;:15;;;:27;;;3716:337;;3791:17;741:6:2;3824::4;:17;;;3812:9;:29;3811:37;;;;;:::i;:::-;;3791:57;;3879:9;3866:22;;;;3906:62;3915:8;3933:4;3940:6;:16;;;3958:9;3906:8;:62::i;:::-;3986:42;4004:8;4014:2;4018:9;3986:17;:42::i;:::-;3763:280;3716:337;4079:12;4067:9;:24;4063:96;;;4100:59;;;;;;;;2666:25:13;;;2707:18;;;2700:34;;;2639:18;;4100:59:4;;;;;;;;4063:96;4178:8;;4188:9;;-1:-1:-1;2571:1634:4;;-1:-1:-1;;;;;;;;;;2571:1634:4:o;1585:179:2:-;1685:14;:12;:14::i;:::-;1709:48;1735:11;1748:8;1709:25;:48::i;:::-;1585:179;;:::o;5267:2023:4:-;5501:15;5518:16;5546:32;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5546:32:4;5651:41;5667:5;;5674:7;5683:8;5651:15;:41::i;:::-;5633:14;;;5588:104;5616:15;;;5588:104;;;5604:10;;;5588:104;;;;;;;5760:53;;5775:5;;;;5588:104;5760:14;:53::i;:::-;5739:17;;;5703:110;;;;;5721:16;;;5703:110;;5704:15;;;5703:110;10715:22:8;10697:41;;10684:55;10661:21;10657:83;10650:91;5824:83:4;;5869:38;;;;;;;;;;;;;;5824:83;5918:17;5945:24;5972:9;5945:36;;6014:8;5995:27;;:6;:15;;;:27;;;5991:253;;6058:4;6038:25;;6156:6;:17;;;741:6:2;6150:23:4;741:6:2;6124:16:4;:22;:50;;;;;:::i;:::-;;6105:69;;5991:253;;;6231:2;6219:14;;5991:253;6255:23;6280:26;6322:83;6336:16;6354:6;:15;;;6371:6;:14;;;6387:6;:10;;;6399:5;;6322:13;:83::i;:::-;6254:151;;;;6439:7;6420:26;;:6;:15;;;:26;;;6416:306;;6490:17;6555:6;:17;;;741:6:2;6549:23:4;6528:6;:17;;;6510:15;:35;:63;;;;;:::i;:::-;;6490:83;;6610:9;6591:28;;;;6637:60;6646:6;:15;;;6663:4;6669:6;:16;;;6687:9;6637:8;:60::i;:::-;6462:250;6416:306;6754:11;6736:15;:29;6732:103;;;6774:61;;;;;;;;2666:25:13;;;2707:18;;;2700:34;;;2639:18;;6774:61:4;2492:248:13;6732:103:4;6846:93;6860:4;6866:9;6877;6888:6;:10;;;6900:6;:15;;;6917:6;:14;;;6933:5;;6846:13;:93::i;:::-;6973:8;6954:27;;:6;:15;;;:27;;;6950:288;;7025:17;7064:9;7045:16;:28;7025:48;;7091:62;7100:8;7118:4;7125:6;:16;;;7143:9;7091:8;:62::i;:::-;7171:42;7189:8;7199:2;7203:9;7171:17;:42::i;:::-;6997:231;6950:288;-1:-1:-1;7256:15:4;7273:9;;-1:-1:-1;5267:2023:4;;-1:-1:-1;;;;;;;;;;;5267:2023:4:o;7657:244:12:-;7754:16;7751:134;;;7810:16;7807:1;;7789:38;7854:16;7807:1;7844:27;7751:134;7657:244::o;10370:523:9:-;10469:19;;;10664:3;10649:18;;10645:67;;10676:36;;;;;;;;;;;;;;10645:67;-1:-1:-1;;10791:1:9;10778:15;;10835:2;10822:16;;-1:-1:-1;10873:3:9;10860:17;;-1:-1:-1;10370:523:9;-1:-1:-1;10370:523:9:o;3971:769:12:-;4051:15;4076:18;4104:19;4192:4;4186:11;4221:10;4218:1;4211:21;4285:2;4281;4274:14;4312:6;4308:2;4301:18;4377:2;4374:1;4370:2;4366;4363:1;4356:5;4349;4344:36;4333:47;;4408:16;4394:30;;4458:1;4452:8;4437:23;;4487:5;4481:4;4474:19;;4517:7;4528:1;4517:12;4513:112;;4545:22;:20;:22::i;:::-;4588:26;;;;;;;;;;;;;;4513:112;4639:15;;:59;;4682:11;4697:1;4682:16;;4639:59;;;4657:17;;;;:22;4639:59;4635:98;;;4707:26;;;;;;;;;;;;;;4635:98;4041:699;;;3971:769;;;:::o;26251:1526:9:-;26367:14;26383;26413;26437;26462:15;26487:22;26575:4;26569:11;26606:10;26601:3;26594:23;26772:2;26759:11;26755:20;26750:2;26737:11;26733:20;26728:2;26723:3;26719:12;26706:70;26803:46;26817:30;26821:21;;;26817:30;26803:46;26797:4;26790:60;26977:2;26974:1;26952:20;;;26947:2;26938:12;;26974:1;26925:8;26918:5;26913:67;27062:1;27056:8;27092:3;27088:17;;;;-1:-1:-1;27140:2:9;27129:23;;-1:-1:-1;26902:78:9;-1:-1:-1;27011:16:9;;-1:-1:-1;27246:9:9;27236:204;;27285:4;27282:1;27275:15;27318:4;27314:2;27307:16;27351:7;27347:2;27340:19;27387:7;27383:2;27376:19;27422:3;27419:1;27412:14;27236:204;27479:2;27474:3;27470:12;27464:19;27454:29;;27521:2;27516:3;27512:12;27506:19;27496:29;;;27544:22;27558:7;27544:13;:22::i;:::-;27580:14;27598:2;27580:20;27576:69;;27609:36;;;;;;;;;;;;;;27576:69;27656:52;27672:6;27680:9;27691:7;27700;27656:15;:52::i;:::-;27718;27734:6;27742:9;27753:7;27762;27718:15;:52::i;:::-;26403:1374;;;;26251:1526;;;;;;;:::o;7816:144:4:-;7899:6;7891:21;;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7877:37;;:10;:37;;;7873:80;;7923:30;;;;;;;;;;;;;;2089:326:12;2160:15;2269:1;2266;2263;2260;2252:6;2248:2;2241:5;2236:35;2225:46;;2295:7;2306:1;2295:12;2291:118;;2323:22;:20;:22::i;:::-;2366:32;;;;;;;;;;;;;;8653:1005:4;8781:14;;;;8863:10;:20;8877:6;8863:20;;8859:58;;8892:25;;;;;;;;;;;;;;8859:58;8955:24;8973:5;;8955:17;:24::i;:::-;8928:51;;-1:-1:-1;8928:51:4;-1:-1:-1;8928:51:4;-1:-1:-1;9005:1:4;8994:12;;8990:58;;;9015:33;;;;;;;;;;;;;;8990:58;13526:10:8;13517:20;;13470:22;;;13457:36;14852:11;14848:23;;;911:6:7;1884:15;9133:325:4;;9217:1;9207:7;:11;9203:47;;;9227:23;;;;;;;;;;;;;;9203:47;9338:3;;-1:-1:-1;9361:7:4;9292:9;;;;;9361:7;;9133:325;;;9403:7;9414:1;9403:12;9399:48;;9424:23;;;;;;;;;;;;;;9399:48;11220:13:8;11198:60;;11185:74;11170:13;11166:94;9472:38:4;;;;9468:80;;9519:29;;;;;;;;;;;;;;9468:80;9562:50;;;:38;9580:5;;9587:12;9598:1;9587:8;:12;:::i;:::-;11243::8;11235:21;11198:60;;;;11220:13;11198:60;11185:74;11170:13;11166:94;;11029:247;-1:-1:-1;11029:247:8;9562:38:4;:50;;;9558:93;;9621:30;;;;;;;;;;;;;;9558:93;8849:809;;8653:1005;;;;;;;;;:::o;10276:919::-;10402:16;;;10477:10;;10473:716;;13470:22:8;;;13457:36;14326:13;14322:25;;;10634:35:4;;10696:26;10716:5;14586:13:8;14582:25;14609:11;14578:43;;14444:193;10696:26:4;10683:39;;10737:18;10758:28;10780:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;10758:28:4;10737:49;-1:-1:-1;11220:13:8;11243:12;11235:21;;11198:60;;;11185:74;11170:13;11166:94;10800:47:4;;10925:29;10948:5;15406:15:8;15402:27;15431:10;15398:44;;15255:203;10925:29:4;10912:10;:42;10884:24;10902:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;10884:24:4;:71;10883:78;;;:149;;-1:-1:-1;10986:15:4;;;;;:45;;-1:-1:-1;11019:12:4;11030:1;11019:8;:12;:::i;:::-;11005:10;:26;;10986:45;10862:221;;;11054:29;;;;;;;;;;;;;;10862:221;11101:15;;;:36;;;741:6:2;11120:10:4;:17;;11101:36;11097:81;;;11146:32;;;;;;;;;;;;;;11097:81;10489:700;;10473:716;10276:919;;;;;;;;:::o;2754:659:2:-;2863:13;;2859:548;;2896:23;;;2892:68;;2928:32;;;;;;;;;;;;;;2892:68;2975:25;3016:17;;741:6;;3004:29;;3016:17;;;;;3004:9;:29;:::i;:::-;3003:37;;;;:::i;:::-;2975:65;-1:-1:-1;3054:26:2;3083:29;2975:65;3083:9;:29;:::i;:::-;3054:58;-1:-1:-1;3131:22:2;;3127:85;;3155:57;3168:5;3175;3182:9;3193:18;3155:12;:57::i;:::-;3230:21;;3226:95;;3280:21;;3253:68;;3266:5;;3273;;3280:21;;3303:17;3253:12;:68::i;:::-;3356:9;3341:55;;3349:5;3341:55;;;3367:9;3378:17;3341:55;;;;;;2666:25:13;;;2722:2;2707:18;;2700:34;2654:2;2639:18;;2492:248;3341:55:2;;;;;;;;2878:529;;2859:548;2754:659;;;;:::o;13777:813:4:-;14000:17;14053:25;14095:8;14081:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;14081:23:4;;14053:51;;14118:19;14151:1;14140:8;:12;14118:34;;14181:8;14167;14176:1;14167:11;;;;;;;;:::i;:::-;;;;;;:22;;;;;14204:13;14236:9;14231:213;14251:7;14247:1;:11;14231:213;;;13526:10:8;13517:20;;;13470:22;;13457:36;;-1:-1:-1;14357:72:4;14376:11;13470:22:8;14389:5:4;14396:8;14406:4;14412:9;13457:36:8;14357:18:4;:72::i;:::-;14345:84;;;;14260:3;;14231:213;;;;14470:8;14479:11;14470:21;;;;;;;;:::i;:::-;;;;;;;14458:33;;14521:9;14509:8;:21;14505:68;;14539:34;;;;;;;;;;;;;;14505:68;14029:555;;;13777:813;;;;;;;;;;:::o;2000:474:2:-;2112:33;;;2108:86;;2154:40;;;;;;;;;;;;;;2108:86;741:6;2208:16;:22;;;2204:72;;;2239:37;;;;;;;;;;;;;;2204:72;2287:43;;;2340:36;;;;;;;;;2287:21;2340:36;2392:75;;5607:58:13;;;2417:10:2;;2392:75;;5595:2:13;5580:18;2392:75:2;;;;;;;2000:474;;:::o;11813:1364:4:-;11958:16;11976;9498:2:8;12976:20;;12965:31;;12008:38:4;;12057:26;12100:7;12086:22;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;12086:22:4;;12057:51;;12118:25;12160:8;12146:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;12146:23:4;-1:-1:-1;12118:51:4;-1:-1:-1;12205:9:4;12118:51;12189:12;12200:1;12189:8;:12;:::i;:::-;12180:22;;;;;;;;:::i;:::-;;;;;;;;;;:34;12240:9;12224:13;12300:7;12283:742;12309:5;;12283:742;;14015:20:8;;;;;14070:30;;;14057:44;;-1:-1:-1;12474:17:4;15431:10:8;15406:15;15402:27;;;15398:44;12474:49:4;;12537:18;12558:28;12580:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;12558:28:4;12537:49;;12601:14;741:6:2;12640:26:4;12660:5;14586:13:8;14582:25;14609:11;14578:43;;14444:193;12640:26:4;12618:8;12627:9;12618:19;;;;;;;;:::i;:::-;;;;;;;:48;;;;:::i;:::-;:54;;;;:::i;:::-;12601:71;;12709:6;12686:8;12695:9;12686:19;;;;;;;;:::i;:::-;;;;;;:29;;;;;;;:::i;:::-;;;-1:-1:-1;12730:20:4;12743:6;12730:12;:20::i;:::-;12775:34;12788:5;;12795;12802:6;12775:12;:34::i;:::-;12764:45;;12847:8;12823;12832:10;12823:20;;;;;;;;:::i;:::-;;;;;;:32;;;;;;;:::i;:::-;;;-1:-1:-1;12869:22:4;12882:8;12869:12;:22::i;:::-;12923:8;12906:9;12916:3;;;:::i;:::-;;;;12906:14;;;;;;;;:::i;:::-;;;;;;:25;;;;;12994:6;12983:8;:17;12974:26;;;;12317:708;;;12283:742;;;;13046:8;13055:1;13046:11;;;;;;;;:::i;:::-;;;;;;;13035:22;;13080:8;13071:5;:17;13067:64;;13097:34;;;;;;;;;;;;;;13067:64;-1:-1:-1;13160:9:4;;-1:-1:-1;;;11813:1364:4;;;;;;;;;:::o;15103:455::-;15338:13;15366:9;15361:191;15381:7;15377:1;:11;15361:191;;;13526:10:8;13517:20;;13470:22;;;13457:36;15409:43:4;;;;;;;;15467:74;15487:5;;15494:8;15504:4;15510:9;15521:5;15528:9;15538:1;15528:12;;;;;;;;:::i;:::-;;;;;;;15467:19;:74::i;:::-;15390:3;;15361:191;;;;15328:230;15103:455;;;;;;;;:::o;34470:419:9:-;34582:7;34572:301;;34612:16;34609:146;;;34672:16;34669:1;34666;34651:38;34720:16;34717:1;34710:27;34609:146;34783:10;34780:1;34773:21;34857:1;34851:4;34844:15;34572:301;34470:419;:::o;28168:2370::-;28289:1;28281:5;:9;28277:2255;;;28306:15;28500:5;28497:1;28493:13;28484:22;;28534:10;28531:1;28524:21;28590:5;28586:2;28579:17;28660:1;28657;28653:2;28649;28646:1;28636:8;28629:5;28624:38;28613:49;;28689:22;28703:7;28689:13;:22::i;:::-;28726:19;28763;;;28759:278;;28869:59;28885:7;28909:17;:5;:15;:17::i;:::-;28895:31;;;28869:15;:59::i;:::-;28759:278;;;28967:55;28985:5;28992:10;29004:17;:5;:15;:17::i;28967:55::-;29051:14;29079:22;29168:10;29165:1;29158:21;29264:2;29261:1;29258;29254:2;29241:11;29231:8;29224:5;29219:48;29208:59;;29302:16;29284:34;;29351:1;29345:8;29335:18;;29380:22;29394:7;29380:13;:22::i;:::-;29420:14;29438:2;29420:20;29416:69;;29449:36;;;;;;;;;;;;;;29416:69;28292:1204;;;;28277:2255;;;29514:1;29506:5;:9;29502:1030;;;29683:10;29696:19;;;;:47;;29734:9;29696:47;;;29726:4;29696:47;29683:60;;29757:15;29846:4;29840:11;29881:10;29876:3;29869:23;29963:5;29958:2;29953:3;29949:12;29942:27;30007:2;30002;29997:3;29993:12;29986:24;30048:5;30043:2;30038:3;30034:12;30027:27;30130:1;30127;30122:3;30117:2;30112:3;30108:12;30105:1;30095:8;30088:5;30083:49;30072:60;;30172:3;30167;30163:13;30157:4;30150:27;;30204:22;30218:7;30204:13;:22::i;:::-;30250:9;30244:15;;:2;:15;;;30240:282;;30451:56;30469:7;30478:9;30489:17;:5;:15;:17::i;11634:908:8:-;11812:26;;11807:3;11803:36;9547:2;11905:23;;9452:1;11889:39;;;11803:36;11694:11;;11963:18;;;;11999;;;11995:59;;;12026:28;;;;;;;;;;;;;;11995:59;9498:2;12073:10;:23;:28;12069:409;;9682:1;12317:34;12333:5;;12340:10;12317:15;:34::i;:::-;:59;12303:73;;9498:2;12303:73;12398:23;:28;12394:69;;12435:28;;;;;;;;;;;;;;12394:69;9498:2;12502:10;:23;12492:33;;11859:677;11634:908;;;;;:::o;19313:243:4:-;19440:4;19424:21;;;;19420:129;;19447:36;19465:5;19472:2;19476:6;19447:17;:36::i;:::-;19420:129;;;19498:51;19517:6;19525:5;19532:4;19538:2;19542:6;19498:18;:51::i;15870:1071::-;16080:7;16099:13;16115:24;16133:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;16115:24:4;16099:40;;16149:17;16169:28;16191:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;16169:28:4;16149:48;;16207:18;16228:29;16251:5;15406:15:8;15402:27;15431:10;15398:44;;15255:203;16228:29:4;16207:50;;16267:17;16301:11;16287:10;:25;:46;;16328:4;16287:46;;;16315:2;16287:46;16267:66;;16344:16;741:6:2;16385:26:4;16405:5;14586:13:8;14582:25;14609:11;14578:43;;14444:193;16385:26:4;16363:8;16372:9;16363:19;;;;;;;;:::i;:::-;;;;;;;:48;;;;:::i;:::-;:54;;;;:::i;:::-;16344:73;;16450:8;16427;16436:9;16427:19;;;;;;;;:::i;:::-;;;;;;:31;;;;;;;:::i;:::-;;;-1:-1:-1;16470:15:4;;16513:141;16547:5;;16554:9;16565:4;822:1:7;2261:16;;;:28;16571:63:4;;16611:23;16628:5;14326:13:8;14322:25;;14197:166;16611:23:4;16571:63;;;16603:4;16571:63;16636:8;16513:20;:141::i;:::-;16469:185;;;;16665:28;16678:14;16665:12;:28::i;:::-;16703:17;16723:62;16729:5;;16736;16743:7;16752:14;16768:9;16779:5;16723;:62::i;:::-;16703:82;;16795:23;16808:9;16795:12;:23::i;:::-;16853:9;16829:8;16838:10;16829:20;;;;;;;;:::i;:::-;;;;;;:33;;;;;;;:::i;:::-;;;-1:-1:-1;16904:20:4;;;;;15870:1071;-1:-1:-1;;;;;;;;;;;;;;15870:1071:4:o;8138:150::-;8203:11;;;:41;;-1:-1:-1;8227:17:4;8218:26;;8203:41;8199:82;;;8253:28;;;;;;;;;;;;;;2857:1228:3;2947:16;2975:12;2990:23;3007:5;14326:13:8;14322:25;;14197:166;2990:23:3;2975:38;;3023:13;3039:24;3057:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;3039:24:3;3023:40;-1:-1:-1;911:6:7;1884:15;;3117:25:3;;;3113:965;;3155:39;3171:4;3177:5;3184:9;3155:15;:39::i;:::-;3144:50;;3113:965;;;3213:40;;;3209:869;;3266:44;3287:4;3293:5;3300:9;3266:20;:44::i;3209:869::-;3329:33;;;3325:753;;3375:38;3390:4;3396:5;3403:9;3375:14;:38::i;3325:753::-;3432:25;;;3428:650;;3470:39;3486:4;3492:5;3499:9;3470:15;:39::i;3428:650::-;3528:29;;;3524:554;;3570:38;3585:4;3591:5;3598:9;3570:14;:38::i;3524:554::-;3627:32;;;3623:455;;3672:40;3689:4;3695:5;3702:9;3672:16;:40::i;3623:455::-;4052:26;;;;;;;;;;;;;;3623:455;2965:1120;;;2857:1228;;;;;;:::o;17166:661:4:-;17366:12;17381:23;17398:5;14326:13:8;14322:25;;14197:166;17381:23:4;17366:38;;17414:13;17430:24;17448:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;17430:24:4;17414:40;;17466:15;17483:22;17509:141;17543:5;;17550:28;17572:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;17550:28:4;17580:4;822:1:7;2261:16;;;:28;17586:44:4;;17626:4;17586:44;;;17618:4;17586:44;17632:8;17509:20;:141::i;:::-;17465:185;;-1:-1:-1;17465:185:4;-1:-1:-1;17661:17:4;17714:12;17725:1;17714:8;:12;:::i;:::-;17681:29;17704:5;15406:15:8;15402:27;15431:10;15398:44;;15255:203;17681:29:4;:45;:66;;17742:4;17681:66;;;17729:2;17681:66;17661:86;;17758:62;17764:5;;17771;17778:7;17787:14;17803:9;17814:5;17758;:62::i;:::-;;17356:471;;;;;17166:661;;;;;;;:::o;751:202:11:-;803:7;889:1;884;:6;880:29;;-1:-1:-1;907:1:11;751:202::o;880:29::-;926:20;;;;;;;;;;;;;;3310:404:12;3378:15;3453:10;3450:1;3443:21;3509:6;3505:2;3498:18;3576:1;3573;3569:2;3565;3562:1;3553:7;3546:5;3541:37;3530:48;;3602:7;3613:1;3602:12;3598:110;;3630:22;:20;:22::i;:::-;3673:24;;;;;;;;;;;;;;15919:451:8;16240:22;16105:38;;16203:60;16207:31;;;16203:60;16190:74;16165:23;16161:104;16084:195;16311:1;16302:10;;;:15;;16298:65;;16326:37;;;;;;;;;;;;;;3271:550:10;3439:4;3433:11;3484:5;3480:2;3476:14;3472:2;3468:23;3465:1;3458:34;3524:4;3520:2;3516:13;3512:2;3505:25;3562:2;3558;3554:11;3550:2;3543:23;3590:6;3586:2;3579:18;3654:1;3651;3647:2;3644:1;3641;3633:6;3626:5;3621:35;3611:161;;3697:16;3694:1;3691;3676:38;3741:16;3738:1;3731:27;3611:161;3793:4;3786:19;-1:-1:-1;;;;;3271:550:10:o;18388:697:4:-;18532:7;;11243:12:8;11235:21;;11198:60;;11220:13;11198:60;11185:74;11170:13;11166:94;18628:12:4;;;18624:422;;10715:22:8;10697:41;;10684:55;10661:21;10657:83;10650:91;10643:99;18656:18:4;10643:99:8;18742:49:4;;18790:1;18742:49;;;18758:29;18777:5;18784:2;18758:18;:29::i;:::-;18724:67;;18805:51;18824:6;18832:5;18839:4;18845:2;18849:6;18805:18;:51::i;:::-;18879:13;:64;;18937:6;18879:64;;;18927:7;18895:29;18914:5;18921:2;18895:18;:29::i;:::-;:39;;;;:::i;:::-;18870:73;;18642:312;;18624:422;;;18964:19;;;18978:4;18964:19;18960:86;;18999:36;19017:5;19024:2;19028:6;18999:17;:36::i;:::-;19064:5;19071:6;;-1:-1:-1;18388:697:4;;-1:-1:-1;;;;;;18388:697:4:o;4257:1256:3:-;4406:17;4439:12;4454:23;4471:5;14326:13:8;14322:25;;14197:166;4454:23:3;4439:38;-1:-1:-1;911:6:7;1884:15;;4530:25:3;;;4526:980;;4569:42;4578:4;4584:5;4591:8;4601:9;4569:8;:42::i;:::-;4557:54;;4526:980;;;4630:40;;;4626:880;;4684:37;4698:4;4704:5;4711:9;4684:13;:37::i;4626:880::-;4740:33;;;4736:770;;4787:31;4795:4;4801:5;4808:9;4787:7;:31::i;4736:770::-;4837:25;;;4833:673;;4876:51;4885:4;4891:5;4898:9;4909:8;4919:7;4876:8;:51::i;4833:673::-;4946:29;;;4942:564;;4989:41;4997:4;5003:5;5010:9;5021:8;4989:7;:41::i;4942:564::-;5049:32;;;5045:461;;5095:43;5105:4;5111:5;5118:9;5129:8;5095:9;:43::i;5045:461::-;5274:21;;;5270:236;;5309:47;5321:4;5327:9;5338:8;5348:7;5309:11;:47::i;5270:236::-;5375:18;;;5371:135;;5407:51;5416:4;5422:5;5429:9;5440:8;5450:7;5407:8;:51::i;5371:135::-;4429:1084;;4257:1256;;;;;;;;;:::o;5649:315::-;5745:7;;;5806:61;5837:4;778:1:7;2064:20;;;:36;5806:30:3;:61::i;:::-;5764:103;;-1:-1:-1;5764:103:3;-1:-1:-1;5923:22:3;5936:9;5764:103;5923:22;:::i;:::-;5922:30;;5949:3;5922:30;:::i;:::-;5916:1;5885:21;5897:9;5885;:21;:::i;:::-;:28;;5909:4;5885:28;:::i;:::-;:32;;;;:::i;:::-;5884:69;;;;:::i;:::-;:73;;5956:1;5884:73;:::i;:::-;5877:80;5649:315;-1:-1:-1;;;;;;5649:315:3:o;6824:259::-;6949:16;6988:88;7022:11;7035:4;7041:9;778:1:7;2064:20;;;:36;6988:33:3;:88::i;:::-;6981:95;6824:259;-1:-1:-1;;;;6824:259:3:o;7533:334::-;7628:7;;;7688:69;7716:4;7722:9;778:1:7;2064:20;;;:36;7688:27:3;:69::i;:::-;7647:110;;;;7771:10;7785:1;7771:15;7767:68;;7795:40;;;;;;;;;;;;;;7767:68;-1:-1:-1;7852:8:3;7533:334;-1:-1:-1;;;;7533:334:3:o;8296:203::-;8387:16;8422:70;8451:4;778:1:7;2064:20;;;:36;8482:9:3;8422:28;:70::i;9995:364::-;10090:7;;;10167:69;10195:4;10201:9;778:1:7;2064:20;;;:36;10167:27:3;:69::i;:::-;10109:127;;;;10269:9;10250:15;:28;10246:81;;10287:40;;;;;;;;;;;;;;10991:370;11088:7;;;11165:71;11195:4;11201:9;778:1:7;2064:20;;;:36;11165:29:3;:71::i;:::-;11107:129;;;;11269:9;11250:15;:28;11246:83;;11287:42;;;;;;;;;;;;;;680:731:12;754:14;780:15;805:22;887:10;884:1;877:21;944:7;940:2;933:19;1013:2;1010:1;1006:2;1002;995:5;988;977:39;966:50;;1048:16;1030:34;;1094:1;1088:8;1078:18;;1120:7;1131:1;1120:12;1116:40;;1134:22;:20;:22::i;:::-;1366:2;1349:14;:19;1345:59;;;1377:27;;;;;;;;;;;;;;1345:59;770:641;;680:731;;;;:::o;6071:610:3:-;6189:17;778:1:7;2064:20;;;:36;6189:17:3;;6312:45;6343:4;2064:36:7;6312:30:3;:45::i;:::-;6270:87;;-1:-1:-1;6270:87:3;-1:-1:-1;6368:23:3;6394:14;:8;6405:3;6394:14;:::i;:::-;6368:40;-1:-1:-1;6368:40:3;6464:16;:9;6476:4;6464:16;:::i;:::-;:34;;;;:::i;:::-;6431:28;6449:10;6431:15;:28;:::i;:::-;6430:69;;;;:::i;:::-;6418:81;;6511:15;6528;6547:7;:59;;6584:9;6603:1;6547:59;;;6566:1;6570:9;6547:59;6510:96;;;;6616:58;6640:4;6646:7;6655;6664:9;6616:23;:58::i;:::-;6212:469;;;;;;6071:610;;;;;;:::o;7193:202::-;7282:17;7318:70;7347:4;778:1:7;2064:20;;;:36;7378:9:3;7318:28;:70::i;7970:190::-;8053:17;8089:64;8112:4;778:1:7;2064:20;;;:36;8143:9:3;8089:22;:64::i;8656:624::-;8831:28;8857:2;8831:28;;;;1358:14:7;8830:52:3;8814:13;:68;8791:7;;;;8965:84;8847:4;8995:9;9006:23;9023:5;778:1:7;2064:20;;;:36;;1983:124;9006:23:3;9031:8;9041:7;8965:23;:84::i;:::-;8893:156;;;;;;9081:4;9064:13;;:21;9060:69;;9094:35;;;;;;;;;;;;;;9060:69;9161:8;9143:14;:26;9139:74;;9178:35;;;;;;;;;;;;;;9139:74;-1:-1:-1;;9240:6:3;9224:13;:22;9264:9;8656:624;-1:-1:-1;;;;;;8656:624:3:o;10470:375::-;10571:7;;;10648:74;10671:4;10677:9;10688:8;778:1:7;2064:20;;;:36;10648:22:3;:74::i;:::-;10590:132;;;;10755:8;10737:14;:26;10733:79;;10772:40;;;;;;;;;;;;;;10733:79;-1:-1:-1;10829:9:3;10470:375;-1:-1:-1;;;;;10470:375:3:o;11475:381::-;11578:7;;;11655:76;11680:4;11686:9;11697:8;778:1:7;2064:20;;;:36;11655:24:3;:76::i;:::-;11597:134;;;;11764:8;11746:14;:26;11742:81;;11781:42;;;;;;;;;;;;;;14166:288;14289:17;14322:46;14344:7;14353:4;14359:8;14322:21;:46::i;:::-;14385:62;14412:4;14418:9;14429:8;14439:7;14385:26;:62::i;:::-;14378:69;14166:288;-1:-1:-1;;;;;14166:288:3:o;14569:617::-;14744:28;14770:2;14744:28;;;;1668:14:7;14743:45:3;14727:13;:61;14704:7;;;;14871:84;14760:4;14901:9;14912:8;14922:23;14939:5;778:1:7;2064:20;;;:36;;1983:124;14922:23:3;14947:7;14871:23;:84::i;1187:752:9:-;1262:17;1281:18;1311:22;1392:10;1389:1;1382:21;1471:2;1468:1;1465;1461:2;1455:4;1448:5;1437:37;1434:79;;;-1:-1:-1;1495:16:9;1434:79;1534:7;1554:138;;;;1758:12;1754:1;1748:8;1744:27;1731:40;;1817:12;1812:2;1806:9;1802:28;1788:42;;1527:317;;1554:138;1607:12;1602:2;1596:9;1592:28;1579:41;;1665:12;1661:1;1655:8;1651:27;1637:41;;1527:317;;1885:2;1868:14;:19;1864:68;;;1896:36;;;;;;;;;;;;;;1864:68;1301:638;1187:752;;;;;:::o;3269:812::-;3407:16;3439:22;3471:15;3552:4;3546:11;3583:10;3578:3;3571:23;3663:4;3658:2;3653:3;3649:12;3642:26;3702:9;3697:2;3692:3;3688:12;3681:31;3746:8;3741:2;3736:3;3732:12;3725:30;3791:3;3786;3782:13;3776:4;3769:27;3869:2;3866:1;3861:3;3856:2;3851:3;3847:12;3839:6;3832:5;3821:51;3810:62;;;3904:16;3886:34;;3952:1;3946:8;3934:20;;3973:22;3987:7;3973:13;:22::i;:::-;4027:2;4010:14;:19;4006:68;;;4038:36;;;;;;;;;;;;;;4006:68;3429:652;;3269:812;;;;;;:::o;5336:758::-;5452:16;5470:18;5504:22;5536:15;5619:4;5613:11;5648:10;5645:1;5638:21;5710:9;5706:2;5699:21;5744:8;5740:2;5733:20;5813:2;5810:1;5806:2;5802;5796:4;5789:5;5778:38;5767:49;;5848:16;5830:34;;5896:1;5890:8;5878:20;;5931:2;5925:9;5911:23;;5961:5;5955:4;5948:19;;5986:22;6000:7;5986:13;:22::i;:::-;6040:2;6023:14;:19;6019:68;;;6051:36;;;;;;;;;;;;;;6019:68;5494:600;;5336:758;;;;;;:::o;7566:781::-;7656:16;7685:15;7702:11;7717:79;7729:4;7743;7750:10;7763:20;:9;:18;:20::i;:::-;7762:21;;;:::i;:::-;7793:1;7717:11;:79::i;:::-;7684:112;;;;8004:3;7986:16;7983:25;7973:7;7966:15;7962:47;8027:1;8022:121;;;;8189:10;8216:42;;;;8312:1;8307:3;8303:11;8297:18;8285:30;;8182:135;;8216:42;8252:2;8247:3;8243:12;8237:19;8225:31;;8182:135;;7955:376;;8022:121;8068:16;8065:1;8062;8047:38;8112:16;8109:1;8102:27;7955:376;;7832:509;;7566:781;;;;;:::o;12277:1130::-;12393:16;12411:23;12450:22;12482:15;12565:4;12559:11;12594:10;12591:1;12584:21;12668:9;12665:1;12661:17;12657:2;12650:29;12703:8;12699:2;12692:20;12772:2;12769:1;12765:2;12761;12755:4;12748:5;12737:38;12807:16;;-1:-1:-1;12726:49:9;-1:-1:-1;12844:8:9;12865:104;;;;13026:1;13020:8;13008:20;;13070:2;13064:9;13045:28;;12837:250;;12865:104;12908:2;12902:9;12890:21;;12953:1;12947:8;12928:27;;12837:250;;13127:1;13117:8;13113:16;13101:28;;13212:15;13209:1;13205:23;13186:42;;13274:5;13268:4;13261:19;;13299:22;13313:7;13299:13;:22::i;16096:1223::-;16214:16;16232:23;16271:25;16299:39;16323:4;16329:8;16299:23;:39::i;:::-;16271:67;;16349:22;16381:15;16462:4;16456:11;16493:10;16488:3;16481:23;16578:8;16573:2;16568:3;16564:12;16557:30;16628:9;16625:1;16621:17;16616:2;16611:3;16607:12;16600:39;16673:17;16668:2;16663:3;16659:12;16652:39;16727:3;16722;16718:13;16712:4;16705:27;16803:2;16800:1;16795:3;16790:2;16785:3;16781:12;16775:4;16768:5;16757:49;16746:60;;;16837:16;16819:34;;16874:8;16900:1;16895:104;;;;17056:1;17050:8;17038:20;;17100:2;17094:9;17075:28;;16867:250;;16895:104;16938:2;16932:9;16920:21;;16983:1;16977:8;16958:27;;16867:250;;17157:15;17154:1;17150:23;17131:42;;17211:22;17225:7;17211:13;:22::i;:::-;17265:2;17248:14;:19;17244:68;;;17276:36;;;;;;;;;;;;;;17244:68;16261:1058;;;16096:1223;;;;;;:::o;2231:686::-;2334:15;2415:4;2409:11;2446:10;2441:3;2434:23;2530:7;2525:2;2520:3;2516:12;2509:29;2572:7;2567:2;2562:3;2558:12;2551:29;2614:9;2609:2;2604:3;2600:12;2593:31;2659:3;2653;2648;2644:13;2637:26;2698:1;2692:3;2687;2683:13;2676:24;2736:3;2731;2727:13;2721:4;2714:27;2867:1;2864;2859:3;2854:2;2849:3;2845:12;2842:1;2836:4;2829:5;2824:45;2813:56;;;2888:22;2902:7;2888:13;:22::i;:::-;2324:593;2231:686;;;;:::o;4313:751::-;4401:17;4430:22;4462:15;4546:4;4540:11;4575:10;4572:1;4565:21;4632:8;4628:2;4621:20;4665:9;4661:2;4654:21;4732:2;4729:1;4725:2;4721;4718:1;4712:4;4705:5;4700:35;4767:16;;-1:-1:-1;4689:46:9;-1:-1:-1;4804:8:9;4825:32;;;;4899:2;4893:9;4880:22;;4797:107;;4825:32;4853:1;4847:8;4834:21;;4797:107;-1:-1:-1;4925:4:9;4918:19;4956:22;4970:7;4956:13;:22::i;:::-;5010:2;4993:14;:19;4989:68;;;5021:36;;;;;;;;;;;;;;4989:68;4420:644;;4313:751;;;;;:::o;6335:765::-;6417:17;6446:22;6478:15;6562:4;6556:11;6591:10;6588:1;6581:21;6648:8;6644:2;6637:20;6681:9;6677:2;6670:21;6748:2;6745:1;6741:2;6737;6734:1;6728:4;6721:5;6716:35;6783:16;;-1:-1:-1;6705:46:9;-1:-1:-1;6820:8:9;6841:43;;;;6935:1;6929:8;6924:3;6920:18;6907:31;;6813:127;;6841:43;6878:2;6872:9;6867:3;6863:19;6850:32;;6813:127;-1:-1:-1;6961:4:9;6954:19;6992:22;7006:7;6992:13;:22::i;:::-;7046:2;7029:14;:19;7025:68;;;7057:36;;;;;;;;;;;;;;8576:1035;8712:23;8737:22;8761:20;8798:15;8815:11;8830:70;8842:4;8848:9;8859:10;8871:19;:8;:17;:19::i;:::-;8892:7;8830:11;:70::i;:::-;8797:103;;;;8910:22;8924:7;8910:13;:22::i;:::-;9079:2;9070:12;;9063:29;;;9136:2;9121:18;;;-1:-1:-1;9033:16:9;9160:10;9183:122;;;;9368:3;9362:10;9344:28;;9423:2;9418:3;9414:12;9408:19;9389:38;;9153:288;;9183:122;9233:3;9227:10;9208:29;;9287:2;9282:3;9278:12;9272:19;9254:37;;9153:288;;9481:15;9478:1;9474:23;9455:42;;9557:2;9540:14;:19;9536:68;;;9568:36;;;;;;;;;;;;;;9536:68;8787:824;;;8576:1035;;;;;;;;;:::o;13640:1286::-;13756:17;13775:22;13813;13845:15;13927:4;13921:11;13958:10;13953:3;13946:23;14046:9;14041:2;14036:3;14032:12;14025:31;14090:8;14085:2;14080:3;14076:12;14069:30;14133:8;14128:2;14123:3;14119:12;14112:30;14177:3;14171;14166;14162:13;14155:26;14216:1;14210:3;14205;14201:13;14194:24;14253:1;14247:3;14242;14238:13;14231:24;14291:3;14286;14282:13;14276:4;14269:27;14422:2;14419:1;14414:3;14409:2;14404:3;14400:12;14397:1;14391:4;14384:5;14379:46;14368:57;;;14456:16;14438:34;;14493:8;14519:1;14514:104;;;;14681:1;14675:8;14657:26;;14719:2;14713:9;14700:22;;14486:250;;14514:104;14563:2;14557:9;14539:27;;14602:1;14596:8;14583:21;;14486:250;;14770:9;14767:1;14763:17;14750:30;;14818:22;14832:7;14818:13;:22::i;:::-;14872:2;14855:14;:19;14851:68;;;14883:36;;;;;;;;;;;;;;14851:68;13803:1123;;13640:1286;;;;;;;:::o;17555:1242::-;17673:17;17692:22;17730:25;17758:39;17782:4;17788:8;17758:23;:39::i;:::-;17730:67;;17808:22;17840:15;17921:4;17915:11;17952:10;17947:3;17940:23;18034:9;18029:2;18024:3;18020:12;18013:31;18078:8;18073:2;18068:3;18064:12;18057:30;18121:8;18116:2;18111:3;18107:12;18100:30;18165:17;18159:3;18154;18150:13;18143:40;18219:3;18214;18210:13;18204:4;18197:27;18292:2;18289:1;18284:3;18279:2;18274:3;18270:12;18267:1;18261:4;18254:5;18249:46;18238:57;;;18327:16;18309:34;;18364:8;18390:1;18385:104;;;;18552:1;18546:8;18528:26;;18590:2;18584:9;18571:22;;18357:250;;18385:104;18434:2;18428:9;18410:27;;18473:1;18467:8;18454:21;;18357:250;;18641:9;18638:1;18634:17;18621:30;;18689:22;18703:7;18689:13;:22::i;:::-;18743:2;18726:14;:19;18722:68;;;18754:36;;;;;;;;;;;;;;18722:68;17720:1077;;;17555:1242;;;;;;;:::o;6170:1190:12:-;6259:15;6284:18;6312:19;6381:164;;;6456:7;6452:2;6445:19;6418:8;6528:2;6524;6520;6516;6513:1;6505:6;6498:5;6493:38;6481:50;6381:164;-1:-1:-1;;;6381:164:12:o;:::-;6578:4;6572:11;6607:10;6604:1;6597:21;6670:7;6666:2;6659:19;6703:22;6718:6;6711:5;6703:22;:::i;:::-;6692:33;;6770:7;6766:1;6761:2;6755:9;6752:16;6748:30;6738:263;;6861:7;6842:16;6833:5;6821:18;6814:26;6811:48;6808:61;6798:189;;6897:17;6912:1;6905:5;6897:17;:::i;:::-;6893:22;6947;6962:6;6955:5;6947:22;:::i;:::-;6936:33;;6798:189;7029:16;7015:30;;7079:2;7073:9;7058:24;;7109:5;7103:4;7096:19;;7139:7;7150:1;7139:12;7135:111;;7167:22;:20;:22::i;:::-;7210:25;;;;;;;;;;;;;;7135:111;7260:15;;:59;;7303:11;7318:1;7303:16;;7260:59;;;7278:17;;;;:22;7260:59;7256:97;;;7328:25;;;;;;;;;;;;;;31776:862:9;31898:17;31931:22;31963:15;32044:4;32038:11;32075:10;32070:3;32063:23;32158:7;32153:2;32148:3;32144:12;32137:29;32200:1;32195:2;32190:3;32186:12;32179:23;32261:8;32256:2;32251:3;32247:12;32240:30;32305:9;32299:3;32294;32290:13;32283:32;32383:2;32380:1;32375:3;32370:2;32365:3;32361:12;32358:1;32352:4;32345:5;32340:46;32329:57;;32418:16;32400:34;;32467:1;32461:8;32448:21;;32505:3;32500;32496:13;32490:4;32483:27;;32529:22;32543:7;32529:13;:22::i;:::-;32566:14;32584:2;32566:20;32562:69;;32595:36;;;;;;;;;;;;;;32860:1372;32994:23;33019:22;33043:20;33079:22;33111:15;33192:4;33186:11;33223:10;33218:3;33211:23;33304:9;33299:2;33294:3;33290:12;33283:31;33348:8;33343:2;33338:3;33334:12;33327:30;33391:8;33386:2;33381:3;33377:12;33370:30;33435:3;33429;33424;33420:13;33413:26;33474:2;33468:3;33463;33459:13;33452:25;33512:7;33506:3;33501;33497:13;33490:30;33590:2;33585:3;33580;33575:2;33570:3;33566:12;33563:1;33557:4;33550:5;33545:48;33534:59;;33625:16;33607:34;;33676:7;33671:2;33666:3;33662:12;33655:29;33728:2;33723:3;33713:18;33697:34;;33752:8;33778:1;33773:122;;;;33913:1;33908:122;;;;33745:285;;33773:122;33823:3;33817:10;33798:29;;33877:2;33872:3;33868:12;33862:19;33844:37;;33773:122;;33908;33957:3;33951:10;33933:28;;34012:2;34007:3;34003:12;33997:19;33978:38;;33745:285;;;34070:15;34067:1;34063:23;34044:42;;34124:22;34138:7;34124:13;:22::i;:::-;34178:2;34161:14;:19;34157:68;;;34189:36;;;;;;;;;;;;;;34157:68;33069:1163;;32860:1372;;;;;;;;;:::o;11173:840::-;11315:15;11332:11;11359:18;11380:10;:54;;718:53;11380:54;;;650:14;11380:54;11359:75;;11497:4;11491:11;11484:18;;11528:10;11523:3;11516:23;11616:9;11611:2;11606:3;11602:12;11595:31;11660:10;11655:2;11650:3;11646:12;11639:32;11705:11;11700:2;11695:3;11691:12;11684:33;11752:10;11746:3;11741;11737:13;11730:33;11798:3;11792;11787;11783:13;11776:26;11837:2;11831:3;11826;11822:13;11815:25;11875:7;11869:3;11864;11860:13;11853:30;11919:3;11914;11910:13;11904:4;11897:27;11994:2;11989:3;11984;11979:2;11974:3;11970:12;11967:1;11961:4;11954:5;11949:48;11938:59;;11470:537;11173:840;;;;;;;;:::o;15198:631::-;15283:25;15320:22;15408:4;15402:11;15437:10;15434:1;15427:21;15524:2;15521:1;15518;15514:2;15508:4;15501:5;15490:37;15487:79;;;15548:16;15530:34;;15487:79;15587:8;15608:41;;;;15699:1;15693:8;15672:29;;15580:123;;15608:41;15644:2;15638:9;15617:30;;15580:123;-1:-1:-1;15724:4:9;15717:17;15775:2;15758:19;;15754:68;;;15786:36;;;;;;;;;;;;;;15754:68;15310:519;15198:631;;;;:::o;196:154:13:-;282:42;275:5;271:54;264:5;261:65;251:93;;340:1;337;330:12;355:508;432:6;440;448;501:2;489:9;480:7;476:23;472:32;469:52;;;517:1;514;507:12;469:52;556:9;543:23;575:31;600:5;575:31;:::i;:::-;625:5;-1:-1:-1;682:2:13;667:18;;654:32;695:33;654:32;695:33;:::i;:::-;355:508;;747:7;;-1:-1:-1;;;827:2:13;812:18;;;;799:32;;355:508::o;1099:1388::-;1223:6;1231;1239;1247;1255;1263;1271;1279;1332:3;1320:9;1311:7;1307:23;1303:33;1300:53;;;1349:1;1346;1339:12;1300:53;1388:9;1375:23;1407:31;1432:5;1407:31;:::i;:::-;1457:5;-1:-1:-1;1514:2:13;1499:18;;1486:32;1527:33;1486:32;1527:33;:::i;:::-;1579:7;-1:-1:-1;1659:2:13;1644:18;;1631:32;;-1:-1:-1;1762:2:13;1747:18;;1734:32;;-1:-1:-1;1844:3:13;1829:19;;1816:33;1858;1816;1858;:::i;:::-;1910:7;-1:-1:-1;1969:3:13;1954:19;;1941:33;1983;1941;1983;:::i;:::-;2035:7;-1:-1:-1;2093:3:13;2078:19;;2065:33;2121:18;2110:30;;2107:50;;;2153:1;2150;2143:12;2107:50;2176:22;;2229:4;2221:13;;2217:27;-1:-1:-1;2207:55:13;;2258:1;2255;2248:12;2207:55;2298:2;2285:16;2324:18;2316:6;2313:30;2310:50;;;2356:1;2353;2346:12;2310:50;2401:7;2396:2;2387:6;2383:2;2379:15;2375:24;2372:37;2369:57;;;2422:1;2419;2412:12;2369:57;1099:1388;;;;-1:-1:-1;1099:1388:13;;-1:-1:-1;1099:1388:13;;;;;;2453:2;2445:11;;-1:-1:-1;;1099:1388:13:o;2745:435::-;2812:6;2820;2873:2;2861:9;2852:7;2848:23;2844:32;2841:52;;;2889:1;2886;2879:12;2841:52;2928:9;2915:23;2947:31;2972:5;2947:31;:::i;:::-;2997:5;-1:-1:-1;3054:2:13;3039:18;;3026:32;3102:26;3089:40;;3077:53;;3067:81;;3144:1;3141;3134:12;3067:81;3167:7;3157:17;;;2745:435;;;;;:::o;3185:184::-;3237:77;3234:1;3227:88;3334:4;3331:1;3324:15;3358:4;3355:1;3348:15;4055:184;4107:77;4104:1;4097:88;4204:4;4201:1;4194:15;4228:4;4225:1;4218:15;4244:251;4314:6;4367:2;4355:9;4346:7;4342:23;4338:32;4335:52;;;4383:1;4380;4373:12;4335:52;4415:9;4409:16;4434:31;4459:5;4434:31;:::i;4500:184::-;4552:77;4549:1;4542:88;4649:4;4646:1;4639:15;4673:4;4670:1;4663:15;4689:128;4756:9;;;4777:11;;;4774:37;;;4791:18;;:::i;4822:168::-;4895:9;;;4926;;4943:15;;;4937:22;;4923:37;4913:71;;4964:18;;:::i;4995:274::-;5035:1;5061;5051:189;;5096:77;5093:1;5086:88;5197:4;5194:1;5187:15;5225:4;5222:1;5215:15;5051:189;-1:-1:-1;5254:9:13;;4995:274::o;5274:184::-;5326:77;5323:1;5316:88;5423:4;5420:1;5413:15;5447:4;5444:1;5437:15;5676:125;5741:9;;;5762:10;;;5759:36;;;5775:18;;:::i;5806:196::-;5845:3;5873:5;5863:39;;5882:18;;:::i;:::-;-1:-1:-1;5929:66:13;5918:78;;5806:196::o;6007:191::-;6042:3;6073:66;6066:5;6063:77;6060:103;;6143:18;;:::i;:::-;-1:-1:-1;6183:1:13;6179:13;;6007:191::o
Swarm Source
ipfs://8b73127399dde8946da9be162faea1fd4faab80441131109f005eacf4d928049
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.