Launch App

Integration Guide

Place a limit order or DCA plan from your own dApp in minutes. Approve, calculate slippage, submit order, and watch execution events.

1 · Clients Setup (Viem vs Ethers.js)

Use the client library that best fits your stack. Below is client initialization code for both Viem and Ethers.js:
setup-viem.ts
import { createPublicClient, createWalletClient, custom, http, parseAbi, parseUnits } from "viem";

export const robinhoodChain = {
  id: 4663,
  name: "Robinhood Chain",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } },
};

const publicClient = createPublicClient({ chain: robinhoodChain, transport: http() });
const wallet = createWalletClient({ chain: robinhoodChain, transport: custom(window.ethereum) });

const EXECUTOR = "0x…";                                        // your deployment
const USDG    = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";  // 6 decimals!
const TSLA    = "0x322F0929c4625eD5bAd873c95208D54E1c003b2d";
const TSLA_FEED = "0x…";                                       // from docs.chain.link
setup-ethers.js
import { ethers } from "ethers";

const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

const EXECUTOR_ADDRESS = "0x...";
const USDG_ADDRESS = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";

const EXECUTOR_ABI = [
  "function placeOrder(address tokenIn, address tokenOut, address priceFeed, uint256 amountIn, uint256 minAmountOut, int256 triggerPrice, uint8 triggerDir, uint8 orderType, uint64 interval, uint32 runs, uint64 expiry) external returns (uint256 id)",
  "function cancelOrder(uint256 id) external",
];
const ERC20_ABI = ["function approve(address spender, uint256 value) returns (bool)"];

const executorContract = new ethers.Contract(EXECUTOR_ADDRESS, EXECUTOR_ABI, signer);
const usdgContract = new ethers.Contract(USDG_ADDRESS, ERC20_ABI, signer);

2 · Calculating Slippage Out Off-chain

Instead of passing 0 as the slippage protection (which exposes user swaps to frontrunning or high pool price impact), query the Uniswap V2 Router and deduct a toll (e.g. 0.5% or 1% slippage):

calculate-slippage.ts
// Query Uniswap V2 Router to estimate stock output and apply 0.5% slippage tolerance
const routerAbi = parseAbi([
  "function getAmountsOut(uint256 amountIn, address[] calldata path) view returns (uint256[] memory amounts)",
]);
const UNISWAP_V2_ROUTER = "0x89e5db8b5aa49aa85ac63f691524311aeb649eba";

const expectedAmounts = await publicClient.readContract({
  address: UNISWAP_V2_ROUTER,
  abi: routerAbi,
  functionName: "getAmountsOut",
  args: [parseUnits("1500", 6), [USDG, TSLA]],
});

const expectedTslaOut = expectedAmounts[1];
// Compute minAmountOut (expectedTslaOut * 99.5% = expectedTslaOut * 995 / 1000)
const minAmountOut = (expectedTslaOut * 995n) / 1000n;

3 · Approve + Place Order

Call approve first, then submit the order parameter struct to the contract executor:

place-order-viem.ts
const [account] = await wallet.getAddresses();

// 1 — Approve spender (allowance of 1,500 USDG)
await wallet.writeContract({
  account, address: USDG, abi: erc20Abi,
  functionName: "approve",
  args: [EXECUTOR, parseUnits("1500", 6)],
});

// 2 — Place limit buy with dynamic slippage
await wallet.writeContract({
  account, address: EXECUTOR, abi: executorAbi,
  functionName: "placeOrder",
  args: [
    USDG, TSLA, TSLA_FEED,
    parseUnits("1500", 6),  // amountIn
    minAmountOut,           // slippage guard
    262_00000000n,          // trigger: ≤ $262 (Chainlink decimals 8)
    0,                      // triggerDir: 0 = BELOW
    0,                      // orderType: 0 = LIMIT
    0n, 1, 0n,              // interval, runs, expiry
  ],
});
place-order-ethers.js
// 1 — Approve USDG spend (1,500 USDG)
const approveTx = await usdgContract.approve(EXECUTOR_ADDRESS, ethers.utils.parseUnits("1500", 6));
await approveTx.wait();

// 2 — Place limit order
const placeTx = await executorContract.placeOrder(
  USDG_ADDRESS, TSLA, TSLA_FEED,
  ethers.utils.parseUnits("1500", 6), // amountIn (USDG decimals 6)
  minAmountOutEthers,                 // minAmountOut (slippage-adjusted stock token)
  26200000000,                        // triggerPrice: $262.00 (8 decimals)
  0,                                  // triggerDir: BELOW
  0,                                  // orderType: LIMIT
  0, 1, 0                             // interval, runs, expiry
);
await placeTx.wait();
USDG is 6 decimals while stock tokens are 18. Ensure you use parseUnits(val, 6) when setting amountIn for USDG tokens and 18 decimals for stock outputs.

4 · Cancelling Orders

Only the order's owner can cancel it. Cancelling releases no funds (as funds stay in the owner's wallet) but immediately invalidates the order's active execution eligibility:

cancel-order.ts
// Cancel order #42 using viem
await wallet.writeContract({
  account,
  address: EXECUTOR,
  abi: executorAbi,
  functionName: "cancelOrder",
  args: [42n],
});

5 · Listen for Execution

watch-fills.ts
// Listen to execution logs
publicClient.watchContractEvent({
  address: EXECUTOR,
  abi: executorAbi,
  eventName: "OrderExecuted",
  onLogs: (logs) => {
    for (const log of logs) {
      console.log(`order #${log.args.id} filled: ${log.args.amountOut} out`);
    }
  },
});

Reading Prices & Balances Correctly

reads.ts
const feedAbi = parseAbi([
  "function latestRoundData() view returns (uint80, int256, uint256, uint256, uint80)",
]);
const stockAbi = parseAbi([
  "function balanceOf(address) view returns (uint256)",
  "function uiMultiplier() view returns (uint256)",
]);

// Chainlink price — 8 decimals, ALREADY multiplier-adjusted (total return)
const [, answer] = await publicClient.readContract({
  address: TSLA_FEED, abi: feedAbi, functionName: "latestRoundData",
});
const priceUsd = Number(answer) / 1e8;

// share-equivalent balance (ERC-8056)
const [raw, multiplier] = await Promise.all([
  publicClient.readContract({ address: TSLA, abi: stockAbi, functionName: "balanceOf", args: [account] }),
  publicClient.readContract({ address: TSLA, abi: stockAbi, functionName: "uiMultiplier" }),
]);
const shares = (raw * multiplier) / 10n ** 18n;
The multiplier rule: Chainlink stock feeds on Robinhood Chain are total-return (dividends reinvested via uiMultiplier). Apply the multiplier to balances (balanceOf × uiMultiplier / 1e18), never to the feed price. Applying it twice overstates values.