CurveRouterV2
0xa08278b06f6ed56d834df94f5505497bc2a899a1
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
3,867 bytes
ABI entries
9
1 read · 3 write
License
none
Contract information
- Address
- 0xa08278b06f6ed56d834df94f5505497bc2a899a1
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x26E8134eCC…f318B25158
- Creation tx
- 0xc5386868c0…c61688a027
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (1)
poolManager() → address
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "_pm",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "OnlyPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "SlippageExceeded",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAmount",
"type": "error"
},
{
"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": "minOut",
"type": "uint256"
}
],
"name": "buy",
"outputs": [
{
"internalType": "uint256",
"name": "out",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"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": "amountIn",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "minOut",
"type": "uint256"
}
],
"name": "sell",
"outputs": [
{
"internalType": "uint256",
"name": "out",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "raw",
"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 {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {Currency} from "v4-core/src/types/Currency.sol";
import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
import {SwapParams} from "v4-core/src/types/PoolOperation.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";
import {CurrencySettler} from "./CurrencySettler.sol";
import {CurveToken} from "./CurveToken.sol";
/// @notice Standard settle-AFTER swap entry for the v2 protected curve. Because
/// v2 uses normal v4 claims accounting, this is an ordinary router — no
/// settle-before hack — and any standard v4 router (incl. the Universal Router)
/// would work identically. Exact-in only: buy with ETH, sell a token amount.
contract CurveRouterV2 is IUnlockCallback {
using CurrencySettler for Currency;
IPoolManager public immutable poolManager;
error OnlyPoolManager();
error SlippageExceeded();
error ZeroAmount();
struct SwapData {
PoolKey key;
bool isBuy;
uint256 amountIn;
uint256 minOut;
address user;
}
constructor(IPoolManager _pm) {
poolManager = _pm;
}
receive() external payable {}
function buy(PoolKey calldata key, uint256 minOut) external payable returns (uint256 out) {
if (msg.value == 0) revert ZeroAmount();
out = abi.decode(
poolManager.unlock(abi.encode(SwapData(key, true, msg.value, minOut, msg.sender))), (uint256)
);
}
function sell(PoolKey calldata key, uint256 amountIn, uint256 minOut) external returns (uint256 out) {
if (amountIn == 0) revert ZeroAmount();
CurveToken(Currency.unwrap(key.currency1)).transferFrom(msg.sender, address(this), amountIn);
out = abi.decode(
poolManager.unlock(abi.encode(SwapData(key, false, amountIn, minOut, msg.sender))), (uint256)
);
}
function unlockCallback(bytes calldata raw) external returns (bytes memory) {
if (msg.sender != address(poolManager)) revert OnlyPoolManager();
SwapData memory d = abi.decode(raw, (SwapData));
// Standard v4 order: swap first, then resolve deltas.
BalanceDelta delta = poolManager.swap(
d.key,
SwapParams({
zeroForOne: d.isBuy,
amountSpecified: -int256(d.amountIn),
sqrtPriceLimitX96: d.isBuy ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1
}),
abi.encode(d.user)
);
uint256 out;
if (d.isBuy) {
// pay ETH input, take token output to the user
d.key.currency0.settle(poolManager, address(this), d.amountIn, false);
out = uint256(uint128(delta.amount1()));
if (out < d.minOut) revert SlippageExceeded();
d.key.currency1.take(poolManager, d.user, out, false);
} else {
// pay token input, take ETH output to the user
d.key.currency1.settle(poolManager, address(this), d.amountIn, false);
out = uint256(uint128(delta.amount0()));
if (out < d.minOut) revert SlippageExceeded();
d.key.currency0.take(poolManager, d.user, out, false);
}
return abi.encode(out);
}
}
Chain explorer6287msChain node100ms