Contract detail
RobinLimitSwap
Live Robinhood Chain account facts for 0x341efb45…fb99ca75 with at least 1 observed transactions. The history total is updating as indexing continues.
- Address type
- Smart contract
- Transactions
- 1+ observed
- First indexed block
- #668923
- Latest indexed block
- #668923
- History coverage
- Recent activity available; total is catching up
- Emitted events
- 1
- Verification
- Verified
- Compiler
- v0.8.24+commit.e11b9ed9
- Verification match
- partial
- Creation transaction
- 0x6e74d5a6…a4d28621
- Created at block
- #668923
- Language
- solidity
- EVM version
- cancun
- License
- none
- Compiler optimization
- Enabled · 200 runs
Related activity
Transactions
| Transaction | Block | From | To | Value (wei) |
|---|---|---|---|---|
| 0xbc4f11c9…4e1f7cb6 | #4886528 | 0x2cecee82…f74aad47 | 0x341efb45…fb99ca75 | 0 |
Verified contract
Contract source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @notice SwapRouter02-style exact-input (no deadline field) used on Robinhood Chain 4663.
interface ISwapRouter02 {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;
}
/// @title RobinLimitSwap
/// @notice Limit-order fill router for RobinSwap — skims 0.2% of input to treasury, swaps the rest on Uni V3.
/// Market swaps bypass this contract and go direct to SwapRouter02 (no platform fee).
contract RobinLimitSwap is Ownable {
using SafeERC20 for IERC20;
uint16 public constant FEE_BPS = 20; // 0.2%
uint256 internal constant BPS = 10_000;
address public treasury;
address public immutable router;
address public immutable weth;
uint24 public immutable poolFee;
event LimitSwap(
address indexed user,
address indexed tokenIn,
address indexed tokenOut,
uint256 amountIn,
uint256 feeTaken,
uint256 amountOut
);
event TreasurySet(address treasury);
constructor(address treasury_, address router_, address weth_, uint24 poolFee_, address owner_) Ownable(owner_) {
require(treasury_ != address(0) && router_ != address(0) && weth_ != address(0), "zero");
treasury = treasury_;
router = router_;
weth = weth_;
poolFee = poolFee_;
}
function setTreasury(address t) external onlyOwner {
require(t != address(0), "zero");
treasury = t;
emit TreasurySet(t);
}
/// @notice Limit fill entrypoint. Pass `weth` as tokenIn with `msg.value == amountIn` for native ETH input.
/// Set `unwrapToEth` when tokenOut is WETH and the user should receive native ETH.
function swapExactInLimit(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address recipient,
bool unwrapToEth
) external payable returns (uint256 amountOut) {
require(recipient != address(0), "recipient");
require(amountIn > 0, "amount");
uint256 fee = (amountIn * FEE_BPS) / BPS;
uint256 swapIn = amountIn - fee;
require(swapIn > 0, "fee");
if (tokenIn == weth) {
require(msg.value == amountIn, "eth mismatch");
if (fee > 0) {
(bool ok,) = treasury.call{value: fee}("");
require(ok, "fee eth");
}
require(!unwrapToEth, "eth in eth out");
amountOut = ISwapRouter02(router).exactInputSingle{value: swapIn}(
ISwapRouter02.ExactInputSingleParams({
tokenIn: weth,
tokenOut: tokenOut,
fee: poolFee,
recipient: recipient,
amountIn: swapIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
})
);
} else {
require(msg.value == 0, "no eth");
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
if (fee > 0) IERC20(tokenIn).safeTransfer(treasury, fee);
IERC20(tokenIn).forceApprove(router, swapIn);
if (unwrapToEth) {
require(tokenOut == weth, "unwrap");
bytes[] memory calls = new bytes[](2);
calls[0] = abi.encodeCall(
ISwapRouter02.exactInputSingle,
ISwapRouter02.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: weth,
fee: poolFee,
recipient: router,
amountIn: swapIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
})
);
calls[1] = abi.encodeCall(ISwapRouter02.unwrapWETH9, (amountOutMin, recipient));
ISwapRouter02(router).multicall(calls);
amountOut = 0; // unwrap path — amount not returned by multicall here
} else {
amountOut = ISwapRouter02(router).exactInputSingle(
ISwapRouter02.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: poolFee,
recipient: recipient,
amountIn: swapIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
})
);
}
}
emit LimitSwap(msg.sender, tokenIn, tokenOut, amountIn, fee, amountOut);
}
receive() external payable {}
}