# Pons Trading API > Trade and launch tokens on Pons, on Robinhood Chain --- # Pons API for Trading Bots on Robinhood Chain > Build bots that trade and launch tokens on Pons and Uniswap on Robinhood Chain through one free, keyless API. Buy, sell, stream every launch live, create tokens, claim creator fees. # Pons Trading API The simplest way to trade and launch tokens on Pons - the launchpad on Robinhood Chain - through an API. Buy and sell any Pons token, v2 (bonding curve, then Uniswap v4) or v1 (Uniswap V3 from launch); launch new ones on Pons v2. shrine.trade is a Pons API for executing buys and sells, streaming every launch and graduation the moment it happens, creating tokens with a dev buy, reading curve state, and claiming creator fees - across every venue a Pons token passes through. [Buy & Sell](/local-trade) · [Live Feed](/live-launches) · [Create Token](/create-token) · [Fees](/fees) ## One API, every Pons venue Pons v2 tokens on their bonding curve, Pons v2 tokens in their Uniswap v4 pool after graduation, Pons v1 tokens in the Uniswap V3 pool they launched into, and anything else with a Uniswap pool on Robinhood Chain - NVDA, TSLA and the other tokenised stocks included. Pass a token address and the API routes it. Tokens priced in USDG, NVDA, TSLA and the other tokenised stocks are bought with plain ETH; the conversion is part of the transaction. ## Why shrine.trade - **Keyless.** No account, no API key, no sign-up. Call it. - **Your key stays yours.** Local only. Every endpoint returns unsigned transactions. You sign them with your own wallet. Your private key never leaves your machine. - **Everything in ETH.** Buy any token with ETH, whatever it is priced in. Sell back to ETH with one flag. - **FREE live feed.** Every launch, graduation and new pool over WebSocket, within a second of the block. - **Sub-second quotes.** Independent chain reads run in parallel; a quote comes back in a few hundred milliseconds. - **Checked before you sign.** Funds, balances, approvals and slippage are verified up front, so what you get back lands. - **Front-run protected.** - **Copy-paste scripts.** Every page has a plain JavaScript and Python script: change three lines, run it. - **Built for bots.** Stable error codes, consistent nonces, ETH-denominated amounts, and the [sniper tutorial](/tutorials/snipe-pons-launches) to start from. ``` Base URL: https://api.shrine.trade/rh ``` ## Endpoints | Endpoint | What it does | |---|---| | `POST /api/local-trade` | Buy or sell any Pons v2 or v1 token → [Buy & Sell](/local-trade) | | `POST /api/send` | Broadcast a transaction you signed - no RPC of your own needed → [Sending](/local-trade#sending) | | `WS /api/launches/ws` | Live feed of launches, graduations and new pools → [Live Launches](/live-launches) | | `WS /api/stream/ws` | Every buy, sell and liquidity change for the tokens you name, curve and Uniswap. Free key → [Advanced Data Stream](/advanced-stream) | | `POST /api/create-token` | Launch a token on Pons v2, with an optional dev buy → [Create Token](/create-token) | | `GET /api/token/{address}` | Curve state, phase, reserves, snipe tax, unswept creator fees → [Token Info](/token-info) | | `GET /api/fees/{wallet}` · `POST /api/claim-fees` | What your launches earned, and the transactions that pay it out → [Claim Creator Fees](/creator-fees) | ## Questions? Read the [FAQ](/faq) for supported platforms, fees, amounts, slippage, private-key safety and the live feed. Or follow a [tutorial](/tutorials) - sniping launches, trading stock-priced tokens, and what gas costs on Robinhood Chain. ## Stay updated Follow [@shrinetrade on X](https://x.com/shrinetrade) for releases, and join the [Telegram group](https://t.me/+nEqAowTK8BZhZjFk) for support from the team. AI coding tools can read the full documentation as a single text file: [llms-full.txt](/llms-full.txt). *shrine.trade is a Pons and Uniswap API for Robinhood Chain developers and traders. It is an independent integration, not affiliated with Pons or Robinhood Markets, Inc.; Pons's contracts and its launch fees belong to Pons. Built by traders, for traders.* --- # Pons Trade Stream: Every Buy, Sell and Liquidity Change on Robinhood Chain > WebSocket stream of every buy, sell, liquidity add and liquidity removal for the Pons tokens you choose, on the bonding curve, in Uniswap v4 after graduation, and in Uniswap V3. Free key, live. # Advanced Data Stream Every trade and every liquidity change for the Pons tokens you name, pushed as they land. One socket covers the whole life of a token: - **On the bonding curve** - each buy and sell on the Pons v2 curve, with the curve's fee and any creator or opening-snipe tax broken out. - **In Uniswap v4 after graduation** - each swap in the token's pool, and each liquidity position opened or closed. - **In Uniswap V3** - the same for Pons v1 tokens, which launch straight into a V3 pool. Use it for a strategy, a wallet tracker, volume and holder analytics, or a bot that reacts to what others do instead of only to launches. The [launch feed](/live-launches) tells you a token exists; this tells you what happens to it. > **Free key, by request.** This stream needs a free api key. Join the [Telegram group](https://t.me/+nEqAowTK8BZhZjFk) to request one. ``` WS wss://api.shrine.trade/rh/api/stream/ws?key=YOUR_KEY&tokens=0x…,0x… WS wss://api.shrine.trade/rh/api/stream/ws?key=YOUR_KEY&wallets=0x…,0x… ``` Follow **tokens** to get everything that happens to them, **wallets** to get everything they do on any Pons token, or both on one socket. ## Copy this **JavaScript** ```js const KEY = "sk_…"; // your free api key const TOKENS = ["0x94FDe7626988Ae15E406E89C2277cd1a820dfbD0"]; // any Pons tokens const ws = new WebSocket(`wss://api.shrine.trade/rh/api/stream/ws?key=${KEY}&tokens=${TOKENS.join(",")}`); ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.type === "websocket_active") { console.log("following", m.tokens.length, "tokens from block", m.block); return; } if (m.type === "trade") console.log(`${m.side.toUpperCase()} ${m.tokenAmountFormatted} ${m.symbol} for ${m.quoteAmountFormatted} ${m.quoteSymbol} on ${m.venue} by ${m.origin}`); if (m.type === "liquidity") console.log(`liquidity ${m.action} ${m.symbol} on ${m.venue}: ${m.liquidityDelta}`); if (m.type === "lagged") console.warn("read too slowly, missed", m.dropped, "events"); }; // Change what you follow without reconnecting. // ws.send(JSON.stringify({ subscribe: ["0x…"], unsubscribe: ["0x…"] })); ``` Node 22+ has `WebSocket` built in; older Node needs `npm install ws`. **Python** ```python import json, websocket # pip install websocket-client KEY = "sk_…" # your free api key TOKENS = ["0x94FDe7626988Ae15E406E89C2277cd1a820dfbD0"] # any Pons tokens def on_message(ws, raw): m = json.loads(raw) if m["type"] == "websocket_active": print("following", len(m["tokens"]), "tokens from block", m["block"]) elif m["type"] == "trade": print(f'{m["side"].upper()} {m["tokenAmountFormatted"]} {m["symbol"]} for {m["quoteAmountFormatted"]} {m["quoteSymbol"]} on {m["venue"]} by {m.get("origin")}') elif m["type"] == "liquidity": print(f'liquidity {m["action"]} {m["symbol"]} on {m["venue"]}: {m["liquidityDelta"]}') elif m["type"] == "lagged": print("read too slowly, missed", m["dropped"], "events") url = f"wss://api.shrine.trade/rh/api/stream/ws?key={KEY}&tokens={','.join(TOKENS)}" websocket.WebSocketApp(url, on_message=on_message).run_forever() ``` ## What you get On connect, one `websocket_active` frame confirming what is being followed: ```json { "type": "websocket_active", "stream": "advanced", "tokens": ["0x94FDe7626988Ae15E406E89C2277cd1a820dfbD0"], "wallets": [], "unknown": [], "maxTokens": 50, "maxWallets": 50, "block": 57705543, "upstream": "open" } ``` `unknown` lists anything you asked for that the stream can't follow, with a reason: an address that isn't a Pons token, or not an address at all. The rest of the connection is events. `upstream` is `open` when logs are flowing from the node and `connecting` while the subscription is being set up, which the first client to connect triggers; it takes a second or two. If a token you follow stays silent, check this before assuming the token is quiet. ### Trades ```json { "type": "trade", "venue": "pons_curve", "side": "buy", "token": "0x94FDe7626988Ae15E406E89C2277cd1a820dfbD0", "symbol": "LAPTOP", "protocol": "PONS_V2", "quoteToken": "0x…", "quoteSymbol": "PLTR", "origin": "0xf0816aa238BaA4774D41B88556292413A6059B75", "caller": "0xEc20E594D28a17511264dc73a84cd4AA957B0ABc", "tokenAmount": "885663926228359252587", "tokenAmountFormatted": "885.663926228359252587", "quoteAmount": "21060197598119", "quoteAmountFormatted": "0.000021060197598119", "price": "0.000000023779", "curveFee": "210601975981", "tax": "0", "priceAfter": "0.000000023779", "marketCap": "23.779", "tokenReserve": "807536513474090402491482633", "tokenReserveFormatted": "807536513.474090402491482633", "quoteReserve": "2080401284608788604", "quoteReserveFormatted": "2.080401284608788604", "reservesSource": "curve", "block": 55978479, "timestamp": 1788550107, "tx": "0xcf9c…", "logIndex": 8 } ``` | Field | Meaning | |---|---| | `venue` | `pons_curve`, `uniswap_v4` (a graduated v2 token's pool) or `uniswap_v3` (a v1 token's pool). | | `side` | `buy` - the quote asset went in and tokens came out. `sell` - the reverse. | | `quoteToken` / `quoteSymbol` | What the token is priced in: ETH (address zero), USDG, a tokenised stock. Amounts are in this. | | `origin` | The wallet that sent the transaction. Most trades go through a router - ours, Uniswap's, Pons's own site - so `caller` is a contract and `origin` is the person. Watch wallets by this. Missing on the rare event where the node didn't return the transaction in time. | | `caller` | Whoever called the venue: a router, or the wallet itself on a direct trade. | | `tokenAmount` / `quoteAmount` | Base units, plus a `…Formatted` decimal for each. On the curve these are what the curve received and paid out. | | `price` | Quote per token, from this trade's own two amounts. | | `curveFee` / `tax` | Curve trades only: the curve's fee and any creator or opening-snipe tax taken from the quote leg, base units. | | `priceAfter` | Quote per token once this trade is done: the pool's price for pool trades, this trade's own price on the curve. | | `marketCap` | `priceAfter` times the token's total supply, in the quote asset. Convert with the quote's own price if you want dollars. | | `tokenReserve` / `quoteReserve` | What the venue holds after the trade, base units plus `…Formatted`. On the curve, its real reserves. In a pool, the active range's virtual reserves derived from the swap's liquidity and price - what the next trade is priced against. `reservesSource` says which: `curve` or `pool_virtual`. | | `sqrtPriceX96` / `tick` | Pool trades only: the pool's price after the swap, in Uniswap's own units. | | `block` / `timestamp` / `tx` / `logIndex` | Where it happened. `block` and `logIndex` together order events exactly. | ### Liquidity ```json { "type": "liquidity", "venue": "uniswap_v4", "action": "add", "token": "0x…", "symbol": "CONTRA", "protocol": "PONS_V2", "quoteToken": "0x0000000000000000000000000000000000000000", "quoteSymbol": "ETH", "origin": "0x…", "caller": "0x…", "liquidityDelta": "184467440737095516", "tickLower": -887220, "tickUpper": 887220, "block": 57708240, "timestamp": 1788779311, "tx": "0x…", "logIndex": 3 } ``` | Field | Meaning | |---|---| | `action` | `add` or `remove`. | | `liquidityDelta` | Liquidity units added (positive) or removed (negative), in Uniswap's own measure. | | `tokenAmount` / `quoteAmount` | V3 pools report how much of each asset moved; v4 pools report only the liquidity delta, so these are absent there. | | `tickLower` / `tickUpper` | The position's price range. A full-range position on a Pons pool is the one Pons itself created at graduation. | ### Following wallets Pass `wallets=` in the URL, or send `subscribeWallets`, and every trade or liquidity change those wallets **send** comes through, whatever token it's on, with the token attributed. The match is on `origin` (the transaction sender) or `caller`, so a wallet trading through a router, ours or Pons's site, is still caught. Use it for copy-trading, whale watching, or following a creator's own wallet. One limit: a wallet's swap in a Uniswap v4 pool can only be attributed if the stream has seen that pool since it started or someone follows the token. Curve trades are always attributed. If you care about one graduated token, follow the token as well. ### Control messages Send JSON text frames to change what you follow; the reply is a `subscribed` message with the full current lists: ```json { "subscribe": ["0x…", "0x…"], "unsubscribe": ["0x…"] } { "subscribeWallets": ["0x…"], "unsubscribeWallets": ["0x…"] } { "ping": true } ``` Add `&pretty=1` to the URL to have every frame indented and newline-terminated, handy for watching a tape with curl. `{"type":"lagged","dropped":n}` means your client read too slowly and `n` events were skipped rather than queued without bound. Process faster or follow fewer tokens. ## Notes - **Filtering is required.** The chain sees over a million trades a day. You name up to 50 tokens and 50 wallets per connection, and only their events come through. There is no "everything" mode. - **Live.** Events are pushed from a node subscription and arrive within about a second of the block, same as the [launch feed](/live-launches). - **Pons and Uniswap tokens supported.** v2 (curve, then Uniswap v4) and v1 (Uniswap V3). A token that isn't a Pons launch is reported in `unknown`. - **Limits.** 50 tokens and 50 wallets per connection, one connection per key and per IP. Follow more on the one socket rather than opening a second; ask if you need more. --- # Create a Token on Pons (Robinhood Chain) > Launch a token on the Pons v2 bonding curve on Robinhood Chain with one signed transaction - metadata, image, creator tax, dev buy and any quote asset from ETH to NVDA. # Create Token ``` POST https://api.shrine.trade/rh/api/create-token ``` Builds the Pons launch transaction for you: metadata, launch config, the pinned economics hash, and — if you want one — a dev buy in the same transaction. You sign and send it; the token and its bonding curve exist as soon as it confirms. shrine.trade takes **no fee** on creation. The transaction value covers Pons's own launch fee (plus your dev buy, if any). Launching is **Pons v2 only**: the bonding curve, graduating to Uniswap v4 on its own once it sells out. Pons v1 launches are not offered (v1 is superseded), though v1 tokens still [buy and sell](/local-trade) here. > **About the private key field.** **The key never leaves your machine.** It signs locally; only the signed transaction is handed to `/api/send` to broadcast. The key itself is never sent to shrine.trade. Uploading happens first: **Build waits for the image to finish pinning to IPFS**, so the token's `logo` is set the moment it launches rather than being patched in afterwards. ## Example **JavaScript** ```js const { readFileSync } = require("node:fs"); const { Wallet } = require("ethers"); // ─── your token ─────────────────────────────────────── const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"; const NAME = "Grene"; const SYMBOL = "GRENE"; const IMAGE = "./logo.png"; // image file, next to this script const DESCRIPTION = "the greenest coin on Robinhood Chain"; // ─── links — all optional, "" to leave one out ──────── const TWITTER = "https://x.com/grene"; const TELEGRAM = ""; const DISCORD = ""; const WEBSITE = "https://grene.example"; const FARCASTER = ""; // ─── economics ──────────────────────────────────────── const PAIR_TOKEN = "ETH"; // what buyers pay with: ETH, USDG, NVDA, MSTR, … const DEV_BUY = ""; // your own opening buy, in ETH. "" = none const CREATOR_TAX = 0; // % of every trade you earn, 0–10. Fixed at launch const FEE_RECIPIENT = ""; // where your fees go. "" = the launching wallet const EXEMPTIONS = []; // wallets that skip the opening snipe tax. // Max 32, or 31 when you take a dev buy const BUYBACK = false; // route creator fees into buybacks // ────────────────────────────────────────────────────── async function main() { const wallet = new Wallet(PRIVATE_KEY); // signs only; no node needed // 1. Upload the image. We pin it to IPFS and hand back a hosted URL. const form = new FormData(); form.append("file", new Blob([readFileSync(IMAGE)], { type: "image/png" }), "logo.png"); form.append("name", NAME); form.append("symbol", SYMBOL); form.append("description", DESCRIPTION); const up = await fetch("https://api.shrine.trade/rh/api/upload-image", { method: "POST", body: form, }); const uploaded = await up.json(); if (uploaded.error) throw new Error(`${uploaded.error}: ${uploaded.message}`); console.log("image pinned:", uploaded.image); // 2. Ask shrine.trade to build the launch transaction. const res = await fetch("https://api.shrine.trade/rh/api/create-token", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ name: NAME, symbol: SYMBOL, description: DESCRIPTION, logo: uploaded.image, socials: { twitter: TWITTER, telegram: TELEGRAM, discord: DISCORD, website: WEBSITE, farcaster: FARCASTER }, pairToken: PAIR_TOKEN, creatorTaxBps: CREATOR_TAX * 100, buybackEnabled: BUYBACK, // Left out entirely when empty - the API picks sane defaults. ...(DEV_BUY ? { initialBuy: DEV_BUY } : {}), ...(EXEMPTIONS.length ? { snipeTaxExemptions: EXEMPTIONS } : {}), ...(FEE_RECIPIENT ? { creatorFeeRecipient: FEE_RECIPIENT } : {}), from: wallet.address, }), }); const body = await res.json(); if (body.error) throw new Error(`${body.error}: ${body.message}`); console.log("launch fee", body.launchFeeFormatted, "ETH"); // 3. Sign locally - your private key never leaves this script. // 4. Hand each signed transaction to the API to broadcast, in order (a dev // buy in an ERC-20 needs an approval first). No RPC of your own needed. let launched; for (const tx of body.txs) { const signed = await wallet.signTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gasLimit: BigInt(tx.gas), maxFeePerGas: BigInt(tx.maxFeePerGas), maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), nonce: tx.nonce, chainId: tx.chainId, type: 2, }); const sent = await (await fetch("https://api.shrine.trade/rh/api/send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signedTx: signed }), })).json(); if (sent.error) throw new Error(`${sent.error}: ${sent.message}`); if (sent.status !== "landed") throw new Error(`${tx.description} ${sent.status}: ${sent.explorer}`); if (sent.token) launched = sent; } // 5. The relay reads the new addresses out of the TokenLaunched event for you. console.log("token launched:", launched.token); console.log("curve:", launched.curve); console.log("tx:", launched.explorer); } main(); ``` Save it as `launch.js`, put your image next to it as `logo.png`, then: ```bash npm install ethers node launch.js ``` **Python** ```python import requests from eth_account import Account # ─── your token ─────────────────────────────────────── PRIVATE_KEY = "0xYOUR_PRIVATE_KEY" NAME = "Grene" SYMBOL = "GRENE" IMAGE = "./logo.png" # image file, next to this script DESCRIPTION = "the greenest coin on Robinhood Chain" # ─── links - all optional, "" to leave one out ──────── TWITTER = "https://x.com/grene" TELEGRAM = "" DISCORD = "" WEBSITE = "https://grene.example" FARCASTER = "" # ─── economics ──────────────────────────────────────── PAIR_TOKEN = "ETH" # what buyers pay with: ETH, USDG, NVDA, MSTR, … DEV_BUY = "" # your own opening buy, in ETH. "" = none CREATOR_TAX = 0 # % of every trade you earn, 0-10. Fixed at launch FEE_RECIPIENT = "" # where your fees go. "" = the launching wallet EXEMPTIONS = [] # wallets that skip the opening snipe tax. # Max 32, or 31 when you take a dev buy BUYBACK = False # route creator fees into buybacks # ────────────────────────────────────────────────────── account = Account.from_key(PRIVATE_KEY) # signs only; no node needed # 1. Upload the image. We pin it to IPFS and hand back a hosted URL. with open(IMAGE, "rb") as fh: up = requests.post( "https://api.shrine.trade/rh/api/upload-image", files={"file": ("logo.png", fh, "image/png")}, data={"name": NAME, "symbol": SYMBOL, "description": DESCRIPTION}, ).json() if "error" in up: raise SystemExit(f"{up['error']}: {up['message']}") print("image pinned:", up["image"]) # 2. Ask shrine.trade to build the launch transaction. res = requests.post( "https://api.shrine.trade/rh/api/create-token", json={ "name": NAME, "symbol": SYMBOL, "description": DESCRIPTION, "logo": up["image"], "socials": {"twitter": TWITTER, "telegram": TELEGRAM, "discord": DISCORD, "website": WEBSITE, "farcaster": FARCASTER}, "pairToken": PAIR_TOKEN, "creatorTaxBps": CREATOR_TAX * 100, "buybackEnabled": BUYBACK, # Left out entirely when empty - the API picks sane defaults. **({"initialBuy": DEV_BUY} if DEV_BUY else {}), **({"snipeTaxExemptions": EXEMPTIONS} if EXEMPTIONS else {}), **({"creatorFeeRecipient": FEE_RECIPIENT} if FEE_RECIPIENT else {}), "from": account.address, }, ) body = res.json() if "error" in body: raise SystemExit(f"{body['error']}: {body['message']}") print("launch fee", body["launchFeeFormatted"], "ETH") # 3. Sign locally - your private key never leaves this script. # 4. Hand each signed transaction to the API to broadcast. A dev buy in an ERC-20 needs an approval # first, so send whatever comes back, in order. for tx in body["txs"]: signed = Account.sign_transaction( { "to": tx["to"], "data": tx["data"], "value": int(tx["value"]), # launch fee (+ dev buy) in wei "gas": tx["gas"], "maxFeePerGas": int(tx["maxFeePerGas"]), "maxPriorityFeePerGas": int(tx["maxPriorityFeePerGas"]), "nonce": tx["nonce"], "chainId": tx["chainId"], "type": tx["type"], }, account.key, ) sent = requests.post("https://api.shrine.trade/rh/api/send", json={"signedTx": "0x" + signed.raw_transaction.hex().removeprefix("0x")}).json() if "error" in sent: raise SystemExit(f"{sent['error']}: {sent['message']}") if sent["status"] != "landed": raise SystemExit(f"{tx['description']} {sent['status']}: {sent['explorer']}") if sent.get("token"): launched = sent # 5. The relay reads the new addresses out of the TokenLaunched event for you. print("token launched:", launched["token"]) print("curve:", launched["curve"]) print("tx:", launched["explorer"]) ``` Save it as `launch.py`, put your image next to it as `logo.png`, then: ```bash pip install eth-account requests python launch.py ``` ## Supported quote assets A Pons token can be priced in ETH or in one of Robinhood Chain's tokenised assets. Pass the **ticker** as `pairToken` - `"MSTR"`, `"USDG"`, `"NVDA"` - or the address if you prefer; both work, and tickers are case-insensitive. Whatever you pick is fixed at launch and is what buyers pay with. The launch fee itself is always ETH. | Ticker | Decimals | Address | |---|---|---| | `ETH` | 18 | native ETH — no address | | `USDG` | 6 | `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` | | `SPCX` | 18 | `0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa` | | `NVDA` | 18 | `0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC` | | `RDDT` | 18 | `0x05b37Fb53A299a1b874A619e1c4C404D52C36F4C` | | `AMZN` | 18 | `0x12f190a9F9d7D37a250758b26824B97CE941bF54` | | `DJT` | 18 | `0x1D11f0496982706C5e14A514D4E79F2e6BdE4516` | | `TTWO` | 18 | `0x5e81213613b6B86EaB4c6c50d718d34359459786` | | `GME` | 18 | `0x1b0E319c6A659F002271B69dB8A7df2F911c153E` | | `SPY` | 18 | `0x117cc2133c37B721F49dE2A7a74833232B3B4C0C` | | `AAPL` | 18 | `0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9` | | `MSFT` | 18 | `0xe93237C50D904957Cf27E7B1133b510C669c2e74` | | `COIN` | 18 | `0x6330D8C3178a418788dF01a47479c0ce7CCF450b` | | `TSLA` | 18 | `0x322F0929c4625eD5bAd873c95208D54E1c003b2d` | | `QQQ` | 18 | `0xD5f3879160bc7c32ebb4dC785F8a4F505888de68` | | `PLTR` | 18 | `0x894E1EC2D74FFE5AEF8Dc8A9e84686acCB964F2A` | | `cbBTC` | 8 | `0xCEC185eB182c47d1bA1EFc84e6959e18cd620Be4` | | `GOOGL` | 18 | `0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3` | | `GLD` | 18 | `0xC9a981FEE1F9DEc688bb123ccDeCc63D0deBFC4e` | | `META` | 18 | `0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35` | | `CRCL` | 18 | `0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5` | | `COST` | 18 | `0x4EA005168D7F09a7A0Ba9D1DEf21a479950E44C2` | | `MSTR` | 18 | `0xec262a75e413fAfD0dF80480274532C79D42da09` | | `AMD` | 18 | `0x86923f96303D656E4aa86D9d42D1e57ad2023fdC` | | `SNDK` | 18 | `0xB90A19fF0Af67f7779afF50A882A9CfF42446400` | | `BB` | 18 | `0x48E39E56aCdbA37b09020C0b734A613C9a2f100A` | | `MU` | 18 | `0xfF080c8ce2E5feadaCa0Da81314Ae59D232d4afD` | | `HIMS` | 18 | `0xCceE82fE024c36fA15E1005edE3E9e4787e23D09` | Pons scales each curve's economics into its quote asset, so the graduation threshold differs per asset - 4.2 ETH, 8090 USDG, 41.6 NVDA. `create-token` returns the one that applies as `graduationThreshold`. ## Request | Field | Type | Description | |---|---|---| | `name` | string | Token name. Not unique — always identify tokens by address. | | `symbol` | string | Ticker. | | `description` | string | Project description. | | `socials` | object | `{ twitter, telegram, discord, website, farcaster }` — any may be `""`. | | `logo` | string, optional | Hosted image URL. The scripts above pin your image file to IPFS and fill this in for you. | | `creatorFeeRecipient` | address, optional | Where creator fees are paid. Defaults to `from`. | | `snipeTaxExemptions` | string[], optional | Up to **32** wallets that skip the opening snipe tax (**31** with an `initialBuy` - the dev-buy wallet takes one slot on-chain). In the form above, separate them with commas (spaces and new lines work too). Your own sniper, a partner, whoever. Fixed at launch and never changeable. | | `creatorTaxBps` | number, optional | Extra creator tax on every trade, in basis points. Capped by Pons at 10%; immutable after launch. Default `0`. | | `buybackEnabled` | boolean, optional | Route the creator's fee share into token buybacks (vested). Default `false`. | | `launchConfigId` | number, optional | Which Pons launch config to use. Only config `0` exists today and it is the default, so leave it out. | | `pairToken` | string, optional | What the curve is priced in. `"ETH"` (default) or the address of an approved quote asset — USDG, NVDA, SPCX, GME, cbBTC and the rest of Robinhood Chain's tokenised assets. An asset Pons hasn't approved returns `pair_token_not_approved`. | | `initialBuy` | decimal string, optional | Your own opening buy, in the same transaction, **in ETH** whatever the token is priced in. On a USDG or NVDA launch the API works out how much of that asset the ETH buys (`initialBuy` in the response, `initialBuyEth` echoes your figure); your wallet's own holding of the asset is used first and only the shortfall is swapped, with a `buy_quote_with_eth` transaction in front. Your wallet is exempt from the snipe tax on this buy, and there is no slippage to set on the buy itself: it runs inside the transaction that creates the curve, so nobody can trade ahead of it. | | `salt` | 32-byte hex, optional | CREATE2 salt. Lets you know the token address before launching. Random if omitted. | | `from` | address | Your wallet (deployer). | ### What a launch costs Two numbers matter, and they are not the same: | | plain launch | + 0.001 dev buy | |---|---|---| | **ETH you must hold** | ~0.0026 | ~0.0036 | | **ETH actually spent** | ~0.0019 | ~0.0029 | The gap is not a fee. A node reserves `gasLimit x maxFeePerGas` for the whole transaction before it runs, and neither figure is what gets used: the limit is padded 20% above the estimate, and the price is a ceiling set at 1.25x the current base fee, not the rate. Unused gas is never taken - you get the difference back in the same block. So **budget around 0.003 ETH on Robinhood Chain** for a plain launch, plus your dev buy. A wallet holding exactly the launch fee plus the expected gas will be rejected, which is not a bug. The ceiling is deliberately tight: if the base fee climbs more than 25% between quoting and sending, the node rejects the transaction and you request a fresh one. Of what is spent, `0.0005` is Pons's launch fee and the rest is gas. shrine.trade takes nothing on creation. ## Response A real one, for a launch with a 0.001 ETH dev buy: ```json { "txs": [ { "to": "0xe33E9E479dF8802cb0866d5d05258bEc4cF62948", "data": "0x\u2026", "value": "1500000000000000", "gas": 4600821, "maxFeePerGas": "581672501", "maxPriorityFeePerGas": "1", "nonce": 5, "chainId": 4663, "type": 2, "description": "launch_and_buy" } ], "launchFee": "500000000000000", "launchFeeFormatted": "0.0005", "expectedEconomics": "0xa9fc75d4203a33fe660e8fa32c74c3aa41c1fda4bf23d3a39b6bc22a1f8b1ca7", "launchConfigId": 0, "pairToken": "ETH", "pairTokenSymbol": "ETH", "pairTokenDecimals": 18, "graduationThreshold": "4200000000000000000", "graduationThresholdFormatted": "4.2", "salt": "0x\u2026", "creatorFeeRecipient": "0x7b444D22f099Fd238210161791dE26d16c3cEdf2", "creatorTaxBps": 100, "snipeTaxExemptions": [], "initialBuy": "1000000000000000", "expectedTokensOut": "582993253935204464062630", "minTokensOut": "553843591238444240859498" } ``` - `txs` - sign and send in order. Usually one: `launch` (plain) or `launch_and_buy` (with a dev buy). A dev buy in an ERC-20 pair token can put `buy_quote_with_eth` (if the wallet is short of it) and `approve_quote` in front. - `launchFee` - Pons's fee, always ETH, read from the factory at request time. The transaction's `value` is this plus an ETH dev buy. - `pairToken`, `pairTokenSymbol`, `pairTokenDecimals` - what the curve is priced in. `graduationThreshold` is how much of it the curve must take in before it graduates; Pons scales it per asset. - `expectedEconomics` - the launch config's economics hash, pinned into the transaction so the terms can't change between quote and landing. - `salt` - yours, or the random one we picked. `creatorFeeRecipient`, `creatorTaxBps`, `snipeTaxExemptions` echo what the launch will use. - `initialBuy`, `expectedTokensOut`, `minTokensOut` - the dev buy in the pair token's base units and what it returns. Only with an `initialBuy`. On an ERC-20-priced launch `initialBuyEth` is the ETH you gave and `initialBuy` what it became. - `quoteSwap` - only when an ERC-20 dev buy needed a swap in front: what it buys, the ETH quoted and carried, and the pools. --- # Claim Pons Creator Fees > See what your Pons launches have earned on Robinhood Chain and claim it to your wallet in two calls. # Claim Creator Fees If you launched a token, every trade on it earns you a share: what's left of the trading fee after Pons's 30%, plus all of your creator tax. It isn't sent to you automatically - you claim it. Two endpoints, free and keyless. ## What's waiting ``` GET https://api.shrine.trade/rh/api/fees/{wallet}?token={yourToken} ``` ```json { "wallet": "0xfc1FFd8b43631Ba04a347FAF930fA1Dc547BFcb2", "escrow": "0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e", "asset": "USDG", "decimals": 6, "claimable": "39388954", "claimableFormatted": "39.388954", "unswept": "1935146", "unsweptFormatted": "1.935146", "total": "41324100", "totalFormatted": "41.3241", "hasClaimable": true } ``` `total` is what a claim pays out right now, in the launch's quote asset (`asset`). `claimable` is already in Pons's escrow; `unswept` is still on the token's curve and gets collected as part of the claim. `hasClaimable: false` means don't bother - you'd only pay gas. Leave `token` out to see just the ETH escrow balance. ## Claim it ``` POST https://api.shrine.trade/rh/api/claim-fees ``` Send `{ "from": "0x…", "token": "0x…" }` - your wallet and your launched token - and you get back the transactions that pay everything out to your wallet: one or two, send them in order. **JavaScript** ```js const { Wallet } = require("ethers"); // ─── change these ───────────────────────────────────── const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"; const TOKEN = "0xYOUR_LAUNCHED_TOKEN"; // ────────────────────────────────────────────────────── async function main() { const wallet = new Wallet(PRIVATE_KEY); // signs only; no node needed // 1. Ask shrine.trade to build the claim. const res = await fetch("https://api.shrine.trade/rh/api/claim-fees", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ from: wallet.address, token: TOKEN }), }); const data = await res.json(); if (data.error) throw new Error(`${data.error}: ${data.message}`); console.log("claiming", data.claimingFormatted, data.asset); // 2. Sign locally and send, in order - the key never leaves this script. for (const tx of data.txs) { const signed = await wallet.signTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gasLimit: BigInt(tx.gas), maxFeePerGas: BigInt(tx.maxFeePerGas), maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), nonce: tx.nonce, chainId: tx.chainId, type: 2, }); const sent = await (await fetch("https://api.shrine.trade/rh/api/send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signedTx: signed }), })).json(); if (sent.error) throw new Error(`${sent.error}: ${sent.message}`); if (sent.status !== "landed") throw new Error(`${tx.description} ${sent.status}: ${sent.explorer}`); console.log(tx.description + ": " + sent.explorer); } } main(); ``` Save it as `claim.js`, then: ```bash npm install ethers node claim.js ``` **Python** ```python import requests from eth_account import Account # ─── change these ───────────────────────────────────── PRIVATE_KEY = "0xYOUR_PRIVATE_KEY" TOKEN = "0xYOUR_LAUNCHED_TOKEN" # ────────────────────────────────────────────────────── account = Account.from_key(PRIVATE_KEY) # signs only; no node needed # 1. Ask shrine.trade to build the claim. data = requests.post( "https://api.shrine.trade/rh/api/claim-fees", json={"from": account.address, "token": TOKEN}, ).json() if "error" in data: raise SystemExit(f"{data['error']}: {data['message']}") print("claiming", data["claimingFormatted"], data["asset"]) # 2. Sign locally and send, in order - the key never leaves this script. for tx in data["txs"]: signed = Account.sign_transaction( { "to": tx["to"], "data": tx["data"], "value": int(tx["value"]), "gas": tx["gas"], "maxFeePerGas": int(tx["maxFeePerGas"]), "maxPriorityFeePerGas": int(tx["maxPriorityFeePerGas"]), "nonce": tx["nonce"], "chainId": tx["chainId"], }, account.key, ) sent = requests.post("https://api.shrine.trade/rh/api/send", json={"signedTx": "0x" + signed.raw_transaction.hex().removeprefix("0x")}).json() if "error" in sent: raise SystemExit(f"{sent['error']}: {sent['message']}") if sent["status"] != "landed": raise SystemExit(f"{tx['description']} {sent['status']}: {sent['explorer']}") print(tx["description"] + ":", sent["explorer"]) ``` Save it as `claim.py`, then: ```bash pip install eth-account requests python claim.py ``` ## Notes - **Only the `creatorFeeRecipient` can claim** - your wallet unless you set another at launch. `/api/token/{address}` shows which. - **Launches priced in an ERC-20** (USDG, NVDA, …) pay out in that asset. Pass the launched token and the API works out the rest. - **Launched with buybacks on?** Then Pons collects the curve's fees for you on its own schedule, and the claim covers what has arrived in the escrow so far. `nothing_to_claim` tells you when the rest is still on the curve. - These are Pons's fees to you as a creator. Our 0.5% is separate and never touches this escrow. --- # API Errors > Every error code the Pons Trading API returns, grouped by endpoint, with what to do about each. # Errors Errors are JSON with a stable `error` code and a plain-language `message`. Check the code in your script and show the message to a human. ```json { "error": "insufficient_balance", "message": "wallet holds 0 tokens, tried to sell 1000" } ``` ## Your request | Code | HTTP | Meaning | |---|---|---| | `invalid_request` | 400 | A required field is missing or malformed. | | `invalid_address` | 400 | `token`, `from` or another address field is not a valid address. | | `invalid_amount` | 400 | `amount` is zero, negative, not a number, or a percentage on a buy. | | `invalid_action` | 400 | `action` is not `"buy"` or `"sell"`. | | `token_not_found` | 404 | The address is not a Pons token on Robinhood Chain, v2 or v1. Check you copied the token, not a pool or a curve. | ## Trading | Code | HTTP | Meaning | |---|---|---| | `insufficient_funds` | 400 | Not enough ETH in `from` for the trade plus gas. The message says what the wallet holds and what it needs. It has to be **ETH on Robinhood Chain** - ETH on Ethereum mainnet or another L2 must be bridged first, and holding USDG, NVDA or another asset does not pay for gas. | | `insufficient_balance` | 400 | Selling more tokens than the wallet holds. `"100%"` sells everything. | | `slippage_exceeded` | 400 | The price moved between the quote and now by more than `slippage`. Retry, or raise `slippage` on a fast-moving token. | | `no_route` | 400 | The token is priced in an asset your wallet doesn't hold enough of, and no Uniswap pool can buy that asset with ETH. Acquire it yourself, then retry. With `toEth`: no pool can turn the proceeds into ETH - sell without it and keep the asset. | | `amount_too_small` | 400 | So little that the swap would return nothing. Increase `amount`. | | `graduating` | 409 | The token is moving from its curve to its Uniswap pool right now. Retry in a minute; it then trades on Uniswap automatically. | | `graduated` | 400 | The curve closed while your trade was being built. Retry; the trade now routes to Uniswap. | | `curve_closed` | 409 | The curve has sold out and is about to graduate; sells reopen once the Uniswap pool exists. | | `deadline_passed` | 400 | A Uniswap transaction is only valid for 10 minutes after it is built. Request a fresh one. | | `insufficient_allowance` | 400 | An approval the trade needs is missing. Retry; the response will include it. | ## Sending | Code | HTTP | Meaning | |---|---|---| | `not_ours` | 400 | `/api/send` only broadcasts transactions this API built: Pons and Uniswap trades, launches, approvals and claims. Anything else is refused before it reaches the chain. | | `wrong_chain` | 400 | The signed transaction is for another chain id; this API relays Robinhood Chain (4663). | ## Creating a token | Code | HTTP | Meaning | |---|---|---| | `invalid_pair_token` | 400 | `pairToken` is neither an address nor a ticker we know. The message lists the tickers. | | `pair_token_not_approved` | 400 | Pons doesn't accept that asset as a quote. Use ETH or one of the [approved assets](/create-token#supported-quote-assets). | | `too_many_exemptions` | 400 | More than 32 `snipeTaxExemptions` (31 with an `initialBuy`). | | `creator_tax_too_high` | 400 | `creatorTaxBps` is above the maximum Pons allows (10%). | | `invalid_salt` | 400 | `salt` is not 32 bytes of hex. | | `launch_config_disabled` | 400 | The requested `launchConfigId` is not available. Leave it out. | | `launcher_not_permitted` | 403 | Pons currently only lets allow-listed wallets launch, and `from` isn't one. | | `launch_disabled` | 503 | Pons has paused launching. Try later. | | `missing_file` | 400 | The image upload had no `file` field. | | `not_an_image` | 400 | The uploaded file is not an image. | | `file_too_large` | 400 | The image is over 5 MB. | | `invalid_upload` | 400 | The upload could not be read. | ## Creator fees | Code | HTTP | Meaning | |---|---|---| | `nothing_to_claim` | 400 | Nothing is waiting for this wallet. If the message names an amount, that much has been earned but is still being collected by Pons; check back later. | | `sweep_requires_operator` | 400 | This launch's fees are collected by Pons on its own schedule, not by you. Nothing to do - claim again once they arrive. | ## Limits | Code | HTTP | Meaning | |---|---|---| | `rate_limited` | 429 | More than 3 requests per second from your IP to one endpoint. Wait a second and retry (`Retry-After: 1`). | | `too_many_connections` | 429 | Your IP already holds a socket of this kind: one launch feed and one data stream per address. Close the other one first. | ## Everything else | Code | HTTP | Meaning | |---|---|---| | `revert` | 400 | The transaction would fail on-chain for a reason not listed above; `message` says what the contract reported. | | `rpc_error` | 502 | Robinhood Chain could not be reached. Retry. | A transaction can still fail after you send it if the price moves past your slippage. `minOut` is your protection; a failed trade costs only gas. --- # Frequently Asked Questions > Answers about supported platforms, fees, private-key safety, amounts, slippage, the live feed and rate limits for the Pons Trading API. # Frequently asked questions ### Which platforms are supported? Every Pons token on Robinhood Chain - **Pons v2** on its bonding curve and in its Uniswap v4 pool after graduation, **Pons v1** in the Uniswap V3 pool it launched into - and any other token with a Uniswap pool here, such as the tokenised stocks (NVDA, TSLA, USDG …) themselves. One endpoint, the API works out where the token lives. Launching is Pons v2 only. ### Do I need an API key or an account? No. Every endpoint is open. The only limits are 3 requests per second per IP per endpoint, and one open socket per stream per IP: one launch feed and one data stream. ### Is my private key safe? It never leaves your machine. The API returns unsigned transactions; your script signs them with your own key and posts only the signed bytes to `/api/send`, which broadcasts them and can do nothing else with them. shrine.trade never sees the key, never holds funds, and cannot move anything on your behalf. ### What does it cost? 0.5% of each buy and sell, taken in the asset the trade settles in. Reads, the live feed and launching are free. Pons's own curve fee, creator tax and opening snipe tax apply on top, and gas is a few cents. See [Fees](/fees). ### Which currency is `amount` in? On a buy, always ETH, whatever the token is priced in - the API converts into USDG, NVDA or whichever asset the token uses. On a sell, tokens, or a percentage of your balance like `"50%"`. Add `toEth: true` to a sell to receive ETH instead of the quote asset. ### Why did my buy come back as three transactions? The token is priced in a tokenised asset you didn't hold. The first transaction buys exactly the missing amount with ETH, the second is a one-time approval, the third is the buy. Send them in order; the script does. Next time it is one. ### Are trades front-run protected? Yes, in three ways. Robinhood Chain is an Arbitrum-based L2 with a single sequencer and no public mempool: your transaction is not visible to anyone before it is ordered, so there is nothing for a bot to jump ahead of. Every buy and sell carries a minimum (`minOut`, from your slippage) that the contract enforces, so a trade that would fill worse than quoted fails instead. And a dev buy on a launch runs inside the transaction that creates the curve, so it cannot be sniped. What remains is ordinary price movement between your quote and your transaction landing, which the slippage setting covers. ### What is slippage and what should I set? How far the price may move against you between the quote and the transaction landing, in percent. Default 5. Raise it on fast-moving tokens if you see `slippage_exceeded`; lower it on quiet ones. It is enforced on-chain, so a trade that would exceed it fails instead of filling badly. ### Why is the wallet rejected when it holds enough for the trade? Because the node reserves gas at the limit price before running the transaction. The API tells you the exact amount to hold. Keep 0.01 ETH extra and this never comes up. Details in [gas and fees](/tutorials/robinhood-chain-gas-and-fees). ### When can I buy a new launch? Wait about five seconds. Pons taxes opening buys from near 99% down to zero. `GET /api/token/{address}?recipient=YOUR_WALLET` returns `snipeTaxBps`; buy when it is `0`. The [sniper tutorial](/tutorials/snipe-pons-launches) does exactly that. ### Can I get every trade, not just launches? Yes. The [Advanced Data Stream](/advanced-stream) pushes every buy, sell, liquidity add and liquidity removal for the tokens you name, on the curve and in Uniswap after graduation. It needs a free key: join the [Telegram group](https://t.me/+nEqAowTK8BZhZjFk) and ask. It is live and keeps no history. ### Is the live feed really free? Yes. `wss://api.shrine.trade/rh/api/launches/ws` streams every Pons launch, graduation and new Uniswap v4 pool. No key, one connection per IP. There is no history endpoint; it is a live feed. ### Where are my creator fees? They pile up on your token's curve until swept into Pons's escrow, then you claim them. `/api/fees/{wallet}?token=YOUR_TOKEN` shows both parts and `/api/claim-fees` builds the transactions. See [Claim Creator Fees](/creator-fees). ### Can AI coding tools read these docs? Yes. [llms.txt](/llms.txt) is the index and [llms-full.txt](/llms-full.txt) is every page as one text file, both regenerated on every release. ### Where do I get help? The [Telegram group](https://t.me/+nEqAowTK8BZhZjFk) for support; releases are announced on [X](https://x.com/shrinetrade). --- # Fees > shrine.trade charges 0.5% per trade on Pons and Uniswap v4 tokens on Robinhood Chain; launching and reading are free. Pons fees and the opening snipe tax apply on top. # Fees ## shrine.trade | Action | Fee | |---|---| | Buy | **0.5%** | | Sell | **0.5%** | | Create token | **Free** (Pons's launch fee applies — see below) | | Token info | Free | The 0.5% is taken in the asset the trade settles in - ETH on an ETH-priced token, USDG on a USDG-priced one, and so on - on the curve and on Uniswap alike. It excludes Pons's own trading fee, creator tax and opening snipe tax, and Uniswap pool fees. ## Network Robinhood Chain gas is paid in ETH and is small (100 ms blocks, cheap L2 execution). The API fills `gas` and the EIP-1559 fee fields for you. ## Lower fees **The 0.5% is negotiable.** Trading real size, building a bot or app on top of this, or need an endpoint that isn't here yet? Message me on [Telegram](https://t.me/+nEqAowTK8BZhZjFk) and we'll sort out a rate that works. --- # Live Pons Launches on Robinhood Chain > WebSocket feed of every new Pons launch, every graduation and every new Uniswap v4 pool on Robinhood Chain, the moment it happens. Free and keyless. # Live Launches & Graduations The whole token lifecycle on Robinhood Chain, pushed the moment it happens. Three things arrive on this one socket: - **Launches** - a token appears on the Pons bonding curve. - **Graduations** - a curve sells out and the token moves to its permanent Uniswap v4 pool. - **New Uniswap v4 pools** - including tokens launched straight on Uniswap, without a curve. Free, no key, no account. ``` wss://api.shrine.trade/rh/api/launches/ws ``` ## Copy this Click Pons or Uniswap v4 to choose what the script receives - the URL updates. **JavaScript** (Node 22+, no packages): ```js // Add ?protocols=PONS for launches only, or ?protocols=UNISWAP_V4 for // graduations and new pools only. No query = everything. const ws = new WebSocket("wss://api.shrine.trade/rh/api/launches/ws"); ws.onmessage = (e) => { const t = JSON.parse(e.data); // t.type: new_launch, graduated or new_pool console.log(JSON.stringify(t, null, 2)); }; ``` **Python** (`pip install websockets`): ```python import json from websockets.sync.client import connect with connect("wss://api.shrine.trade/rh/api/launches/ws") as ws: for raw in ws: t = json.loads(raw) # t["type"]: new_launch, graduated or new_pool print(json.dumps(t, indent=2)) ``` ## What you get On connect you're sent the **single most recent event** so the screen isn't blank, then everything new as it happens. Each message has a `type` and a `protocol`: | `type` | `protocol` | Means | |---|---|---| | `new_launch` | `PONS` | A token launched on the Pons curve - tradeable immediately. | | `graduated` | `UNISWAP_V4` | A Pons curve sold out; the token moved to its Uniswap v4 pool. | | `new_pool` | `UNISWAP_V4` | A Uniswap v4 pool was created. Includes Pons graduations **and** tokens launched directly on Uniswap. | Filter with `?protocols=PONS` or `?protocols=UNISWAP_V4` (omit it for both), or use the picker above. Fields that don't apply to an event are simply absent, so check `type` first. ```json { "type": "new_launch", "protocol": "PONS", "token": "0x84BD8478c0…", "curve": "0x2ffCc8d7…", "deployer": "0x1a2b3c…", "name": "Tollbooth", "symbol": "TOLLBOOTH", "pairToken": "ETH", "launchConfigId": 0, "graduationThreshold": "4200000000000000000", "uri": "ipfs://bafy…", "description": "the greenest coin on Robinhood Chain", "socials": { "twitter": "https://x.com/grene", "telegram": "", "discord": "", "website": "", "farcaster": "" }, "blockNumber": 50952986, "txHash": "0x…", "timestamp": 1756654321 } ``` **`uri`, `description` and `socials` come off the token contract itself** - Pons stores them on-chain, so the creator's image URI and links arrive with the event and there's nothing extra to fetch. A `new_pool` event carries `poolId`, `currency0`, `currency1`, `fee`, `tickSpacing` and `hooks` instead. A `hooks` of `0x0000…0000` means a plain Uniswap pool with no Pons hook - i.e. someone launched directly on Uniswap rather than through a curve. > **Don't buy the instant you see a launch.** Pons has sniper protection and taxes buys for about **3 seconds** after a launch, starting near **99%** and decaying to zero. Buying the moment a launch appears in this feed hands over almost your entire order as tax. ## Notes - **Reconnect on drop.** Long-lived sockets die; reconnect and you're re-seeded with the recent launches, so you won't miss much. - **One subscription per IP.** A second launch-feed connection from the same address is refused with `too_many_connections`. One socket with no filter carries everything, so you need no more; the [data stream](/advanced-stream) is a separate kind and can be open alongside it. - **Nothing is stored.** This is a live feed, not an archive - there's no history endpoint and no backfill. --- # Buy & Sell Pons Tokens on Robinhood Chain > Buy or sell any Pons v2 token on Robinhood Chain with one script, on the bonding curve or on Uniswap v4. Pay in ETH; tokens priced in USDG, NVDA or TSLA are converted for you. # Buy & Sell Copy the script, change three lines, run it. We build the transaction, your script signs and sends it - your private key never leaves your machine. One endpoint for every Pons token on Robinhood Chain, whichever generation launched it and wherever it lives: - **Pons v2** - on its bonding curve, then in its Uniswap v4 pool after graduation. - **Pons v1** - in the Uniswap V3 pool it launched into. - **Any Uniswap token** on Robinhood Chain - the tokenised stocks (NVDA, TSLA, USDG …) and tokens launched straight into a pool. Pass the token address and we work out which. The response says so in `protocol` (`PONS_V2` / `PONS_V1` / `UNISWAP`) and `venue` (`pons_curve`, `uniswap_v4`, `uniswap_v3`). ## What you need - **Node.js** or **Python**. - A wallet private key with some **ETH on Robinhood Chain**. - A token address. Take one from [any Pons token page](https://www.ponsfamily.com) or the [live launches feed](/live-launches). ## Example **JavaScript** ```js const { Wallet } = require("ethers"); // ─── change these three ─────────────────────────────── const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"; const TOKEN = "0xTHE_TOKEN_ADDRESS"; const AMOUNT = "0.01"; // to buy: ETH to spend. To sell: "1000" tokens, or "100%" const ACTION = "buy"; // "buy" or "sell" const TO_ETH = true; // selling a token priced in USDG/NVDA/…: also // convert the proceeds to ETH const SLIPPAGE = 5; // %, how far the price may move against you // ────────────────────────────────────────────────────── async function main() { const wallet = new Wallet(PRIVATE_KEY); // signs only; no node needed const res = await fetch("https://api.shrine.trade/rh/api/local-trade", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ action: ACTION, token: TOKEN, amount: AMOUNT, toEth: TO_ETH, slippage: SLIPPAGE, from: wallet.address, }), }); const data = await res.json(); if (data.error) throw new Error(`${data.error}: ${data.message}`); console.log("you should get about", data.quote.expectedOutFormatted, ACTION === "buy" ? "tokens" : data.quoteAsset.symbol); if (data.quoteSwap) { console.log("buying", data.quoteSwap.quoteOutFormatted, data.quoteAsset.symbol, "with", data.quoteSwap.ethInMaxFormatted, "ETH first"); } // Sign locally and hand the signed bytes to the API to broadcast - the key // never leaves this script, and you need no RPC of your own. for (const tx of data.txs) { const signed = await wallet.signTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gasLimit: BigInt(tx.gas), maxFeePerGas: BigInt(tx.maxFeePerGas), maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), nonce: tx.nonce, chainId: tx.chainId, type: 2, }); const sent = await (await fetch("https://api.shrine.trade/rh/api/send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signedTx: signed }), })).json(); if (sent.error) throw new Error(`${sent.error}: ${sent.message}`); if (sent.status !== "landed") throw new Error(`${tx.description} ${sent.status}: ${sent.explorer}`); console.log(tx.description + ": " + sent.explorer); } if (ACTION === "buy") { console.log(`bought ${data.name} (${data.symbol}) ${data.token} for ${data.ethInFormatted ?? data.amountInFormatted} ETH`); } else if (data.toEth) { console.log(`sold ${data.amountInFormatted} ${data.name} (${data.symbol}) ${data.token} for ${data.toEth.ethOutFormatted} ETH`); } else { console.log(`sold ${data.amountInFormatted} ${data.name} (${data.symbol}) ${data.token} for ${data.quote.expectedOutFormatted} ${data.quoteAsset.symbol}`); } } main(); ``` Save it as `trade.js`, then: ```bash npm install ethers node trade.js ``` **Python** ```python import requests from eth_account import Account # ─── change these three ─────────────────────────────── PRIVATE_KEY = "0xYOUR_PRIVATE_KEY" TOKEN = "0xTHE_TOKEN_ADDRESS" AMOUNT = "0.01" # to buy: ETH to spend. To sell: "1000" tokens, or "100%" ACTION = "buy" # "buy" or "sell" TO_ETH = True # selling a token priced in USDG/NVDA/…: also convert the proceeds to ETH SLIPPAGE = 5 # %, how far the price may move against you # ────────────────────────────────────────────────────── account = Account.from_key(PRIVATE_KEY) # signs only; no node needed res = requests.post( "https://api.shrine.trade/rh/api/local-trade", json={"action": ACTION, "token": TOKEN, "amount": AMOUNT, "toEth": TO_ETH, "slippage": SLIPPAGE, "from": account.address}, ) data = res.json() if "error" in data: raise SystemExit(f"{data['error']}: {data['message']}") unit = "tokens" if ACTION == "buy" else data["quoteAsset"]["symbol"] print("you should get about", data["quote"]["expectedOutFormatted"], unit) for tx in data["txs"]: signed = Account.sign_transaction( { "to": tx["to"], "data": tx["data"], "value": int(tx["value"]), "gas": tx["gas"], "maxFeePerGas": int(tx["maxFeePerGas"]), "maxPriorityFeePerGas": int(tx["maxPriorityFeePerGas"]), "nonce": tx["nonce"], "chainId": tx["chainId"], }, account.key, ) sent = requests.post("https://api.shrine.trade/rh/api/send", json={"signedTx": "0x" + signed.raw_transaction.hex().removeprefix("0x")}).json() if "error" in sent: raise SystemExit(f"{sent['error']}: {sent['message']}") if sent["status"] != "landed": raise SystemExit(f"{tx['description']} {sent['status']}: {sent['explorer']}") print(tx["description"] + ":", sent["explorer"]) if ACTION == "buy": print(f"bought {data['name']} ({data['symbol']}) {data['token']} for {data.get('ethInFormatted', data['amountInFormatted'])} ETH") elif data.get("toEth"): print(f"sold {data['amountInFormatted']} {data['name']} ({data['symbol']}) {data['token']} for {data['toEth']['ethOutFormatted']} ETH") else: print(f"sold {data['amountInFormatted']} {data['name']} ({data['symbol']}) {data['token']} for {data['quote']['expectedOutFormatted']} {data['quoteAsset']['symbol']}") ``` Save it as `trade.py`, then: ```bash pip install eth-account requests python trade.py ``` The script sends whatever comes back, in order. Usually that is one transaction; a first sell, or a buy on a token priced in USDG, NVDA or another asset, may need a one-time approval in front of it. Trades are front-run protected: Robinhood Chain has no public mempool, so nobody sees your transaction before the sequencer orders it, and the on-chain `minOut` guarantees the fill is never worse than your slippage allows. > **Don't buy a launch in its first seconds.** Pons has sniper protection and taxes buys for about 3 seconds after a launch. ## Selling Set `ACTION = "sell"`. `AMOUNT` is a number of tokens or a share of what you hold: | `AMOUNT` | Sells | |---|---| | `"1000"` | 1000 tokens | | `"50%"` | half your balance | | `"100%"` | everything | ## Tokens priced in USDG, NVDA, … A lot of Pons launches are priced in one of Robinhood Chain's tokenised assets instead of ETH. Nothing changes in the script: `AMOUNT` on a buy is always ETH. For a token priced in USDG, `"0.001"` means "spend 0.001 ETH", and the API works out how much USDG that is (`amountIn`, in `quoteAsset`) and hands the curve or pool that. If your wallet already holds that much USDG it is used as is; otherwise the transactions start with a swap that buys the missing amount with ETH, and `quoteSwap` says what that costs. `ethIn` in the response is the ETH you asked to spend. Selling pays out in that asset. With `toEth: true` (the scripts' `TO_ETH`) the proceeds are converted to ETH as well: on a graduated token it happens inside the same transaction, on a curve token it is a follow-up swap of the guaranteed minimum. `toEth` in the response says how much ETH you end up with and through which pools. The full list is on [Create Token](/create-token#supported-quote-assets). ## If it doesn't work | Error | What to do | |---|---| | `insufficient_funds` | Add ETH to the wallet - the amount plus a little for gas. | | `insufficient_balance` | You're selling more than you hold. Try `"100%"`. | | `token_not_found` | Not a Pons token. Check you copied the token, not the pool. | | `slippage_exceeded` | The price moved. Raise `SLIPPAGE` (percent, default `5`) and retry. | | `graduating` | The token is moving to its Uniswap pool. Wait a minute and retry. | Full list on [Errors](/errors). ## Sending `POST https://api.shrine.trade/rh/api/send` takes one signed transaction and broadcasts it through our Robinhood Chain endpoint, so the scripts need no RPC at all. It only forwards transactions this API built - Pons and Uniswap trades, launches, approvals and claims - and refuses anything else with `not_ours`. | Field | | | |---|---|---| | `signedTx` | 0x-hex | The signed transaction, as `wallet.signTransaction` returns it. | | `wait` | boolean, optional | Default `true`: wait for the receipt. `false` returns the hash as soon as the node accepts it. | ```json { "hash": "0x…", "status": "landed", "blockNumber": 55020118, "gasUsed": 119746, "explorer": "https://robinscan.io/tx/0x…" } ``` `status` is `landed`, `reverted`, or `pending` (not waited for, or not mined within 45 seconds). A landed launch also carries `token` and `curve`, read from the factory's event. ### Request and response `POST https://api.shrine.trade/rh/api/local-trade` **Request** | Field | | | |---|---|---| | `action` | `"buy"` or `"sell"` | | | `token` | address | The Pons token. | | `amount` | decimal string | Buy: ETH to spend, whatever the token is priced in. Sell: tokens (`"1000"`) or a share of your balance (`"50%"`, `"100%"`). | | `from` | address | Your wallet. | | `slippage` | number, optional | Percent, default `5`. | | `toEth` | boolean, optional | Sell only, tokens priced in an ERC-20: also convert the proceeds to ETH. | **Response** - a real one, a buy of an RDDT-priced token for 0.001 ETH: ```json { "action": "buy", "token": "0x77df8B452670deb3DA51B367CD58044422dcE4b0", "name": "Honest Work", "symbol": "HONEST", "curve": "0x07365E838a3f62675b81b3438137824F4B03457e", "phase": "NotGraduated", "venue": "pons_curve", "amountIn": "14171692423151839", "amountInFormatted": "0.014171692423151839", "quoteAsset": { "address": "0x05b37Fb53A299a1b874A619e1c4C404D52C36F4C", "symbol": "RDDT", "decimals": 18, "isNative": false }, "quote": { "expectedOut": "819167564794497226305697", "expectedOutFormatted": "819167.564794497226305697", "minOut": "778209186554772364990412", "curveFeeBps": 100, "creatorTaxBps": 0, "snipeTaxBps": 0, "shrineFeeBps": 50, "slippagePct": 5.0 }, "ethIn": "1000000000000000", "ethInFormatted": "0.001", "txs": [ { "to": "0x05b37Fb53A299a1b874A619e1c4C404D52C36F4C", "data": "0x\u2026", "value": "0", "gas": 80000, "maxFeePerGas": "580242501", "maxPriorityFeePerGas": "1", "nonce": 5, "chainId": 4663, "type": 2, "description": "approve_quote" }, { "to": "0xEc20E594D28a17511264dc73a84cd4AA957B0ABc", "data": "0x\u2026", "value": "0", "gas": 400000, "maxFeePerGas": "580242501", "maxPriorityFeePerGas": "1", "nonce": 6, "chainId": 4663, "type": 2, "description": "buy" } ] } ``` - `txs` - sign and send in order. `value`, `maxFeePerGas`, `maxPriorityFeePerGas` are decimal strings in wei; the rest are numbers. `description` says what each one is: `buy`, `sell`, a one-time `approve` / `approve_quote` (curve) or `approve_permit2` / `approve_router` (Uniswap), `buy_quote_with_eth` (a swap in front of a buy), `convert_to_eth` (a swap after a curve sell). - `name` / `symbol` - the token's own, so your script can say what it traded. - `protocol` - `PONS_V2` or `PONS_V1`. `venue` - `pons_curve` while a v2 token is on its bonding curve, `uniswap_v4` after graduation, `uniswap_v3` for every v1 token. The request is the same in all cases. v1 tokens are always priced in ETH, so `toEth` and the quote-asset conversion don't apply. - `quoteAsset` - what the trade is settled in: ETH, or the tokenised asset the token is priced in. `decimals` is not always 18. - `amountIn` - what the curve or pool receives, in `quoteAsset`. On a buy of an ERC-20-priced token, `ethIn` is the ETH you asked for and `amountIn` is what that ETH buys of the asset. - `quote.expectedOut` - what you should receive; `minOut` has your slippage applied and is enforced on-chain. Base units; the `*Formatted` fields are the human numbers. - `quote` fees - `curveFeeBps` and `creatorTaxBps` are Pons's, `shrineFeeBps` is ours (50 = 0.5%), `snipeTaxBps` is Pons's opening tax for your wallet right now. - `quoteSwap` - only when your wallet is short of the quote asset and a swap was added in front: `quoteOut` bought, `ethIn` quoted, `ethInMax` carried (refunded if unused), `route` the pools, found on-chain. - `toEth` - only on a sell with `toEth: true`: `ethOut` after our fee, `ethOutMin` enforced on-chain, `route` the pools. Graduated tokens do it in the same transaction; curve tokens add a `convert_to_eth` transaction. --- # Pons Token Info API > Read any Pons token on Robinhood Chain: curve reserves, graduation phase, snipe tax for your wallet and unswept creator fees. # Token Info Free, keyless reads. Use them to decide what to trade and to size a buy before you call `local-trade`. Amounts are raw base-unit strings (wei for ETH, 18-decimal units for tokens); fields ending in `Formatted` are the human-readable decimal. ## Token ``` GET https://api.shrine.trade/rh/api/token/{address} GET https://api.shrine.trade/rh/api/token/{address}?recipient=0x… ``` **JavaScript** ```js const TOKEN = "0xEad55618651062963DeE22728EFb1F3275DA7393"; async function main() { const res = await fetch(`https://api.shrine.trade/rh/api/token/${TOKEN}`); const t = await res.json(); if (t.error) throw new Error(`${t.error}: ${t.message}`); console.log(t.phase, "| sellable:", t.sellableTokensFormatted, "| curve fee:", t.curveFeeBps, "bps"); console.log("reserves:", t.quoteReserve, "wei /", t.tokenReserve, "tokens"); console.log("unswept creator fees:", t.unsweptFees.creatorShareEstimateFormatted); } main(); ``` Save it as `token.js` and run `node token.js` - no packages needed. **Python** ```python import requests TOKEN = "0xEad55618651062963DeE22728EFb1F3275DA7393" t = requests.get(f"https://api.shrine.trade/rh/api/token/{TOKEN}").json() if "error" in t: raise SystemExit(f"{t['error']}: {t['message']}") print(t["phase"], "| sellable:", t["sellableTokensFormatted"], "| curve fee:", t["curveFeeBps"], "bps") print("reserves:", t["quoteReserve"], "wei /", t["tokenReserve"], "tokens") print("unswept creator fees:", t["unsweptFees"]["creatorShareEstimateFormatted"]) ``` Save it as `token.py`, `pip install requests`, then `python token.py`. Add `?recipient=0x…` to get `snipeTaxBps` for that wallet. Example response: ```json { "token": "0x1111111111111111111111111111111111111111", "curve": "0x2222222222222222222222222222222222222222", "deployer": "0x3333333333333333333333333333333333333333", "creatorFeeRecipient": "0x3333333333333333333333333333333333333333", "pairToken": "ETH", "phase": "NotGraduated", "graduated": false, "readyToGraduate": false, "graduationThreshold": "4200000000000000000", "quoteReserve": "1712522872270353145", "tokenReserve": "981008795387803266283543676", "sellableTokens": "695294509673517551997829391", "sellableTokensFormatted": "695294509.673517551997829391", "curveFeeBps": 100, "creatorTaxBps": 0, "buybackEnabled": false, "poolFee": 0, "tickSpacing": 200, "snipeTaxBps": 0, "unsweptFees": { "heldBy": "curve", "tradingFee": "747321571942453", "tradingFeeFormatted": "0.000747321571942453", "creatorTax": "682955361004953", "creatorTaxFormatted": "0.000682955361004953", "creatorShareEstimate": "944517911184812", "creatorShareEstimateFormatted": "0.000944517911184812", "protocolShareBps": 3000, "buybackShareBps": 5000, "buybackEnabled": true, "creatorCanSweep": false, "hasUnswept": true } } ``` | Field | Meaning | |---|---| | `protocol` | `PONS_V2`, `PONS_V1`, `UNISWAP_V4` or `UNISWAP_V3`. For v1 tokens `curve` is the Uniswap V3 pool, `phase` is always `PoolCreated`, `quoteReserve` / `tokenReserve` are the pool's balances, and the fee and creator-fee fields are zero - v1 has no curve, no creator tax and no escrow. `UNISWAP_*` is any other token with a Uniswap pool on Robinhood Chain (the tokenised stocks included): `curve` is the pool, `pairToken` what it is priced in, `poolFee` the tier, and every Pons-only field is zero. | | `curve` | The bonding-curve contract — where trades go while `phase` is `NotGraduated`. | | `pairToken` | The quote asset. `"ETH"` for native-ETH launches (the only kind this API trades). | | `phase` / `graduated` | `NotGraduated` (curve live) · `Swept` · `PoolCreated` (trading on Uniswap v4) · `Rescued`. `graduated` is `true` for anything but `NotGraduated`. | | `readyToGraduate` | `true` once nothing is left to sell; sells revert from here on. | | `graduationThreshold` | Quote (wei) the curve must collect before it graduates. | | `quoteReserve` / `tokenReserve` | Current curve reserves — the price is their ratio. | | `sellableTokens` | Tokens still purchasable on the curve. `0` means it's about to graduate. | | `curveFeeBps` / `creatorTaxBps` | Pons's base curve fee and the launcher's creator tax (basis points, on the quote asset). | | `poolFee` / `tickSpacing` | The Uniswap v4 pool parameters this token graduates into. | | `snipeTaxBps` | Only meaningful with `?recipient=` — the opening tax *that wallet* would pay on a buy right now. | | `unsweptFees` | Fees this launch has earned that are still sitting on the curve (`heldBy: "curve"`) or the pool hook after graduation (`heldBy: "hook"`), in the quote asset. `creatorShareEstimate` is what the creator gets of them, and [`/api/claim-fees`](/creator-fees) collects it together with the escrow balance; `creatorCanSweep` is `false` for launches with buybacks on, where Pons collects on its own schedule. | --- # Robinhood Chain Gas and Fees Explained > What ETH on Robinhood Chain is, how to get it, what a trade or a launch actually costs, and why the wallet needs more than the trade amount. # Robinhood Chain gas and fees explained Robinhood Chain is an Ethereum L2 built on Arbitrum technology, chain id 4663. Gas is paid in ETH, blocks are about 100 ms apart, and a transaction costs a fraction of a cent to a few cents. ## Getting ETH onto Robinhood Chain ETH on Ethereum mainnet, Base or Arbitrum does not count. It has to be bridged to Robinhood Chain first, through the official bridge or a third-party bridge that lists the chain. Holding USDG, NVDA or another tokenised asset does not pay for gas either. The API's `insufficient_funds` error spells this out because it is the most common first-run problem. ## What a trade costs | | Typical gas | At 0.4 gwei | |---|---|---| | Buy on the curve | ~150k | ~0.00006 ETH | | Buy on Uniswap v4 | ~250k | ~0.0001 ETH | | One-time approval | ~50-65k | ~0.00003 ETH | | Swap ETH into a quote asset first | ~200k | ~0.00008 ETH | ## What a launch costs Launching deploys two contracts, so it is heavy: about 4.4 million gas, around 0.002 ETH, plus Pons's 0.0005 ETH launch fee. The API's response includes the exact figures. ## Why the wallet needs more than the trade amount A node reserves `gasLimit × maxFeePerGas` before running a transaction and refunds what is unused. The API pads the gas limit by 20% and sets the fee ceiling at 1.25× the current base fee, so the reservation is roughly one and a half times what is actually spent. A wallet holding exactly the trade amount plus the expected gas is rejected. The API checks this before you sign, sums the whole batch when there are approvals or swaps in front, and tells you the figure to hold. A practical rule: keep 0.01 ETH in a trading wallet on top of what you trade with. ## Where the fees go - **Gas** to the chain. - **Pons's curve fee and creator tax** to Pons and the creator, collected by Pons and claimable by the creator - see [Claim Creator Fees](/creator-fees). - **Uniswap pool fees** to the pool, on graduated tokens and on the ETH conversion swaps. Full list on [Fees](/fees). --- # How to Snipe Pons Launches on Robinhood Chain > A complete Node.js bot that watches the Pons launch feed, waits out the snipe tax, buys new tokens with ETH and sells at a target or a stop. # How to snipe Pons launches The whole loop in one script: watch every new Pons v2 launch, skip the ones you don't want, wait until the opening snipe tax is gone, buy with a fixed amount of ETH, and sell when the position hits a take-profit or a stop-loss. ## What you need - Node.js 22 or newer (WebSocket is built in) and `npm install ethers`. - A wallet private key with some ETH on Robinhood Chain - see [gas and fees](/tutorials/robinhood-chain-gas-and-fees). - Ten minutes. ## Why not buy the instant a launch appears Pons taxes buys for about five seconds after a launch, starting near 99% and decaying to zero. A bot that buys on the first event hands almost the whole order to the tax. The script below polls [`/api/token/{address}?recipient=YOUR_WALLET`](/token-info) until `snipeTaxBps` is `0`, then buys. ## The script ```js const { Wallet } = require("ethers"); // ─── change these ───────────────────────────────────── const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"; const BUY_ETH = "0.002"; // ETH per launch const TAKE_PROFIT = 2.0; // sell when the position is worth 2x what you paid const STOP_LOSS = 0.5; // ... or half const MAX_POSITIONS = 3; const ONLY_ETH_PRICED = true; // skip tokens priced in USDG, NVDA, … (they need a swap first) const DRY_RUN = true; // quote and log, never send // ────────────────────────────────────────────────────── const API = "https://api.shrine.trade/rh"; const wallet = new Wallet(PRIVATE_KEY); // signs only; no node needed const positions = new Map(); // token -> { paidEth, symbol } const get = async (path) => (await fetch(`${API}${path}`)).json(); const post = async (path, body) => (await fetch(`${API}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })).json(); async function send(txs) { for (const tx of txs) { if (DRY_RUN) { console.log(" dry-run:", tx.description); continue; } const signed = await wallet.signTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gasLimit: BigInt(tx.gas), maxFeePerGas: BigInt(tx.maxFeePerGas), maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), nonce: tx.nonce, chainId: tx.chainId, type: 2, }); const sent = await (await fetch("https://api.shrine.trade/rh/api/send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signedTx: signed }), })).json(); if (sent.error) throw new Error(`${sent.error}: ${sent.message}`); if (sent.status !== "landed") throw new Error(`${tx.description} ${sent.status}: ${sent.explorer}`); console.log(" " + tx.description + ": " + sent.explorer); } } async function waitForSnipeTaxToClear(token) { for (let i = 0; i < 20; i++) { const t = await get(`/api/token/${token}?recipient=${wallet.address}`); if (t.error) throw new Error(`${t.error}: ${t.message}`); if (t.snipeTaxBps === 0) return t; await new Promise((r) => setTimeout(r, 1000)); } throw new Error("snipe tax never cleared"); } async function onLaunch(t) { if (positions.size >= MAX_POSITIONS || positions.has(t.token)) return; if (ONLY_ETH_PRICED && t.pairToken !== "ETH") return; console.log(`launch ${t.symbol} ${t.token} priced in ${t.pairToken}`); await waitForSnipeTaxToClear(t.token); const q = await post("/api/local-trade", { action: "buy", token: t.token, amount: BUY_ETH, from: wallet.address }); if (q.error) { console.log(" skip:", q.error, q.message); return; } console.log(` buying ${BUY_ETH} ETH -> ~${q.quote.expectedOutFormatted} ${q.symbol}`); await send(q.txs); positions.set(t.token, { paidEth: Number(BUY_ETH), symbol: q.symbol }); } async function checkPositions() { for (const [token, pos] of positions) { const q = await post("/api/local-trade", { action: "sell", token, amount: "100%", toEth: true, from: wallet.address }); if (q.error) continue; // insufficient_balance right after a dry-run buy is expected const worth = Number(q.toEth ? q.toEth.ethOutFormatted : q.quote.expectedOutFormatted); const ratio = worth / pos.paidEth; if (ratio >= TAKE_PROFIT || ratio <= STOP_LOSS) { console.log(`selling ${pos.symbol}: worth ${worth.toFixed(5)} ETH, ${ratio.toFixed(2)}x`); await send(q.txs); positions.delete(token); } } } const ws = new WebSocket(`${API.replace("https", "wss")}/api/launches/ws?protocols=PONS`); ws.onmessage = (e) => { const t = JSON.parse(e.data); if (t.type === "new_launch") onLaunch(t).catch((err) => console.log(" error:", err.message)); }; ws.onclose = () => { console.log("feed closed"); process.exit(1); }; setInterval(() => checkPositions().catch(() => {}), 10_000); console.log("watching launches as", wallet.address, DRY_RUN ? "(dry run)" : ""); ``` Save it as `sniper.js`, then `node sniper.js`. Leave `DRY_RUN` on for the first run: it prints every launch it would buy and every transaction it would send. ## What each part does - **The feed** delivers every launch within a second of the block. `?protocols=PONS` keeps Uniswap pool events out. - **Filters** are yours to extend. The event carries `name`, `symbol`, `description`, `socials` and `pairToken`, so a blocklist of names or "only tokens with a website" is a one-liner. - **The buy** is in ETH whatever the token is priced in; the API converts. `ONLY_ETH_PRICED` is on by default because a stock-priced buy needs an extra swap and approval, which costs gas and time you don't have on a snipe. - **The sell check** quotes a full exit every ten seconds and compares it with what you paid. `toEth: true` makes the comparison honest for stock-priced tokens. - **Nothing is stored.** Restarting the script forgets its positions. Write `positions` to a file if you run it for real. --- # Trade Tokens Priced in NVDA, TSLA or USDG with ETH > How Pons tokens priced in tokenised stocks work on Robinhood Chain, and how to buy and sell them holding nothing but ETH. # Trade tokens priced in NVDA, TSLA or USDG with ETH About half of Pons v2 launches are not priced in ETH. The creator picks one of Robinhood Chain's tokenised assets - USDG, NVDA, TSLA, SPCX, GME and the rest - and the whole curve trades in that asset. You still don't need to hold any of it. ## What "priced in NVDA" means The token's bonding curve takes NVDA in and gives NVDA out. Its price, its graduation threshold and the creator's fees are all in NVDA. After graduation it moves to a Uniswap v4 pool paired with NVDA. Every response from the API tells you which asset in `quoteAsset`: ```json "quoteAsset": { "address": "0xd0601CE1…", "symbol": "NVDA", "decimals": 18, "isNative": false } ``` Decimals vary: USDG is 6, cbBTC is 8. The `*Formatted` fields already account for that. ## Buying with ETH Send the same buy as for any token, with `amount` in ETH: ```js const q = await post("/api/local-trade", { action: "buy", token: TOKEN, amount: "0.01", from: wallet.address }); ``` The API works out how much NVDA 0.01 ETH buys on Uniswap, reserves your slippage, and hands the curve that amount. If your wallet already holds enough NVDA it is used and no swap happens. Otherwise the response's `txs` opens with a swap: | `description` | What it is | |---|---| | `buy_quote_with_eth` | Swap ETH for exactly the missing NVDA on Uniswap v4. Unused ETH is refunded in the same transaction. | | `approve_quote` | One-time allowance for the router to spend your NVDA. | | `buy` | The buy itself. | `quoteSwap` in the response shows what the swap costs and the pools it goes through. NVDA has a direct ETH pool; an asset like RDDT routes through USDG, found on-chain each time. ## Selling back to ETH A sell pays out in the quote asset. Add `toEth: true` to get ETH instead: ```js const q = await post("/api/local-trade", { action: "sell", token: TOKEN, amount: "100%", toEth: true, from: wallet.address }); ``` On a graduated token the sell and the conversion are one transaction. On a curve token the conversion is a second transaction of the guaranteed minimum, so a small amount of the asset can remain. `toEth` in the response says how much ETH you end up with. ## Things that bite - **The dev buy on a launch is also in ETH** and converts the same way. - **A first buy of any stock-priced token needs an approval**, per asset, one time. The script sends it for you. - **Thin pools move.** Some graduated stock-priced tokens swing several percent a minute. If you see `slippage_exceeded`, retry or raise `slippage`.