TypeScript SDK for interacting with the ZK Payroll smart contracts.
npm install @zk-payroll/sdkThe SDK provides configuration presets for common environments to simplify initialization:
import { PayrollService, ConfigPresets } from "@zk-payroll/sdk";
// Initialize config for a specific environment
const config = ConfigPresets.testnet()
.withContractId("CCONTRACT_ID...")
.withProofConfig({
wasmUrl: "https://cdn.example.com/payroll_circuit.wasm",
zkeyUrl: "https://cdn.example.com/payroll_circuit.zkey",
})
.build(); // Validates required fields
// Initialize service
const service = new PayrollService(config);
// Process a private payment
await service.processPayment(
"G...", // Recipient Stellar address
1000n // Amount
);The ConfigBuilder fails fast if required configuration is missing or malformed:
// Throws Error: "Configuration validation failed:\n- contractId is malformed: invalid_id"
ConfigPresets.testnet().withContractId("invalid_id").build();
## Idempotent retries
For safe retries, pass an `idempotencyKey` when processing a payment.
```typescript
import { PayrollService, createPaymentIdempotencyKey } from "@zk-payroll/sdk";
const idempotencyKey = createPaymentIdempotencyKey({
recipient: "G...",
amount: 1000n,
asset: "native",
});
await service.processPayment({
recipient: "G...",
amount: 1000n,
asset: "native",
idempotencyKey,
});- Typed Contract Clients: Fully typed client wrappers for PayrollRegistry, SalaryCommitment, ProofVerifier, and PaymentExecutor contracts.
- ZK Proof Generation: Client-side proof generation using snarkjs for privacy.
- Caching: Built-in caching for proofs and circuit artifacts.
- Error Handling: Robust error typing and management.
- Mock Testing Environment: Comprehensive testing utilities for unit tests without a live network.
The SDK includes production-ready ZK proof generation using snarkjs:
import { SnarkjsProofGenerator, MemoryCacheProvider } from "@zk-payroll/sdk";
// Configure circuit artifacts
const config = {
wasmUrl: "https://cdn.example.com/payroll_circuit.wasm",
zkeyUrl: "https://cdn.example.com/payroll_circuit.zkey",
artifactCacheTTL: 86400, // 24 hours
};
// Create generator with caching
const cache = new MemoryCacheProvider<string>();
const generator = new SnarkjsProofGenerator(config, cache);
// Generate proof
const witness = {
recipient: "GDZQHV...",
amount: 1000000n,
nullifier: 123456789n,
secret: 987654321n,
};
const proof = await generator.generateProof(witness);See ZK Proof Generation Guide for detailed documentation.
Teams building internal payroll automation workers can follow the Backend Worker Quickstart for a practical end-to-end prototype covering setup, polling, retries, and event handling.
The SDK includes a powerful mock testing environment for writing unit tests:
import { MockContractEnvironment, MockPayrollContract } from "@zk-payroll/sdk";
const mockEnv = new MockContractEnvironment();
mockEnv.expectInvoke("deposit").toReturn("tx_hash_123");
const mockContract = new MockPayrollContract(mockEnv);
const txHash = await mockContract.deposit(1000n);See the Testing Guide for complete documentation.
Runnable examples covering two core use cases are in the examples/ directory.
Each example works out of the box in demo mode (no Stellar node required) and switches to a
live network automatically when the relevant environment variables are set.
examples/employee-onboarding.ts
Shows how to onboard a new employee: verify they have no existing payroll account, fund it with an initial allocation, and confirm the deposit was recorded.
npx tsx examples/employee-onboarding.tsShows how to run a full private payroll batch: configure SnarkjsProofGenerator with circuit
artifacts and caching, wire up PayrollService, process multiple payments, and report results.
npx tsx examples/payroll-execution.tsCopy the environment variable template and fill in your values to run against a live network:
cp examples/.env.example examples/.env
# edit examples/.env
source examples/.env && npx tsx examples/payroll-execution.tsSee examples/.env.example for all available variables.
The SDK provides typed client wrappers for the core ZK Payroll contracts. Each client exposes typed methods that encode arguments and decode responses automatically.
import { PayrollRegistryClient, rpc } from "@zk-payroll/sdk";
const server = new rpc.Server("https://soroban-testnet.stellar.org");
const client = new PayrollRegistryClient(server, "CCONTRACT_ID...");
// Register a payroll relationship
await client.register(
{ employer: "G...", employee: "G...", salary: 1000n, token: "C...", metadata: "engineering" },
signer
);
// Query a registry entry
const entry = await client.getRegistry("G...", "G...", signer);
console.log(entry.salary, entry.active);
// List employees
const employees = await client.getEmployees("G...", 0, 10, signer);
// Check if a registry exists
const exists = await client.registryExists("G...", "G...", signer);import { SalaryCommitmentClient } from "@zk-payroll/sdk";
const client = new SalaryCommitmentClient(server, "CCONTRACT_ID...");
// Commit to a salary amount (hidden via hash)
await client.commit(
{ employer: "G...", employee: "G...", commitmentHash: "abcd...", cycleId: 1n },
signer
);
// Retrieve a commitment
const commitment = await client.getCommitment("G...", "G...", 1n, signer);
// Batch commit multiple salaries
await client.batchCommit("G...", [
{ employee: "G...1", commitmentHash: "abcd", cycleId: 1n },
{ employee: "G...2", commitmentHash: "ef01", cycleId: 1n },
], signer);
// Verify a commitment against a ZK proof
const isValid = await client.verifyCommitment("G...", "G...", 1n, proof, signer);
// Reveal the actual salary
await client.revealSalary("G...", "G...", 1n, 1000n, signer);import { ProofVerifierClient } from "@zk-payroll/sdk";
const client = new ProofVerifierClient(server, "CCONTRACT_ID...");
// Verify a ZK proof on-chain
const valid = await client.verify(
{ pi_a: ["1","2"], pi_b: [["3","4"],["5","6"]], pi_c: ["7","8"], publicSignals: ["sig1"] },
["input1"],
1, // verification key ID
signer
);
// Add a new verification key
const vkId = await client.addVerificationKey("aabbcc...", "groth16 key", signer);
// Get active verification key
const activeId = await client.getActiveVerificationKeyId(signer);
// Get verification key info
const info = await client.getVerificationKeyInfo(1, signer);import { PaymentExecutorClient } from "@zk-payroll/sdk";
const client = new PaymentExecutorClient(server, "CCONTRACT_ID...");
// Execute an immediate payment
const result = await client.execute(
{ recipient: "G...", amount: 1000n, asset: "C...", memo: "salary" },
signer
);
console.log("Transaction:", result.txHash);
// Schedule a future payment
const scheduled = await client.schedule(
{ recipient: "G...", amount: 500n, asset: "C...", executeAt: 1700000000, memo: "bonus" },
signer
);
console.log("Payment ID:", scheduled.paymentId);
// Cancel a scheduled payment
await client.cancel(scheduled.paymentId, signer);
// Get pending payments
const payments = await client.getPendingPayments("G...", 0n, 20, signer);To catch configuration integration problems (such as misconfigured RPC endpoints, invalid contract IDs, or missing/unreachable circuit artifacts) before starting runtime work, the SDK provides the validateEnvironment helper:
import { validateEnvironment } from "@zk-payroll/sdk";
const clientConfig = {
networkUrl: "https://soroban-testnet.stellar.org",
contractId: "CCONTRACT_ID...",
};
const proofConfig = {
wasmUrl: "https://cdn.example.com/payroll_circuit.wasm",
zkeyUrl: "https://cdn.example.com/payroll_circuit.zkey",
};
const result = await validateEnvironment(clientConfig, proofConfig);
if (!result.isValid) {
console.error("Environment check failed!");
for (const diagnostic of result.diagnostics) {
if (diagnostic.status === "error") {
console.error(`- [${diagnostic.component}] ${diagnostic.message}`);
}
}
} else {
console.log("Environment is ready!");
}validateEnvironment returns a SanityCheckResult containing:
isValid: boolean-trueif all validations pass with no errors.diagnostics: DiagnosticEntry[]- List of diagnostics for each checked component.
Each DiagnosticEntry contains:
component: "rpc" | "contract" | "artifacts"- The checked component.status: "success" | "warning" | "error"- The validation status.message: string- Actionable diagnostic message explaining the result.error?: Error- The caught error object, if any.details?: Record<string, unknown>- Extra context (e.g. network passphrases or RPC response details).
The SDK provides a centralised AssetRegistry that maps asset identifiers to labels,
decimal precision, and display behaviour. Applications can extend it with any custom
Soroban token.
import { AssetRegistry, formatAmount, parseAmount } from "@zk-payroll/sdk";
// Use a built-in asset (native XLM, USDC, EUROC ship pre-registered)
const xlm = AssetRegistry.getOrThrow("native");
formatAmount(10_000_000n, xlm); // "1.0000000 XLM"
// Register a custom Soroban token
AssetRegistry.register({
id: "CTOKEN_CORP123",
symbol: "CORP",
label: "Corp Company Token",
decimals: 7,
});
const corp = AssetRegistry.getOrThrow("CTOKEN_CORP123");
parseAmount("500.00 CORP", corp); // 3_500_000_000nSee the Multi-Asset Guide for the full API reference, isolation patterns for tests, and rules for extending the registry in production.
- Runtime Support Matrix - Supported Node.js and browser versions
- API Reference - Complete API documentation
- Error Handling - Public error hierarchy and recovery patterns
- ZK Proof Generation - Detailed proof generation guide
- Versioning & Compatibility - SDK semantic versioning and contract compatibility matrix
- SDK Migration Cookbook - Step-by-step upgrade checklist and migration patterns
- Troubleshooting - Solutions for common CI, dependency, and environment issues
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Lint
npm run lintHaving trouble? See the Troubleshooting Guide.