Type-safe TypeScript SDK for building gig-economy applications on the TrustFlow Protocol (Stellar/Soroban).
The TrustFlow SDK provides a developer-friendly interface for interacting with TrustFlow smart contracts on the Stellar network. Build escrow systems, dispute resolution platforms, and decentralized freelance marketplaces with clean, type-safe APIs.
npm install @trustflow/sdk
# or
yarn add @trustflow/sdkimport { TrustFlowClient } from '@trustflow/sdk';
const client = new TrustFlowClient({
contractId: process.env.TRUSTFLOW_CONTRACT_ID!,
network: 'TESTNET', // or 'MAINNET'
});
await client.connect();
console.log('Connected to', client.network);import { TrustFlowEscrowClient, EscrowBuilder } from '@trustflow/sdk';
const escrowClient = new TrustFlowEscrowClient({
contractId: process.env.TRUSTFLOW_CONTRACT_ID!,
network: 'TESTNET',
rpcUrl: 'https://soroban-testnet.stellar.org',
networkPassphrase: 'Test SDF Network ; September 2015',
});
const params = new EscrowBuilder()
.setDepositor('GDEPOSITOR...')
.setBeneficiary('GBENEFICIARY...')
.setAmount('50') // XLM
.setDeadline(17280) // ~1 day in ledgers
.build();
const result = await escrowClient.createEscrow(params);
if (result.ok) {
console.log('Escrow ID:', result.data.escrowId);
console.log('Tx Hash:', result.data.txHash);
}import { TrustFlowClient } from '@trustflow/sdk';
import { createEscrow, releaseEscrow } from '@trustflow/sdk/escrow';
import { connectWallet } from '@trustflow/sdk/wallet';
import { xlmToStroops } from '@trustflow/sdk/utils';
const wallet = await connectWallet('freighter');
const client = new TrustFlowClient({
contractId: process.env.TRUSTFLOW_CONTRACT_ID!,
network: 'TESTNET',
});
await client.connect();
// Create
const escrow = await createEscrow(client, {
sender: wallet.publicKey,
recipient: 'GRECIPIENT...',
amountStroops: xlmToStroops('50'),
durationBlocks: 17280,
metadata: { orderId: 'ORD-001' },
});
console.log('Escrow created:', escrow.id);
// Release
const txHash = await releaseEscrow(client, {
escrowId: escrow.id,
caller: wallet.publicKey,
});
console.log('Released! Transaction:', txHash);See docs/QUICKSTART.md for the full walkthrough including disputes, multi-sig, and pagination.
Collect signatures from multiple approvers before a release is broadcast:
import { MultiSigEscrowClient } from '@trustflow/sdk';
import { Networks } from '@stellar/stellar-sdk';
const client = new MultiSigEscrowClient({
contractId: process.env.TRUSTFLOW_CONTRACT_ID!,
network: 'TESTNET',
rpcUrl: 'https://soroban-testnet.stellar.org',
networkPassphrase: Networks.TESTNET,
});
// Register a 2-of-2 release operation
const { data: { operationId } } = client.initMultiSigOperation({
escrowId: 'esc-42',
signers: [APPROVER_A, APPROVER_B],
threshold: 2,
operationType: 'release',
unsignedXdr: UNSIGNED_RELEASE_XDR,
networkPassphrase: Networks.TESTNET,
});
// Each approver submits their signed XDR independently
client.addSignature({ operationId, signerAddress: APPROVER_A, signedXdr: SIGNED_XDR_A });
client.addSignature({ operationId, signerAddress: APPROVER_B, signedXdr: SIGNED_XDR_B });
// Broadcast once threshold is met
const result = await client.submitWhenReady(operationId, 'https://horizon-testnet.stellar.org');
console.log('Released! tx:', result.data?.txHash);See examples/multisig-escrow.ts for the full walkthrough.
Cast a juror's vote on a dispute, either in the open or as ciphertext (e.g. for a commit-reveal
scheme — the SDK does not perform the encryption itself, ciphertext must already be
base64-encoded by the caller):
import { JurorClient } from '@trustflow/sdk';
const jurors = new JurorClient({
contractId: process.env.TRUSTFLOW_CONTRACT_ID!,
network: 'TESTNET',
rpcUrl: 'https://soroban-testnet.stellar.org',
networkPassphrase: 'Test SDF Network ; September 2015',
});
// Plaintext vote
const result = await jurors.vote({
disputeId: 'dsp-1',
jurorAddress: 'GJUROR...',
vote: { encrypted: false, choice: 'approve' },
});
// Encrypted vote (commit-reveal style)
const encryptedResult = await jurors.vote({
disputeId: 'dsp-1',
jurorAddress: 'GJUROR...',
vote: { encrypted: true, ciphertext: myCiphertext.toString('base64') },
});
if (result.ok) console.log('Voted! tx:', result.data.txHash);saveSession / loadSession / clearSession detect their environment per call (via
typeof localStorage), so no setup is needed in either place:
-
Browser: uses
localStorageautomatically — sessions survive page reloads. -
Node / CLI / backend: falls back to an in-memory store scoped to the current process. This does not survive process restarts. If you need durability (a long-running server, a CLI invoked repeatedly), inject your own adapter:
import { configureSessionStorage } from '@trustflow/sdk'; configureSessionStorage({ get: (key) => myFileOrRedisStore.get(key), set: (key, value) => myFileOrRedisStore.set(key, value), remove: (key) => myFileOrRedisStore.delete(key), });
-
SSR / bundler edge cases (Next.js, Remix, etc.):
typeof localStoragecan be ambiguous when server and client code share a module graph. If session calls run on the server during SSR, they'll silently use the in-memory fallback for that request rather than throwing — which is usually not what you want. CallconfigureSessionStorage()explicitly with a no-op or server-appropriate adapter for server-rendered code paths, and only rely on the automaticlocalStoragedetection in code you know runs client-side.
Sessions also carry an expiresAt, checked via isSessionExpired(). This is a best-effort,
client-side value — the backend does not currently return a token TTL (tracked in
#82), so treat it as a lower
bound, not a guarantee, and still handle a 401 from the backend even when
isSessionExpired() returns false.
MultiSigEscrowClient keeps operation state in-memory per process. To coordinate signers running
in separate processes today, round-trip state through your own store with exportState() /
importState():
// Process A (initiator)
const snapshot = client.exportState(operationId); // -> hand this to your own backend/queue
// Process B (a signer), after fetching that snapshot from your store
const imported = client.importState(snapshot);
if (!imported.ok) {
throw new Error(imported.error); // malformed/corrupted snapshot
}
client.addSignature({ operationId, signerAddress, signedXdr });
const reExported = client.exportState(operationId); // hand the updated state back to your storeimportState overwrites any existing local operation with the same operationId — last write
wins. If two processes both mutate after diverging from the same snapshot and both re-export,
importing one after the other discards the first's signatures rather than merging them.
Serializing concurrent writes (e.g. one writer at a time through your store) is the caller's
responsibility until a native, backend-backed MultiSigStateStore lands — tracked in
#83.
- 🔐 Escrow Management: Create, fund, release, and monitor escrows
- 🚀 Transaction Pipeline: Assemble, simulate, auto-adjust resource fees, fee-bump, and retry Soroban transactions via
TransactionPipeline, with typedPipelineResult<T>errors - ✍️ Multi-Sig Escrows: M-of-N signature collection for shared backend Escrows via
MultiSigEscrowClient - ⚖️ Dispute Resolution: Raise and track disputes with on-chain governance
- 🗳️ Juror Voting: Cast plaintext or encrypted votes on disputes via
JurorClient - 🔁 Backend API Auto-Retries: Resilient backend calls via
axios-retryfor transient failures - 🔑 Wallet Integration: Built-in support for Freighter wallet
- 📊 Event Monitoring: Real-time escrow state change tracking
- 🛡️ Type Safety: Full TypeScript support with Zod validation schemas
- 🧪 Test Coverage: Comprehensive Jest test suite
- Result Types: No thrown exceptions in public APIs — all errors returned as
SDKResult<T> - Immutable Builders: Fluent APIs like
EscrowBuilderfor parameter construction - Network Agnostic: Easily switch between Testnet and Mainnet
- Pure Utilities: Side-effect-free helper functions for formatting and validation
Read more in docs/ARCHITECTURE.md
- Quick Start Guide — Get up and running in 5 minutes
- API Reference — Complete API documentation
- Architecture — Design principles and module structure
- Examples — Working code examples for common use cases
The SDK is under active development. Here's what's coming:
- Tsup bundler configuration for ESM/CJS exports
- NPM publishing pipeline with provenance
- Simulation wrappers for transaction cost estimation (
TransactionPipeline) - Auto-retry logic for backend API endpoints (
axios-retry)
- Multi-signature support for corporate escrows
- IPFS storage helpers for file uploads
- Pagination support for high-volume queries
- Event parsing utilities for XDR decoding
- Juror voting system integration
See our GitHub Issues for detailed progress tracking.
We welcome contributions! To get started:
- Fork the repository
- Install dependencies:
npm install - Run tests:
npm test - Submit a PR
Please ensure:
- Tests pass (
npm test) - Linting passes (
npm run lint) - Code is formatted (
npm run format)
Check CONTRIBUTING.md for detailed guidelines.
- Strict Linting: ESLint strict mode enforced across the codebase
- Input Validation: All parameters validated with Zod schemas
- Type Safety: TypeScript strict mode prevents runtime errors
- Test Coverage: Critical paths covered by Jest integration tests
Report security issues to: security@trustflow.xyz
MIT License - Copyright (c) 2026 TrustFlow Protocol
See LICENSE for details.
- Issues: Report bugs or request features
- Contributors: See CONTRIBUTORS.md
- Changelog: See CHANGELOG.md
Securing the future of work, one transaction at a time.