Source Code
Overview
MNT Balance
MNT Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
RouterLogic
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
paris EvmVersion
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);
}
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 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);
}
/**
* @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);
}
}// 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;
/// 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;
/**
* @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 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",
"devdoc",
"userdoc",
"metadata",
"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
61010060405261dead60015534801561001757600080fd5b5060405161355f38038061355f83398101604081905261003691610164565b8484848484610045828261009b565b50506001600160a01b03928316608052821660c052811660a05286163b600003610082576040516320ee910f60e21b815260040160405180910390fd5b5050506001600160a01b0390921660e052506101e99050565b6001600160a01b0382166100c25760405163ec049e3760e01b815260040160405180910390fd5b612710816001600160601b031611156100ee576040516318fd95d160e11b815260040160405180910390fd5b6001600160a01b038216600160a01b6001600160601b038316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b80516001600160a01b038116811461015f57600080fd5b919050565b60008060008060008060c0878903121561017d57600080fd5b61018687610148565b955061019460208801610148565b94506101a260408801610148565b93506101b060608801610148565b92506101be60808801610148565b60a08801519092506001600160601b03811681146101db57600080fd5b809150509295509295509295565b60805160a05160c05160e05161331c61024360003960008181610c0801528181610d66015281816119b80152611f3601526000818160860152818160b001526103870152600061015a015260006121bf015261331c6000f3fe6080604052600436106100695760003560e01c8063bd08443511610043578063bd0844351461023f578063c60bc20314610274578063cb7e000714610294576100d7565b8063192bcad3146101a157806362c06767146101ea57806372c8fc0e1461020a576100d7565b366100d7573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100d5576100d57f0000000000000000000000000000000000000000000000000000000000000000346102b4565b005b3480156100e357600080fd5b50600154600090369060609061ff00811681831c6104008214801561011d57503373ffffffffffffffffffffffffffffffffffffffff8216145b156101365761012c8686610312565b9350505050610196565b6107008214801561017c57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b1561018c5761012c86868361037a565b3660008037366000fd5b915050805190602001f35b3480156101ad57600080fd5b506000547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166040519081526020015b60405180910390f35b3480156101f657600080fd5b506100d5610205366004612fcc565b6103ed565b34801561021657600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e1565b34801561024b57600080fd5b5061025f61025a36600461300d565b610426565b604080519283526020830191909152016101e1565b34801561028057600080fd5b506100d561028f3660046130de565b61067c565b3480156102a057600080fd5b5061025f6102af36600461300d565b610692565b600063d0e30db06000526000806004601c85875af190508060000361030d576102db610982565b6040517fdc0c8cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b606060008060006103238686610994565b6040805160008581526020859052838352606090209152929550909350915060015561035f81336000861361035857846109e6565b855b6109e6565b50506040805160008152602081019091529150505b92915050565b60606000806103ab8686867f0000000000000000000000000000000000000000000000000000000000000000610ac5565b60408051602081810152808201919091526060810183905260808101829052919350915060a001604051602081830303815290604052925050505b9392505050565b6103f5610c06565b73ffffffffffffffffffffffffffffffffffffffff83161561041c5761030d8383836109e6565b61030d8282610cf9565b6000806104b360405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6104bf85858d8d610d49565b60608501526040840181905260208401919091528183526104e4918791879190610fad565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152908116608083018190528a918d16900361054d5760006127108360c0015183028161053357610533613157565b049050808203915061054b8d8a8560a00151846110bb565b505b60006105a98284604001518b8f73ffffffffffffffffffffffffffffffffffffffff16876080015173ffffffffffffffffffffffffffffffffffffffff1614610596578b610598565b305b876020015188606001518d8d611218565b90508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036106205760006127108460c001518302816105fb576105fb613157565b04905080820391506106138d308660a00151846110bb565b61061e8d8a846109e6565b505b89811015610669576040517fc18a3e5800000000000000000000000000000000000000000000000000000000815260048101829052602481018b90526044015b60405180910390fd5b999c999b50989950505050505050505050565b610684610c06565b61068e828261131e565b5050565b60008061071f60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b61072b85858d8d610d49565b6060850152604084018190526020840191909152818352610750918791879190610fad565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152166080820152600185013560f81c156107b7576040517fe4c5b0d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808990508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff160361081a573091508260c001516127100361271082028161081257610812613157565b04905061081e565b8791505b60008061083b838660400151876060015188602001518d8d611432565b915091508e73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16036108b35760008560c00151612710038660c0015184028161089557610895613157565b04905080830192506108b186608001518d8860a00151846110bb565b505b8c8211156108f7576040517f20cd355100000000000000000000000000000000000000000000000000000000815260048101839052602481018e9052604401610660565b6109138b8583886020015189604001518a606001518f8f61169f565b8d73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff160361096e5760008c840390506109618f308860a00151846110bb565b61096c8f8c8f6109e6565b505b509d999c50989a5050505050505050505050565b3d15610992573d6000803e3d6000fd5b565b6000808060a484146109d2576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505060043593602435935060843592509050565b600080600060405163a9059cbb6000528560205284604052602060006044601c60008b5af193503d92506000519150806040525082600003610a5c57610a2a610982565b6040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115610a6c578060011415610a86565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610abd576040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b60008060008060008060405163f3cd914c815260448a0360448c0160208301377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb828c010116604052602060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08c01601c840182335af1600051608081901d9850600f0b965092503d915088610b8f5760206000526040602052866040528560605260806000fd5b602081015194506040810151935050610ba7826116f7565b80602014610be1576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bed8489888a61171d565b610bf98389878a61171d565b5050505094509492505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c959190613186565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610992576040517f303693ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600085875af190508060000361030d57610d17610982565b6040517fa01b460600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080803373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610dbd576040517f4b2fd34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dc788886118b4565b919450925090506002821015610e09576040517f7c741b7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a830188840135604081901c61ff0016610e89576002831015610e59576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b939450927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101908390610ec3565b82600003610ec3576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028a013560601c73ffffffffffffffffffffffffffffffffffffffff891614610f19576040517f80e4580000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8716610f538b8b610f406001896131d2565b601402919091016002013560601c919050565b73ffffffffffffffffffffffffffffffffffffffff1614610fa0576040517ffc37688f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050945094509450949050565b6000808084156110b15786850135606081901c9250610fd08160501c61ffff1690565b91506000610fe18260381c60ff1690565b90506002601482028a01013560601c9450610fff8260301c60ff1690565b811861100f8360401c61ffff1690565b1715158061103157508015801590611031575061102d6001876131d2565b8114155b15611068576040517fee393ce900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158061107757506127108310155b156110ae576040517fa4c2399e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b9450945094915050565b80156112125773ffffffffffffffffffffffffffffffffffffffff821661110e576040517fae0edfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805461271090611146907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16846131e5565b61115091906131fc565b9050600061115e82846131d2565b905080156111725761117286868684611986565b811561119f5760005461119f908790879073ffffffffffffffffffffffffffffffffffffffff1685611986565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f77c197a1ae17318af19ae49d654709f9fe13e90a7fb70e1bdd5f665f6b9bce828585604051611207929190918252602082015260400190565b60405180910390a350505b50505050565b6000808867ffffffffffffffff81111561123457611234613128565b60405190808252806020026020018201604052801561125d578160200160208202803683370190505b509050600060018a0390508a8260008151811061127c5761127c613237565b6020026020010181815250506000805b878110156112b957601a89019887013591506112ad838888878f8f886119e0565b909c019b60010161128c565b508282815181106112cc576112cc613237565b60200260200101519350838c1461130f576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661136b576040517fec049e3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710816bffffffffffffffffffffffff1611156113b5576040517f31fb2ba200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216740100000000000000000000000000000000000000006bffffffffffffffffffffffff8316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b60006060601a86028501945060008667ffffffffffffffff81111561145957611459613128565b604051908082528060200260200182016040528015611482578160200160208202803683370190505b50905060008867ffffffffffffffff8111156114a0576114a0613128565b6040519080825280602002602001820160405280156114c9578160200160208202803683370190505b50905089816114d960018c6131d2565b815181106114e9576114e9613237565b6020908102919091010152896000895b8015611637577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe68a81019a8a0101359150600060ff603084901c16905060006115458460381c60ff1690565b9050600061271061155a8660501c61ffff1690565b88858151811061156c5761156c613237565b602002602001015161157e91906131e5565b61158891906131fc565b90508087848151811061159d5761159d613237565b602002602001018181516115b191906131d2565b9052506115bd81611b29565b6115c98c8c8784611b7c565b9950898783815181106115de576115de613237565b602002602001018181516115f29190613266565b9052506115fe8a611b29565b898861160986613279565b9550858151811061161c5761161c613237565b602002602001018181525050808a03860195505050506114f9565b508260008151811061164b5761164b613237565b6020026020010151955085821461168e576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509192505050965096945050505050565b6000805b848110156116eb57601a87018785013580935081985050506116e38484888d8d878e88815181106116d6576116d6613237565b6020026020010151611d12565b6001016116a3565b50505050505050505050565b8061171a573d1561170c573d6000803e3d6000fd5b63824d22356000526004601cfd5b50565b600082121561180357600082600003925063a5841194600052846020526000806040601c6000335af19050611751816116f7565b600073ffffffffffffffffffffffffffffffffffffffff8616611788576117838361177b86611dac565b925082611deb565b611796565b611796863361035a87611dac565b6000806311da60b4600052602060006004601c86335af193503d905060005191506117c0846116f7565b806020146117fa576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050611212565b600082131561121257600073ffffffffffffffffffffffffffffffffffffffff8516156118305783611832565b305b90506000604051630b0d9c0981528660208201528260408201528460608201526000806064601c84016000335af191506080810160405250611873816116f7565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610abd57610abd838661035a87611dac565b813560f81c6014810260028101919060009084037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0183851015611924576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81061561197857600361193a878784611e49565b019003601a810615611978576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81049150509250925092565b3073ffffffffffffffffffffffffffffffffffffffff8416036119b3576119ae8483836109e6565b611212565b6112127f000000000000000000000000000000000000000000000000000000000000000085858585611eb4565b6000806119f18360401c61ffff1690565b90506000611a028460381c60ff1690565b90506000611a138560301c60ff1690565b905060008b8214611a245730611a26565b865b90506000612710611a3b8860501c61ffff1690565b8b8681518110611a4d57611a4d613237565b6020026020010151611a5f91906131e5565b611a6991906131fc565b9050808a8581518110611a7e57611a7e613237565b60200260200101818151611a9291906131d2565b905250600080611ac08e8e888e6002808d1614611ab857611ab38e60601c90565b611aba565b305b88611ef6565b91509150611acd81611b29565b6000611ade8f8f8c86868a8e611fbd565b9050611ae981611b29565b808d8781518110611afc57611afc613237565b60200260200101818151611b109190613266565b905250929092039e9d5050505050505050505050505050565b801580611b4557506fffffffffffffffffffffffffffffffff81115b1561171a576040517f5e2ab1b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611b898460601c90565b90506000611b9b8560401c61ffff1690565b905061ff0081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008101611bdb57611bd4838387612149565b9350611d07565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe008101611c0d57611bd48383876121b8565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd008101611c3f57611bd48383876121f3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101611c7157611bd483838761224e565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101611ca357611bd4838387612260565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101611cd557611bd48383876122b1565b6040517f0886b18e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b6000611d1e8360601c90565b90506000611d308460401c61ffff1690565b9050600080611d608b8b611d478960381c60ff1690565b8b600280891614611d585788611d5a565b305b8a611ef6565b90925090506000611d7260018b6131d2565b611d7f8860301c60ff1690565b14611d8a5730611d8c565b875b9050611d9d8c8c898686868a611fbd565b50505050505050505050505050565b6000808212611db9575090565b6040517f20e4c52f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000632e1a7d4d600052816020526000806024601c6000875af190508060000361030d57611e17610982565b6040517fa39163c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd848401013560e81c02600180821690036103e6576040517faecb172600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60201c6000528360601b6018528260601b602c528160405260008060606000808a5af1611eec573d6000803e3d6000fd5b6040525050505050565b6000806014860288016002013560601c868203611f8757600189013560f81c1515600081611f25576000611f2f565b611f2f8388612302565b9050611f5e7f0000000000000000000000000000000000000000000000000000000000000000848a8a8a611eb4565b81611f695785611f7e565b80611f748489612302565b611f7e91906131d2565b95505050611faf565b73ffffffffffffffffffffffffffffffffffffffff85163014611faf57611faf8186866109e6565b989297509195505050505050565b600080611fca8760601c90565b905061ff0083167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00810161200b576120048285888861237b565b925061213c565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00810161203d57612004828587612403565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00810161206f57612004828587612415565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0081016120a357612004828587898b612427565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0081016120d657612004828587896124ff565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101612109576120048285878961255b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8008101611cd5576120048286888a6125ad565b5050979650505050505050565b6000808061215c866001808816146125cf565b909250905061216b84826131d2565b612177906103e56131e5565b600161218386856131e5565b61218f906103e86131e5565b61219991906131d2565b6121a391906131fc565b6121ae906001613266565b9695505050505050565b60006121eb7f00000000000000000000000000000000000000000000000000000000000000008584600180881614612694565b949350505050565b600080806122078685600180891614612722565b9150915080600014612245576040517f48614bfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60006121eb84600180861614846127a8565b600080806122748685600180891614612814565b91509150838114612245576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080806122c5868560018089161461287c565b91509150838114612245576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006370a0823160005283602052602060006024601c885afa91503d905060005192508160000361233857612338610982565b6020811015612373576040517f07e05a0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b6000600184811614818061238f88846125cf565b909250905060006123a2876103e56131e5565b9050806123b1846103e86131e5565b6123bb9190613266565b6123c583836131e5565b6123cf91906131fc565b9450600080856123e1578660006123e5565b6000875b915091506123f58b83838b612942565b505050505050949350505050565b60006121eb8460018086161484612995565b60006121eb8460018086161484612a27565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16610400176001556000808080612473898861246c8b60019081161490565b8989612ab6565b92509250925080600154146124b4576040517f8430856b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8582146124ed576040517f594caabc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061dead6001559695505050505050565b600080806125148786866001808b1614612b61565b91509150838114612551576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b600080806125708786866001808b1614612c2d565b91509150838114612551576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006125ba828685612cf9565b6125c685858585612e18565b95945050505050565b6000806000630902f1ac600052604060006004601c885afa156125ef57503d5b838015612625576dffffffffffffffffffffffffffff6000511693506dffffffffffffffffffffffffffff602051169250612650565b6dffffffffffffffffffffffffffff6020511693506dffffffffffffffffffffffffffff6000511692505b50604081101561268c576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509250929050565b6000806000604051635bdd4b7c815286602082015285604082015284606082015260808101604052602060006064601c84018b5afa9150503d915060005192506126dd816116f7565b6020821015612718576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b60008060008060405163abcd78306000528660205285604052604060006044601c8b5afa91503d9250600051945060205193508060405250612763816116f7565b604082101561279e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050935093915050565b60008060006127cc8630876127bc88611dac565b6127c5906132ae565b6000612ea4565b9150915060a43d1482151660008114612800578580156127f257600483015194506127fa565b602483015194505b5061280a565b3d6000803e3d6000fd5b5050509392505050565b60008060008060405163cd56aadc6000528660000360205285604052604060006044601c8b5afa3d935091508580156128565760005195506020519450612861565b602051955060005194505b506001850194508360000393508060405250612763816116f7565b600080600061288b8685612f2b565b9050600080604051633419341c815286602082015287600003604082015283606082015260808101604052604060006064601c84018c5afa9150503d915085600081146128e157600051955060205194506128ec565b602051955060005194505b508360000393506128fc816116f7565b6040821015612937576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050935093915050565b600060405163022c0d9f8152846020820152836040820152826060820152608080820152600060a082015260a0810160405260008060a4601c840160008a5af191505061298e816116f7565b5050505050565b60008060006040516353c059a06000528560205284604052604060006044601c60008b5af13d935091508580156129d05760205194506129d6565b60005194505b506040526129e3816116f7565b6040821015612a1e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509392505050565b60008060006040516353c059a06000528560205284604052602060006044601c60008b5af13d93509150858015612a655760005160801c9450612a6e565b60105160801c94505b50604052612a7b816116f7565b6020821015612a1e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806000612ad38a8a8a612acd8b611dac565b8a612ea4565b91509150612ae0826116f7565b604081018690526060812092503d888015612b05578251955060208301519650612b11565b82519650602083015195505b508560000395506040811015612b53576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050955095509592505050565b60008060008060405163dc35ff77815287602082015286604082015285606082015260a06080820152600060a0820152600060c082015260a081016040526040600060c4601c840160008d5af19150503d91508460008114612bcc5760005193506020519450612bd7565b602051935060005194505b50836000039350612be7816116f7565b6040821015612c22576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505094509492505050565b6000806000612c3c8785612f2b565b905060008060405163abb1db2a815288602082015286604082015287606082015283608082015260a08101604052604060006084601c840160008e5af19150503d91508560008114612c975760005194506020519550612ca2565b602051945060005195505b50846000039450612cb2816116f7565b6040821015612ced576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505094509492505050565b6000806000612d1d565b816040526000602060406044601c6000865af19392505050565b60405163095ea7b360005285602052612d368588612d03565b93508360016040511416612d6857833d883b151710612d6857612d5a600088612d03565b50612d658588612d03565b93505b3d92506040519150806040525082600003612db757612d85610982565b6040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612dc7578060011415612de1565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610abd576040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600060405163f946c2a2815284602082015260016040820152856060820152866080820152602060006084601c840160008c5af191503d9250600051935060a0810160405250612e6a816116f7565b81602014612718576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600085612ec85773fffd8963efd1fc6a506488495d951d5263988d25612ecf565b6401000276a45b9050604051915063128acb08825286602083015285604083015284606083015280608083015260a080830152602060c08301528360e0830152610100820160405260448260e4601c850160008c5af19250509550959350505050565b60008060405163a0b6ea01600052606060006004601c885afa15612f4d573d91505b838015612f5e576000519350612f64565b60405193505b506040526060811015612fa3576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461171a57600080fd5b600080600060608486031215612fe157600080fd5b8335612fec81612faa565b92506020840135612ffc81612faa565b929592945050506040919091013590565b60008060008060008060008060e0898b03121561302957600080fd5b883561303481612faa565b9750602089013561304481612faa565b96506040890135955060608901359450608089013561306281612faa565b935060a089013561307281612faa565b925060c089013567ffffffffffffffff81111561308e57600080fd5b8901601f81018b1361309f57600080fd5b803567ffffffffffffffff8111156130b657600080fd5b8b60208284010111156130c857600080fd5b989b979a50959850939692959194602001935050565b600080604083850312156130f157600080fd5b82356130fc81612faa565b915060208301356bffffffffffffffffffffffff8116811461311d57600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561319857600080fd5b81516103e681612faa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610374576103746131a3565b8082028115828204841417610374576103746131a3565b600082613232577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115610374576103746131a3565b600081613288576132886131a3565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007f800000000000000000000000000000000000000000000000000000000000000082036132df576132df6131a3565b506000039056fea26469706673582212201939e01bc92d13c06a5481d5a5e7197dc2bc270b329a17f3af9f723654d3b99664736f6c634300081a003300000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078c1b0c915c4faa5fffa6cabf0219da63d7f4cb8000000000000000000000000244305969310527b29d8ff3aa263f686db61df6f00000000000000000000000000000000000000000000000000000000000005dc
Deployed Bytecode
0x6080604052600436106100695760003560e01c8063bd08443511610043578063bd0844351461023f578063c60bc20314610274578063cb7e000714610294576100d7565b8063192bcad3146101a157806362c06767146101ea57806372c8fc0e1461020a576100d7565b366100d7573373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000078c1b0c915c4faa5fffa6cabf0219da63d7f4cb816146100d5576100d57f00000000000000000000000078c1b0c915c4faa5fffa6cabf0219da63d7f4cb8346102b4565b005b3480156100e357600080fd5b50600154600090369060609061ff00811681831c6104008214801561011d57503373ffffffffffffffffffffffffffffffffffffffff8216145b156101365761012c8686610312565b9350505050610196565b6107008214801561017c57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b1561018c5761012c86868361037a565b3660008037366000fd5b915050805190602001f35b3480156101ad57600080fd5b506000547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166040519081526020015b60405180910390f35b3480156101f657600080fd5b506100d5610205366004612fcc565b6103ed565b34801561021657600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e1565b34801561024b57600080fd5b5061025f61025a36600461300d565b610426565b604080519283526020830191909152016101e1565b34801561028057600080fd5b506100d561028f3660046130de565b61067c565b3480156102a057600080fd5b5061025f6102af36600461300d565b610692565b600063d0e30db06000526000806004601c85875af190508060000361030d576102db610982565b6040517fdc0c8cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b606060008060006103238686610994565b6040805160008581526020859052838352606090209152929550909350915060015561035f81336000861361035857846109e6565b855b6109e6565b50506040805160008152602081019091529150505b92915050565b60606000806103ab8686867f00000000000000000000000078c1b0c915c4faa5fffa6cabf0219da63d7f4cb8610ac5565b60408051602081810152808201919091526060810183905260808101829052919350915060a001604051602081830303815290604052925050505b9392505050565b6103f5610c06565b73ffffffffffffffffffffffffffffffffffffffff83161561041c5761030d8383836109e6565b61030d8282610cf9565b6000806104b360405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6104bf85858d8d610d49565b60608501526040840181905260208401919091528183526104e4918791879190610fad565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152908116608083018190528a918d16900361054d5760006127108360c0015183028161053357610533613157565b049050808203915061054b8d8a8560a00151846110bb565b505b60006105a98284604001518b8f73ffffffffffffffffffffffffffffffffffffffff16876080015173ffffffffffffffffffffffffffffffffffffffff1614610596578b610598565b305b876020015188606001518d8d611218565b90508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036106205760006127108460c001518302816105fb576105fb613157565b04905080820391506106138d308660a00151846110bb565b61061e8d8a846109e6565b505b89811015610669576040517fc18a3e5800000000000000000000000000000000000000000000000000000000815260048101829052602481018b90526044015b60405180910390fd5b999c999b50989950505050505050505050565b610684610c06565b61068e828261131e565b5050565b60008061071f60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b61072b85858d8d610d49565b6060850152604084018190526020840191909152818352610750918791879190610fad565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152166080820152600185013560f81c156107b7576040517fe4c5b0d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808990508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff160361081a573091508260c001516127100361271082028161081257610812613157565b04905061081e565b8791505b60008061083b838660400151876060015188602001518d8d611432565b915091508e73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16036108b35760008560c00151612710038660c0015184028161089557610895613157565b04905080830192506108b186608001518d8860a00151846110bb565b505b8c8211156108f7576040517f20cd355100000000000000000000000000000000000000000000000000000000815260048101839052602481018e9052604401610660565b6109138b8583886020015189604001518a606001518f8f61169f565b8d73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff160361096e5760008c840390506109618f308860a00151846110bb565b61096c8f8c8f6109e6565b505b509d999c50989a5050505050505050505050565b3d15610992573d6000803e3d6000fd5b565b6000808060a484146109d2576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505060043593602435935060843592509050565b600080600060405163a9059cbb6000528560205284604052602060006044601c60008b5af193503d92506000519150806040525082600003610a5c57610a2a610982565b6040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115610a6c578060011415610a86565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610abd576040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b60008060008060008060405163f3cd914c815260448a0360448c0160208301377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb828c010116604052602060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08c01601c840182335af1600051608081901d9850600f0b965092503d915088610b8f5760206000526040602052866040528560605260806000fd5b602081015194506040810151935050610ba7826116f7565b80602014610be1576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bed8489888a61171d565b610bf98389878a61171d565b5050505094509492505050565b7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c959190613186565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610992576040517f303693ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600085875af190508060000361030d57610d17610982565b6040517fa01b460600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080803373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b1614610dbd576040517f4b2fd34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dc788886118b4565b919450925090506002821015610e09576040517f7c741b7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a830188840135604081901c61ff0016610e89576002831015610e59576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b939450927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101908390610ec3565b82600003610ec3576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028a013560601c73ffffffffffffffffffffffffffffffffffffffff891614610f19576040517f80e4580000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8716610f538b8b610f406001896131d2565b601402919091016002013560601c919050565b73ffffffffffffffffffffffffffffffffffffffff1614610fa0576040517ffc37688f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050945094509450949050565b6000808084156110b15786850135606081901c9250610fd08160501c61ffff1690565b91506000610fe18260381c60ff1690565b90506002601482028a01013560601c9450610fff8260301c60ff1690565b811861100f8360401c61ffff1690565b1715158061103157508015801590611031575061102d6001876131d2565b8114155b15611068576040517fee393ce900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158061107757506127108310155b156110ae576040517fa4c2399e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b9450945094915050565b80156112125773ffffffffffffffffffffffffffffffffffffffff821661110e576040517fae0edfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805461271090611146907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16846131e5565b61115091906131fc565b9050600061115e82846131d2565b905080156111725761117286868684611986565b811561119f5760005461119f908790879073ffffffffffffffffffffffffffffffffffffffff1685611986565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f77c197a1ae17318af19ae49d654709f9fe13e90a7fb70e1bdd5f665f6b9bce828585604051611207929190918252602082015260400190565b60405180910390a350505b50505050565b6000808867ffffffffffffffff81111561123457611234613128565b60405190808252806020026020018201604052801561125d578160200160208202803683370190505b509050600060018a0390508a8260008151811061127c5761127c613237565b6020026020010181815250506000805b878110156112b957601a89019887013591506112ad838888878f8f886119e0565b909c019b60010161128c565b508282815181106112cc576112cc613237565b60200260200101519350838c1461130f576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661136b576040517fec049e3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710816bffffffffffffffffffffffff1611156113b5576040517f31fb2ba200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216740100000000000000000000000000000000000000006bffffffffffffffffffffffff8316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b60006060601a86028501945060008667ffffffffffffffff81111561145957611459613128565b604051908082528060200260200182016040528015611482578160200160208202803683370190505b50905060008867ffffffffffffffff8111156114a0576114a0613128565b6040519080825280602002602001820160405280156114c9578160200160208202803683370190505b50905089816114d960018c6131d2565b815181106114e9576114e9613237565b6020908102919091010152896000895b8015611637577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe68a81019a8a0101359150600060ff603084901c16905060006115458460381c60ff1690565b9050600061271061155a8660501c61ffff1690565b88858151811061156c5761156c613237565b602002602001015161157e91906131e5565b61158891906131fc565b90508087848151811061159d5761159d613237565b602002602001018181516115b191906131d2565b9052506115bd81611b29565b6115c98c8c8784611b7c565b9950898783815181106115de576115de613237565b602002602001018181516115f29190613266565b9052506115fe8a611b29565b898861160986613279565b9550858151811061161c5761161c613237565b602002602001018181525050808a03860195505050506114f9565b508260008151811061164b5761164b613237565b6020026020010151955085821461168e576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509192505050965096945050505050565b6000805b848110156116eb57601a87018785013580935081985050506116e38484888d8d878e88815181106116d6576116d6613237565b6020026020010151611d12565b6001016116a3565b50505050505050505050565b8061171a573d1561170c573d6000803e3d6000fd5b63824d22356000526004601cfd5b50565b600082121561180357600082600003925063a5841194600052846020526000806040601c6000335af19050611751816116f7565b600073ffffffffffffffffffffffffffffffffffffffff8616611788576117838361177b86611dac565b925082611deb565b611796565b611796863361035a87611dac565b6000806311da60b4600052602060006004601c86335af193503d905060005191506117c0846116f7565b806020146117fa576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050611212565b600082131561121257600073ffffffffffffffffffffffffffffffffffffffff8516156118305783611832565b305b90506000604051630b0d9c0981528660208201528260408201528460608201526000806064601c84016000335af191506080810160405250611873816116f7565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610abd57610abd838661035a87611dac565b813560f81c6014810260028101919060009084037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0183851015611924576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81061561197857600361193a878784611e49565b019003601a810615611978576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81049150509250925092565b3073ffffffffffffffffffffffffffffffffffffffff8416036119b3576119ae8483836109e6565b611212565b6112127f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b85858585611eb4565b6000806119f18360401c61ffff1690565b90506000611a028460381c60ff1690565b90506000611a138560301c60ff1690565b905060008b8214611a245730611a26565b865b90506000612710611a3b8860501c61ffff1690565b8b8681518110611a4d57611a4d613237565b6020026020010151611a5f91906131e5565b611a6991906131fc565b9050808a8581518110611a7e57611a7e613237565b60200260200101818151611a9291906131d2565b905250600080611ac08e8e888e6002808d1614611ab857611ab38e60601c90565b611aba565b305b88611ef6565b91509150611acd81611b29565b6000611ade8f8f8c86868a8e611fbd565b9050611ae981611b29565b808d8781518110611afc57611afc613237565b60200260200101818151611b109190613266565b905250929092039e9d5050505050505050505050505050565b801580611b4557506fffffffffffffffffffffffffffffffff81115b1561171a576040517f5e2ab1b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611b898460601c90565b90506000611b9b8560401c61ffff1690565b905061ff0081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008101611bdb57611bd4838387612149565b9350611d07565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe008101611c0d57611bd48383876121b8565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd008101611c3f57611bd48383876121f3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101611c7157611bd483838761224e565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101611ca357611bd4838387612260565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101611cd557611bd48383876122b1565b6040517f0886b18e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b6000611d1e8360601c90565b90506000611d308460401c61ffff1690565b9050600080611d608b8b611d478960381c60ff1690565b8b600280891614611d585788611d5a565b305b8a611ef6565b90925090506000611d7260018b6131d2565b611d7f8860301c60ff1690565b14611d8a5730611d8c565b875b9050611d9d8c8c898686868a611fbd565b50505050505050505050505050565b6000808212611db9575090565b6040517f20e4c52f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000632e1a7d4d600052816020526000806024601c6000875af190508060000361030d57611e17610982565b6040517fa39163c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd848401013560e81c02600180821690036103e6576040517faecb172600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60201c6000528360601b6018528260601b602c528160405260008060606000808a5af1611eec573d6000803e3d6000fd5b6040525050505050565b6000806014860288016002013560601c868203611f8757600189013560f81c1515600081611f25576000611f2f565b611f2f8388612302565b9050611f5e7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b848a8a8a611eb4565b81611f695785611f7e565b80611f748489612302565b611f7e91906131d2565b95505050611faf565b73ffffffffffffffffffffffffffffffffffffffff85163014611faf57611faf8186866109e6565b989297509195505050505050565b600080611fca8760601c90565b905061ff0083167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00810161200b576120048285888861237b565b925061213c565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00810161203d57612004828587612403565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00810161206f57612004828587612415565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0081016120a357612004828587898b612427565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0081016120d657612004828587896124ff565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101612109576120048285878961255b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8008101611cd5576120048286888a6125ad565b5050979650505050505050565b6000808061215c866001808816146125cf565b909250905061216b84826131d2565b612177906103e56131e5565b600161218386856131e5565b61218f906103e86131e5565b61219991906131d2565b6121a391906131fc565b6121ae906001613266565b9695505050505050565b60006121eb7f00000000000000000000000000000000000000000000000000000000000000008584600180881614612694565b949350505050565b600080806122078685600180891614612722565b9150915080600014612245576040517f48614bfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60006121eb84600180861614846127a8565b600080806122748685600180891614612814565b91509150838114612245576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080806122c5868560018089161461287c565b91509150838114612245576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006370a0823160005283602052602060006024601c885afa91503d905060005192508160000361233857612338610982565b6020811015612373576040517f07e05a0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b6000600184811614818061238f88846125cf565b909250905060006123a2876103e56131e5565b9050806123b1846103e86131e5565b6123bb9190613266565b6123c583836131e5565b6123cf91906131fc565b9450600080856123e1578660006123e5565b6000875b915091506123f58b83838b612942565b505050505050949350505050565b60006121eb8460018086161484612995565b60006121eb8460018086161484612a27565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16610400176001556000808080612473898861246c8b60019081161490565b8989612ab6565b92509250925080600154146124b4576040517f8430856b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8582146124ed576040517f594caabc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061dead6001559695505050505050565b600080806125148786866001808b1614612b61565b91509150838114612551576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b600080806125708786866001808b1614612c2d565b91509150838114612551576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006125ba828685612cf9565b6125c685858585612e18565b95945050505050565b6000806000630902f1ac600052604060006004601c885afa156125ef57503d5b838015612625576dffffffffffffffffffffffffffff6000511693506dffffffffffffffffffffffffffff602051169250612650565b6dffffffffffffffffffffffffffff6020511693506dffffffffffffffffffffffffffff6000511692505b50604081101561268c576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509250929050565b6000806000604051635bdd4b7c815286602082015285604082015284606082015260808101604052602060006064601c84018b5afa9150503d915060005192506126dd816116f7565b6020821015612718576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b60008060008060405163abcd78306000528660205285604052604060006044601c8b5afa91503d9250600051945060205193508060405250612763816116f7565b604082101561279e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050935093915050565b60008060006127cc8630876127bc88611dac565b6127c5906132ae565b6000612ea4565b9150915060a43d1482151660008114612800578580156127f257600483015194506127fa565b602483015194505b5061280a565b3d6000803e3d6000fd5b5050509392505050565b60008060008060405163cd56aadc6000528660000360205285604052604060006044601c8b5afa3d935091508580156128565760005195506020519450612861565b602051955060005194505b506001850194508360000393508060405250612763816116f7565b600080600061288b8685612f2b565b9050600080604051633419341c815286602082015287600003604082015283606082015260808101604052604060006064601c84018c5afa9150503d915085600081146128e157600051955060205194506128ec565b602051955060005194505b508360000393506128fc816116f7565b6040821015612937576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050935093915050565b600060405163022c0d9f8152846020820152836040820152826060820152608080820152600060a082015260a0810160405260008060a4601c840160008a5af191505061298e816116f7565b5050505050565b60008060006040516353c059a06000528560205284604052604060006044601c60008b5af13d935091508580156129d05760205194506129d6565b60005194505b506040526129e3816116f7565b6040821015612a1e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509392505050565b60008060006040516353c059a06000528560205284604052602060006044601c60008b5af13d93509150858015612a655760005160801c9450612a6e565b60105160801c94505b50604052612a7b816116f7565b6020821015612a1e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806000612ad38a8a8a612acd8b611dac565b8a612ea4565b91509150612ae0826116f7565b604081018690526060812092503d888015612b05578251955060208301519650612b11565b82519650602083015195505b508560000395506040811015612b53576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050955095509592505050565b60008060008060405163dc35ff77815287602082015286604082015285606082015260a06080820152600060a0820152600060c082015260a081016040526040600060c4601c840160008d5af19150503d91508460008114612bcc5760005193506020519450612bd7565b602051935060005194505b50836000039350612be7816116f7565b6040821015612c22576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505094509492505050565b6000806000612c3c8785612f2b565b905060008060405163abb1db2a815288602082015286604082015287606082015283608082015260a08101604052604060006084601c840160008e5af19150503d91508560008114612c975760005194506020519550612ca2565b602051945060005195505b50846000039450612cb2816116f7565b6040821015612ced576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505094509492505050565b6000806000612d1d565b816040526000602060406044601c6000865af19392505050565b60405163095ea7b360005285602052612d368588612d03565b93508360016040511416612d6857833d883b151710612d6857612d5a600088612d03565b50612d658588612d03565b93505b3d92506040519150806040525082600003612db757612d85610982565b6040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612dc7578060011415612de1565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610abd576040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600060405163f946c2a2815284602082015260016040820152856060820152866080820152602060006084601c840160008c5af191503d9250600051935060a0810160405250612e6a816116f7565b81602014612718576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600085612ec85773fffd8963efd1fc6a506488495d951d5263988d25612ecf565b6401000276a45b9050604051915063128acb08825286602083015285604083015284606083015280608083015260a080830152602060c08301528360e0830152610100820160405260448260e4601c850160008c5af19250509550959350505050565b60008060405163a0b6ea01600052606060006004601c885afa15612f4d573d91505b838015612f5e576000519350612f64565b60405193505b506040526060811015612fa3576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461171a57600080fd5b600080600060608486031215612fe157600080fd5b8335612fec81612faa565b92506020840135612ffc81612faa565b929592945050506040919091013590565b60008060008060008060008060e0898b03121561302957600080fd5b883561303481612faa565b9750602089013561304481612faa565b96506040890135955060608901359450608089013561306281612faa565b935060a089013561307281612faa565b925060c089013567ffffffffffffffff81111561308e57600080fd5b8901601f81018b1361309f57600080fd5b803567ffffffffffffffff8111156130b657600080fd5b8b60208284010111156130c857600080fd5b989b979a50959850939692959194602001935050565b600080604083850312156130f157600080fd5b82356130fc81612faa565b915060208301356bffffffffffffffffffffffff8116811461311d57600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561319857600080fd5b81516103e681612faa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610374576103746131a3565b8082028115828204841417610374576103746131a3565b600082613232577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80820180821115610374576103746131a3565b600081613288576132886131a3565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007f800000000000000000000000000000000000000000000000000000000000000082036132df576132df6131a3565b506000039056fea26469706673582212201939e01bc92d13c06a5481d5a5e7197dc2bc270b329a17f3af9f723654d3b99664736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078c1b0c915c4faa5fffa6cabf0219da63d7f4cb8000000000000000000000000244305969310527b29d8ff3aa263f686db61df6f00000000000000000000000000000000000000000000000000000000000005dc
-----Decoded View---------------
Arg [0] : router (address): 0x45A62B090DF48243F12A21897e7ed91863E2c86b
Arg [1] : routerV2_0 (address): 0x0000000000000000000000000000000000000000
Arg [2] : uniswapV4Manager (address): 0x0000000000000000000000000000000000000000
Arg [3] : wnative (address): 0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8
Arg [4] : protocolFeeReceiver (address): 0x244305969310527b29d8Ff3Aa263f686dB61Df6f
Arg [5] : protocolFeeShare (uint96): 1500
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 00000000000000000000000078c1b0c915c4faa5fffa6cabf0219da63d7f4cb8
Arg [4] : 000000000000000000000000244305969310527b29d8ff3aa263f686db61df6f
Arg [5] : 00000000000000000000000000000000000000000000000000000000000005dc
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MNT
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.