Skip to main content

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.

Pons
Uniswap v4

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

Example

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

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.

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:

AMOUNTSells
"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

ErrorWhat to do
insufficient_fundsAdd ETH to the wallet - the amount plus a little for gas.
insufficient_balanceYou're selling more than you hold. Try "100%".
token_not_foundNot a Pons token. Check you copied the token, not the pool.
slippage_exceededThe price moved. Raise SLIPPAGE (percent, default 5) and retry.
graduatingThe 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"
tokenaddressThe Pons token.
amountdecimal stringBuy: ETH to spend, whatever the token is priced in. Sell: tokens ("1000") or a share of your balance ("50%", "100%").
fromaddressYour wallet.
slippagenumber, optionalPercent, default 5.
toEthboolean, optionalSell 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, 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 (100 = 1%), 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.