Robinhood MainnetChain ID: 4663
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

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 {}
}