Skip to main content
This guide walks a trader through buying or selling a Convallax option entirely from code (e.g. a Node.js script with ethers). It’s the same flow the website runs when you click Trade — just driven by you directly. The mental model: trading is two phases.
  1. Price discovery (off-chain, HTTP). You ask our backend for a price, market makers compete, and you accept the best quote. No wallet signing, no gas.
  2. Settlement (on-chain, one transaction). You sign a single fill() transaction with your own wallet. This atomically pays the premium, locks any collateral, and mints your option tokens.
Your keys never leave your machine. The backend only coordinates pricing — it never holds your funds and never signs for you.

What You Need

1

A funded wallet on Polygon Amoy

Any EVM wallet. You need test USDC for the premium (and collateral, if you sell) plus a little POL for gas. Get USDC from the faucet in the dashboard or the testnet guide.
2

No API key

Trading endpoints are public — no API key required. (API keys are only for market makers.) Your on-chain fill() transaction is your authorization.
3

One USDC approval

Before fill(), you grant a USDC allowance to the contract the backend tells you to:
  • Buying (long): approve the Settlement contract for the premium.
  • Selling (short): approve the Core contract for the collateral.
The commit response returns the exact spender and amount in a takerApproval object — you don’t have to compute it.

The Four Steps

1. Discover what you can trade

Find the markets and series that exist on-chain. A trade can only settle against a registered series.
# What markets are available?
curl https://api.convallax.com/v1/markets

# What series exist for a market? (filter by conditionId)
curl "https://api.convallax.com/v1/series?conditionId=0xa4ddc188...&optionType=call&settled=false"
From this you get the building blocks of a trade: the market’s conditionId and yesClobTokenId, plus a strikeBps, expiry, and optionType. See List Markets and List Series.

2. Request a live quote

Open a quote request describing exactly what you want. The backend broadcasts it to all connected market makers.
curl -X POST https://api.convallax.com/quote-requests \
  -H "Content-Type: application/json" \
  -d '{
    "wallet": "0xYourWallet...",
    "market": {
      "conditionId": "0xa4ddc188...",
      "yesTokenId": "51508280778...",
      "question": "Will there be a Hantavirus outbreak in 2026?"
    },
    "option": {
      "optionType": "call",
      "strikeBps": 50,
      "expiryMs": 1788220800000
    },
    "trade": { "side": "buy", "budgetUsd": 100 }
  }'
The option block fully identifies the contract (type, strike, expiry); makers price it using their own data and models.
Sizing. On a buy you submit a USDC budget (trade.budgetUsd). Makers quote a per-option price, and at commit you receive floor(budgetUsd / price) whole options (capped by the winning maker’s quoted size), paying contracts × price. On a sell you submit a contract count instead: "trade": { "side": "sell", "size": 100 }.
The response gives you a requestId:
{ "success": true, "requestId": "abc-123", "expiresAt": "...", "makersConnected": 2 }
No wallet signature is needed to open a quote request — your authorization happens later, at the on-chain fill(). See Create Quote Request.

3. Watch quotes, then accept the best

Quotes arrive within a second or two. Read the current best price, then commit when you’re happy. The recommended way to watch is the SSE stream (prices push to you live); polling also works.
# Option A — stream live pricing (recommended)
curl -N https://api.convallax.com/quote-requests/abc-123/stream

# Option B — poll
curl https://api.convallax.com/quote-requests/abc-123/quotes
When you like the price, commit. You only send your wallet address — the backend selects the best valid quote and asks that maker to cryptographically sign the exact order:
curl -X POST https://api.convallax.com/quote-requests/abc-123/commit \
  -H "Content-Type: application/json" \
  -d '{ "wallet": "0xYourWallet..." }'
The response hands you everything needed to settle on-chain, in the onchain object:
{
  "success": true,
  "message": "Order signed by maker — call fill() on-chain",
  "onchain": {
    "settlementAddress": "0x721C428f...",
    "coreAddress": "0x5bcE81D5...",
    "order": {
      "maker": "0xMarketMaker...",
      "seriesId": "...",
      "optionAmount": "100000000",
      "premiumAmount": "12000000",
      "makerSelling": true,
      "taker": "0xYourWallet...",
      "validUntil": 1750000000,
      "nonce": 12345
    },
    "makerSignature": "0x...",
    "takerApproval": { "spender": "0x721C428f...", "amount": "12000000", "reason": "premium" }
  }
}
You now hold the maker’s signature authorizing this exact trade. Nothing has touched the chain yet. See Commit.

4. Settle on-chain (fill())

Two wallet actions — approve USDC, then call fill() with the order and the maker’s signature. This single transaction atomically pulls the premium, locks the maker’s collateral, and mints your option tokens.
import { ethers } from "ethers";

const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
const { settlementAddress, order, makerSignature, takerApproval } = res.onchain;

// 1. Approve USDC for the spender the backend specified
const usdc = new ethers.Contract(USDC_ADDRESS, [
  "function approve(address,uint256) returns (bool)",
  "function allowance(address,address) view returns (uint256)",
], wallet);

const current = await usdc.allowance(wallet.address, takerApproval.spender);
if (current < BigInt(takerApproval.amount)) {
  await (await usdc.approve(takerApproval.spender, takerApproval.amount)).wait();
}

// 2. Call fill() with the maker-signed order
const settlement = new ethers.Contract(settlementAddress, [
  "function fill((address maker,uint256 seriesId,uint256 optionAmount,uint256 premiumAmount,bool makerSelling,address taker,uint256 validUntil,uint256 nonce) order, bytes makerSignature)",
], wallet);

const tx = await settlement.fill(order, makerSignature);
await tx.wait();
console.log("Filled:", tx.hash);
That’s it — you now hold the option tokens (an ERC-1155 balance keyed by seriesId).
Commit and fill promptly. The signed order has a short validUntil window; if it expires before your fill() lands, re-commit to get a fresh signature.

The Whole Flow

// 1. Discover
const { series } = await get("/v1/series?conditionId=0x...&optionType=call&settled=false");

// 2. Request a quote
const { requestId } = await post("/quote-requests", { wallet, market, option, trade });

// 3. Watch, then accept
await waitForGoodPrice(`/quote-requests/${requestId}/stream`);
const { onchain } = await post(`/quote-requests/${requestId}/commit`, { wallet });

// 4. Settle on-chain
await usdc.approve(onchain.takerApproval.spender, onchain.takerApproval.amount);
await settlement.fill(onchain.order, onchain.makerSignature);
Steps 1–3 are ordinary HTTP — no wallet, no gas. Only step 4 touches the chain.

After the Trade

  • Check your position: your option tokens are an ERC-1155 balance on ConvallaxOptionToken, keyed by seriesId. See Verify Your Positions.
  • At expiry: once the series is settled, holders claim their payout with claimHolderPayout. See the Settlement guide for how resolution works.

Why a Signature, Not a Custodial Order Book?

Convallax is non-custodial. The maker signs “I agree to these exact terms,” and your fill() redeems that signature on-chain. Neither side can alter the terms, and the backend never touches anyone’s funds or keys — it’s purely a matchmaker for price discovery.