SatoSwapRouter
0x821841dbd64cced4aab7004eaf0cda71e5bc7741
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
3,248 bytes
ABI entries
12
1 read · 3 write
License
none
Contract information
- Address
- 0x821841dbd64cced4aab7004eaf0cda71e5bc7741
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xd3a962DF31…B254c28a91
- Creation tx
- 0x09a1af76d9…fa77dc0e16
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (1)
POOL_MANAGER() → address
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "poolManager",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BadMsgValue",
"type": "error"
},
{
"inputs": [],
"name": "InvalidDelta",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "Slippage",
"type": "error"
},
{
"inputs": [],
"name": "TransferFromFailed",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAmount",
"type": "error"
},
{
"inputs": [],
"name": "POOL_MANAGER",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "key",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "minRatoOut",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "buy",
"outputs": [
{
"internalType": "uint256",
"name": "ratoOut",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "key",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "ratoIn",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "minEthOut",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "sell",
"outputs": [
{
"internalType": "uint256",
"name": "ethOut",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "rawData",
"type": "bytes"
}
],
"name": "unlockCallback",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {IERC20Minimal} from "v4-core/interfaces/external/IERC20Minimal.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {SwapParams} from "v4-core/types/PoolOperation.sol";
import {BalanceDelta, BalanceDeltaLibrary} from "v4-core/types/BalanceDelta.sol";
import {Currency} from "v4-core/types/Currency.sol";
/// @notice Minimal router for the SATO hook. It pre-settles the exact input into the v4 PoolManager,
/// calls swap through the hook-backed pool, and takes the output back to the recipient.
contract SatoSwapRouter {
using BalanceDeltaLibrary for BalanceDelta;
uint160 internal constant MIN_SQRT_PRICE_PLUS_ONE = 4295128740;
uint160 internal constant MAX_SQRT_PRICE_MINUS_ONE = 1461446703485210103287273052203988822378723970341;
IPoolManager public immutable POOL_MANAGER;
enum Action {
Buy,
Sell
}
struct CallbackData {
Action action;
PoolKey key;
address payer;
address recipient;
uint256 amountIn;
uint256 minAmountOut;
}
error NotPoolManager();
error BadMsgValue();
error ZeroAmount();
error Slippage();
error InvalidDelta();
error TransferFromFailed();
constructor(IPoolManager poolManager) {
POOL_MANAGER = poolManager;
}
function buy(PoolKey calldata key, uint256 minRatoOut, address recipient)
external
payable
returns (uint256 ratoOut)
{
if (msg.value == 0) revert ZeroAmount();
if (recipient == address(0)) recipient = msg.sender;
CallbackData memory data = CallbackData({
action: Action.Buy,
key: key,
payer: msg.sender,
recipient: recipient,
amountIn: msg.value,
minAmountOut: minRatoOut
});
ratoOut = abi.decode(POOL_MANAGER.unlock(abi.encode(data)), (uint256));
}
function sell(PoolKey calldata key, uint256 ratoIn, uint256 minEthOut, address recipient)
external
returns (uint256 ethOut)
{
if (ratoIn == 0) revert ZeroAmount();
if (recipient == address(0)) recipient = msg.sender;
CallbackData memory data = CallbackData({
action: Action.Sell,
key: key,
payer: msg.sender,
recipient: recipient,
amountIn: ratoIn,
minAmountOut: minEthOut
});
ethOut = abi.decode(POOL_MANAGER.unlock(abi.encode(data)), (uint256));
}
function unlockCallback(bytes calldata rawData) external returns (bytes memory) {
if (msg.sender != address(POOL_MANAGER)) revert NotPoolManager();
CallbackData memory data = abi.decode(rawData, (CallbackData));
uint256 amountOut;
if (data.action == Action.Buy) {
amountOut = _buy(data);
} else {
amountOut = _sell(data);
}
if (amountOut < data.minAmountOut) revert Slippage();
return abi.encode(amountOut);
}
function _buy(CallbackData memory data) internal returns (uint256 ratoOut) {
if (address(this).balance < data.amountIn) revert BadMsgValue();
POOL_MANAGER.settle{value: data.amountIn}();
BalanceDelta delta = POOL_MANAGER.swap(
data.key,
SwapParams({
zeroForOne: true,
amountSpecified: -int256(data.amountIn),
sqrtPriceLimitX96: MIN_SQRT_PRICE_PLUS_ONE
}),
abi.encode(data.payer)
);
int128 amount1 = delta.amount1();
if (amount1 <= 0) revert InvalidDelta();
ratoOut = uint128(amount1);
POOL_MANAGER.take(data.key.currency1, data.recipient, ratoOut);
}
function _sell(CallbackData memory data) internal returns (uint256 ethOut) {
Currency rato = data.key.currency1;
POOL_MANAGER.sync(rato);
bool success = IERC20Minimal(Currency.unwrap(rato)).transferFrom(data.payer, address(POOL_MANAGER), data.amountIn);
if (!success) revert TransferFromFailed();
POOL_MANAGER.settle();
BalanceDelta delta = POOL_MANAGER.swap(
data.key,
SwapParams({
zeroForOne: false,
amountSpecified: -int256(data.amountIn),
sqrtPriceLimitX96: MAX_SQRT_PRICE_MINUS_ONE
}),
abi.encode(data.payer)
);
int128 amount0 = delta.amount0();
if (amount0 <= 0) revert InvalidDelta();
ethOut = uint128(amount0);
POOL_MANAGER.take(data.key.currency0, data.recipient, ethOut);
}
receive() external payable {}
}
Chain explorer2142msChain node71ms