Partner API

Launch coins on Emerald from your own product: a Telegram bot, a trading terminal, a script. Two HTTP calls to prepare, one on-chain transaction to launch. The chain itself is permissionless; the API only gates our pinning bandwidth.

Base URL

https://emeraldfi.fun/api/v1. Partner keys are issued by the Emerald team. Ask in the Emerald Telegram with a one-line description of your integration.

1. Pin metadata

POST /api/v1/metadata with header X-API-Key: emk_…. Accepts multipart/form-data (image file + fields) or application/json (image as a base64 data URI or an https URL we download). Returns the ipfs:// URI you pass to launch().

namerequired, 1–48 characters
symbolrequired, 1–12 characters, A–Z 0–9 (uppercased server-side)
descriptionoptional, up to 500 characters
imagerequired: PNG, JPG, WEBP or GIF, 2 MB max, EXIF stripped, SVG rejected
website, twitter, telegramoptional https:// URLs

Request (JSON)

curl -X POST https://emeraldfi.fun/api/v1/metadata \
  -H "X-API-Key: emk_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Wen Lambo",
    "symbol": "WEN",
    "description": "Priced in Lamborghinis.",
    "image": "https://example.com/wen.png",
    "telegram": "https://t.me/wenlambo"
  }'

Response 200

{
  "metadataUri": "ipfs://<jsonCID>",      // pass THIS to launch()
  "imageUri":    "ipfs://<imageCID>",
  "gateway": {
    "metadata": "https://emerald-finance.myfilebase.com/ipfs/<jsonCID>",
    "image":    "https://emerald-finance.myfilebase.com/ipfs/<imageCID>"
  }
}

Errors are plain English: 400 bad input (with the reason), 401 bad key, 429 rate limit (30 per minute, 500 per day, Retry-After header set).

2. Read live launch parameters

GET /api/v1/launch-info. Public, cacheable for 30 seconds. Every value is read live from chain: never hardcode the fee.

Response 200

{
  "chainId": 4663,
  "rpc": "https://rpc.mainnet.chain.robinhood.com",
  "launcher": "0x3bA3415217bFb083508F26e955fd61f6C95AbF79",
  "launchFeeWei": "…",                       // launchFee() live
  "pairs": [
    { "unit": "0xD56ac21dd0F3Ee62c8fe2b3473bb967D40fCe0D1", "symbol": "ROLEX", "usdPrice": 12500, "devBuyDecimals": 18 },
    { "unit": "0x886C830987fB5249339A7Cfc2a5422f31fcbA197", "symbol": "LAMBO", "usdPrice": 300000, "devBuyDecimals": 18 },
    { "unit": "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", "symbol": "USDG",  "usdPrice": 1, "devBuyDecimals": 6 },
    { "unit": "0x0000000000000000000000000000000000000000", "symbol": "ETH",   "usdPrice": …, "devBuyDecimals": 18,
      "note": "devBuyAmount rides in msg.value ON TOP of launchFee" }
  ],
  "launchParamsAbi": "(string name, string symbol, string metadataUri, address unit, uint24 poolFeePips, int24 rangeWidthTicks, uint256 devBuyAmount, bool feesToHolders)",
  "poolFeePipsRange": [100, 50000],
  "recommendedRangeWidthTicks": 20000,
  "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
  "locker": "0x69b363A212dED9b83045369CAd33Aa756167dEC5"
}

3. Send the launch transaction

Call launch(p) on the launcher with a single tuple argument of 8 fields, feesToHolders last. msg.value = launchFeeWei, plus devBuyAmount when the unit is ETH.

viem

import { createWalletClient, http, parseAbi } from "viem";

const info = await fetch("https://emeraldfi.fun/api/v1/launch-info").then(r => r.json());
const abi = parseAbi([
  "function launch((string name,string symbol,string metadataUri,address unit,uint24 poolFeePips,int24 rangeWidthTicks,uint256 devBuyAmount,bool feesToHolders) p) payable returns (address token, bytes32 poolId)",
  "event Launched(address indexed token,address indexed creator,address indexed unit,bytes32 poolId,string metadataUri,uint256 totalSupply,uint256 lpSupply)",
]);

const eth = info.pairs.find(p => p.symbol === "ETH");
const devBuy = 0n;                                   // simplest for bots: ETH pair or devBuyAmount 0
const hash = await wallet.writeContract({
  address: info.launcher, abi, functionName: "launch",
  args: [{ name: "Wen Lambo", symbol: "WEN", metadataUri, unit: eth.unit,
           poolFeePips: 10000, rangeWidthTicks: info.recommendedRangeWidthTicks,
           devBuyAmount: devBuy, feesToHolders: true }],
  value: BigInt(info.launchFeeWei) + devBuy,
});

ERC-20 pairs and dev buys

A dev buy in ROLEX, LAMBO or USDG needs the wallet to hold the unit and two Permit2 approvals first: unit.approve(permit2, amount) then permit2.approve(unit, launcher, amount, deadline). If you do not need a dev buy, pass devBuyAmount = 0 and skip both.

4. Read the result from the receipt

  • The coin address is token in the launcher's Launched event.
  • The LP position id is tokenId in the locker's Registered(token, tokenId) event. Never derive it from nextTokenId.
  • The coin appears on emeraldfi.fun automatically: the indexer picks up the event and renders name, symbol and image from the ipfs:// URI you pinned in step 1.

Limits and guardrails

Rate limit30 requests / minute, 500 / day per key
Images2 MB max, PNG · JPG · WEBP · GIF, EXIF stripped, SVG rejected
Textcontrol characters stripped, whitespace collapsed
Loggingevery pin is logged with your key id and the resulting CIDs

Contract addresses live on the contracts page. Questions: the Emerald Telegram.