Build on Hookline
Put best-price swaps on Robinhood Chain inside your bot, agent, wallet or app. Earn your own fee on every swap you route, paid on-chain in the same transaction.
- Up to 0.5% of every swap, straight to your address
- Featured on the showcase and announced on X
- Your own key, higher limits, Proof of Savings for your users
One swap, paid out in one transaction
- Your user receivesto their wallet
- Hookline feeto the protocol
- Your fee, up to 0.5%to your address
What you can build
One API and one MCP server for every pool on the chain, native Uniswap and outside pools through Aggregator Hooks alike.
Trading bots
Quote, build and track swaps over plain HTTP. Every route checked, every swap simulated before you send it.
AI agents through MCP
Give any agent best-price swaps with one URL. The agent prepares, the user's wallet signs.
Wallets and frontends
Best-price swaps on Robinhood Chain inside your app, with your own fee on top.
Analytics on hook data
Every Aggregator Hook pool with its liquidity, volume and swaps, ready to chart.
Telegram bots
Swap in a chat, track to settlement, share the Proof of Savings link.
What you get
0.5%
Your own fee on every swap you route
Set partnerFeeBps on your quotes, up to 50 bps. It is paid on-chain to your payout address in the same transaction, right after the Hookline fee. No invoices, no monthly claims.
fee.partner = { bps, amount, recipient }
Featured and announced
Approved projects join the Built on Hookline showcase, and we announce them on X.
Your own key, higher limits
A builder key lifts the default 60 quotes a minute and shows your traffic on your dashboard.
Proof of Savings for your users
Every swap gets a public proof of how much more it paid than the best Uniswap route. Share it, link it, show it.
The toolkit
Everything is public. Start building before you apply; the key adds limits and your fee.
The Built on Hookline badge
Add it to your site or README. It links to this page.
<a href="https://hookline.trade/build"><img src="https://hookline.trade/badge.svg" alt="Built on Hookline" height="36"></a>Ship in 10 minutes
A whole bot in one file: quote for your wallet, approve and sign when asked, build, verify the transaction, send, track. With your key it earns your fee.
// bot.ts: best-price swaps on Robinhood Chain through Hookline. Run: npm i viem && PRIVATE_KEY=0x… API_KEY=… npx tsx bot.ts
import { createPublicClient, createWalletClient, defineChain, getAddress, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const BASE = "https://hookline.trade/api";
const API_KEY = process.env.API_KEY; // your builder key: higher limits, and your own fee
const PARTNER_FEE_BPS = 30; // 0.30% for you, paid on-chain to your payout address (max 50 bps, capped by your key)
const UNIVERSAL_ROUTER = getAddress("0x204faca1764b154221e35c0d20abb3c525710498");
const PERMIT2 = getAddress("0x000000000022d473030f116ddee9f6b43ac78ba3");
const NATIVE_ETH = "0x0000000000000000000000000000000000000000";
const robinhood = defineChain({
id: 4663,
name: "Robinhood Chain",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } },
});
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const wallet = createWalletClient({ account, chain: robinhood, transport: http() });
const client = createPublicClient({ chain: robinhood, transport: http() });
type Field = { name: string; type: string };
type Permit = {
domain: { name: string; chainId: number; verifyingContract: Hex };
types: Record<string, Field[]>;
message: { details: { token: Hex; amount: string; expiration: number; nonce: number }; spender: Hex; sigDeadline: string };
};
type Step = { kind: "approve"; token: Hex; spender: Hex; tx: { to: Hex; data: Hex } } | { kind: "permit"; token: Hex; typedData: Permit };
type Quote = { id: string; sell: { token: string; amountAtomic: string }; buy: { amount: string; symbol: string }; steps: Step[]; fee: { partner: { amount: string } | null } };
type Built = { quoteId: string; tx: { to: Hex; data: Hex; value: string; chainId: number; gas: string | null } };
type Tracked = { status: "pending" | "confirmed" | "failed" | "unknown"; amountOut: string | null };
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
async function api<T>(path: string, body?: unknown): Promise<T> {
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch(BASE + path, {
method: body === undefined ? "GET" : "POST",
headers: { "Content-Type": "application/json", ...(API_KEY ? { "X-Api-Key": API_KEY } : {}) },
body: body === undefined ? undefined : JSON.stringify(body),
});
if (res.status === 429) {
await sleep(Number(res.headers.get("Retry-After") ?? "5") * 1000);
continue;
}
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`);
return data as T;
}
throw new Error("Still rate limited after 5 attempts.");
}
async function swap(sell: string, buy: string, amount: string): Promise<Tracked> {
const ask = { sell, buy, amount, taker: account.address, slippageBps: 50, ...(API_KEY ? { partnerFeeBps: PARTNER_FEE_BPS } : {}) };
let quote = await api<Quote>("/quote", ask);
// 1. Approve Permit2 once per token, then quote again (the steps change once it lands).
const approve = quote.steps.find((s) => s.kind === "approve");
if (approve) {
if (getAddress(approve.spender) !== PERMIT2 || getAddress(approve.tx.to) !== getAddress(approve.token)) throw new Error("Unexpected approval");
const hash = await wallet.sendTransaction({ to: approve.tx.to, data: approve.tx.data });
if ((await client.waitForTransactionReceipt({ hash })).status !== "success") throw new Error("Approval reverted");
quote = await api<Quote>("/quote", ask);
}
// 2. Sign the Permit2 allowance when asked (no gas). viem builds the EIP712Domain type itself.
let permitSignature: Hex | undefined;
const permit = quote.steps.find((s) => s.kind === "permit");
if (permit) {
const { domain, message } = permit.typedData;
const { EIP712Domain: _domainType, ...types } = permit.typedData.types;
if (getAddress(message.spender) !== UNIVERSAL_ROUTER || getAddress(domain.verifyingContract) !== PERMIT2 || domain.chainId !== 4663) throw new Error("Unexpected Permit2 request");
permitSignature = await wallet.signTypedData({
domain,
types,
primaryType: "PermitSingle",
message: { details: { ...message.details, amount: BigInt(message.details.amount) }, spender: message.spender, sigDeadline: BigInt(message.sigDeadline) },
});
}
// 3. Build: simulated on the live chain before it comes back.
const { tx, quoteId } = await api<Built>("/swap", { quoteId: quote.id, permitSignature });
// 4. Verify before signing anything.
const expectedValue = quote.sell.token === NATIVE_ETH ? quote.sell.amountAtomic : "0";
if (quoteId !== quote.id) throw new Error("Refusing: built for another quote");
if (getAddress(tx.to) !== UNIVERSAL_ROUTER) throw new Error("Refusing: tx.to is not the Uniswap Universal Router");
if (tx.chainId !== 4663) throw new Error("Refusing: wrong chain");
if (tx.value !== expectedValue) throw new Error("Refusing: unexpected ETH value");
// 5. Send, then 6. track until settled.
const hash = await wallet.sendTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gas: tx.gas ? BigInt(tx.gas) : undefined });
let status = await api<Tracked>("/swaps/track", { quoteId: quote.id, txHash: hash });
while (status.status === "pending") {
await sleep(2_000);
status = await api<Tracked>(`/swaps/${hash}`);
}
console.log(status.status, quote.fee.partner ? `you earned ${quote.fee.partner.amount} ${quote.buy.symbol}` : "");
console.log("Proof of Savings:", `https://hookline.trade/proof/${hash}`);
return status;
}
swap("ETH", "USDG", "0.01").catch((err) => {
console.error(err);
process.exit(1);
});
Earn on a quote
curl -s "https://hookline.trade/api/quote?sell=ETH&buy=USDG&amount=0.5&taker=0xYourUser&partnerFeeBps=30" \
-H "X-Api-Key: $HOOKLINE_KEY"
# fee.partner = { bps: 30, amount, amountAtomic, recipient: your payout address }Or give it to an agent
{
"mcpServers": {
"hookline": {
"url": "https://hookline.trade/api/mcp",
"headers": {
"X-Api-Key": "your_builder_key (optional)"
}
}
}
}In MCP, pass partner_fee_bps to get_quote. More clients in the MCP docs.
How review works
- 1
Apply
Tell us what you are building and where to reach you. Add a payout address to earn fees.
- 2
We review
A person looks at every project, usually within a few days. It costs nothing and there is no lock-in.
- 3
Ship
Your key arrives through the contact you gave. Set partnerFeeBps, go live, get featured.
Apply for a builder key
Two minutes. We read every application.
Already approved? Open your builder dashboard to see your traffic and fees.