Getting Started

Pricing Engines

The Maker SDK handles all the plumbing — WebSocket, API-key auth, ed25519 quote signing in the exact XDR the contract verifies, inventory reads, and trade confirmations. You only decide how to price, and that lives in a pluggable MakerEngine.

The engine interface

An engine is an object that answers two questions. The SDK calls it; you never touch the network or the signer.

MethodCalledReturns
getLevels()every ~3syour resting book { sellLevels, buyLevels } (empty arrays → go offline gracefully)
getQuote(ctx)on each RFQamountOut in stroops as a string, or null to skip this trade (no penalty)
onTradeConfirmed(trade)when a fill settles (optional)nothing — refresh inventory, hedge on a CEX, log

Tier 1 — Built-in ghost-price engine (default, no code)

npm run dev <yourname>

Set one ghost price (EURC per USDC); the SDK auto-bids it, fee-adjusted, on every RFQ. It is gated by an inventory check (never quotes more than ~80% of your pool balance) and a drift guard — warns at >1% drift from the live oracle mid, pauses quoting at >3%. Press Ctrl+R to re-price, Ctrl+C to disconnect.

Tier 2 / 3 — Custom engine

Point the SDK at any engine file with --engine. Note the -- separator — without it, npm swallows the flag:

npm run dev <yourname> -- --engine=./examples/fixed-rate-engine.ts
npm run dev <yourname> -- --engine=./examples/binance-engine.ts
npm run dev <yourname> -- --engine=./path/to/your-engine.ts

A custom engine owns its full pricing logic, so the SDK skips the ghost-price prompt and Ctrl+R. If the file is missing or doesn't implement getLevels/getQuote, the SDK logs the error and falls back to the built-in engine — it won't crash. Confirm which one loaded from the Engine: line in the startup banner.

Writing your own

// my-engine.ts
import { MakerEngine, RfqContext, PriceLevels } from '../src/types/MakerEngine'

const engine: MakerEngine = {
  async getLevels(): Promise<PriceLevels> {
    return {
      sellLevels: [{ quantity: '1000000000', price: '0.87800000' }], // USDC→EURC
      buyLevels:  [{ quantity: '1000000000', price: '1.13800000' }], // EURC→USDC
    }
  },
  async getQuote(ctx: RfqContext): Promise<string | null> {
    const rate   = ctx.tokenInSymbol === 'USDC' ? 0.8780 : 1 / 0.8780
    const feeAdj = 1 - ctx.feesBps * 0.0001            // protocol fee
    const out    = Math.floor(ctx.amountInHuman * rate * feeAdj * 1e7)
    return out > 0 ? out.toString() : null             // null = skip
  },
}
export default engine
Two things to get right: 1. Direction. A USDC→EURC rate (~0.88) is the inverse of an EURC→USDC rate (~1.14). If you pull a feed like Binance EURUSDT (~1.14 = USDT per EUR), the USDC→EURC rate is 1 / price, not price. 2. Inventory. The SDK does not stop a custom engine from quoting more than your pool holds — quoting size you can't fill makes the on-chain swap revert. Read your balance and cap your quote (the default engine does this automatically).

Working templates live in maker-sdk/examples/. Full guides: maker-sdk/CUSTOM_ENGINE.md (building engines) and maker-sdk/TESTING_ENGINES.md (E2E-testing + pitfalls).

HyperDex | Trade Without Limits