Spire ProtocolSPIRE PROTOCOLDOCS

Quickstart

From nothing to a novated obligation. The path below runs against the testnet, where collateral is faucet-funded and no real asset moves.

1. Get a venue identifier

Onboarding issues two things: a venueId and a signing key. The key signs fills, and the identifier tells the protocol which venue is making the claim.

export SPIRE_VENUE_ID="vn_7Q2K..."
export SPIRE_SIGNING_KEY="0x..."

The signing key never leaves your infrastructure. The protocol only ever sees signatures.

2. Install

npm install @spireproto/sdk

The package is not published yet. This page describes the intended surface so that an integration can be designed against it now.

3. Connect

import { Clearing } from '@spireproto/sdk';

const clearing = new Clearing({
  venue:   process.env['SPIRE_VENUE_ID'],
  signer:  process.env['SPIRE_SIGNING_KEY'],
  chainId: 4663,
  network: 'testnet',
});

4. Check that you can clear the asset

Clearing is granted per asset, not per venue. Ask before you send.

const asset = await clearing.assets.get('0xA1c...');

console.log(asset.tier);          // 1
console.log(asset.initialMargin); // 0.08
console.log(asset.clearable);     // true

If clearable is false the fill will be rejected with SPIRE-1204. Handle it as a venue level condition, not as a per-trade error.

5. Send a matched fill

const obligation = await clearing.obligations.fromFill({
  fill: {
    fillId:    'v7-9f31c2',
    asset:     '0xA1c...',
    buyer:     '0xBd4...',
    seller:    '0x39e...',
    size:      '250000000000000000000',   // 250 units, 18 decimals
    price:     '187420000',               // 187.42 USDC, 6 decimals
    matchedAt: Math.floor(Date.now() / 1000),
  },
  window: 'current',
});

console.log(obligation.obligationId);  // 0x8c1f...
console.log(obligation.state);         // "novated"
console.log(obligation.windowId);      // 918342

The call returns when novation is done. At that point the bilateral link between buyer and seller no longer exists and both face Spire Protocol.

6. Read the net

The number that matters is not the fill, it is what the member actually has to deliver at the close of the window.

const pos = await clearing.positions.get({
  member:   '0xBd4...',
  asset:    '0xA1c...',
  windowId: obligation.windowId,
});

console.log(pos.gross);       // 1000000000000000000000
console.log(pos.net);         // 250000000000000000000
console.log(pos.utilisation); // 1840  (18.4% of the limit)

Gross four times the net is normal inside an active window. That difference is the capital that never has to move. See Netting.

7. Watch it settle

clearing.on('window.finalised', async ({ windowId }) => {
  const net = await clearing.positions.list({ windowId });
  // deliver against net, or let the settlement contract pull it
});

clearing.on('obligation.settled', ({ obligationId }) => {
  // terminal, nothing further happens to this obligation
});

What you have not had to do

You did not prefund the trade, you did not price the counterparty, and you did not write anything to the chain per fill. One write happens per member per asset per window, against the net.

Next

Read Integration for what a venue has to decide before going live, and Errors for the failure modes worth handling explicitly.

Not on JavaScript? Every step above is a plain HTTP call. See HTTP API.

Last updated 31 August 2026 Docs source on GitHub