Integration
SDK Reference
@fairblock/stellar-stabletrust a TypeScript SDK that abstracts all cryptography, Soroban invocations, and ZK proof generation so you can integrate confidential payments in a few lines of code.
Installation
Requires Node.js 18+ or a modern browser environment.
npm install @fairblock/stellar-stabletrust
# or
yarn add @fairblock/stellar-stabletrust
# or
pnpm add @fairblock/stellar-stabletrustPrerequisites
Quick start
Full lifecycle: account creation, deposit, transfer, apply, balance query, and withdrawal.
import { StellarStabletrustClient } from "@fairblock/stellar-stabletrust";
import { SorobanRpc, Keypair } from "@stellar/stellar-sdk";
// 1. Initialise the client
const client = new StellarStabletrustClient({
sorobanRpcUrl: "https://soroban-rpc.testnet.stellar.org",
contractAddress: "CAAAA...", // Soroban confidential-mirror contract
networkPassphrase: "Test SDF Network ; September 2015",
});
// 2. One-time account creation (generates ElGamal keypair from wallet signature)
const keypair = Keypair.fromSecret("S...");
await client.createAccount(keypair);
// Waits for FairyPort relay and Fairyring confirmation (~10–30s)
// 3. Deposit Stellar tokens into the confidential layer
await client.deposit({
keypair,
tokenAddress: "CBBBB...", // Stellar token contract address
amount: 100n, // plaintext amount (BigInt, in token's base unit)
});
// 4. Send a private confidential transfer
await client.transfer({
keypair,
tokenAddress: "CBBBB...",
recipientAddress: "GABC...",
amount: 50, // Number; generates ZK proofs client-side
useOffchainVerify: false, // false = onchain ZKP via Fairyring (trustless)
});
// 5. Apply incoming pending credits (must be called to make received funds spendable)
await client.applyPending({ keypair });
// 6. Query encrypted balance (decrypted client-side with your private key)
const balance = await client.getBalance({
keypair,
tokenAddress: "CBBBB...",
});
// { available: "50", pending: "0" }
// 7. Withdraw back to standard Stellar tokens
await client.withdraw({
keypair,
tokenAddress: "CBBBB...",
amount: 25,
useOffchainVerify: false,
});useOffchainVerify: set to false (default) for trustless onchain ZKP verification via Fairyring. Set to true for faster settlement where FairyPort pre-verifies proofs off-chain.
Key management
The SDK derives your ElGamal keypair deterministically from your Stellar wallet. The private key never leaves your device.
import { StellarStabletrustClient } from "@fairblock/stellar-stabletrust";
// The SDK derives your ElGamal keypair deterministically from a signature
// over a well-known message, using your Stellar keypair.
// The private key is NEVER transmitted to any server.
const client = new StellarStabletrustClient({ ... });
// Option A: derive and cache the keypair in-memory
const elgamalKeypair = await client.deriveElGamalKeypair(keypair);
// { publicKey: Uint8Array, secretKey: Uint8Array }
// Option B: supply a pre-derived keypair directly (for hardware wallets)
const balance = await client.getBalance({
keypair,
tokenAddress: "CBBBB...",
elgamalSecretKey: storedSecretKey, // Uint8Array (32 bytes)
});Key loss is permanent. If you lose your Stellar private key, your confidential balances cannot be decrypted. Store your Stellar keypair securely. If using a hardware wallet, the ElGamal key is re-derived on each session.
Proof generation
All ZK proofs are generated client-side. Nothing sensitive leaves the user's device.
// Proofs are generated entirely client-side inside the SDK.
// No proof material leaves the user's device.
// Transfer proof bundle contains three proofs:
// 1. Equality proof same plaintext encrypted under both pubkeys
// 2. Range proof sender balance after deduction is >= 0
// 3. Ciphertext validity transfer ciphertexts are well-formed
// Withdraw proof bundle contains two proofs:
// 1. Equality proof correctly links ciphertext to amount
// 2. Range proof balance after deduction is >= 0 (bound to tx_id nonce)
// The SDK handles all proof generation transparently:
await client.transfer({
keypair,
tokenAddress: "...",
recipientAddress: "...",
amount: 10,
// useOffchainVerify defaults to false (trustless, onchain ZKP)
});Method reference
createAccount(keypair)Promise<void>Registers a new confidential account. Derives an ElGamal keypair from the Stellar keypair, submits a create request to the Soroban contract, and waits for FairyPort to relay the confirmation from Fairyring. Must be called exactly once per address.
Waits for full cross-chain finalization. Allow 10–60 seconds on testnet.
deposit({ keypair, tokenAddress, amount })Promise<void>Transfers plain tokens from the user's Stellar account into the Soroban contract escrow and updates their encrypted available balance on Fairyring. amount is a BigInt in the token's base unit.
transfer({ keypair, tokenAddress, recipientAddress, amount, useOffchainVerify? })Promise<void>Generates ZK proofs client-side and submits a confidential transfer request. The sender's available balance decreases immediately; the recipient's pending balance increases and must be applied via applyPending(). amount is a Number.
applyPending({ keypair })Promise<void>Merges all pending credits into the spendable available balance. Must be called by the recipient after receiving a transfer before those funds can be spent or withdrawn.
withdraw({ keypair, tokenAddress, amount, useOffchainVerify? })Promise<void>Generates a ZK range proof and submits a withdrawal. On Fairyring confirmation, the Soroban contract releases the escrowed tokens back to the user's Stellar account.
getBalance({ keypair, tokenAddress })Promise<{ available: string; pending: string }>Queries the encrypted ciphertexts from Soroban storage and decrypts them client-side using the derived ElGamal private key. Returns string representations of the plaintext amounts in the token's base unit.
deriveElGamalKeypair(keypair)Promise<{ publicKey: Uint8Array; secretKey: Uint8Array }>Deterministically derives the ElGamal keypair from a Stellar Keypair by signing a well-known domain-separated message. The same wallet always yields the same ElGamal key.
Error reference
| Error | Cause | Resolution |
|---|---|---|
AccountDoesNotExist | No account registered for this address | Call createAccount() before any other operation |
PendingActionExists | A previous operation has not been finalized | Wait for FairyPort to relay the prior action and retry |
RecipientNotFinalized | Transfer recipient has not completed account creation | Recipient must call createAccount() and wait for finalization |
InsufficientFunds | Available balance too low for the requested withdraw | Check balance with getBalance() before withdrawing |
ProofVerificationFailed | ZK proof rejected by Fairyring or FairyPort | Ensure the correct ElGamal keypair is used; retry once |
SenderHasNoBalance | No encrypted balance slot for this token | Deposit the token first before transferring |
Choosing a verification mode
onchain (default)
useOffchainVerify: false
Proofs are verified by the Fairyring zkp module. Fully trustless no assumption on the relayer. Slightly higher latency due to the cross-chain round trip.
Recommended for production
Off-chain
useOffchainVerify: true
FairyPort verifies proofs via its Rust FFI library before forwarding to Fairyring. Lower latency but requires trusting the relayer operator to run the verifier correctly.
Suitable for controlled/internal deployments
