Overview
Selling gold converts gold tokens back to USDC. The flow mirrors buying:
- Get an estimate for the USDC you’ll receive
- Create a sell transaction
- Sign and submit the transaction
There are two sell types:
- User sale — sells gold from a specific user’s account
- Partner sale — sells gold from the partner’s PDA (reserves/treasury)
All trading endpoints require an API key with PARTNER_EXECUTIVE_AUTHORITY scope.
Step 1: Get a Sell Estimate
Calculate how much USDC you’ll receive for a given amount of gold.
curl -X POST https://oro-tradebook-devnet.up.railway.app/api/trading/estimate/sell \
-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 minimumUsdcAmount in the next step.
Step 2: Create a Sell Transaction
User Sale
Sell gold from a specific user’s account. The flow differs based on your partner type.
User sale transactions are Versioned Transactions (V0). Use VersionedTransaction from @solana/web3.js for signing.
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/sales/user \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key" \
-d '{
"userId": "user-id",
"goldAmount": 0.1,
"minimumUsdcAmount": 480
}'
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/sales/user \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key" \
-d '{
"userId": "user-id",
"goldAmount": 0.1,
"minimumUsdcAmount": 480,
"co_sign": false,
"userAsFeePayer": true
}'
Both return:
{
"success": true,
"data": {
"sellId": "uuid-sale-id",
"goldAmount": 0.1,
"quoteUsdcAmount": 508.62,
"minimumUsdcAmount": 480,
"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 Sale
Sell gold from the partner’s PDA (reserves). This is a Legacy Transaction signed only by the executive authority.
Partner sales deduct gold from the partner PDA and deposit USDC to the PDA. The partner must have sufficient gold balance.
curl -X POST https://oro-tradebook-devnet.up.railway.app/api/trading/sales/partner \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key" \
-d '{
"goldAmount": 1.0,
"minimumUsdcAmount": 4800
}'
{
"success": true,
"data": {
"sellId": "uuid-sale-id",
"goldAmount": 1.0,
"quoteUsdcAmount": 5086.20,
"minimumUsdcAmount": 4800,
"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 sales:
| Partner Type | Signers Required |
|---|
| Custodial | Executive 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 Sales
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 Sales
Partner sales 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 sales
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 minimumUsdcAmount too close to the estimate may cause transactions to fail during volatile markets. Setting it too low means you accept larger price drops.
The minimumUsdcAmount parameter protects against receiving less USDC than expected:
- If the actual USDC received would be less than
minimumUsdcAmount, the on-chain program rejects the transaction
- Set it to the
estimatedUsdcAmount value minus a buffer (e.g., 5–10%) to account for price fluctuations
minimumUsdcAmount = estimatedUsdcAmount * 0.95 // 5% slippage tolerance
Next Steps
To manage your treasury and partner settings, proceed to Withdrawals & Partner Management.