Skip to main content

Overview

Buying gold follows a three-step flow:
  1. Get an estimate for the USDC cost
  2. Create a purchase transaction
  3. Sign and submit the transaction
There are two purchase types:
  • User purchase — buys gold for a specific user’s account
  • Partner purchase — buys gold into the partner’s PDA (reserves/treasury)
All trading endpoints require an API key with PARTNER_EXECUTIVE_AUTHORITY scope.

Step 1: Get a Buy Estimate

Calculate how much USDC is needed for a given amount of gold.
curl -X POST https://oro-tradebook-devnet.up.railway.app/api/trading/estimate/buy \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key" \
  -d '{
    "goldAmount": 0.5
  }'
{
  "success": true,
  "data": {
    "goldAmount": 0.5,
    "estimatedUsdcAmount": 2543.10,
    "goldPricePerOunce": 5086.2,
    "timestamp": "2024-01-15T10:30:00.000Z"
  }
}
Use the estimatedUsdcAmount value to set your maxUsdcAmount in the next step.

Step 2: Create a Purchase Transaction

User Purchase

Buy gold for a specific user. The flow differs based on your partner type.
User purchase transactions are Versioned Transactions (V0). Use VersionedTransaction from @solana/web3.js for signing — not Transaction.

Custodial

For custodial partners, only the executive authority signs. The co_sign and userAsFeePayer parameters are ignored.
curl -X POST https://oro-tradebook-devnet.up.railway.app/api/trading/purchases/user \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key" \
  -d '{
    "userId": "user-id",
    "goldAmount": 0.1,
    "maxUsdcAmount": 550
  }'

Self-Custody

For self-custody partners, the user must sign (and optionally the executive authority co-signs).
curl -X POST https://oro-tradebook-devnet.up.railway.app/api/trading/purchases/user \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key" \
  -d '{
    "userId": "user-id",
    "goldAmount": 0.1,
    "maxUsdcAmount": 550,
    "co_sign": false,
    "userAsFeePayer": true
  }'
Both return:
{
  "success": true,
  "data": {
    "purchaseId": "uuid-purchase-id",
    "goldAmount": 0.1,
    "quoteUsdcAmount": 508.62,
    "maxUsdcAmount": 550,
    "quotedGoldPrice": 5086.2,
    "status": "pending_signature",
    "transaction": {
      "serializedTx": "base64-encoded-transaction",
      "signingInstructions": {
        "walletType": "user_wallet",
        "signers": ["UserWalletAddress"],
        "expiresAt": "2024-01-15T10:35:00.000Z"
      }
    },
    "createdAt": "2024-01-15T10:30:00.000Z"
  }
}

Partner Purchase

Buy gold directly into the partner’s PDA (reserves). This is a Legacy Transaction signed only by the executive authority.
Partner purchases require USDC to be pre-funded in the partner PDA (centralVaultAddress). USDC is deducted from the PDA and gold is deposited to the PDA.
curl -X POST https://oro-tradebook-devnet.up.railway.app/api/trading/purchases/partner \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-api-key" \
  -d '{
    "goldAmount": 1.0,
    "maxUsdcAmount": 5500
  }'
{
  "success": true,
  "data": {
    "purchaseId": "uuid-purchase-id",
    "goldAmount": 1.0,
    "quoteUsdcAmount": 5086.20,
    "maxUsdcAmount": 5500,
    "quotedGoldPrice": 5086.2,
    "status": "pending_signature",
    "transaction": {
      "serializedTx": "base64-encoded-transaction",
      "signingInstructions": {
        "walletType": "PARTNER_AUTHORITY",
        "signers": ["ExecutiveAuthorityAddress"],
        "expiresAt": "2024-01-15T10:35:00.000Z"
      }
    },
    "createdAt": "2024-01-15T10:30:00.000Z"
  }
}

Step 3: Sign and Submit

Who signs user purchases:
Partner TypeSigners Required
CustodialExecutive authority only
Self-Custody (co_sign: false, userAsFeePayer: true)User wallet only
Self-Custody (all other combinations)User wallet + Executive authority
The code below is a simplified reference. In practice, the user signs on the client side and the partner’s executive authority signs on the backend before submitting the transaction.

Signing a Versioned Transaction (V0) — User Purchases

import { Keypair, VersionedTransaction, Connection } from "@solana/web3.js";

const connection = new Connection("https://api.devnet.solana.com");

// Deserialize the versioned transaction
const serializedTx = "base64-encoded-transaction"; // from response.data.transaction.serializedTx
const transaction = VersionedTransaction.deserialize(
  Buffer.from(serializedTx, "base64")
);

// Sign with required wallet(s) — see table above
const userKeypair = Keypair.fromSecretKey(/* user's secret key bytes */);
const executiveAuthority = Keypair.fromSecretKey(/* executive authority key bytes */);
transaction.sign([userKeypair, executiveAuthority]);

// Send to Solana
const txId = await connection.sendTransaction(transaction);

console.log("Transaction ID:", txId);

Signing a Legacy Transaction — Partner Purchases

Partner purchases are signed only by the executive authority.
import { Keypair, Transaction, Connection, sendAndConfirmTransaction } from "@solana/web3.js";

const connection = new Connection("https://api.devnet.solana.com");

const serializedTx = "base64-encoded-transaction"; // from response.data.transaction.serializedTx
const transaction = Transaction.from(Buffer.from(serializedTx, "base64"));

// Only executive authority signs partner purchases
const executiveAuthority = Keypair.fromSecretKey(/* your secret key bytes */);

// Sign and send to Solana
const txId = await sendAndConfirmTransaction(connection, transaction, [executiveAuthority]);

console.log("Transaction ID:", txId);
If you prefer not to send the transaction yourself, you can use the POST /api/transactions/submit endpoint instead. Just pass the base64-encoded signed transaction and the API will send it to the network for you.

Slippage Protection

Setting maxUsdcAmount too close to the estimate may cause transactions to fail during volatile markets. Setting it too high increases your exposure to price swings.
The maxUsdcAmount parameter protects against price movements between your estimate and transaction execution:
  • If the actual USDC cost exceeds maxUsdcAmount, the on-chain program rejects the transaction
  • Set it to the estimatedUsdcAmount value plus a buffer (e.g., 5–10%) to account for price fluctuations
maxUsdcAmount = estimatedUsdcAmount * 1.05  // 5% slippage tolerance

Next Steps

To convert gold back to USDC, proceed to Selling Gold.