TypeScript client and Merkle proof tooling for the YieldStream distributor — a multi-tenant Merkle claim protocol on Stellar/Soroban.
- Byte-Identical Merkle Engine:
computeLeaf,hashPair, andbuildTreematch the Rust Soroban contract's XDR-encoded leaf hashing and RFC 6962 domain separation byte-for-byte (validated againsttest-vectors/merkle_v1.json). - Typed Protocol Client:
YieldStreamClientwith contract address binding, RPC simulation view queries (getDistribution,isClaimed,getVersion,getAdmin), and transaction builders. - Freighter Wallet Ready: Built for
@stellar/stellar-sdkand@stellar/freighter-apiintegration for web frontends. - Lifecycle Event Decoding: Decodes contract events (
created,claimed,cancel,swept,upgrade).
npm install @yieldstream/sdk @stellar/stellar-sdkimport { generateTree, YieldStreamClient } from "@yieldstream/sdk";
// Define campaign recipient allocations
const recipients = [
{ address: "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI", amount: 100_000_000n }, // 10.0 tokens (7 decimals)
{ address: "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7", amount: 250_000_000n }, // 25.0 tokens
{ address: "GBLA4V54TC6K5544SZB7F24JYCJQ3B44673B3U6I4B624CYE2Z2Z2ABC", amount: 500_000_000n }, // 50.0 tokens
];
// Build the deterministic Merkle tree for campaign ID 1
const distributionId = 1;
const tree = generateTree(distributionId, recipients);
console.log("Root Hex:", tree.rootHex);
console.log("Total tokens:", tree.totalAmount);
// Find the claim proof for a specific wallet
const targetWallet = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI";
const userClaim = tree.proofs.find((p) => p.recipient === targetWallet);
console.log("User Leaf Hash:", userClaim?.leafHex);
console.log("Inclusion Proof:", userClaim?.proofHex);import { YieldStreamClient } from "@yieldstream/sdk";
const client = new YieldStreamClient({
contractId: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
rpcUrl: "https://soroban-testnet.stellar.org",
});
// Build transaction to create and fund the distribution
const tx = await client.buildCreateCampaignTx({
creator: "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI",
token: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWUIE3USNONGSBEQECU",
merkleRoot: tree.root,
totalAmount: tree.totalAmount,
numRecipients: tree.numRecipients,
endTime: 1750000000n, // Unix timestamp (0 for no expiry)
});
// Sign with Freighter and submit:
// const signedTxXdr = await signTransaction(tx.toXDR(), { networkPassphrase: client.networkPassphrase });
// const result = await client.server.sendTransaction(new Transaction(signedTxXdr, client.networkPassphrase));import { YieldStreamClient } from "@yieldstream/sdk";
const client = new YieldStreamClient({
contractId: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
});
// Check if wallet already claimed
const alreadyClaimed = await client.isClaimed(distributionId, userClaim.index);
if (alreadyClaimed) {
console.log("Allocation already claimed!");
} else {
// Build claim transaction
const claimTx = await client.buildClaimTx({
distributionId,
index: userClaim.index,
recipient: userClaim.recipient,
amount: userClaim.amount,
proof: userClaim.proofHex,
});
// Sign via Freighter extension:
// import { signTransaction } from "@stellar/freighter-api";
// const signed = await signTransaction(claimTx.toXDR(), { networkPassphrase: client.networkPassphrase });
}computeLeaf(distributionId, index, recipientAddress, amount): Uint8Array— Generates canonical leaf hash.hashPair(a, b): Uint8Array— Sibling sorted-pair hasher.buildTree(leaves): { root, proofs }— Power-of-two padded Merkle tree constructor.generateTree(distributionId, recipients): TreeResult— End-to-end tree generator with hex outputs.verifyProof(proof, root, leaf): boolean— Off-chain inclusion verification.
buildCreateCampaignTx(params, options): Promise<Transaction>buildClaimTx(params, options): Promise<Transaction>buildClaimManyTx(claims, options): Promise<Transaction>buildCancelTx(distributionId, creator, options): Promise<Transaction>buildSweepTx(distributionId, creator, toAddress, options): Promise<Transaction>buildUpgradeTx(admin, newWasmHash, options): Promise<Transaction>getDistribution(distributionId): Promise<Distribution>isClaimed(distributionId, index): Promise<boolean>getVersion(): Promise<number>getAdmin(): Promise<string>
npm install
npm run typecheck # Typecheck against TypeScript strict mode
npm run build # Build distribution bundle with declarations & sourcemaps
npm test # Run Vitest test suite including Rust cross-language vectorsDual licensed under MIT OR Apache-2.0, at your option.