Docs

TypeScript SDK

@winsznx/blindmarkets-sdk gives you everything you need to build on top of BlindMarkets — intent construction, encryption, gateway communication, and status polling. Works in Node.js and the browser.


Install

bash
npm install @winsznx/blindmarkets-sdk

Or with yarn / pnpm / bun — same package name.

Published on GitHub Packages: @winsznx/blindmarkets-sdk


Initialize the client

The GatewayClient handles all HTTP communication. It retries on rate limits and server errors with exponential backoff, and cancels stalled requests after a configurable timeout.

client.ts
import { GatewayClient } from '@winsznx/blindmarkets-sdk';

const client = new GatewayClient({
  baseUrl: 'https://gateway-production-7e9c.up.railway.app',
  apiKeyHeader: 'X-API-KEY',
  apiKey: process.env.GATEWAY_API_KEY!,
  timeoutMs: 10_000,
  maxRetries: 3,
  retryBaseDelayMs: 200,
  retryMaxDelayMs: 5_000,
  retryJitterMs: 100,
});

Build an intent

IntentBuilder constructs the intent object and computes all the commitments your wallet needs to sign. It validates every field before returning — bad inputs throw a ValidationError, not a silent failure later.

build-intent.ts
import { IntentBuilder } from '@winsznx/blindmarkets-sdk';

const intent = new IntentBuilder()
  .userAddress('0xYOUR_WALLET_ADDRESS')
  .assetIn('0xSTRK_CONTRACT_ADDRESS')
  .assetOut('0xUSDC_CONTRACT_ADDRESS')
  .amount(1_000_000_000_000_000_000n)   // 1 STRK (18 decimals)
  .minOutput(2_000_000n)                 // 2 USDC (6 decimals)
  .maxFeeBps(50)                         // 0.5% max solver fee
  .deadlineSeconds(300n)                 // expires in 5 minutes
  .privacyMode('HIDDEN_AMOUNT')          // hide the amounts
  .build();

The builder generates a random nonce automatically and derives the intent ID, amount commitment, and intent hash from your inputs. You get back a fully formed Intent object.


Encrypt and submit

Before sending to the gateway, encrypt the intent using the gateway's public key. The encryption uses X25519 key exchange and AES-256-GCM — your actual amounts are never sent in plain text.

submit.ts
import { encryptIntentForGateway, createCommitment } from '@winsznx/blindmarkets-sdk';

// Fetch the gateway's current public key
const { gateway_public_key } = await client.getGatewayPublicKey();

// Encrypt the intent payload
const encrypted = await encryptIntentForGateway(intent, gateway_public_key);

// Build the on-chain commitment (for your wallet to sign)
const commitment = createCommitment(intent);

// Submit to gateway
const response = await client.submitIntent({
  intent_id: intent.intentId,
  user_address: intent.userAddress,
  ciphertext: encrypted.ciphertextHex,
  encrypted_session_key: encrypted.encryptedSessionKeyHex,
  client_public_key: encrypted.clientPublicKeyHex,
  commitment: commitment.intentHash,
  user_signature: [],   // filled in after wallet signs
  nonce: intent.nonce,
});

console.log(response.batch_id);                  // which batch window this landed in
console.log(response.awaiting_user_transaction); // true — wallet tx still needed

Sign the commitment with the wallet

After submitting to the gateway, your wallet sends the on-chain commitment transaction. Use starknet.js or the wallet's provider to invoke commit_intent on the IntentRegistry contract:

commit-onchain.ts
import { Contract, RpcProvider, WalletAccount } from 'starknet';

const provider = new RpcProvider({ nodeUrl: 'https://starknet-sepolia.drpc.org' });
const account = new WalletAccount(provider, window.starknet);

const registry = new Contract(INTENT_REGISTRY_ABI, INTENT_REGISTRY_ADDRESS, account);

const tx = await registry.commit_intent(
  commitment.intentId,
  commitment.intentHash,
  commitment.amountCommitment,
  commitment.minOutputCommitment,
  commitment.maxFeeBps,
  commitment.deadline,
  privacyModeToFelt(commitment.privacyMode),
  commitment.nonce,
);

await provider.waitForTransaction(tx.transaction_hash);

// Tell the gateway the wallet tx confirmed
await client.reconcileOnchainIntent(intent.intentId, {
  action: 'COMMITTED',
  user_address: intent.userAddress,
  tx_hash: tx.transaction_hash,
});

Why two steps?

Submitting to the gateway and committing on-chain are separate so the encrypted payload reaches the gateway before the on-chain fingerprint is visible. This prevents timing attacks where someone sees the chain tx and races to read the gateway.

Poll for status

poll-status.ts
const status = await client.getIntentStatus(intent.intentId);
// status.status: 'pending' | 'committed' | 'in_batch' | 'settled' | 'failed' | 'cancelled' | 'expired'

// Or list all your intents
const { intents } = await client.listIntents({
  userAddress: '0xYOUR_ADDRESS',
  status: 'settled',
  limit: 20,
  offset: 0,
});

Error handling

The SDK throws two error types. Import them to catch specifically:

error-handling.ts
import { ValidationError, NetworkError } from '@winsznx/blindmarkets-sdk';

try {
  const intent = new IntentBuilder().build(); // throws — missing required fields
} catch (e) {
  if (e instanceof ValidationError) {
    console.error('Bad input:', e.message);
  }
  if (e instanceof NetworkError) {
    console.error('Gateway unreachable or returned an error:', e.message);
  }
}
  • ValidationError — bad inputs, missing required fields, expired deadlines
  • NetworkError — gateway returned an error after all retries exhausted

Full type reference

All types are exported from the package root. The key ones:

Intent

The fully built order object returned by IntentBuilder.build()

IntentCommitment

The on-chain commitment fields returned by createCommitment()

PrivacyMode

'PUBLIC' | 'HIDDEN_AMOUNT' | 'HIDDEN_DIRECTION_AND_AMOUNT'

SubmitIntentRequest

Shape of the POST /v1/intents body

IntentStatusResponse

What getIntentStatus() returns

BatchListItem

One entry from listBatches()

GatewayClientConfig

Constructor config for GatewayClient


Rust SDK

A Rust SDK lives in client-sdk/ in the monorepo. Same concepts — intent construction, gateway client, retry logic. Useful if you're building a solver or a backend service that needs to interact with the gateway.

NoteThe Rust SDK is not yet published to crates.io. Add it as a path or git dependency for now.
Cargo.toml
[dependencies]
blindmarkets-sdk = { git = "https://github.com/winsznx/blindmarkets", subdirectory = "client-sdk" }