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.
Pass the token address and we work out which. The response says so in protocol (PONS_V2 / PONS_V1) 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 or the live launches feed.
Example
- JavaScript
- Python
const { JsonRpcProvider, 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,
new JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com", 4663),
);
const res = await fetch("https://api.shrine.trade/evm/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 send, in order - the key never leaves this script.
for (const tx of data.txs) {
const sent = await wallet.sendTransaction({
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,
});
await sent.wait();
console.log(tx.description + ": https://robinscan.io/tx/" + sent.hash);
}
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 web3 import Web3
# ─── 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
# ──────────────────────────────────────────────────────
w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))
account = w3.eth.account.from_key(PRIVATE_KEY)
res = requests.post(
"https://api.shrine.trade/evm/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 = w3.eth.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,
)
h = w3.eth.send_raw_transaction(signed.raw_transaction)
w3.eth.wait_for_transaction_receipt(h)
print(tx["description"] + ":", f"https://robinscan.io/tx/0x{h.hex().removeprefix('0x')}")
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 web3 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.
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.
Request and response
POST https://api.shrine.trade/evm/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": 100,
"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 (100 = 1%),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.