Protocol Spec
Quote Struct & XDR
The Quote is a Soroban #[contracttype] struct. It is the core data structure of the protocol — everything revolves around constructing, signing, and verifying it.
Struct definition (Rust)
#[contracttype]
pub struct Quote {
pub quote_id: BytesN<32>, // SHA256(tokenIn|tokenOut|amountIn|taker|expiry) — unique
pub maker: Address, // Registered maker Stellar address
pub taker: Address, // Specific taker — quote is non-transferable
pub token_in: Address, // SAC address of token taker sends (EURC or USDC)
pub token_out: Address, // SAC address of token taker receives
pub amount_in: i128, // Amount taker sends, in stroops (1 token = 1e7 stroops)
pub amount_out: i128, // Amount taker receives (before fee deduction)
pub expiry: u64, // Unix timestamp (seconds) — quote invalid after this
pub salt: BytesN<32>, // Random 32 bytes — prevents signature replay
}Stroops — the unit of account
All amounts are in stroops — Stellar's base unit. 1 token = 10,000,000 stroops (1e7). Always multiply human-readable amounts before building a Quote struct.
// 20 EURC in stroops:
const amount_in = BigInt(20) * BigInt(10_000_000); // = 200_000_000nSigning a Quote (TypeScript)
import * as StellarSdk from "@stellar/stellar-sdk";
import { createHash } from "crypto";
// 1. Build the Quote struct fields
const quote = {
quote_id: sha256(tokenIn + tokenOut + amountIn + taker + expiry),
maker: MAKER_ADDRESS,
taker: TAKER_ADDRESS,
token_in: EURC_SAC,
token_out: USDC_SAC,
amount_in: 200_000_000n, // 20 EURC
amount_out: 218_100_000n, // 21.81 USDC (before fee)
expiry: Math.floor(Date.now() / 1000) + 35,
salt: crypto.randomBytes(32),
};
// 2. Encode as XDR ScVal (matches Soroban contracttype layout)
const quoteXdr = encodeQuoteAsScVal(quote); // using @stellar/stellar-sdk
// 3. Hash and sign
const msgHash = createHash("sha256").update(quoteXdr).digest();
const keypair = StellarSdk.Keypair.fromSecret(SIGNER_SECRET);
const signature = keypair.sign(msgHash); // 64-byte ed25519 signature
// 4. Submit bid to backend auction
await fetch(`/api/auctions/${auctionId}/bid`, {
method: "POST",
headers: { "Authorization": `Bearer ${API_KEY}` },
body: JSON.stringify({
quote,
signature: Buffer.from(signature).toString("hex"),
}),
});