Create Token
POST https://api.shrine.trade/evm/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 here.
The key never leaves your machine. It signs locally and the signed transaction goes straight to the RPC you included in the api request; it 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
- Python
const { readFileSync } = require("node:fs");
const { JsonRpcProvider, Wallet, id, getAddress, dataSlice } = 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 provider = new JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com", 4663);
const wallet = new Wallet(PRIVATE_KEY, provider);
// 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/evm/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/evm/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. Submit through your own RPC. A dev buy in an ERC-20 needs an approval
// first, so send whatever comes back, in order.
let receipt, hash;
for (const tx of body.txs) {
const sent = await wallet.sendTransaction({
to: tx.to,
data: tx.data,
value: BigInt(tx.value), // launch fee (+ dev buy) in wei
gasLimit: BigInt(tx.gas),
maxFeePerGas: BigInt(tx.maxFeePerGas),
maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas),
nonce: tx.nonce,
chainId: tx.chainId,
type: tx.type,
});
hash = sent.hash;
receipt = await sent.wait();
}
// 5. Read the new addresses out of the TokenLaunched event.
const topic0 = id("TokenLaunched(address,address,address,address,uint256,uint256)");
const log = receipt.logs.find((l) => l.topics[0] === topic0);
console.log("token launched:", getAddress(dataSlice(log.topics[1], 12)));
console.log("curve:", getAddress(dataSlice(log.topics[2], 12)));
console.log("tx:", "https://robinscan.io/tx/" + hash);
}
main();
Save it as launch.js, put your image next to it as logo.png, then:
npm install ethers
node launch.js
import requests
from web3 import Web3
# ─── 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
# ──────────────────────────────────────────────────────
w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))
account = w3.eth.account.from_key(PRIVATE_KEY)
# 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/evm/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/evm/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. Submit through your own RPC. A dev buy in an ERC-20 needs an approval
# first, so send whatever comes back, in order.
for tx in body["txs"]:
signed = w3.eth.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,
)
h = w3.eth.send_raw_transaction(signed.raw_transaction) # web3.py v6: signed.rawTransaction
receipt = w3.eth.wait_for_transaction_receipt(h)
# 5. Read the new addresses out of the TokenLaunched event.
topic0 = w3.keccak(text="TokenLaunched(address,address,address,address,uint256,uint256)")
log = next(l for l in receipt["logs"] if l["topics"] and l["topics"][0] == topic0)
print("token launched:", w3.to_checksum_address(log["topics"][1][-20:]))
print("curve:", w3.to_checksum_address(log["topics"][2][-20:]))
print("tx:", f"https://robinscan.io/tx/0x{h.hex().removeprefix('0x')}")
Save it as launch.py, put your image next to it as logo.png, then:
pip install web3 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:
{
"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) orlaunch_and_buy(with a dev buy). A dev buy in an ERC-20 pair token can putbuy_quote_with_eth(if the wallet is short of it) andapprove_quotein front.launchFee- Pons's fee, always ETH, read from the factory at request time. The transaction'svalueis this plus an ETH dev buy.pairToken,pairTokenSymbol,pairTokenDecimals- what the curve is priced in.graduationThresholdis 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,snipeTaxExemptionsecho what the launch will use.initialBuy,expectedTokensOut,minTokensOut- the dev buy in the pair token's base units and what it returns. Only with aninitialBuy. On an ERC-20-priced launchinitialBuyEthis the ETH you gave andinitialBuywhat 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.