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 a token from any launchpad on Robinhood Chain, wherever it lives right now:
- Pons v2 - on its bonding curve, then in its Uniswap v4 pool after graduation. Pons v1 - in the Uniswap V3 pool it launched into.
- Flap - on Flap's curve, then in its Uniswap v2 pair; Flap's Portal fills both.
- Bags - on its bonding curve, then in its Uniswap v4 pool.
- o1, Pools.trade, letscash, PAIR, Long - in the Uniswap v4 pool they launched into, hook fees included in the quote.
- 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, FLAP, BAGS, O1, POOLS_TRADE, LETSCASH, PAIR, LONG or UNISWAP) and venue (pons_curve, flap_curve, bags_curve, uniswap_v4, uniswap_v3, uniswap_v2).
Varo and Virtuals tokens are not tradable through the API yet; their launches are on the live feed.
What you need
- Node.js or Python.
- A wallet private key with some ETH on Robinhood Chain.
- A token address. Take one from any launchpad's site or the live launches feed.
Example
- JavaScript
- Python
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:
npm install ethers
node trade.js
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:
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.
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.
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.
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. |
{ "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:
{
"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,maxPriorityFeePerGasare decimal strings in wei; the rest are numbers.descriptionsays what each one is:buy,sell, a one-timeapprove/approve_quote(curve) orapprove_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_V2orPONS_V1.venue-pons_curvewhile a v2 token is on its bonding curve,uniswap_v4after graduation,uniswap_v3for every v1 token. The request is the same in all cases. v1 tokens are always priced in ETH, sotoEthand the quote-asset conversion don't apply.quoteAsset- what the trade is settled in: ETH, or the tokenised asset the token is priced in.decimalsis not always 18.amountIn- what the curve or pool receives, inquoteAsset. On a buy of an ERC-20-priced token,ethInis the ETH you asked for andamountInis what that ETH buys of the asset.quote.expectedOut- what you should receive;minOuthas your slippage applied and is enforced on-chain. Base units; the*Formattedfields are the human numbers.quotefees -curveFeeBpsandcreatorTaxBpsare Pons's,shrineFeeBpsis ours (50 = 0.5%),snipeTaxBpsis 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:quoteOutbought,ethInquoted,ethInMaxcarried (refunded if unused),routethe pools, found on-chain.toEth- only on a sell withtoEth: true:ethOutafter our fee,ethOutMinenforced on-chain,routethe pools. Graduated tokens do it in the same transaction; curve tokens add aconvert_to_ethtransaction.