RobinhoodPostBondRouter
0x04eda72fa05772bea3d516f446b5afbf55b74cdf
Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
8,292 bytes
ABI entries
26
4 read · 3 write
License
none
Contract information
- Address
- 0x04eda72fa05772bea3d516f446b5afbf55b74cdf
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.28+commit.7893614a
- Optimization
- Enabled
- Creator
- 0x4D8E431f3d…248678933B
- Creation tx
- 0x0aa59f5b44…e34433ef3d
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (4)
PERMIT2() → address
POOL_MANAGER() → address
UNIVERSAL_ROUTER() → address
WETH() → address
Events (2)
PostBondExactOutputSwapPostBondSwap
ABI
[
{
"inputs": [
{
"internalType": "contract IRobinhoodHelperUniversalRouter",
"name": "universalRouter_",
"type": "address"
},
{
"internalType": "contract IRobinhoodHelperPermit2",
"name": "permit2_",
"type": "address"
},
{
"internalType": "contract IRobinhoodHelperWETH",
"name": "weth_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "target",
"type": "address"
}
],
"name": "AddressEmptyCode",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "AddressInsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "FailedInnerCall",
"type": "error"
},
{
"inputs": [],
"name": "InvalidMinimumOutput",
"type": "error"
},
{
"inputs": [],
"name": "InvalidNativeRoute",
"type": "error"
},
{
"inputs": [],
"name": "InvalidNativeValue",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPoolKey",
"type": "error"
},
{
"inputs": [],
"name": "InvalidRecipient",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSwapAmount",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSwapDependency",
"type": "error"
},
{
"inputs": [],
"name": "NativeTransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maximum",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "actual",
"type": "uint256"
}
],
"name": "SwapInputTooHigh",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minimum",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "actual",
"type": "uint256"
}
],
"name": "SwapOutputTooLow",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "input",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "output",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "nativeIn",
"type": "bool"
},
{
"indexed": false,
"internalType": "bool",
"name": "nativeOut",
"type": "bool"
}
],
"name": "PostBondExactOutputSwap",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "input",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "output",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "nativeIn",
"type": "bool"
},
{
"indexed": false,
"internalType": "bool",
"name": "nativeOut",
"type": "bool"
}
],
"name": "PostBondSwap",
"type": "event"
},
{
"inputs": [],
"name": "PERMIT2",
"outputs": [
{
"internalType": "contract IRobinhoodHelperPermit2",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POOL_MANAGER",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "UNIVERSAL_ROUTER",
"outputs": [
{
"internalType": "contract IRobinhoodHelperUniversalRouter",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "WETH",
"outputs": [
{
"internalType": "contract IRobinhoodHelperWETH",
"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": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "uint128",
"name": "amountIn",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "minAmountOut",
"type": "uint128"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "bool",
"name": "nativeIn",
"type": "bool"
},
{
"internalType": "bool",
"name": "nativeOut",
"type": "bool"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "swapExactInput",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"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": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "uint128",
"name": "amountIn",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "minAmountOut",
"type": "uint128"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "bool",
"name": "nativeIn",
"type": "bool"
},
{
"internalType": "bool",
"name": "nativeOut",
"type": "bool"
},
{
"internalType": "uint256",
"name": "minHopPriceX36",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "hookData",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "swapExactInputAdvanced",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"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": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "uint128",
"name": "amountOut",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "maxAmountIn",
"type": "uint128"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "bool",
"name": "nativeIn",
"type": "bool"
},
{
"internalType": "bool",
"name": "nativeOut",
"type": "bool"
},
{
"internalType": "uint256",
"name": "minHopPriceX36",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "hookData",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct RobinhoodPostBondRouter.ExactOutputRequest",
"name": "request",
"type": "tuple"
}
],
"name": "swapExactOutput",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {
IRobinhoodHelperPermit2,
IRobinhoodHelperUniversalRouter,
IRobinhoodHelperWETH
} from "./RobinhoodHelperInterfaces.sol";
import {RobinhoodV4SwapExecutor} from "./RobinhoodV4SwapExecutor.sol";
/// @notice User-facing exact-input router for graduated Robinhood launch pools.
/// @dev Handles native ETH wrapping/unwrapping while retaining wARENA as ERC20.
contract RobinhoodPostBondRouter is RobinhoodV4SwapExecutor, ReentrancyGuard {
using SafeERC20 for IERC20;
IRobinhoodHelperWETH public immutable WETH;
struct ExactOutputRequest {
PoolKey key;
bool zeroForOne;
uint128 amountOut;
uint128 maxAmountIn;
address recipient;
bool nativeIn;
bool nativeOut;
uint256 minHopPriceX36;
bytes hookData;
uint256 deadline;
}
error InvalidRecipient();
error InvalidNativeValue();
error InvalidNativeRoute();
error NativeTransferFailed();
event PostBondSwap(
address indexed sender,
address indexed recipient,
address indexed input,
address output,
uint256 amountIn,
uint256 amountOut,
bool nativeIn,
bool nativeOut
);
event PostBondExactOutputSwap(
address indexed sender,
address indexed recipient,
address indexed input,
address output,
uint256 amountIn,
uint256 amountOut,
bool nativeIn,
bool nativeOut
);
constructor(
IRobinhoodHelperUniversalRouter universalRouter_,
IRobinhoodHelperPermit2 permit2_,
IRobinhoodHelperWETH weth_
) RobinhoodV4SwapExecutor(universalRouter_, permit2_) {
if (address(weth_) == address(0) || address(weth_).code.length == 0) {
revert InvalidSwapDependency();
}
WETH = weth_;
}
function swapExactInput(
PoolKey calldata key,
bool zeroForOne,
uint128 amountIn,
uint128 minAmountOut,
address recipient,
bool nativeIn,
bool nativeOut,
uint256 deadline
) external payable nonReentrant returns (uint256 amountOut) {
return _swapExactInput(
key, zeroForOne, amountIn, minAmountOut, recipient, nativeIn,
nativeOut, 0, bytes(""), deadline
);
}
function swapExactInputAdvanced(
PoolKey calldata key,
bool zeroForOne,
uint128 amountIn,
uint128 minAmountOut,
address recipient,
bool nativeIn,
bool nativeOut,
uint256 minHopPriceX36,
bytes calldata hookData,
uint256 deadline
) external payable nonReentrant returns (uint256 amountOut) {
return _swapExactInput(
key, zeroForOne, amountIn, minAmountOut, recipient, nativeIn,
nativeOut, minHopPriceX36, hookData, deadline
);
}
function _swapExactInput(
PoolKey calldata key,
bool zeroForOne,
uint128 amountIn,
uint128 minAmountOut,
address recipient,
bool nativeIn,
bool nativeOut,
uint256 minHopPriceX36,
bytes memory hookData,
uint256 deadline
) internal returns (uint256 amountOut) {
if (recipient == address(0)) revert InvalidRecipient();
address input = Currency.unwrap(zeroForOne ? key.currency0 : key.currency1);
address output = Currency.unwrap(zeroForOne ? key.currency1 : key.currency0);
if (nativeIn) {
if (input != address(WETH)) revert InvalidNativeRoute();
if (msg.value != amountIn) revert InvalidNativeValue();
WETH.deposit{value: amountIn}();
} else {
if (msg.value != 0) revert InvalidNativeValue();
IERC20(input).safeTransferFrom(msg.sender, address(this), amountIn);
}
if (nativeOut && output != address(WETH)) revert InvalidNativeRoute();
amountOut = _swapExactInputSingle(
key, zeroForOne, amountIn, minAmountOut, minHopPriceX36,
hookData, deadline
);
if (nativeOut) {
WETH.withdraw(amountOut);
(bool success,) = payable(recipient).call{value: amountOut}("");
if (!success) revert NativeTransferFailed();
} else {
IERC20(output).safeTransfer(recipient, amountOut);
}
emit PostBondSwap(
msg.sender,
recipient,
input,
output,
amountIn,
amountOut,
nativeIn,
nativeOut
);
}
function swapExactOutput(ExactOutputRequest calldata request)
external
payable
nonReentrant
returns (uint256 amountIn)
{
if (request.recipient == address(0)) revert InvalidRecipient();
address input = Currency.unwrap(
request.zeroForOne ? request.key.currency0 : request.key.currency1
);
address output = Currency.unwrap(
request.zeroForOne ? request.key.currency1 : request.key.currency0
);
if (request.nativeIn) {
if (input != address(WETH)) revert InvalidNativeRoute();
if (msg.value != request.maxAmountIn) revert InvalidNativeValue();
WETH.deposit{value: request.maxAmountIn}();
} else {
if (msg.value != 0) revert InvalidNativeValue();
IERC20(input).safeTransferFrom(
msg.sender, address(this), request.maxAmountIn
);
}
if (request.nativeOut && output != address(WETH)) revert InvalidNativeRoute();
amountIn = _swapExactOutputSingle(
RobinhoodExactOutputSingleParams({
poolKey: request.key,
zeroForOne: request.zeroForOne,
amountOut: request.amountOut,
amountInMaximum: request.maxAmountIn,
minHopPriceX36: request.minHopPriceX36,
hookData: request.hookData
}),
request.deadline
);
uint256 refund = uint256(request.maxAmountIn) - amountIn;
if (request.nativeIn) {
if (refund != 0) {
WETH.withdraw(refund);
(bool refundSuccess,) = payable(msg.sender).call{value: refund}("");
if (!refundSuccess) revert NativeTransferFailed();
}
} else if (refund != 0) {
IERC20(input).safeTransfer(msg.sender, refund);
}
if (request.nativeOut) {
WETH.withdraw(request.amountOut);
(bool success,) = payable(request.recipient).call{value: request.amountOut}("");
if (!success) revert NativeTransferFailed();
} else {
IERC20(output).safeTransfer(request.recipient, request.amountOut);
}
emit PostBondExactOutputSwap(
msg.sender, request.recipient, input, output, amountIn,
request.amountOut, request.nativeIn, request.nativeOut
);
}
}
Chain explorer1587msChain node91ms