Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- PancakeInfinityAdapter
- Optimization enabled
- false
- Compiler version
- v0.8.19+commit.7dd6d404
- EVM Version
- Verified at
- 2026-08-21T13:29:18.586858Z
Constructor Arguments
0000000000000000000000005ce9c2e3e803712e6fec5368968b61a55d851cdf0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd000000000000000000000000768caf810ea58f32054da66ba951c14ca998d19f
Arg [0] (address) : 0x5ce9c2e3e803712e6fec5368968b61a55d851cdf
Arg [1] (address) : 0x7941808b1d3f76786aa66b72d74989310995afbd
Arg [2] (address) : 0x768caf810ea58f32054da66ba951c14ca998d19f
contracts/dex/adapters/PancakeInfinityAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { SwapStep } from "contracts/dex/SwapTypes.sol";
import { DexAdapterBase } from "contracts/dex/DexAdapterBase.sol";
import {
IAllowanceTransfer
} from "contracts/interfaces/external/IAllowanceTransfer.sol";
import {
IUniversalRouter
} from "contracts/interfaces/external/uniswap/v4/IUniversalRouter.sol";
import {
Actions
} from "contracts/interfaces/external/uniswap/v4/libraries/Actions.sol";
import { IHooks } from "contracts/interfaces/external/uniswap/v4/IHooks.sol";
import {
Currency
} from "contracts/interfaces/external/uniswap/v4/types/Currency.sol";
import {
PancakePoolKey
} from "contracts/interfaces/external/pancake/infinity/PancakePoolKey.sol";
import {
Permit2AllowanceLib
} from "contracts/libraries/Permit2AllowanceLib.sol";
/// @title PancakeInfinityAdapter
/// @notice Per-DEX adapter for `DexType.PancakeInfinity`. Mirrors the
/// V4 adapter but targets the Pancake Infinity Permit2 deployment and
/// the Pancake-flavoured `PoolKey`.
contract PancakeInfinityAdapter is DexAdapterBase {
// Pancake's UniversalRouter command shares the byte value of
// Uniswap V4's command (0x10).
uint8 internal constant INFI_SWAP = 0x10;
address public constant PANCAKE_INFINITY_PERMIT2 =
0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768;
struct ExactInputSingleParams {
PancakePoolKey poolKey;
bool zeroForOne;
uint128 amountIn;
uint128 amountOutMinimum;
bytes hookData;
}
constructor(
address admin_,
address swapRouter_,
address allowlist_
) DexAdapterBase(admin_, swapRouter_, allowlist_) { }
function version() external pure override returns (uint16) {
return 1;
}
function _swap(
SwapStep calldata step,
uint256 amountIn
) internal override returns (uint256) {
PancakePoolKey memory poolKey =
abi.decode(step.dexData, (PancakePoolKey));
if (poolKey.hooks != IHooks(address(0))) {
allowlist.requireAllowedHook(address(poolKey.hooks));
}
bool hasFixedPermit2Allowance;
if (step.tokenIn != address(0)) {
hasFixedPermit2Allowance = Permit2AllowanceLib.hasFixedAllowance(
step.tokenIn, address(swapRouter), PANCAKE_INFINITY_PERMIT2
);
if (!hasFixedPermit2Allowance) {
_adapterApprove(
step.tokenIn, PANCAKE_INFINITY_PERMIT2, amountIn
);
}
_adapterCall(
PANCAKE_INFINITY_PERMIT2,
0,
abi.encodeCall(
IAllowanceTransfer.approve,
(
step.tokenIn,
step.router,
// forge-lint: disable-next-line(unsafe-typecast)
uint160(amountIn),
uint48(block.timestamp)
)
)
);
}
{
bytes memory input = _buildInput(step, poolKey, amountIn);
bytes[] memory inputs = new bytes[](1);
inputs[0] = input;
uint256 value = step.tokenIn == address(0) ? amountIn : 0;
_adapterCall(
step.router,
value,
abi.encodeCall(
IUniversalRouter.execute,
(abi.encodePacked(INFI_SWAP), inputs, block.timestamp)
)
);
}
if (step.tokenIn != address(0)) {
if (!hasFixedPermit2Allowance) {
_adapterApprove(step.tokenIn, PANCAKE_INFINITY_PERMIT2, 0);
}
_adapterCall(
PANCAKE_INFINITY_PERMIT2,
0,
abi.encodeCall(
IAllowanceTransfer.approve,
(step.tokenIn, step.router, 0, 0)
)
);
}
return 0;
}
function _buildInput(
SwapStep calldata step,
PancakePoolKey memory poolKey,
uint256 amountIn
) internal pure returns (bytes memory) {
(address currency0, address currency1) = step.tokenIn < step.tokenOut
? (step.tokenIn, step.tokenOut)
: (step.tokenOut, step.tokenIn);
bool zeroForOne = step.tokenIn == currency0;
bytes memory actions = abi.encodePacked(
uint8(Actions.SWAP_EXACT_IN_SINGLE),
uint8(Actions.SETTLE_ALL),
uint8(Actions.TAKE_ALL)
);
bytes[] memory params = new bytes[](3);
ExactInputSingleParams memory swapParams = ExactInputSingleParams({
poolKey: PancakePoolKey({
currency0: Currency.wrap(currency0),
currency1: Currency.wrap(currency1),
hooks: poolKey.hooks,
poolManager: poolKey.poolManager,
fee: poolKey.fee,
parameters: poolKey.parameters
}),
zeroForOne: zeroForOne,
// forge-lint: disable-next-line(unsafe-typecast)
amountIn: uint128(amountIn),
amountOutMinimum: 0,
hookData: new bytes(0)
});
params[0] = abi.encode(swapParams);
params[1] = abi.encode(step.tokenIn, amountIn);
params[2] = abi.encode(step.tokenOut, 0);
return abi.encode(actions, params);
}
}
contracts/interfaces/external/uniswap/v4/types/BalanceDelta.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { SafeCast } from "../libraries/SafeCast.sol";
/// @dev Two `int128` values packed into a single `int256` where the upper 128
/// bits represent the amount0
/// and the lower 128 bits represent the amount1.
type BalanceDelta is int256;
using { add as +, sub as -, eq as ==, neq as != } for BalanceDelta global;
using BalanceDeltaLibrary for BalanceDelta global;
using SafeCast for int256;
function toBalanceDelta(
int128 _amount0,
int128 _amount1
) pure returns (BalanceDelta balanceDelta) {
assembly ("memory-safe") {
balanceDelta :=
or(shl(128, _amount0), and(sub(shl(128, 1), 1), _amount1))
}
}
function add(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta) {
int256 res0;
int256 res1;
assembly ("memory-safe") {
let a0 := sar(128, a)
let a1 := signextend(15, a)
let b0 := sar(128, b)
let b1 := signextend(15, b)
res0 := add(a0, b0)
res1 := add(a1, b1)
}
return toBalanceDelta(res0.toInt128(), res1.toInt128());
}
function sub(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta) {
int256 res0;
int256 res1;
assembly ("memory-safe") {
let a0 := sar(128, a)
let a1 := signextend(15, a)
let b0 := sar(128, b)
let b1 := signextend(15, b)
res0 := sub(a0, b0)
res1 := sub(a1, b1)
}
return toBalanceDelta(res0.toInt128(), res1.toInt128());
}
function eq(BalanceDelta a, BalanceDelta b) pure returns (bool) {
return BalanceDelta.unwrap(a) == BalanceDelta.unwrap(b);
}
function neq(BalanceDelta a, BalanceDelta b) pure returns (bool) {
return BalanceDelta.unwrap(a) != BalanceDelta.unwrap(b);
}
/// @notice Library for getting the amount0 and amount1 deltas from the
/// BalanceDelta type
library BalanceDeltaLibrary {
/// @notice A BalanceDelta of 0
BalanceDelta public constant ZERO_DELTA = BalanceDelta.wrap(0);
function amount0(
BalanceDelta balanceDelta
) internal pure returns (int128 _amount0) {
assembly ("memory-safe") {
_amount0 := sar(128, balanceDelta)
}
}
function amount1(
BalanceDelta balanceDelta
) internal pure returns (int128 _amount1) {
assembly ("memory-safe") {
_amount1 := signextend(15, balanceDelta)
}
}
}
contracts/dex/SwapTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/// @notice DEX types `SwapRouter` knows how to dispatch. This enum is a
/// set of named constants only — `SwapStep.dexType` is a raw `uint8` (not
/// this enum), so the dispatch table `dexAdapter[uint8]` carries the byte
/// directly with no enum range-check at the ABI boundary. A new *standard*
/// type (allow-listed router + ERC20 `tokenIn`) can therefore be appended
/// and dispatched on an already-deployed `SwapRouter` via `setDexAdapter`
/// alone — no redeploy. NOTE this does NOT cover a type that needs a new
/// `SwapRouter._executeStep` exemption (router-allowlist bypass or
/// native-ETH `tokenIn`); those exemption lists are hardcoded and still
/// require a `SwapRouter` redeploy. Append new entries rather than
/// reordering existing ones.
enum DexType {
UniswapV2,
UniswapV3Router02,
Solidly,
Algebra,
UniswapV4,
VelodromeUniversalRouter,
UniswapV3Router,
AerodromeRouter,
AlgebraPool,
SlipstreamRouter,
BlackholeV2Router,
UniswapV3Pool,
WrapWETH,
UnwrapWETH,
CamelotV2Router,
PancakeInfinity,
AaveSupply,
AaveWithdraw
}
/// @notice One hop of a multi-step swap. Stable, ABI-stable struct used
/// by `SwapRouter`, every `IDexAdapter`, every bridge swap receiver, and
/// every off-chain encoder (vfat-api, deploy scripts).
struct SwapStep {
/// @dev Raw `uint8` (not the `DexType` enum) so dispatch is purely
/// `dexAdapter[dexType]` — adding a new DEX type never requires
/// redeploying `SwapRouter`. Compare against `uint8(DexType.X)`.
uint8 dexType;
address router;
address tokenIn;
address tokenOut;
bytes dexData;
}
contracts/dex/ISwapRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IRouterAllowlist } from "contracts/interfaces/IRouterAllowlist.sol";
/// @title ISwapRouter
/// @notice Minimal subset of `SwapRouter` consumed by per-DEX adapters.
/// @dev Lives in its own file (rather than importing `SwapRouter` directly)
/// so the dependency between `DexAdapterBase` and the router stays a thin
/// interface and Solidity doesn't have to resolve the import cycle that
/// `IDexAdapter` already establishes.
interface ISwapRouter {
/// @notice Move ERC20 custody from the router to `to`. Only callable
/// by the in-flight active adapter; reverts otherwise.
function adapterPull(address token, uint256 amount, address to) external;
/// @notice Set an ERC20 approval from the router itself to
/// `spender`. Used by router-style adapters so the eventual
/// `transferFrom` lifts tokens directly from `SwapRouter` rather
/// than via the adapter (single-FOT-hit accounting).
function adapterApprove(
address token,
address spender,
uint256 amount
) external;
/// @notice Execute an external call from `SwapRouter` on behalf of
/// the active adapter. Used so `msg.sender` seen by the target
/// equals `SwapRouter`.
function adapterCall(
address target,
uint256 value,
bytes calldata data
) external returns (bytes memory);
/// @notice Arm the V3 / Algebra pool callback for the next direct
/// pool call in the current adapter dispatch. The callback rejects
/// any payment whose pool, token, or amount does not match the
/// armed expectations.
function adapterArmPoolCallback(
address pool,
address tokenToPay,
uint256 maxAmountToPay
) external;
function allowlist() external view returns (IRouterAllowlist);
function wrappedNative() external view returns (address);
function PERMIT2() external view returns (address);
function PANCAKE_INFINITY_PERMIT2() external view returns (address);
}
contracts/interfaces/external/uniswap/v4/types/BeforeSwapDelta.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Return type of the beforeSwap hook.
// Upper 128 bits is the delta in specified tokens. Lower 128 bits is delta in
// unspecified tokens (to match the afterSwap hook)
type BeforeSwapDelta is int256;
// Creates a BeforeSwapDelta from specified and unspecified
function toBeforeSwapDelta(
int128 deltaSpecified,
int128 deltaUnspecified
) pure returns (BeforeSwapDelta beforeSwapDelta) {
assembly ("memory-safe") {
beforeSwapDelta :=
or(shl(128, deltaSpecified), and(sub(shl(128, 1), 1), deltaUnspecified))
}
}
/// @notice Library for getting the specified and unspecified deltas from the
/// BeforeSwapDelta type
library BeforeSwapDeltaLibrary {
/// @notice A BeforeSwapDelta of 0
BeforeSwapDelta public constant ZERO_DELTA = BeforeSwapDelta.wrap(0);
/// extracts int128 from the upper 128 bits of the BeforeSwapDelta
/// returned by beforeSwap
function getSpecifiedDelta(
BeforeSwapDelta delta
) internal pure returns (int128 deltaSpecified) {
assembly ("memory-safe") {
deltaSpecified := sar(128, delta)
}
}
/// extracts int128 from the lower 128 bits of the BeforeSwapDelta
/// returned by beforeSwap and afterSwap
function getUnspecifiedDelta(
BeforeSwapDelta delta
) internal pure returns (int128 deltaUnspecified) {
assembly ("memory-safe") {
deltaUnspecified := signextend(15, delta)
}
}
}
contracts/interfaces/external/pancake/infinity/PancakePoolKey.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {
Currency
} from "contracts/interfaces/external/uniswap/v4/types/Currency.sol";
import { IHooks } from "contracts/interfaces/external/uniswap/v4/IHooks.sol";
import { ICLPoolManager } from "./ICLPoolManager.sol";
/// @notice PancakeSwap Infinity PoolKey structure
/// @dev Different from Uniswap V4 PoolKey - has poolManager and parameters fields
struct PancakePoolKey {
/// @notice The lower currency of the pool, sorted numerically
Currency currency0;
/// @notice The higher currency of the pool, sorted numerically
Currency currency1;
/// @notice The hooks of the pool
IHooks hooks;
/// @notice The pool manager (CLPoolManager or BinPoolManager)
ICLPoolManager poolManager;
/// @notice The pool LP fee, capped at 1_000_000
uint24 fee;
/// @notice Parameters encoding hook permissions and tickSpacing
/// Bits [0-16): hook permissions
/// Bits [16-40): tickSpacing (24-bit)
bytes32 parameters;
}
/// @notice Library for PancakePoolKey operations
library PancakePoolKeyLibrary {
uint8 internal constant OFFSET_TICK_SPACING = 16;
/// @notice Extract tickSpacing from parameters
function getTickSpacing(
PancakePoolKey memory key
) internal pure returns (int24) {
return int24(uint24(uint256(key.parameters) >> OFFSET_TICK_SPACING));
}
/// @notice Encode tickSpacing into parameters
function setTickSpacing(
bytes32 parameters,
int24 tickSpacing
) internal pure returns (bytes32) {
return
parameters | bytes32(uint256(uint24(tickSpacing)) << OFFSET_TICK_SPACING);
}
}
contracts/interfaces/external/uniswap/v4/IERC20Minimal.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Minimal ERC20 interface for Uniswap
/// @notice Contains a subset of the full ERC20 interface that is used in
/// Uniswap V3
interface IERC20Minimal {
/// @notice Returns an account's balance in the token
/// @param account The account for which to look up the number of tokens it
/// has, i.e. its balance
/// @return The number of tokens held by the account
function balanceOf(
address account
) external view returns (uint256);
/// @notice Transfers the amount of token from the `msg.sender` to the
/// recipient
/// @param recipient The account that will receive the amount transferred
/// @param amount The number of tokens to send from the sender to the
/// recipient
/// @return Returns true for a successful transfer, false for an
/// unsuccessful transfer
function transfer(
address recipient,
uint256 amount
) external returns (bool);
/// @notice Returns the current allowance given to a spender by an owner
/// @param owner The account of the token owner
/// @param spender The account of the token spender
/// @return The current allowance granted by `owner` to `spender`
function allowance(
address owner,
address spender
) external view returns (uint256);
/// @notice Sets the allowance of a spender from the `msg.sender` to the
/// value `amount`
/// @param spender The account which will be allowed to spend a given amount
/// of the owners tokens
/// @param amount The amount of tokens allowed to be used by `spender`
/// @return Returns true for a successful approval, false for unsuccessful
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Transfers `amount` tokens from `sender` to `recipient` up to the
/// allowance given to the `msg.sender`
/// @param sender The account from which the transfer will be initiated
/// @param recipient The recipient of the transfer
/// @param amount The amount of the transfer
/// @return Returns true for a successful transfer, false for unsuccessful
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/// @notice Event emitted when tokens are transferred from one address to
/// another, either via `#transfer` or `#transferFrom`.
/// @param from The account from which the tokens were sent, i.e. the
/// balance decreased
/// @param to The account to which the tokens were sent, i.e. the balance
/// increased
/// @param value The amount of tokens that were transferred
event Transfer(address indexed from, address indexed to, uint256 value);
/// @notice Event emitted when the approval amount for the spender of a
/// given owner's tokens changes.
/// @param owner The account that approved spending of its tokens
/// @param spender The account for which the spending allowance was modified
/// @param value The new allowance from the owner to the spender
event Approval(
address indexed owner, address indexed spender, uint256 value
);
}
contracts/dex/DexAdapterBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { IRouterAllowlist } from "contracts/interfaces/IRouterAllowlist.sol";
import { IDexAdapter } from "contracts/dex/IDexAdapter.sol";
import { ISwapRouter } from "contracts/dex/ISwapRouter.sol";
import { SwapStep } from "contracts/dex/SwapTypes.sol";
/// @title DexAdapterBase
/// @notice Shared base for DEX adapters. Adapters in this codebase are
/// pure orchestrators — they decode `step.dexData`, build the target
/// router's calldata, and tell `SwapRouter` what to approve, transfer,
/// or call. They never hold tokens or ETH between (or during) swap
/// steps. `SwapRouter` itself remains `msg.sender` for every external
/// call, which means pool callbacks land on the existing callbacks on
/// `SwapRouter`, FOT accounting stays consistent, and there is no
/// per-step custody surface that a buggy adapter could leak.
/// @dev Mirrors `contracts/bridges/adapters/BridgeAdapterBase.sol` in
/// spirit (admin + rescue helpers); guardian role is intentionally
/// omitted to match `SwapRouter`'s admin-only governance model.
abstract contract DexAdapterBase is IDexAdapter {
error NotAdmin();
error NotSwapRouter();
error InvalidAddress();
event AdminUpdated(address oldAdmin, address newAdmin);
/// @notice Router that dispatches swap steps to this adapter and
/// holds all custody for the swap.
ISwapRouter public immutable swapRouter;
/// @notice Router allowlist shared with `SwapRouter`. Pool/factory-
/// style adapters read this directly to authorise their factory or
/// custom deployer checks.
IRouterAllowlist public immutable allowlist;
/// @notice Admin authorised to rotate this adapter's admin and
/// rescue any stuck balances. Adapter swap dispatch itself is
/// gated by `SwapRouter`'s active-adapter slot, not by this admin.
address public admin;
constructor(address admin_, address swapRouter_, address allowlist_) {
if (admin_ == address(0)) revert InvalidAddress();
if (swapRouter_ == address(0)) revert InvalidAddress();
if (allowlist_ == address(0)) revert InvalidAddress();
admin = admin_;
swapRouter = ISwapRouter(swapRouter_);
allowlist = IRouterAllowlist(allowlist_);
}
modifier onlyAdmin() {
if (msg.sender != admin) revert NotAdmin();
_;
}
/// @notice Defensive — adapters are not supposed to hold ETH, but
/// stay payable so an over-refund from a router (e.g. universal
/// router excess ETH on a partial swap) doesn't get rejected and
/// can be swept by admin afterwards.
receive() external payable { }
/// @notice External `swap` entry point implementing `IDexAdapter`.
/// Centralised here so every concrete adapter inherits the
/// `msg.sender == swapRouter` guard structurally — impossible to
/// forget per-impl. Without this guard, a reentrant callee invoked
/// from inside the adapter's own helpers (token transferFrom, V4
/// hook, malicious router) could call `swap` directly with an
/// attacker-chosen `SwapStep`, satisfy `_activeAdapter` since this
/// contract is still the active adapter, and instruct `SwapRouter`
/// to approve / call / pull against attacker-controlled targets.
/// Concrete adapters override `_swap` instead.
function swap(
SwapStep calldata step,
uint256 amountIn
) external payable override returns (uint256) {
if (msg.sender != address(swapRouter)) revert NotSwapRouter();
return _swap(step, amountIn);
}
/// @dev Concrete per-DEX implementation. Always invoked via the
/// guarded `swap` entry point above.
function _swap(
SwapStep calldata step,
uint256 amountIn
) internal virtual returns (uint256);
function setAdmin(
address newAdmin
) external onlyAdmin {
if (newAdmin == address(0)) revert InvalidAddress();
emit AdminUpdated(admin, newAdmin);
admin = newAdmin;
}
function rescueTokens(
address token,
address to,
uint256 amount
) external virtual onlyAdmin {
SafeTransferLib.safeTransfer(token, to, amount);
}
function rescueETH(
address to
) external virtual onlyAdmin {
SafeTransferLib.safeTransferETH(to, address(this).balance);
}
// ── Internal helpers shared by adapter implementations ────────────────
/// @dev Pull `amount` of `token` from `SwapRouter` directly into
/// `recipient` (used for pre-deposit-style routers such as
/// Velodrome's UniversalRouter with `payerIsUser = false`).
function _adapterPullTo(
address token,
uint256 amount,
address recipient
) internal {
swapRouter.adapterPull(token, amount, recipient);
}
/// @dev Set an ERC20 approval from `SwapRouter` to `spender`. Use
/// `amount = 0` to revoke. Wraps `swapRouter.adapterApprove`.
function _adapterApprove(
address token,
address spender,
uint256 amount
) internal {
swapRouter.adapterApprove(token, spender, amount);
}
/// @dev Execute an external call from `SwapRouter`'s address.
function _adapterCall(
address target,
uint256 value,
bytes memory data
) internal returns (bytes memory) {
return swapRouter.adapterCall(target, value, data);
}
/// @dev Arm the V3 / Algebra pool callback before invoking a direct
/// pool swap via `_adapterCall`.
function _armPoolCallback(
address pool,
address tokenToPay,
uint256 maxAmountToPay
) internal {
swapRouter.adapterArmPoolCallback(pool, tokenToPay, maxAmountToPay);
}
/// @dev Resolve the target pool for a direct pool-swap step: the
/// `(address pool)` (optionally followed by a custom deployer) encoded
/// in `step.dexData`, falling back to `step.router` when no pool is
/// encoded. Shared by the V3 and Algebra pool adapters.
function _decodePoolAddress(
SwapStep calldata step
) internal pure returns (address pool) {
if (step.dexData.length >= 32) {
pool = abi.decode(step.dexData, (address));
} else {
pool = step.router;
}
}
/// @dev Common router-style swap: `SwapRouter` approves the router,
/// calls it (with `msg.sender == SwapRouter`), revokes the approval.
/// Output recipient must be baked into `data` as `address(swapRouter)`.
function _swapViaRouter(
address token,
uint256 amount,
address router,
bytes memory data
) internal {
_adapterApprove(token, router, amount);
_adapterCall(router, 0, data);
_adapterApprove(token, router, 0);
}
}
lib/solmate/src/utils/SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
error ETHTransferFailed();
error TransferFromFailed();
error TransferFailed();
error ApproveFailed();
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
if (!success) revert ETHTransferFailed();
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
address token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
if (!success) revert TransferFromFailed();
}
function safeTransfer(
address token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
if (!success) revert TransferFailed();
}
function safeApprove(
address token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
if (!success) revert ApproveFailed();
}
}
contracts/interfaces/external/uniswap/v4/libraries/SafeCast.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { CustomRevert } from "./CustomRevert.sol";
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
using CustomRevert for bytes4;
error SafeCastOverflow();
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param x The uint256 to be downcasted
/// @return y The downcasted integer, now type uint160
function toUint160(
uint256 x
) internal pure returns (uint160 y) {
y = uint160(x);
if (y != x) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a uint128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return y The downcasted integer, now type uint128
function toUint128(
uint256 x
) internal pure returns (uint128 y) {
y = uint128(x);
if (x != y) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a int128 to a uint128, revert on overflow or underflow
/// @param x The int128 to be casted
/// @return y The casted integer, now type uint128
function toUint128(
int128 x
) internal pure returns (uint128 y) {
if (x < 0) SafeCastOverflow.selector.revertWith();
y = uint128(x);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param x The int256 to be downcasted
/// @return y The downcasted integer, now type int128
function toInt128(
int256 x
) internal pure returns (int128 y) {
y = int128(x);
if (y != x) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param x The uint256 to be casted
/// @return y The casted integer, now type int256
function toInt256(
uint256 x
) internal pure returns (int256 y) {
y = int256(x);
if (y < 0) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a int128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return The downcasted integer, now type int128
function toInt128(
uint256 x
) internal pure returns (int128) {
if (x >= 1 << 127) SafeCastOverflow.selector.revertWith();
return int128(int256(x));
}
}
contracts/interfaces/external/IAllowanceTransfer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// import {IEIP712} from "./IEIP712.sol";
/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance
/// setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer {
//is IEIP712
/// @notice Thrown when an allowance on a token has expired.
/// @param deadline The timestamp at which the allowed amount is no longer
/// valid
error AllowanceExpired(uint256 deadline);
/// @notice Thrown when an allowance on a token has been depleted.
/// @param amount The maximum amount allowed
error InsufficientAllowance(uint256 amount);
/// @notice Thrown when too many nonces are invalidated.
error ExcessiveInvalidation();
/// @notice Emits an event when the owner successfully invalidates an
/// ordered nonce.
event NonceInvalidation(
address indexed owner,
address indexed token,
address indexed spender,
uint48 newNonce,
uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a
/// token for the spender.
event Approval(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions
/// using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with
/// the lockdown function.
event Lockdown(address indexed owner, address token, address spender);
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each
// signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allowance
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed
/// over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each
// signature
uint48 nonce;
}
/// @notice A token spender pair.
struct TokenSpenderPair {
// the token the spender is approved
address token;
// the spender address
address spender;
}
/// @notice Details for a token transfer.
struct AllowanceTransferDetails {
// the owner of the token
address from;
// the recipient of the token
address to;
// the amount of the token
uint160 amount;
// the token to be transferred
address token;
}
/// @notice A mapping from owner address to token address to spender address
/// to PackedAllowance struct, which contains details and conditions of the
/// approval.
/// @notice The mapping is indexed in the above order see:
/// allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the
/// allowed amount is no longer valid, and current nonce thats updated on
/// any signature based approvals.
function allowance(
address user,
address token,
address spender
) external view returns (uint160 amount, uint48 expiration, uint48 nonce);
/// @notice Approves the spender to use up to amount of the specified token
/// up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged
/// in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(
address token,
address spender,
uint160 amount,
uint48 expiration
) external;
/// @notice Permit a spender to a given amount of the owners token via the
/// owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by
/// invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms
/// of approval
/// @param signature The owner's signature over the permit data
function permit(
address owner,
PermitSingle memory permitSingle,
bytes calldata signature
) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via
/// the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by
/// invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitBatch Data signed over by the owner specifying the terms of
/// approval
/// @param signature The owner's signature over the permit data
function permit(
address owner,
PermitBatch memory permitBatch,
bytes calldata signature
) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired
/// amount
/// of tokens to msg.sender.
function transferFrom(
address from,
address to,
uint160 amount,
address token
) external;
/// @notice Transfer approved tokens in a batch
/// @param transferDetails Array of owners, recipients, amounts, and tokens
/// for the transfers
/// @dev Requires the from addresses to have approved at least the desired
/// amount
/// of tokens to msg.sender.
function transferFrom(
AllowanceTransferDetails[] calldata transferDetails
) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity
/// by batch revoking approvals
/// @param approvals Array of approvals to revoke.
function lockdown(
TokenSpenderPair[] calldata approvals
) external;
/// @notice Invalidate nonces for a given (token, spender) pair
/// @param token The token to invalidate nonces for
/// @param spender The spender to invalidate nonces for
/// @param newNonce The new nonce to set. Invalidates all nonces less than
/// it.
/// @dev Can't invalidate more than 2**16 nonces per transaction.
function invalidateNonces(
address token,
address spender,
uint48 newNonce
) external;
}
contracts/interfaces/external/uniswap/v4/types/PoolId.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { PoolKey } from "./PoolKey.sol";
type PoolId is bytes32;
/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
/// @notice Returns value equal to keccak256(abi.encode(poolKey))
function toId(
PoolKey memory poolKey
) internal pure returns (PoolId poolId) {
assembly ("memory-safe") {
// 0xa0 represents the total size of the poolKey struct (5 slots of
// 32 bytes)
poolId := keccak256(poolKey, 0xa0)
}
}
}
contracts/interfaces/external/uniswap/v4/IUniversalRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
interface IUniversalRouter {
/// @notice Thrown when a required command has failed
error ExecutionFailed(uint256 commandIndex, bytes message);
/// @notice Thrown when attempting to send ETH directly to the contract
error ETHNotAccepted();
/// @notice Thrown when executing commands with an expired deadline
error TransactionDeadlinePassed();
/// @notice Thrown when attempting to execute commands and an incorrect
/// number of inputs are provided
error LengthMismatch();
// @notice Thrown when an address that isn't WETH tries to send ETH to the
// router without calldata
error InvalidEthSender();
/// @notice Executes encoded commands along with provided inputs. Reverts if
/// deadline has expired.
/// @param commands A set of concatenated commands, each 1 byte in length
/// @param inputs An array of byte strings containing abi encoded inputs for
/// each command
/// @param deadline The deadline by which the transaction must be executed
function execute(
bytes calldata commands,
bytes[] calldata inputs,
uint256 deadline
) external payable;
}
contracts/interfaces/external/uniswap/v4/types/PoolKey.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Currency } from "./Currency.sol";
import { IHooks } from "../IHooks.sol";
import { PoolIdLibrary } from "./PoolId.sol";
using PoolIdLibrary for PoolKey global;
/// @notice Returns the key for identifying a pool
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
Currency currency0;
/// @notice The higher currency of the pool, sorted numerically
Currency currency1;
/// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1,
/// the pool has a dynamic fee and must be exactly equal to 0x800000
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
IHooks hooks;
}
contracts/interfaces/external/uniswap/v4/libraries/CustomRevert.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Library for reverting with custom errors efficiently
/// @notice Contains functions for reverting with custom errors with different
/// argument types efficiently
/// @dev To use this library, declare `using CustomRevert for bytes4;` and
/// replace `revert CustomError()` with
/// `CustomError.selector.revertWith()`
/// @dev The functions may tamper with the free memory pointer but it is fine
/// since the call context is exited immediately
library CustomRevert {
/// @dev ERC-7751 error for wrapping bubbled up reverts
error WrappedError(
address target, bytes4 selector, bytes reason, bytes details
);
/// @dev Reverts with the selector of a custom error in the scratch space
function revertWith(
bytes4 selector
) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
revert(0, 0x04)
}
}
/// @dev Reverts with a custom error with an address argument in the scratch
/// space
function revertWith(bytes4 selector, address addr) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, and(addr, 0xffffffffffffffffffffffffffffffffffffffff))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with an int24 argument in the scratch
/// space
function revertWith(bytes4 selector, int24 value) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, signextend(2, value))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with a uint160 argument in the scratch
/// space
function revertWith(bytes4 selector, uint160 value) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, and(value, 0xffffffffffffffffffffffffffffffffffffffff))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with two int24 arguments
function revertWith(
bytes4 selector,
int24 value1,
int24 value2
) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), signextend(2, value1))
mstore(add(fmp, 0x24), signextend(2, value2))
revert(fmp, 0x44)
}
}
/// @dev Reverts with a custom error with two uint160 arguments
function revertWith(
bytes4 selector,
uint160 value1,
uint160 value2
) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(
add(fmp, 0x04),
and(value1, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(
add(fmp, 0x24),
and(value2, 0xffffffffffffffffffffffffffffffffffffffff)
)
revert(fmp, 0x44)
}
}
/// @dev Reverts with a custom error with two address arguments
function revertWith(
bytes4 selector,
address value1,
address value2
) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(
add(fmp, 0x04),
and(value1, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(
add(fmp, 0x24),
and(value2, 0xffffffffffffffffffffffffffffffffffffffff)
)
revert(fmp, 0x44)
}
}
/// @notice bubble up the revert message returned by a call and revert with
/// a wrapped ERC-7751 error
/// @dev this method can be vulnerable to revert data bombs
function bubbleUpAndRevertWith(
address revertingContract,
bytes4 revertingFunctionSelector,
bytes4 additionalContext
) internal pure {
bytes4 wrappedErrorSelector = WrappedError.selector;
assembly ("memory-safe") {
// Ensure the size of the revert data is a multiple of 32 bytes
let encodedDataSize := mul(div(add(returndatasize(), 31), 32), 32)
let fmp := mload(0x40)
// Encode wrapped error selector, address, function selector,
// offset, additional context, size, revert reason
mstore(fmp, wrappedErrorSelector)
mstore(
add(fmp, 0x04),
and(
revertingContract,
0xffffffffffffffffffffffffffffffffffffffff
)
)
mstore(
add(fmp, 0x24),
and(
revertingFunctionSelector,
0xffffffff00000000000000000000000000000000000000000000000000000000
)
)
// offset revert reason
mstore(add(fmp, 0x44), 0x80)
// offset additional context
mstore(add(fmp, 0x64), add(0xa0, encodedDataSize))
// size revert reason
mstore(add(fmp, 0x84), returndatasize())
// revert reason
returndatacopy(add(fmp, 0xa4), 0, returndatasize())
// size additional context
mstore(add(fmp, add(0xa4, encodedDataSize)), 0x04)
// additional context
mstore(
add(fmp, add(0xc4, encodedDataSize)),
and(
additionalContext,
0xffffffff00000000000000000000000000000000000000000000000000000000
)
)
revert(fmp, add(0xe4, encodedDataSize))
}
}
}
contracts/interfaces/external/uniswap/v4/IHooks.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { PoolKey } from "./types/PoolKey.sol";
import { BalanceDelta } from "./types/BalanceDelta.sol";
import { IPoolManager } from "./IPoolManager.sol";
import { BeforeSwapDelta } from "./types/BeforeSwapDelta.sol";
/// @notice V4 decides whether to invoke specific hooks by inspecting the least
/// significant bits
/// of the address that the hooks contract is deployed to.
/// For example, a hooks contract deployed to address:
/// 0x0000000000000000000000000000000000002400
/// has the lowest bits '10 0100 0000 0000' which would cause the 'before
/// initialize' and 'after add liquidity' hooks to be used.
/// See the Hooks library for the full spec.
/// @dev Should only be callable by the v4 PoolManager.
interface IHooks {
/// @notice The hook called before the state of a pool is initialized
/// @param sender The initial msg.sender for the initialize call
/// @param key The key for the pool being initialized
/// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
/// @return bytes4 The function selector for the hook
function beforeInitialize(
address sender,
PoolKey calldata key,
uint160 sqrtPriceX96
) external returns (bytes4);
/// @notice The hook called after the state of a pool is initialized
/// @param sender The initial msg.sender for the initialize call
/// @param key The key for the pool being initialized
/// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
/// @param tick The current tick after the state of a pool is initialized
/// @return bytes4 The function selector for the hook
function afterInitialize(
address sender,
PoolKey calldata key,
uint160 sqrtPriceX96,
int24 tick
) external returns (bytes4);
/// @notice The hook called before liquidity is added
/// @param sender The initial msg.sender for the add liquidity call
/// @param key The key for the pool
/// @param params The parameters for adding liquidity
/// @param hookData Arbitrary data handed into the PoolManager by the
/// liquidity provider to be passed on to the hook
/// @return bytes4 The function selector for the hook
function beforeAddLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
bytes calldata hookData
) external returns (bytes4);
/// @notice The hook called after liquidity is added
/// @param sender The initial msg.sender for the add liquidity call
/// @param key The key for the pool
/// @param params The parameters for adding liquidity
/// @param delta The caller's balance delta after adding liquidity; the sum
/// of principal delta, fees accrued, and hook delta
/// @param feesAccrued The fees accrued since the last time fees were
/// collected from this position
/// @param hookData Arbitrary data handed into the PoolManager by the
/// liquidity provider to be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return BalanceDelta The hook's delta in token0 and token1. Positive:
/// the hook is owed/took currency, negative: the hook owes/sent currency
function afterAddLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
BalanceDelta delta,
BalanceDelta feesAccrued,
bytes calldata hookData
) external returns (bytes4, BalanceDelta);
/// @notice The hook called before liquidity is removed
/// @param sender The initial msg.sender for the remove liquidity call
/// @param key The key for the pool
/// @param params The parameters for removing liquidity
/// @param hookData Arbitrary data handed into the PoolManager by the
/// liquidity provider to be be passed on to the hook
/// @return bytes4 The function selector for the hook
function beforeRemoveLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
bytes calldata hookData
) external returns (bytes4);
/// @notice The hook called after liquidity is removed
/// @param sender The initial msg.sender for the remove liquidity call
/// @param key The key for the pool
/// @param params The parameters for removing liquidity
/// @param delta The caller's balance delta after removing liquidity; the
/// sum of principal delta, fees accrued, and hook delta
/// @param feesAccrued The fees accrued since the last time fees were
/// collected from this position
/// @param hookData Arbitrary data handed into the PoolManager by the
/// liquidity provider to be be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return BalanceDelta The hook's delta in token0 and token1. Positive:
/// the hook is owed/took currency, negative: the hook owes/sent currency
function afterRemoveLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
BalanceDelta delta,
BalanceDelta feesAccrued,
bytes calldata hookData
) external returns (bytes4, BalanceDelta);
/// @notice The hook called before a swap
/// @param sender The initial msg.sender for the swap call
/// @param key The key for the pool
/// @param params The parameters for the swap
/// @param hookData Arbitrary data handed into the PoolManager by the
/// swapper to be be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return BeforeSwapDelta The hook's delta in specified and unspecified
/// currencies. Positive: the hook is owed/took currency, negative: the hook
/// owes/sent currency
/// @return uint24 Optionally override the lp fee, only used if three
/// conditions are met: 1. the Pool has a dynamic fee, 2. the value's 2nd
/// highest bit is set (23rd bit, 0x400000), and 3. the value is less than
/// or equal to the maximum fee (1 million)
function beforeSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata hookData
) external returns (bytes4, BeforeSwapDelta, uint24);
/// @notice The hook called after a swap
/// @param sender The initial msg.sender for the swap call
/// @param key The key for the pool
/// @param params The parameters for the swap
/// @param delta The amount owed to the caller (positive) or owed to the
/// pool (negative)
/// @param hookData Arbitrary data handed into the PoolManager by the
/// swapper to be be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return int128 The hook's delta in unspecified currency. Positive: the
/// hook is owed/took currency, negative: the hook owes/sent currency
function afterSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
BalanceDelta delta,
bytes calldata hookData
) external returns (bytes4, int128);
/// @notice The hook called before donate
/// @param sender The initial msg.sender for the donate call
/// @param key The key for the pool
/// @param amount0 The amount of token0 being donated
/// @param amount1 The amount of token1 being donated
/// @param hookData Arbitrary data handed into the PoolManager by the donor
/// to be be passed on to the hook
/// @return bytes4 The function selector for the hook
function beforeDonate(
address sender,
PoolKey calldata key,
uint256 amount0,
uint256 amount1,
bytes calldata hookData
) external returns (bytes4);
/// @notice The hook called after donate
/// @param sender The initial msg.sender for the donate call
/// @param key The key for the pool
/// @param amount0 The amount of token0 being donated
/// @param amount1 The amount of token1 being donated
/// @param hookData Arbitrary data handed into the PoolManager by the donor
/// to be be passed on to the hook
/// @return bytes4 The function selector for the hook
function afterDonate(
address sender,
PoolKey calldata key,
uint256 amount0,
uint256 amount1,
bytes calldata hookData
) external returns (bytes4);
}
contracts/interfaces/IRouterAllowlist.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IRouterAllowlist {
/// @notice Check if a router is allowed
/// @param router The router address to check
/// @return True if the router is allowed
function isAllowed(address router) external view returns (bool);
/// @notice Revert if the router is not allowed
/// @param router The router address to check
function requireAllowed(address router) external view;
/// @notice Check if a factory is allowed
/// @param factory The factory address to check
/// @return True if the factory is allowed
function isAllowedFactory(address factory) external view returns (bool);
/// @notice Revert if the factory is not allowed
/// @param factory The factory address to check
function requireAllowedFactory(address factory) external view;
/// @notice Check if a custom pool deployer is allowed for a factory
/// @param factory The factory address to check
/// @param deployer The custom pool deployer address to check
/// @return True if the custom deployer is allowed for the factory
function isAllowedCustomDeployer(address factory, address deployer) external view returns (bool);
/// @notice Revert if the custom pool deployer is not allowed for a factory
/// @param factory The factory address to check
/// @param deployer The custom pool deployer address to check
function requireAllowedCustomDeployer(address factory, address deployer) external view;
/// @notice Check if a v4 hook is allowed
/// @param hook The hook address to check
/// @return True if the hook is allowed
function isAllowedHook(address hook) external view returns (bool);
/// @notice Revert if the v4 hook is not allowed
/// @param hook The hook address to check
function requireAllowedHook(address hook) external view;
/// @notice Check if a bridge contract is allowed
/// @param bridge The bridge contract address
/// @return True if the bridge is allowed
function isAllowedBridge(address bridge) external view returns (bool);
/// @notice Revert if the bridge contract is not allowed
/// @param bridge The bridge contract address
function requireAllowedBridge(address bridge) external view;
/// @notice Check if a function selector is allowed for a bridge contract
/// @param bridge The bridge contract address
/// @param selector The function selector
/// @return True if the selector is allowed
function isAllowedSelector(address bridge, bytes4 selector) external view returns (bool);
/// @notice Revert if the function selector is not allowed for a bridge contract
/// @param bridge The bridge contract address
/// @param selector The function selector
function requireAllowedSelector(address bridge, bytes4 selector) external view;
/// @notice Enumerate the currently-allowed function selectors for a bridge
/// @param bridge The bridge contract address
/// @return selectors The set of selectors currently marked allowed for the bridge
function bridgeSelectors(address bridge) external view returns (bytes4[] memory selectors);
}
contracts/interfaces/external/pancake/infinity/ICLPoolManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { PoolId } from "contracts/interfaces/external/uniswap/v4/types/PoolId.sol";
/// @notice Position info returned by getPosition
struct CLPositionInfo {
uint128 liquidity;
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
}
/// @notice Per-tick info returned by getPoolTickInfo
struct CLTickInfo {
uint128 liquidityGross;
int128 liquidityNet;
uint256 feeGrowthOutside0X128;
uint256 feeGrowthOutside1X128;
}
/// @title ICLPoolManager
/// @notice Interface for PancakeSwap Infinity CLPoolManager state reading
interface ICLPoolManager {
/// @notice Get the current price and tick of a pool
/// @param id The pool ID
/// @return sqrtPriceX96 The current sqrt price
/// @return tick The current tick
/// @return protocolFee The protocol fee
/// @return lpFee The LP fee
function getSlot0(PoolId id)
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint24 protocolFee,
uint24 lpFee
);
/// @notice Get the liquidity of a pool
/// @param id The pool ID
/// @return liquidity The pool liquidity
function getLiquidity(PoolId id) external view returns (uint128 liquidity);
/// @notice Get the fee growth globals of a pool
/// @param id The pool ID
/// @return feeGrowthGlobal0X128 The fee growth for token0
/// @return feeGrowthGlobal1X128 The fee growth for token1
function getFeeGrowthGlobals(PoolId id)
external
view
returns (uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128);
/// @notice Get per-tick state for a pool
/// @dev PancakeSwap Infinity CLPoolManager does not expose getFeeGrowthInside
/// directly, so callers must read both tick infos and compute fee growth
/// inside manually using the pool's current tick and fee growth globals.
/// @param id The pool ID
/// @param tick The tick to read
/// @return tickInfo The tick's gross/net liquidity and feeGrowthOutside
function getPoolTickInfo(PoolId id, int24 tick)
external
view
returns (CLTickInfo memory tickInfo);
/// @notice Get position info including fee growth
/// @param id The pool ID
/// @param owner The position owner
/// @param tickLower The lower tick
/// @param tickUpper The upper tick
/// @param salt The position salt (tokenId for position manager)
/// @return position The position info with liquidity and fee growth
function getPosition(
PoolId id,
address owner,
int24 tickLower,
int24 tickUpper,
bytes32 salt
) external view returns (CLPositionInfo memory position);
}
contracts/dex/IDexAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { SwapStep } from "contracts/dex/SwapTypes.sol";
/// @title IDexAdapter
/// @notice Interface implemented by per-DEX adapter contracts that
/// `SwapRouter` dispatches to when its `dexAdapter[dexType]` slot is set.
/// @dev Under the Option C custody model, the adapter starts with no
/// tokens and pulls them from `SwapRouter` via `adapterPull` while
/// `SwapRouter` holds it as the in-flight active adapter. Output tokens
/// land back in `SwapRouter`, which measures the balance delta to compute
/// the hop's effective output. The function is `payable` so `SwapRouter`
/// can forward native ETH custody alongside the swap when
/// `step.tokenIn == address(0)`. The interface intentionally does not
/// import the concrete `SwapRouter` contract — adapters depend on the
/// shared `SwapTypes` only.
interface IDexAdapter {
/// @notice Adapter implementation version. Off-chain tooling
/// (vfat-api, deploy scripts) reads this to detect which adapter
/// build is currently wired into `SwapRouter`.
function version() external view returns (uint16);
/// @notice Execute a single swap step on behalf of `SwapRouter`.
/// @param step Swap step parameters (dex type, router, tokens, dex-
/// specific data) as defined by `SwapRouter`'s public ABI.
/// @param amountIn Amount of `step.tokenIn` available for this step.
/// @return amountOut Amount of `step.tokenOut` the adapter believes
/// it delivered to `SwapRouter`. Informational only — `SwapRouter`
/// cross-checks via a balance delta.
function swap(
SwapStep calldata step,
uint256 amountIn
) external payable returns (uint256 amountOut);
}
contracts/libraries/Permit2AllowanceLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {
IERC20Minimal
} from "contracts/interfaces/external/uniswap/v4/IERC20Minimal.sol";
/// @title Permit2AllowanceLib
/// @notice Detects ERC20s whose Permit2 allowance is hardcoded to
/// `type(uint256).max`.
/// @dev Solady's ERC20 enables `_givePermit2InfiniteAllowance()` by default:
/// `allowance(anyone, PERMIT2)` always reads `type(uint256).max`, and
/// `approve(PERMIT2, x)` reverts with `Permit2AllowanceIsFixedAtInfinity()`
/// for every `x != type(uint256).max`. The usual approve-to-zero-then-approve
/// reset dance therefore reverts on *both* legs, so callers must skip the
/// ERC20 approval entirely — the allowance is already infinite — and go
/// straight to the `IAllowanceTransfer.approve` step.
library Permit2AllowanceLib {
/// @notice Whether the ERC20 approve/revoke against `permit2` has to be
/// skipped for `token`.
/// @param token ERC20 to probe.
/// @param owner Account whose balance Permit2 pulls from. For connectors
/// this is `address(this)`: they run under `DELEGATECALL` from the Sickle,
/// so the Sickle is the approver. For DEX adapters it is the `SwapRouter`,
/// which holds custody and issues the approval on the adapter's behalf.
/// @param permit2 Permit2 deployment the token would hardcode against —
/// Uniswap and Pancake Infinity use different ones.
function hasFixedAllowance(
address token,
address owner,
address permit2
) internal view returns (bool) {
// Low-level staticcall rather than `try`: a token that has no code,
// reverts, or returns short data has to degrade to "not fixed" rather
// than bubble up and fail the whole deposit.
(bool success, bytes memory result) = token.staticcall(
abi.encodeCall(IERC20Minimal.allowance, (owner, permit2))
);
return success && result.length >= 32
&& abi.decode(result, (uint256)) == type(uint256).max;
}
}
contracts/interfaces/external/uniswap/v4/libraries/Actions.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Library to define different pool actions.
/// @dev These are suggested common commands, however additional commands should
/// be defined as required
/// Some of these actions are not supported in the Router contracts or Position
/// Manager contracts, but are left as they may be helpful commands for other
/// peripheral contracts.
library Actions {
// pool actions
// liquidity actions
uint256 internal constant INCREASE_LIQUIDITY = 0x00;
uint256 internal constant DECREASE_LIQUIDITY = 0x01;
uint256 internal constant MINT_POSITION = 0x02;
uint256 internal constant BURN_POSITION = 0x03;
uint256 internal constant INCREASE_LIQUIDITY_FROM_DELTAS = 0x04;
uint256 internal constant MINT_POSITION_FROM_DELTAS = 0x05;
// swapping
uint256 internal constant SWAP_EXACT_IN_SINGLE = 0x06;
uint256 internal constant SWAP_EXACT_IN = 0x07;
uint256 internal constant SWAP_EXACT_OUT_SINGLE = 0x08;
uint256 internal constant SWAP_EXACT_OUT = 0x09;
// donate
// note this is not supported in the position manager or router
uint256 internal constant DONATE = 0x0a;
// closing deltas on the pool manager
// settling
uint256 internal constant SETTLE = 0x0b;
uint256 internal constant SETTLE_ALL = 0x0c;
uint256 internal constant SETTLE_PAIR = 0x0d;
// taking
uint256 internal constant TAKE = 0x0e;
uint256 internal constant TAKE_ALL = 0x0f;
uint256 internal constant TAKE_PORTION = 0x10;
uint256 internal constant TAKE_PAIR = 0x11;
uint256 internal constant CLOSE_CURRENCY = 0x12;
uint256 internal constant CLEAR_OR_TAKE = 0x13;
uint256 internal constant SWEEP = 0x14;
uint256 internal constant WRAP = 0x15;
uint256 internal constant UNWRAP = 0x16;
// minting/burning 6909s to close deltas
// note this is not supported in the position manager or router
uint256 internal constant MINT_6909 = 0x17;
uint256 internal constant BURN_6909 = 0x18;
}
contracts/interfaces/external/uniswap/v4/types/Currency.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20Minimal } from "../IERC20Minimal.sol";
import { CustomRevert } from "../libraries/CustomRevert.sol";
type Currency is address;
using {
greaterThan as >,
lessThan as <,
greaterThanOrEqualTo as >=,
equals as ==
} for Currency global;
using CurrencyLibrary for Currency global;
function equals(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(other);
}
function greaterThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) > Currency.unwrap(other);
}
function lessThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) < Currency.unwrap(other);
}
function greaterThanOrEqualTo(
Currency currency,
Currency other
) pure returns (bool) {
return Currency.unwrap(currency) >= Currency.unwrap(other);
}
/// @title CurrencyLibrary
/// @dev This library allows for transferring and holding native tokens and
/// ERC20 tokens
library CurrencyLibrary {
/// @notice Additional context for ERC-7751 wrapped error when a native
/// transfer fails
error NativeTransferFailed();
/// @notice Additional context for ERC-7751 wrapped error when an ERC20
/// transfer fails
error ERC20TransferFailed();
/// @notice A constant to represent the native currency
Currency public constant ADDRESS_ZERO = Currency.wrap(address(0));
function transfer(Currency currency, address to, uint256 amount) internal {
// altered from
// https://github.com/transmissions11/solmate/blob/44a9963d4c78111f77caa0e65d677b8b46d6f2e6/src/utils/SafeTransferLib.sol
// modified custom error selectors
bool success;
if (currency.isAddressZero()) {
assembly ("memory-safe") {
// Transfer the ETH and revert if it fails.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
// revert with NativeTransferFailed, containing the bubbled up error
// as an argument
if (!success) {
CustomRevert.bubbleUpAndRevertWith(
to, bytes4(0), NativeTransferFailed.selector
);
}
} else {
assembly ("memory-safe") {
// Get a pointer to some free memory.
let fmp := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with
// the function selector.
mstore(
fmp,
0xa9059cbb00000000000000000000000000000000000000000000000000000000
)
mstore(
add(fmp, 4),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
) // Append and mask the "to" argument.
mstore(add(fmp, 36), amount) // Append the "amount" argument.
// Masking not required as it's a full 32 byte type.
success :=
and(
// Set success to whether the call reverted, if not we check
// it either
// returned exactly 1 (can't just be non-zero data), or had
// no return data.
or(
and(eq(mload(0), 1), gt(returndatasize(), 31)),
iszero(returndatasize())
),
// We use 68 because the length of our calldata totals up
// like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data
// into the scratch space.
// Counterintuitively, this call must be positioned second
// to the or() call in the
// surrounding and() call or else returndatasize() will be
// zero during the computation.
call(gas(), currency, 0, fmp, 68, 0, 32)
)
// Now clean the memory we used
mstore(fmp, 0) // 4 byte `selector` and 28 bytes of `to` were
// stored here
mstore(add(fmp, 0x20), 0) // 4 bytes of `to` and 28 bytes of
// `amount` were stored here
mstore(add(fmp, 0x40), 0) // 4 bytes of `amount` were stored
// here
}
// revert with ERC20TransferFailed, containing the bubbled up error
// as an argument
if (!success) {
CustomRevert.bubbleUpAndRevertWith(
Currency.unwrap(currency),
IERC20Minimal.transfer.selector,
ERC20TransferFailed.selector
);
}
}
}
function balanceOfSelf(
Currency currency
) internal view returns (uint256) {
if (currency.isAddressZero()) {
return address(this).balance;
} else {
return IERC20Minimal(Currency.unwrap(currency)).balanceOf(
address(this)
);
}
}
function balanceOf(
Currency currency,
address owner
) internal view returns (uint256) {
if (currency.isAddressZero()) {
return owner.balance;
} else {
return IERC20Minimal(Currency.unwrap(currency)).balanceOf(owner);
}
}
function isAddressZero(
Currency currency
) internal pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(ADDRESS_ZERO);
}
function toId(
Currency currency
) internal pure returns (uint256) {
return uint160(Currency.unwrap(currency));
}
// If the upper 12 bytes are non-zero, they will be zero-ed out
// Therefore, fromId() and toId() are not inverses of each other
function fromId(
uint256 id
) internal pure returns (Currency) {
return Currency.wrap(address(uint160(id)));
}
}
contracts/interfaces/external/uniswap/v4/IPoolManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import { Currency } from "./types/Currency.sol";
import { PoolKey } from "./types/PoolKey.sol";
import { PoolId } from "./types/PoolId.sol";
import { BalanceDelta } from "./types/BalanceDelta.sol";
import { IHooks } from "./IHooks.sol";
import { IExtsload } from "./IExtsload.sol";
import { IExttload } from "./IExttload.sol";
/// @notice Interface for the PoolManager
interface IPoolManager is IExtsload, IExttload {
/// @notice Thrown when a currency is not netted out after the contract is
/// unlocked
error CurrencyNotSettled();
/// @notice Thrown when trying to interact with a non-initialized pool
error PoolNotInitialized();
/// @notice Thrown when unlock is called, but the contract is already
/// unlocked
error AlreadyUnlocked();
/// @notice Thrown when a function is called that requires the contract to
/// be unlocked, but it is not
error ManagerLocked();
/// @notice Pools are limited to type(int16).max tickSpacing in #initialize,
/// to prevent overflow
error TickSpacingTooLarge(int24 tickSpacing);
/// @notice Pools must have a positive non-zero tickSpacing passed to
/// #initialize
error TickSpacingTooSmall(int24 tickSpacing);
/// @notice PoolKey must have currencies where address(currency0) <
/// address(currency1)
error CurrenciesOutOfOrderOrEqual(address currency0, address currency1);
/// @notice Thrown when a call to updateDynamicLPFee is made by an address
/// that is not the hook,
/// or on a pool that does not have a dynamic swap fee.
error UnauthorizedDynamicLPFeeUpdate();
/// @notice Thrown when trying to swap amount of 0
error SwapAmountCannotBeZero();
///@notice Thrown when native currency is passed to a non native settlement
error NonzeroNativeValue();
/// @notice Thrown when `clear` is called with an amount that is not exactly
/// equal to the open currency delta.
error MustClearExactPositiveDelta();
/// @notice Emitted when a new pool is initialized
/// @param id The abi encoded hash of the pool key struct for the new pool
/// @param currency0 The first currency of the pool by address sort order
/// @param currency1 The second currency of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in
/// hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param hooks The hooks contract address for the pool, or address(0) if
/// none
/// @param sqrtPriceX96 The price of the pool on initialization
/// @param tick The initial tick of the pool corresponding to the
/// initialized price
event Initialize(
PoolId indexed id,
Currency indexed currency0,
Currency indexed currency1,
uint24 fee,
int24 tickSpacing,
IHooks hooks,
uint160 sqrtPriceX96,
int24 tick
);
/// @notice Emitted when a liquidity position is modified
/// @param id The abi encoded hash of the pool key struct for the pool that
/// was modified
/// @param sender The address that modified the pool
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param liquidityDelta The amount of liquidity that was added or removed
/// @param salt The extra data to make positions unique
event ModifyLiquidity(
PoolId indexed id,
address indexed sender,
int24 tickLower,
int24 tickUpper,
int256 liquidityDelta,
bytes32 salt
);
/// @notice Emitted for swaps between currency0 and currency1
/// @param id The abi encoded hash of the pool key struct for the pool that
/// was modified
/// @param sender The address that initiated the swap call, and that
/// received the callback
/// @param amount0 The delta of the currency0 balance of the pool
/// @param amount1 The delta of the currency1 balance of the pool
/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a
/// Q64.96
/// @param liquidity The liquidity of the pool after the swap
/// @param tick The log base 1.0001 of the price of the pool after the swap
/// @param fee The swap fee in hundredths of a bip
event Swap(
PoolId indexed id,
address indexed sender,
int128 amount0,
int128 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick,
uint24 fee
);
/// @notice Emitted for donations
/// @param id The abi encoded hash of the pool key struct for the pool that
/// was donated to
/// @param sender The address that initiated the donate call
/// @param amount0 The amount donated in currency0
/// @param amount1 The amount donated in currency1
event Donate(
PoolId indexed id,
address indexed sender,
uint256 amount0,
uint256 amount1
);
/// @notice All interactions on the contract that account deltas require
/// unlocking. A caller that calls `unlock` must implement
/// `IUnlockCallback(msg.sender).unlockCallback(data)`, where they interact
/// with the remaining functions on this contract.
/// @dev The only functions callable without an unlocking are `initialize`
/// and `updateDynamicLPFee`
/// @param data Any data to pass to the callback, via
/// `IUnlockCallback(msg.sender).unlockCallback(data)`
/// @return The data returned by the call to
/// `IUnlockCallback(msg.sender).unlockCallback(data)`
function unlock(
bytes calldata data
) external returns (bytes memory);
/// @notice Initialize the state for a given pool ID
/// @dev A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps
/// impossible since the input is entirely consumed by the fee
/// @param key The pool key for the pool to initialize
/// @param sqrtPriceX96 The initial square root price
/// @return tick The initial tick of the pool
function initialize(
PoolKey memory key,
uint160 sqrtPriceX96
) external returns (int24 tick);
struct ModifyLiquidityParams {
// the lower and upper tick of the position
int24 tickLower;
int24 tickUpper;
// how to modify the liquidity
int256 liquidityDelta;
// a value to set if you want unique liquidity positions at the same
// range
bytes32 salt;
}
/// @notice Modify the liquidity for the given pool
/// @dev Poke by calling with a zero liquidityDelta
/// @param key The pool to modify liquidity in
/// @param params The parameters for modifying the liquidity
/// @param hookData The data to pass through to the add/removeLiquidity
/// hooks
/// @return callerDelta The balance delta of the caller of modifyLiquidity.
/// This is the total of both principal, fee deltas, and hook deltas if
/// applicable
/// @return feesAccrued The balance delta of the fees generated in the
/// liquidity range. Returned for informational purposes
/// @dev Note that feesAccrued can be artificially inflated by a malicious
/// actor and integrators should be careful using the value
/// For pools with a single liquidity position, actors can donate to
/// themselves to inflate feeGrowthGlobal (and consequently feesAccrued)
/// atomically donating and collecting fees in the same unlockCallback may
/// make the inflated value more extreme
function modifyLiquidity(
PoolKey memory key,
ModifyLiquidityParams memory params,
bytes calldata hookData
) external returns (BalanceDelta callerDelta, BalanceDelta feesAccrued);
struct SwapParams {
/// Whether to swap token0 for token1 or vice versa
bool zeroForOne;
/// The desired input amount if negative (exactIn), or the desired
/// output amount if positive (exactOut)
int256 amountSpecified;
/// The sqrt price at which, if reached, the swap will stop executing
uint160 sqrtPriceLimitX96;
}
/// @notice Swap against the given pool
/// @param key The pool to swap in
/// @param params The parameters for swapping
/// @param hookData The data to pass through to the swap hooks
/// @return swapDelta The balance delta of the address swapping
/// @dev Swapping on low liquidity pools may cause unexpected swap amounts
/// when liquidity available is less than amountSpecified.
/// Additionally note that if interacting with hooks that have the
/// BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG
/// the hook may alter the swap input/output. Integrators should perform
/// checks on the returned swapDelta.
function swap(
PoolKey memory key,
SwapParams memory params,
bytes calldata hookData
) external returns (BalanceDelta swapDelta);
/// @notice Donate the given currency amounts to the in-range liquidity
/// providers of a pool
/// @dev Calls to donate can be frontrun adding just-in-time liquidity, with
/// the aim of receiving a portion donated funds.
/// Donors should keep this in mind when designing donation mechanisms.
/// @dev This function donates to in-range LPs at slot0.tick. In certain
/// edge-cases of the swap algorithm, the `sqrtPrice` of
/// a pool can be at the lower boundary of tick `n`, but the `slot0.tick` of
/// the pool is already `n - 1`. In this case a call to
/// `donate` would donate to tick `n - 1` (slot0.tick) not tick `n`
/// (getTickAtSqrtPrice(slot0.sqrtPriceX96)).
/// Read the comments in `Pool.swap()` for more information about this.
/// @param key The key of the pool to donate to
/// @param amount0 The amount of currency0 to donate
/// @param amount1 The amount of currency1 to donate
/// @param hookData The data to pass through to the donate hooks
/// @return BalanceDelta The delta of the caller after the donate
function donate(
PoolKey memory key,
uint256 amount0,
uint256 amount1,
bytes calldata hookData
) external returns (BalanceDelta);
/// @notice Writes the current ERC20 balance of the specified currency to
/// transient storage
/// This is used to checkpoint balances for the manager and derive deltas
/// for the caller.
/// @dev This MUST be called before any ERC20 tokens are sent into the
/// contract, but can be skipped
/// for native tokens because the amount to settle is determined by the sent
/// value.
/// However, if an ERC20 token has been synced and not settled, and the
/// caller instead wants to settle
/// native funds, this function can be called with the native currency to
/// then be able to settle the native currency
function sync(
Currency currency
) external;
/// @notice Called by the user to net out some value owed to the user
/// @dev Will revert if the requested amount is not available, consider
/// using `mint` instead
/// @dev Can also be used as a mechanism for free flash loans
/// @param currency The currency to withdraw from the pool manager
/// @param to The address to withdraw to
/// @param amount The amount of currency to withdraw
function take(Currency currency, address to, uint256 amount) external;
/// @notice Called by the user to pay what is owed
/// @return paid The amount of currency settled
function settle() external payable returns (uint256 paid);
/// @notice Called by the user to pay on behalf of another address
/// @param recipient The address to credit for the payment
/// @return paid The amount of currency settled
function settleFor(
address recipient
) external payable returns (uint256 paid);
/// @notice WARNING - Any currency that is cleared, will be non-retrievable,
/// and locked in the contract permanently.
/// A call to clear will zero out a positive balance WITHOUT a corresponding
/// transfer.
/// @dev This could be used to clear a balance that is considered dust.
/// Additionally, the amount must be the exact positive balance. This is to
/// enforce that the caller is aware of the amount being cleared.
function clear(Currency currency, uint256 amount) external;
/// @notice Called by the user to move value into ERC6909 balance
/// @param to The address to mint the tokens to
/// @param id The currency address to mint to ERC6909s, as a uint256
/// @param amount The amount of currency to mint
/// @dev The id is converted to a uint160 to correspond to a currency
/// address
/// If the upper 12 bytes are not 0, they will be 0-ed out
function mint(address to, uint256 id, uint256 amount) external;
/// @notice Called by the user to move value from ERC6909 balance
/// @param from The address to burn the tokens from
/// @param id The currency address to burn from ERC6909s, as a uint256
/// @param amount The amount of currency to burn
/// @dev The id is converted to a uint160 to correspond to a currency
/// address
/// If the upper 12 bytes are not 0, they will be 0-ed out
function burn(address from, uint256 id, uint256 amount) external;
/// @notice Updates the pools lp fees for the a pool that has enabled
/// dynamic lp fees.
/// @dev A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps
/// impossible since the input is entirely consumed by the fee
/// @param key The key of the pool to update dynamic LP fees for
/// @param newDynamicLPFee The new dynamic pool LP fee
function updateDynamicLPFee(
PoolKey memory key,
uint24 newDynamicLPFee
) external;
}
contracts/interfaces/external/uniswap/v4/IExtsload.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Interface for functions to access any storage slot in a contract
interface IExtsload {
/// @notice Called by external contracts to access granular pool state
/// @param slot Key of slot to sload
/// @return value The value of the slot as bytes32
function extsload(
bytes32 slot
) external view returns (bytes32 value);
/// @notice Called by external contracts to access granular pool state
/// @param startSlot Key of slot to start sloading from
/// @param nSlots Number of slots to load into return value
/// @return values List of loaded values.
function extsload(
bytes32 startSlot,
uint256 nSlots
) external view returns (bytes32[] memory values);
/// @notice Called by external contracts to access sparse pool state
/// @param slots List of slots to SLOAD from.
/// @return values List of loaded values.
function extsload(
bytes32[] calldata slots
) external view returns (bytes32[] memory values);
}
lib/solmate/src/tokens/ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
contracts/interfaces/external/uniswap/v4/IExttload.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @notice Interface for functions to access any transient storage slot in a
/// contract
interface IExttload {
/// @notice Called by external contracts to access transient storage of the
/// contract
/// @param slot Key of slot to tload
/// @return value The value of the slot as bytes32
function exttload(
bytes32 slot
) external view returns (bytes32 value);
/// @notice Called by external contracts to access sparse transient pool
/// state
/// @param slots List of slots to tload
/// @return values List of loaded values
function exttload(
bytes32[] calldata slots
) external view returns (bytes32[] memory values);
}
Compiler Settings
{"viaIR":false,"remappings":["solmate/=lib/solmate/src/","@openzeppelin/=lib/openzeppelin-contracts/","@morpho-blue/=lib/morpho-blue/src/","ds-test/=lib/solmate/lib/ds-test/src/","forge-std/=lib/forge-std/src/","morpho-blue/=lib/morpho-blue/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"admin_","internalType":"address"},{"type":"address","name":"swapRouter_","internalType":"address"},{"type":"address","name":"allowlist_","internalType":"address"}]},{"type":"error","name":"ETHTransferFailed","inputs":[]},{"type":"error","name":"InvalidAddress","inputs":[]},{"type":"error","name":"NotAdmin","inputs":[]},{"type":"error","name":"NotSwapRouter","inputs":[]},{"type":"error","name":"TransferFailed","inputs":[]},{"type":"event","name":"AdminUpdated","inputs":[{"type":"address","name":"oldAdmin","internalType":"address","indexed":false},{"type":"address","name":"newAdmin","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"PANCAKE_INFINITY_PERMIT2","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"admin","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IRouterAllowlist"}],"name":"allowlist","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescueETH","inputs":[{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescueTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAdmin","inputs":[{"type":"address","name":"newAdmin","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swap","inputs":[{"type":"tuple","name":"step","internalType":"struct SwapStep","components":[{"type":"uint8","name":"dexType","internalType":"uint8"},{"type":"address","name":"router","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"bytes","name":"dexData","internalType":"bytes"}]},{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ISwapRouter"}],"name":"swapRouter","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"version","inputs":[]},{"type":"receive","stateMutability":"payable"}]
Contract Creation Code
0x60c06040523480156200001157600080fd5b5060405162001362380380620013628339810160408190526200003491620000fc565b8282826001600160a01b0383166200005f5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216620000875760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038116620000af5760405163e6c4247b60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b039485161790559082166080521660a0525062000146915050565b80516001600160a01b0381168114620000f757600080fd5b919050565b6000806000606084860312156200011257600080fd5b6200011d84620000df565b92506200012d60208501620000df565b91506200013d60408501620000df565b90509250925092565b60805160a0516111d46200018e6000396000818160f0015261040f0152600081816101a001528181610247015281816104a2015281816108e7015261096201526111d46000f3fe60806040526004361061008a5760003560e01c8063704b6c0211610059578063704b6c0214610146578063960ae66b14610166578063c31c9c071461018e578063cea9d26f146101c2578063f851a440146101e257600080fd5b806304824e701461009657806325dd2dc4146100b85780632b47da52146100de57806354fd4d501461012a57600080fd5b3661009157005b600080fd5b3480156100a257600080fd5b506100b66100b1366004610d17565b610202565b005b6100cb6100c6366004610d34565b61023a565b6040519081526020015b60405180910390f35b3480156100ea57600080fd5b506101127f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100d5565b34801561013657600080fd5b50604051600181526020016100d5565b34801561015257600080fd5b506100b6610161366004610d17565b610296565b34801561017257600080fd5b506101127331c2f6fcff4f8759b3bd5bf0e1084a055615c76881565b34801561019a57600080fd5b506101127f000000000000000000000000000000000000000000000000000000000000000081565b3480156101ce57600080fd5b506100b66101dd366004610d7e565b610351565b3480156101ee57600080fd5b50600054610112906001600160a01b031681565b6000546001600160a01b0316331461022d57604051637bfa4b9f60e01b815260040160405180910390fd5b610237814761038c565b50565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102855760405163db478f2960e01b815260040160405180910390fd5b61028f83836103b8565b9392505050565b6000546001600160a01b031633146102c157604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b0381166102e85760405163e6c4247b60e01b815260040160405180910390fd5b600054604080516001600160a01b03928316815291831660208301527f101b8081ff3b56bbf45deb824d86a3b0fd38b7e3dd42421105cf8abe9106db0b910160405180910390a1600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b0316331461037c57604051637bfa4b9f60e01b815260040160405180910390fd5b610387838383610780565b505050565b600080600080600085875af19050806103875760405163b12d13eb60e01b815260040160405180910390fd5b6000806103c86080850185610dbf565b8101906103d59190610e23565b60408101519091506001600160a01b03161561046c5760408181015190516329e899d960e01b81526001600160a01b0391821660048201527f0000000000000000000000000000000000000000000000000000000000000000909116906329e899d99060240160006040518083038186803b15801561045357600080fd5b505afa158015610467573d6000803e3d6000fd5b505050505b60008061047f6060870160408801610d17565b6001600160a01b0316146105b0576104db6104a06060870160408801610d17565b7f00000000000000000000000000000000000000000000000000000000000000007331c2f6fcff4f8759b3bd5bf0e1084a055615c7686107dd565b905080610510576105106104f56060870160408801610d17565b7331c2f6fcff4f8759b3bd5bf0e1084a055615c768866108b9565b6105ae7331c2f6fcff4f8759b3bd5bf0e1084a055615c768600061053a6060890160408a01610d17565b61054a60408a0160208b01610d17565b6040516001600160a01b0392831660248201529082166044820152908816606482015265ffffffffffff4216608482015260a4015b60408051601f198184030181529190526020810180516001600160e01b03166387517c4560e01b179052610948565b505b60006105bd8684876109ea565b60408051600180825281830190925291925060009190816020015b60608152602001906001900390816105d8579050509050818160008151811061060357610603610ecf565b602090810291909101015260008061062160608a0160408b01610d17565b6001600160a01b031614610636576000610638565b865b90506106af61064d60408a0160208b01610d17565b604051600160fc1b6020820152839060210160408051601f19818403018152908290526106809187904290602401610f8a565b60408051601f198184030181529190526020810180516001600160e01b0316630d64d59360e21b179052610948565b50600092506106c79150506060870160408801610d17565b6001600160a01b0316146107755780610709576107096106ed6060870160408801610d17565b7331c2f6fcff4f8759b3bd5bf0e1084a055615c76860006108b9565b6107737331c2f6fcff4f8759b3bd5bf0e1084a055615c76860006107336060890160408a01610d17565b61074360408a0160208b01610d17565b6040516001600160a01b03928316602482015291166044820152600060648201819052608482015260a40161057f565b505b506000949350505050565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806107d7576040516312171d8360e31b815260040160405180910390fd5b50505050565b6040516001600160a01b0383811660248301528281166044830152600091829182919087169060640160408051601f198184030181529181526020820180516001600160e01b0316636eb1769f60e11b1790525161083b9190610fc0565b600060405180830381855afa9150503d8060008114610876576040519150601f19603f3d011682016040523d82523d6000602084013e61087b565b606091505b509150915081801561088f57506020815110155b80156108af5750600019818060200190518101906108ad9190610fdc565b145b9695505050505050565b604051630deaa3ed60e31b81526001600160a01b0384811660048301528381166024830152604482018390527f00000000000000000000000000000000000000000000000000000000000000001690636f551f6890606401600060405180830381600087803b15801561092b57600080fd5b505af115801561093f573d6000803e3d6000fd5b50505050505050565b60405163df01653760e01b81526060906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063df0165379061099b90879087908790600401610ff5565b6000604051808303816000875af11580156109ba573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109e29190810190611025565b949350505050565b60606000806109fe60808701878501610d17565b6001600160a01b0316610a176060880160408901610d17565b6001600160a01b031610610a4a57610a356080870160608801610d17565b610a456060880160408901610d17565b610a6a565b610a5a6060870160408801610d17565b610a6a6080880160608901610d17565b909250905060006001600160a01b038316610a8b6060890160408a01610d17565b60408051600360f91b6020820152600360fa1b6021820152600f60f81b602282015281516003818303810182526023830181815260a384019094526001600160a01b0394909416949094149450600092906043015b6060815260200190600190039081610ae057905050905060006040518060a001604052806040518060c00160405280896001600160a01b03168152602001886001600160a01b031681526020018c604001516001600160a01b031681526020018c606001516001600160a01b031681526020018c6080015162ffffff1681526020018c60a0015181525081526020018515158152602001896001600160801b0316815260200160006001600160801b03168152602001600067ffffffffffffffff811115610bb057610bb0610e0d565b6040519080825280601f01601f191660200182016040528015610bda576020820181803683370190505b50815250905080604051602001610bf191906110d2565b60405160208183030381529060405282600081518110610c1357610c13610ecf565b6020908102919091010152610c2e60608b0160408c01610d17565b604080516001600160a01b039092166020830152810189905260600160405160208183030381529060405282600181518110610c6c57610c6c610ecf565b6020908102919091010152610c8760808b0160608c01610d17565b604080516001600160a01b03909216602083015260009082015260600160405160208183030381529060405282600281518110610cc657610cc6610ecf565b60200260200101819052508282604051602001610ce4929190611179565b60405160208183030381529060405296505050505050509392505050565b6001600160a01b038116811461023757600080fd5b600060208284031215610d2957600080fd5b813561028f81610d02565b60008060408385031215610d4757600080fd5b823567ffffffffffffffff811115610d5e57600080fd5b830160a08186031215610d7057600080fd5b946020939093013593505050565b600080600060608486031215610d9357600080fd5b8335610d9e81610d02565b92506020840135610dae81610d02565b929592945050506040919091013590565b6000808335601e19843603018112610dd657600080fd5b83018035915067ffffffffffffffff821115610df157600080fd5b602001915036819003821315610e0657600080fd5b9250929050565b634e487b7160e01b600052604160045260246000fd5b600060c08284031215610e3557600080fd5b60405160c0810181811067ffffffffffffffff82111715610e5857610e58610e0d565b6040528235610e6681610d02565b81526020830135610e7681610d02565b60208201526040830135610e8981610d02565b60408201526060830135610e9c81610d02565b6060820152608083013562ffffff81168114610eb757600080fd5b608082015260a0928301359281019290925250919050565b634e487b7160e01b600052603260045260246000fd5b60005b83811015610f00578181015183820152602001610ee8565b50506000910152565b60008151808452610f21816020860160208601610ee5565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b85811015610f7d578284038952610f6b848351610f09565b98850198935090840190600101610f53565b5091979650505050505050565b606081526000610f9d6060830186610f09565b8281036020840152610faf8186610f35565b915050826040830152949350505050565b60008251610fd2818460208701610ee5565b9190910192915050565b600060208284031215610fee57600080fd5b5051919050565b60018060a01b038416815282602082015260606040820152600061101c6060830184610f09565b95945050505050565b60006020828403121561103757600080fd5b815167ffffffffffffffff8082111561104f57600080fd5b818401915084601f83011261106357600080fd5b81518181111561107557611075610e0d565b604051601f8201601f19908116603f0116810190838211818310171561109d5761109d610e0d565b816040528281528760208487010111156110b657600080fd5b6110c7836020830160208801610ee5565b979650505050505050565b602081526000825160018060a01b038082511660208501528060208301511660408501528060408301511660608501528060608301511660808501525062ffffff60808201511660a084015260a081015160c084015250602083015161113c60e084018215159052565b5060408301516001600160801b039081166101008401526060840151166101208301526080830151610140808401526109e2610160840182610f09565b60408152600061118c6040830185610f09565b828103602084015261101c8185610f3556fea2646970667358221220d849c94dc4307e00dc2e8e1e6457f927f6a3d2eb2b178c22d56078f9495b13df64736f6c634300081300330000000000000000000000005ce9c2e3e803712e6fec5368968b61a55d851cdf0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd000000000000000000000000768caf810ea58f32054da66ba951c14ca998d19f
Deployed ByteCode
0x60806040526004361061008a5760003560e01c8063704b6c0211610059578063704b6c0214610146578063960ae66b14610166578063c31c9c071461018e578063cea9d26f146101c2578063f851a440146101e257600080fd5b806304824e701461009657806325dd2dc4146100b85780632b47da52146100de57806354fd4d501461012a57600080fd5b3661009157005b600080fd5b3480156100a257600080fd5b506100b66100b1366004610d17565b610202565b005b6100cb6100c6366004610d34565b61023a565b6040519081526020015b60405180910390f35b3480156100ea57600080fd5b506101127f000000000000000000000000768caf810ea58f32054da66ba951c14ca998d19f81565b6040516001600160a01b0390911681526020016100d5565b34801561013657600080fd5b50604051600181526020016100d5565b34801561015257600080fd5b506100b6610161366004610d17565b610296565b34801561017257600080fd5b506101127331c2f6fcff4f8759b3bd5bf0e1084a055615c76881565b34801561019a57600080fd5b506101127f0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd81565b3480156101ce57600080fd5b506100b66101dd366004610d7e565b610351565b3480156101ee57600080fd5b50600054610112906001600160a01b031681565b6000546001600160a01b0316331461022d57604051637bfa4b9f60e01b815260040160405180910390fd5b610237814761038c565b50565b6000336001600160a01b037f0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd16146102855760405163db478f2960e01b815260040160405180910390fd5b61028f83836103b8565b9392505050565b6000546001600160a01b031633146102c157604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b0381166102e85760405163e6c4247b60e01b815260040160405180910390fd5b600054604080516001600160a01b03928316815291831660208301527f101b8081ff3b56bbf45deb824d86a3b0fd38b7e3dd42421105cf8abe9106db0b910160405180910390a1600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b0316331461037c57604051637bfa4b9f60e01b815260040160405180910390fd5b610387838383610780565b505050565b600080600080600085875af19050806103875760405163b12d13eb60e01b815260040160405180910390fd5b6000806103c86080850185610dbf565b8101906103d59190610e23565b60408101519091506001600160a01b03161561046c5760408181015190516329e899d960e01b81526001600160a01b0391821660048201527f000000000000000000000000768caf810ea58f32054da66ba951c14ca998d19f909116906329e899d99060240160006040518083038186803b15801561045357600080fd5b505afa158015610467573d6000803e3d6000fd5b505050505b60008061047f6060870160408801610d17565b6001600160a01b0316146105b0576104db6104a06060870160408801610d17565b7f0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd7331c2f6fcff4f8759b3bd5bf0e1084a055615c7686107dd565b905080610510576105106104f56060870160408801610d17565b7331c2f6fcff4f8759b3bd5bf0e1084a055615c768866108b9565b6105ae7331c2f6fcff4f8759b3bd5bf0e1084a055615c768600061053a6060890160408a01610d17565b61054a60408a0160208b01610d17565b6040516001600160a01b0392831660248201529082166044820152908816606482015265ffffffffffff4216608482015260a4015b60408051601f198184030181529190526020810180516001600160e01b03166387517c4560e01b179052610948565b505b60006105bd8684876109ea565b60408051600180825281830190925291925060009190816020015b60608152602001906001900390816105d8579050509050818160008151811061060357610603610ecf565b602090810291909101015260008061062160608a0160408b01610d17565b6001600160a01b031614610636576000610638565b865b90506106af61064d60408a0160208b01610d17565b604051600160fc1b6020820152839060210160408051601f19818403018152908290526106809187904290602401610f8a565b60408051601f198184030181529190526020810180516001600160e01b0316630d64d59360e21b179052610948565b50600092506106c79150506060870160408801610d17565b6001600160a01b0316146107755780610709576107096106ed6060870160408801610d17565b7331c2f6fcff4f8759b3bd5bf0e1084a055615c76860006108b9565b6107737331c2f6fcff4f8759b3bd5bf0e1084a055615c76860006107336060890160408a01610d17565b61074360408a0160208b01610d17565b6040516001600160a01b03928316602482015291166044820152600060648201819052608482015260a40161057f565b505b506000949350505050565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806107d7576040516312171d8360e31b815260040160405180910390fd5b50505050565b6040516001600160a01b0383811660248301528281166044830152600091829182919087169060640160408051601f198184030181529181526020820180516001600160e01b0316636eb1769f60e11b1790525161083b9190610fc0565b600060405180830381855afa9150503d8060008114610876576040519150601f19603f3d011682016040523d82523d6000602084013e61087b565b606091505b509150915081801561088f57506020815110155b80156108af5750600019818060200190518101906108ad9190610fdc565b145b9695505050505050565b604051630deaa3ed60e31b81526001600160a01b0384811660048301528381166024830152604482018390527f0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd1690636f551f6890606401600060405180830381600087803b15801561092b57600080fd5b505af115801561093f573d6000803e3d6000fd5b50505050505050565b60405163df01653760e01b81526060906001600160a01b037f0000000000000000000000007941808b1d3f76786aa66b72d74989310995afbd169063df0165379061099b90879087908790600401610ff5565b6000604051808303816000875af11580156109ba573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109e29190810190611025565b949350505050565b60606000806109fe60808701878501610d17565b6001600160a01b0316610a176060880160408901610d17565b6001600160a01b031610610a4a57610a356080870160608801610d17565b610a456060880160408901610d17565b610a6a565b610a5a6060870160408801610d17565b610a6a6080880160608901610d17565b909250905060006001600160a01b038316610a8b6060890160408a01610d17565b60408051600360f91b6020820152600360fa1b6021820152600f60f81b602282015281516003818303810182526023830181815260a384019094526001600160a01b0394909416949094149450600092906043015b6060815260200190600190039081610ae057905050905060006040518060a001604052806040518060c00160405280896001600160a01b03168152602001886001600160a01b031681526020018c604001516001600160a01b031681526020018c606001516001600160a01b031681526020018c6080015162ffffff1681526020018c60a0015181525081526020018515158152602001896001600160801b0316815260200160006001600160801b03168152602001600067ffffffffffffffff811115610bb057610bb0610e0d565b6040519080825280601f01601f191660200182016040528015610bda576020820181803683370190505b50815250905080604051602001610bf191906110d2565b60405160208183030381529060405282600081518110610c1357610c13610ecf565b6020908102919091010152610c2e60608b0160408c01610d17565b604080516001600160a01b039092166020830152810189905260600160405160208183030381529060405282600181518110610c6c57610c6c610ecf565b6020908102919091010152610c8760808b0160608c01610d17565b604080516001600160a01b03909216602083015260009082015260600160405160208183030381529060405282600281518110610cc657610cc6610ecf565b60200260200101819052508282604051602001610ce4929190611179565b60405160208183030381529060405296505050505050509392505050565b6001600160a01b038116811461023757600080fd5b600060208284031215610d2957600080fd5b813561028f81610d02565b60008060408385031215610d4757600080fd5b823567ffffffffffffffff811115610d5e57600080fd5b830160a08186031215610d7057600080fd5b946020939093013593505050565b600080600060608486031215610d9357600080fd5b8335610d9e81610d02565b92506020840135610dae81610d02565b929592945050506040919091013590565b6000808335601e19843603018112610dd657600080fd5b83018035915067ffffffffffffffff821115610df157600080fd5b602001915036819003821315610e0657600080fd5b9250929050565b634e487b7160e01b600052604160045260246000fd5b600060c08284031215610e3557600080fd5b60405160c0810181811067ffffffffffffffff82111715610e5857610e58610e0d565b6040528235610e6681610d02565b81526020830135610e7681610d02565b60208201526040830135610e8981610d02565b60408201526060830135610e9c81610d02565b6060820152608083013562ffffff81168114610eb757600080fd5b608082015260a0928301359281019290925250919050565b634e487b7160e01b600052603260045260246000fd5b60005b83811015610f00578181015183820152602001610ee8565b50506000910152565b60008151808452610f21816020860160208601610ee5565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b85811015610f7d578284038952610f6b848351610f09565b98850198935090840190600101610f53565b5091979650505050505050565b606081526000610f9d6060830186610f09565b8281036020840152610faf8186610f35565b915050826040830152949350505050565b60008251610fd2818460208701610ee5565b9190910192915050565b600060208284031215610fee57600080fd5b5051919050565b60018060a01b038416815282602082015260606040820152600061101c6060830184610f09565b95945050505050565b60006020828403121561103757600080fd5b815167ffffffffffffffff8082111561104f57600080fd5b818401915084601f83011261106357600080fd5b81518181111561107557611075610e0d565b604051601f8201601f19908116603f0116810190838211818310171561109d5761109d610e0d565b816040528281528760208487010111156110b657600080fd5b6110c7836020830160208801610ee5565b979650505050505050565b602081526000825160018060a01b038082511660208501528060208301511660408501528060408301511660608501528060608301511660808501525062ffffff60808201511660a084015260a081015160c084015250602083015161113c60e084018215159052565b5060408301516001600160801b039081166101008401526060840151166101208301526080830151610140808401526109e2610160840182610f09565b60408152600061118c6040830185610f09565b828103602084015261101c8185610f3556fea2646970667358221220d849c94dc4307e00dc2e8e1e6457f927f6a3d2eb2b178c22d56078f9495b13df64736f6c63430008130033