Overview
A buy converts the user’s USDC into$GOLD on Solana. The flow is:
- Quote —
POST /v1/buy→ GRAIL returns a partially-signed transaction with a quote - Co-sign — add the partner wallet signature and the user wallet signature
- Submit —
POST /v1/buy/:trade_id/submit(or broadcast directly to any Solana RPC) - Wait — the indexer writes the
Traderow after on-chain confirmation (~10–15s on devnet) - Fetch —
GET /v1/trades/:trade_idonce the indexer has caught up
Trade record appears only after the transaction confirms on-chain.
Signers — the most important thing to get right
A buy transaction requires three signatures:
If any of the three is missing,
/submit returns 400 broadcast_failed with Missing signature for public key <pubkey>.
The partially-signed transaction
The quote response containspartially_signed_transaction — a base64 string. It’s a Solana Transaction that already has GRAIL’s signature attached. You:
- Deserialize it
partialSign(partnerKeypair)andpartialSign(userKeypair)- Serialize it back to base64
Step 1 — Get a quote
Slippage
Two ways to set the slippage floor:slippage_bps(default50= 0.5%) — GRAIL computesmin_gold_out = quoted_gold * (10000 - slippage_bps) / 10000min_gold_out— absolute override. If supplied,slippage_bpsis ignored.
SlippageExceeded on-chain. If you see this, widen to 100–300 bps.
Step 2 — Co-sign with partner + user
Step 3 — Submit
Two options — both reach the same indexer.Option A: Submit via GRAIL
400 broadcast_failed with the reason in message.
Option B: Broadcast directly
trade_id memo — it doesn’t matter who broadcasts.
If you want a transaction to actually land and revert on-chain (for testing the failure path), broadcast directly with
skipPreflight: true. GRAIL’s /submit pre-flight would otherwise reject the tx before it lands.Step 4 — Wait and fetch
The indexer writes theTrade row after on-chain confirmation, typically 10–15 seconds on devnet. Polling GET /v1/trades/:trade_id right after submit returns 404 trade_not_found until the indexer catches up.
status: "failed" row is written if the transaction landed on-chain but the inti program reverted (e.g., SlippageExceeded). On failures, usdc_amount reflects the input from the memo; gold_amount and the price/fee fields are 0 (the program never computed them).
