SwapRouter
0x56b2e2ee0487223663b458cf3492bccbe04536f2
Verification
Verified
v0.7.6+commit.7338295f
Type
Contract
9,855 bytes
ABI entries
20
3 read · 15 write
License
none
Contract information
- Address
- 0x56b2e2ee0487223663b458cf3492bccbe04536f2
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.7.6+commit.7338295f
- Optimization
- Enabled
- Creator
- 0x5F378c7C4D…a8a688AD40
- Creation tx
- 0xfe1a1420a7…dd3d1c2114
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (3)
WETH9() → address
deployer() → address
factory() → address
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_deployer",
"type": "address"
},
{
"internalType": "address",
"name": "_factory",
"type": "address"
},
{
"internalType": "address",
"name": "_WETH9",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "WETH9",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "deployer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountOutMinimum",
"type": "uint256"
}
],
"internalType": "struct ISwapRouter.ExactInputParams",
"name": "params",
"type": "tuple"
}
],
"name": "exactInput",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountOutMinimum",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct ISwapRouter.ExactInputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "exactInputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountInMaximum",
"type": "uint256"
}
],
"internalType": "struct ISwapRouter.ExactOutputParams",
"name": "params",
"type": "tuple"
}
],
"name": "exactOutput",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountInMaximum",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct ISwapRouter.ExactOutputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "exactOutputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes[]",
"name": "data",
"type": "bytes[]"
}
],
"name": "multicall",
"outputs": [
{
"internalType": "bytes[]",
"name": "results",
"type": "bytes[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int256",
"name": "amount0Delta",
"type": "int256"
},
{
"internalType": "int256",
"name": "amount1Delta",
"type": "int256"
},
{
"internalType": "bytes",
"name": "_data",
"type": "bytes"
}
],
"name": "pancakeV3SwapCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "refundETH",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "selfPermit",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expiry",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "selfPermitAllowed",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expiry",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "selfPermitAllowedIfNecessary",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "selfPermitIfNecessary",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountMinimum",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "sweepToken",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountMinimum",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "feeBips",
"type": "uint256"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
}
],
"name": "sweepTokenWithFee",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amountMinimum",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "unwrapWETH9",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amountMinimum",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "feeBips",
"type": "uint256"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
}
],
"name": "unwrapWETH9WithFee",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import "../core/libraries/cl/SafeCast.sol";
import "../core/libraries/cl/TickMath.sol";
import "../core/interfaces/ICLPool.sol";
import "./interfaces/ISwapRouter.sol";
import "./base/PeripheryImmutableState.sol";
import "./base/PeripheryValidation.sol";
import "./base/PeripheryPaymentsWithFee.sol";
import "./base/Multicall.sol";
import "./base/SelfPermit.sol";
import "./libraries/Path.sol";
import "./libraries/PoolAddress.sol";
import "./libraries/CallbackValidation.sol";
import "./interfaces/external/IWETH9.sol";
/// @title Apex CL Swap Router
/// @notice Router for stateless execution of swaps against Apex CL
contract SwapRouter is
ISwapRouter,
PeripheryImmutableState,
PeripheryValidation,
PeripheryPaymentsWithFee,
Multicall,
SelfPermit
{
using Path for bytes;
using SafeCast for uint256;
/// @dev Used as the placeholder value for amountInCached, because the computed amount in for an exact output swap
/// can never actually be this value
uint256 private constant DEFAULT_AMOUNT_IN_CACHED = type(uint256).max;
/// @dev Transient storage variable used for returning the computed amount in for an exact output swap.
uint256 private amountInCached = DEFAULT_AMOUNT_IN_CACHED;
constructor(address _deployer, address _factory, address _WETH9)
PeripheryImmutableState(_deployer, _factory, _WETH9)
{}
/// @dev Returns the pool for the given token pair and fee. The pool contract may or may not exist.
function getPool(address tokenA, address tokenB, uint24 fee) private view returns (ICLPool) {
return ICLPool(PoolAddress.computeAddress(deployer, PoolAddress.getPoolKey(tokenA, tokenB, fee)));
}
struct SwapCallbackData {
bytes path;
address payer;
}
/// @inheritdoc IPancakeV3SwapCallback
function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported
SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
(address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
CallbackValidation.verifyCallback(deployer, tokenIn, tokenOut, fee);
(bool isExactInput, uint256 amountToPay) =
amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
if (isExactInput) {
pay(tokenIn, data.payer, msg.sender, amountToPay);
} else {
// either initiate the next swap or pay
if (data.path.hasMultiplePools()) {
data.path = data.path.skipToken();
exactOutputInternal(amountToPay, msg.sender, 0, data);
} else {
amountInCached = amountToPay;
tokenIn = tokenOut; // swap in/out because exact output swaps are reversed
pay(tokenIn, data.payer, msg.sender, amountToPay);
}
}
}
/// @dev Performs a single exact input swap
function exactInputInternal(
uint256 amountIn,
address recipient,
uint160 sqrtPriceLimitX96,
SwapCallbackData memory data
) private returns (uint256 amountOut) {
// allow swapping to the router address with address 0
if (recipient == address(0)) recipient = address(this);
(address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
bool zeroForOne = tokenIn < tokenOut;
(int256 amount0, int256 amount1) = getPool(tokenIn, tokenOut, fee)
.swap(
recipient,
zeroForOne,
amountIn.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);
return uint256(-(zeroForOne ? amount1 : amount0));
}
/// @inheritdoc ISwapRouter
function exactInputSingle(ExactInputSingleParams calldata params)
external
payable
override
checkDeadline(params.deadline)
returns (uint256 amountOut)
{
amountOut = exactInputInternal(
params.amountIn,
params.recipient,
params.sqrtPriceLimitX96,
SwapCallbackData({path: abi.encodePacked(params.tokenIn, params.fee, params.tokenOut), payer: msg.sender})
);
require(amountOut >= params.amountOutMinimum, "Too little received");
}
/// @inheritdoc ISwapRouter
function exactInput(ExactInputParams memory params)
external
payable
override
checkDeadline(params.deadline)
returns (uint256 amountOut)
{
address payer = msg.sender; // msg.sender pays for the first hop
while (true) {
bool hasMultiplePools = params.path.hasMultiplePools();
// the outputs of prior swaps become the inputs to subsequent ones
params.amountIn = exactInputInternal(
params.amountIn,
hasMultiplePools ? address(this) : params.recipient, // for intermediate swaps, this contract custodies
0,
SwapCallbackData({
path: params.path.getFirstPool(), // only the first pool in the path is necessary
payer: payer
})
);
// decide whether to continue or terminate
if (hasMultiplePools) {
payer = address(this); // at this point, the caller has paid
params.path = params.path.skipToken();
} else {
amountOut = params.amountIn;
break;
}
}
require(amountOut >= params.amountOutMinimum, "Too little received");
}
/// @dev Performs a single exact output swap
function exactOutputInternal(
uint256 amountOut,
address recipient,
uint160 sqrtPriceLimitX96,
SwapCallbackData memory data
) private returns (uint256 amountIn) {
// allow swapping to the router address with address 0
if (recipient == address(0)) recipient = address(this);
(address tokenOut, address tokenIn, uint24 fee) = data.path.decodeFirstPool();
bool zeroForOne = tokenIn < tokenOut;
(int256 amount0Delta, int256 amount1Delta) = getPool(tokenIn, tokenOut, fee)
.swap(
recipient,
zeroForOne,
-amountOut.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);
uint256 amountOutReceived;
(amountIn, amountOutReceived) = zeroForOne
? (uint256(amount0Delta), uint256(-amount1Delta))
: (uint256(amount1Delta), uint256(-amount0Delta));
// it's technically possible to not receive the full output amount,
// so if no price limit has been specified, require this possibility away
if (sqrtPriceLimitX96 == 0) require(amountOutReceived == amountOut);
}
/// @inheritdoc ISwapRouter
function exactOutputSingle(ExactOutputSingleParams calldata params)
external
payable
override
checkDeadline(params.deadline)
returns (uint256 amountIn)
{
// avoid an SLOAD by using the swap return data
amountIn = exactOutputInternal(
params.amountOut,
params.recipient,
params.sqrtPriceLimitX96,
SwapCallbackData({path: abi.encodePacked(params.tokenOut, params.fee, params.tokenIn), payer: msg.sender})
);
require(amountIn <= params.amountInMaximum, "Too much requested");
// has to be reset even though we don't use it in the single hop case
amountInCached = DEFAULT_AMOUNT_IN_CACHED;
}
/// @inheritdoc ISwapRouter
function exactOutput(ExactOutputParams calldata params)
external
payable
override
checkDeadline(params.deadline)
returns (uint256 amountIn)
{
// it's okay that the payer is fixed to msg.sender here, as they're only paying for the "final" exact output
// swap, which happens first, and subsequent swaps are paid for within nested callback frames
exactOutputInternal(
params.amountOut, params.recipient, 0, SwapCallbackData({path: params.path, payer: msg.sender})
);
amountIn = amountInCached;
require(amountIn <= params.amountInMaximum, "Too much requested");
amountInCached = DEFAULT_AMOUNT_IN_CACHED;
}
}
Chain explorer4381msChain node76ms