npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zebec-network/stellar-payroll-sdk

v2.2.0

Published

An SDK that interacts with the Zebec's Stellar Streaming Contract

Downloads

1,088

Readme

Stellar Streaming SDK

npm version License: MIT

A TypeScript SDK for interacting with Zebec's Stellar Streaming Contract. This SDK provides a high-level, type-safe interface to create and manage continuous payment streams on the Stellar network using Soroban smart contracts.

Table of Contents

Overview

The Stellar Streaming SDK enables developers to build applications that leverage continuous, programmable token streams on the Stellar network. It wraps the underlying Soroban contract interactions, handling transaction building, simulation, preparation, and submission while exposing strongly-typed, ergonomic APIs.

Typical use cases include:

  • Payroll: Stream salaries to employees continuously per second / minute / hour.
  • Vesting: Distribute tokens over time with optional cliffs.
  • Subscriptions: Recurring payments and metered billing.
  • Grants & Bounties: Time-locked, cancelable token distribution.

Features

  • Type-safe TypeScript API with comprehensive type definitions
  • Full lifecycle management for payment streams (create, pause/resume, cancel, withdraw, transfer)
  • Configurable fee model with global, tenant-level, and tiered fees
  • Token whitelisting and frequency configuration
  • Automatic and manual top-ups
  • Storage TTL management for long-running streams
  • Pluggable wallet adapter for flexible signing flows
  • Built on top of @stellar/stellar-sdk

Installation

Install the package using npm or yarn:

npm install @zebec-network/stellar-payroll-sdk
yarn add @zebec-network/stellar-payroll-sdk

Peer Dependencies

The SDK relies on the following runtime dependencies (installed automatically):

Requirements

  • Node.js 18+ recommended
  • TypeScript 5+ (when consuming from TS projects)

Quick Start

import {
  StellarStreamingService,
  type StellarStreamingSDKConfig,
  type WalletAdapter,
  type CreateStreamParams,
} from "@zebec-network/stellar-payroll-sdk";
import { Networks, Keypair, TransactionBuilder } from "@stellar/stellar-sdk";

// 1. Configure the SDK
const config: StellarStreamingSDKConfig = {
  contractId: "CDWK5GRLUB24FMWLWEAS3NLU2JCDW7T3BMU7HWKGEZEDJRXXESGLQ4YU",
  networkPassphrase: Networks.TESTNET,
  rpcUrl: "https://soroban-testnet.stellar.org",
};

// 2. Provide a wallet adapter
const keypair = Keypair.fromSecret("S...");
const wallet: WalletAdapter = {
  getPublicKey: async () => keypair.publicKey(),
  signTransaction: async (txXDR) => {
    const tx = TransactionBuilder.fromXDR(txXDR, Networks.TESTNET);
    tx.sign(keypair);
    return tx.toXDR();
  },
  signAllTransactions: async (txXDRs) =>
    txXDRs.map((txXDR) => {
      const tx = TransactionBuilder.fromXDR(txXDR, Networks.TESTNET);
      tx.sign(keypair);
      return tx.toXDR();
    }),
};

// 3. Instantiate the service
const service = new StellarStreamingService(config, wallet);

// 4. Create a stream
const params: CreateStreamParams = {
  amount: "100",
  start_time: "0",
  duration: "3600",
  cliff_percentage: "0",
  start_now: true,
  payroll_run_id: "october-payroll-2026",
  accrual_frequency: "1",
  pausable: true,
  cancelable_by_sender: true,
  cancelable_by_recipient: false,
  transferable_by_sender: false,
  transferable_by_recipient: false,
  automatic_withdrawal: false,
  initial_buffer_amount: "0",
  auto_topup: false,
  sender_id: "sender-1",
  receiver_id: "receiver-1",
};

const payload = await service.createStream(
  await wallet.getPublicKey(),
  "GBR...RECEIVER",
  "0a1b2c...", // streamId (hex-encoded BytesN)
  "CTOKEN...ADDR", // token contract address
  params,
);

const result = await payload.signAndSubmit();
console.log("Tx hash:", result.txHash);

Core Concepts

Streams

A stream is an on-chain agreement to transfer a token amount from a sender to a receiver over a defined duration. Streams can be paused, resumed, canceled, transferred, and topped up depending on the flags set at creation.

Stream Identifiers

Stream IDs are 32-byte values passed as hex-encoded strings. Stellar's Soroban runtime expects fixed-size byte arrays (BytesN) — the SDK converts the provided hex string into the appropriate ScVal automatically.

Fee Model

Fees are configured at two layers:

  1. Global config (set by admin) — applies by default to all streams.
  2. Tenant config (set by admin, per sender) — overrides global fees for a specific sender.

Each layer defines a platform_fee, a base_fee, a stream_token_fee, and a list of fee_tiers (amount-banded percentage fees).

Decimals

The SDK automatically converts human-readable amounts to atomic units using the token's on-chain decimals (fetched via getAssetDecimals). Inputs like "100.5" are scaled correctly for the underlying SAC/token contract.

SDK Methods

All write methods return a TransactionPayload that must be signed and submitted. All read methods return parsed, human-readable values directly.

The service is constructed as:

new StellarStreamingService(config: StellarStreamingSDKConfig, wallet: WalletAdapter)

Admin Methods

These methods configure the global protocol state and require the caller's wallet to match the contract admin.

getConfig(caller: string): Promise<Config>

Reads the global protocol configuration: fee recipient, withdraw account, platform fee, base fee, stream token fee, fee tiers, frequencies, whitelisted tokens, and the canonical XLM token address.

setFeeConfig(admin, params, options?): Promise<TransactionPayload>

Sets the protocol-wide fee configuration. The wallet's public key must match admin.

  • params: SetFeeConfigParams — recipient, withdraw account, platform/base/stream-token fees (percent), and fee tiers.

whitelistTokens(admin, tokens, options?): Promise<TransactionPayload>

Adds a list of token contract addresses to the whitelist of streamable tokens.

removeWhitelistedToken(admin, token, options?): Promise<TransactionPayload>

Removes a single token contract address from the whitelist.

setFrequencies(admin, frequencies, options?): Promise<TransactionPayload>

Updates the list of allowed accrual frequencies (in seconds) that streams can use.

updateConfig(admin, params, options?): Promise<TransactionPayload>

Bulk-updates the fee config and the allowed frequencies in a single transaction.

  • params: UpdateConfigParams — same as SetFeeConfigParams plus a frequencies list.

Tenant Methods

Tenant configs let the admin set custom fee rules for a specific sender, overriding the global config for that sender's streams.

getTenantConfig(sender: string): Promise<TenantConfig>

Reads the tenant configuration assigned to a sender address: fee recipient, withdraw account, platform/base/stream-token fees, and fee tiers.

setTenantConfig(admin, params, options?): Promise<TransactionPayload>

Creates or updates the tenant config for params.sender. The wallet must match admin.

removeTenantConfig(admin, sender, options?): Promise<TransactionPayload>

Removes a tenant config, reverting the sender to the global fee defaults.

Token Methods

approveTokenSpending(caller, token, spender, amount, options?): Promise<TransactionPayload>

Approves a spender to transfer up to amount of token on behalf of caller (SEP-41 allowance). Required before invoking flows that pull funds via allowance, such as triggerTopup.

  • amount is in display units; the SDK scales it to raw units using the token's on-chain decimals.
  • The allowance lifetime is pinned to the network-enforced maximum entry TTL (fetched via getMaxEntryTtl), so the approval persists for the largest value the network will accept.
  • The wallet's public key must match caller.

Stream Methods

createStream(sender, receiver, streamId, token, params, options?): Promise<TransactionPayload>

Creates a new payment stream from sender to receiver.

  • streamId — 32-byte hex string. The SDK encodes it as a BytesN.
  • token — token contract address (Stellar Asset Contract or custom).
  • params: CreateStreamParams — full stream configuration (see Data Types).

The amount in params.amount is scaled to the token's decimals automatically. The wallet must match sender.

createMultipleStreams(sender, entries, options?): Promise<TransactionPayload>

Creates multiple payment streams from sender in a single transaction.

  • entries: CreateStreamEntry[] — one entry per stream, each carrying its own stream_id, receiver, token, and params.
  • Each entry's params.amount and params.initial_buffer_amount are scaled to the entry's token decimals. Decimals are fetched once per unique token across the batch.
  • The wallet must match sender.

getStream(caller, streamId): Promise<Stream>

Reads the full state of a stream by its ID. Amounts are returned as human-readable strings (scaled down by token decimals); timestamps as numbers; status as an array of strings.

getWithdrawableAmount(caller, streamId): Promise<string>

Returns the currently-vested, withdrawable amount as a human-readable decimal string. Internally fetches the stream first to determine the token decimals.

pauseResumeStream(caller, streamId, options?): Promise<TransactionPayload>

Toggles the paused/active state of a stream. caller must be the stream's sender, and the stream must have pausable: true.

pauseResumeAllStreams(caller, streamIds, options?): Promise<TransactionPayload>

Toggles the paused/active state of multiple streams in a single transaction.

  • streamIds — array of 32-byte hex-encoded stream IDs.
  • caller must be the sender of every stream in the batch. Streams that are non-pausable, terminal, or whose sender does not match caller are silently skipped by the contract.

cancelStream(caller, streamId, options?): Promise<TransactionPayload>

Cancels a stream. caller must be the sender (if cancelable_by_sender) or the receiver (if cancelable_by_recipient). Unvested funds are refunded to the sender.

cancelAllStreams(caller, streamIds, options?): Promise<TransactionPayload>

Cancels multiple streams in a single transaction.

  • streamIds — array of 32-byte hex-encoded stream IDs.
  • The caller-eligibility rule for cancelStream applies to each stream individually (sender with cancelable_by_sender, or receiver with cancelable_by_recipient). Streams that fail authorization, are terminal, or lack the vault balance to cover vested debt are silently skipped by the contract.

withdrawStream(caller, streamId, options?): Promise<TransactionPayload>

Withdraws the vested amount.

  • If automatic_withdrawal == true, caller must be stream.withdraw_account.
  • If automatic_withdrawal == false, caller must be stream.receiver.

withdrawAllStreams(caller, streamIds, options?): Promise<TransactionPayload>

Withdraws the vested amount from multiple streams in a single transaction.

  • streamIds — array of 32-byte hex-encoded stream IDs.
  • The caller-eligibility rule for withdrawStream applies to each stream individually (stream.withdraw_account if automatic_withdrawal == true, otherwise stream.receiver); the same caller must satisfy this for every stream in the batch.

changeRecipient(caller, streamId, newReceiver, options?): Promise<TransactionPayload>

Reassigns the stream's receiver. caller must be the sender (if transferable_by_sender) or the current receiver (if transferable_by_recipient).

topupStream(caller, streamId, amount, options?): Promise<TransactionPayload>

Manually deposits additional funds into a stream. caller must be the stream's sender. amount is in display units; the SDK fetches the stream's token decimals and scales it to atomic units automatically.

triggerTopup(caller, streamId, amount, options?): Promise<TransactionPayload>

Triggers an automated top-up for a stream that has auto_topup_enabled. Callable by anyone (subject to contract rules). amount is in display units and is auto-scaled to the stream token's decimals. Requires a prior approveTokenSpending call so the contract can pull funds via allowance.

TTL Methods

Soroban contract data has a time-to-live (TTL) for persistent storage. These methods extend it.

bumpStreamTtl(caller, streamId, options?): Promise<TransactionPayload>

Extends the TTL of a specific stream's persistent storage entry. Callable by anyone.

bumpConfigTtl(caller, options?): Promise<TransactionPayload>

Extends the TTL of the protocol's config instance storage. Callable by anyone.

Data Types

All types are exported from the package root.

StellarStreamingSDKConfig

SDK constructor configuration.

| Field | Type | Description | | ------------------- | -------- | ------------------------------------------------------------------------- | | contractId | string | Soroban contract address of the streaming contract. | | networkPassphrase | string | Stellar network passphrase (Networks.TESTNET, Networks.PUBLIC, etc.). | | rpcUrl | string | URL of a Soroban RPC endpoint. |

WalletAdapter

Interface every wallet implementation must satisfy.

| Field | Type | Description | | --------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | getPublicKey | () => Promise<string> | Returns the source account's public key (G…). | | signTransaction | (txXDR: string) => Promise<string> | Signs a transaction in XDR format and returns the signed XDR. | | signAllTransactions | (txXDRs: string[]) => Promise<string[]> (optional) | Signs an array of transaction XDRs and returns the signed XDRs in the same order. Used by batch / multi-sign flows. Optional — only needed if you intend to use multi-sign helpers. |

TransactionOptions

Optional overrides for transaction building.

| Field | Type | Description | | --------- | --------- | -------------------------------------------------- | | fee | string? | Custom fee (stroops). Defaults to BASE_FEE. | | timeout | number? | Transaction timeout in seconds. Defaults to 180. |

FeeTier

A single fee tier band, expressed in human-readable units.

| Field | Type | Description | | ------------- | ------------------ | -------------------------------------- | | min_amount | string \| number | Lower bound (inclusive) for this tier. | | max_amount | string \| number | Upper bound (exclusive) for this tier. | | fee_percent | number | Fee percentage (e.g. 0.5 for 0.5%). |

ParsedFeeTier

Internal representation after scaling: amounts in atomic units and fee converted to basis points. Returned by the conversion utilities.

| Field | Type | | ------------ | -------- | | min_amount | string | | max_amount | string | | fee_bps | string |

TenantConfig

Read shape returned by getTenantConfig, and base shape for fee-related parameter types.

| Field | Type | Description | | ------------------ | ----------- | -------------------------------------------- | | fee_recipient | string | Address that receives collected fees. | | withdraw_account | string | Address that performs automatic withdrawals. | | platform_fee | number | Platform-level fee percent. | | base_fee | number | Base fee percent applied to every stream. | | stream_token_fee | number | Fee percent charged in the stream token. | | fee_tiers | FeeTier[] | Amount-banded percentage fees. |

SetFeeConfigParams

Parameters for setFeeConfig. Same shape as TenantConfig.

UpdateConfigParams

Parameters for updateConfig. Same fields as SetFeeConfigParams, plus:

| Field | Type | Description | | ------------- | ---------- | --------------------------------------- | | frequencies | number[] | Allowed accrual frequencies in seconds. |

SetTenantConfigParams

Parameters for setTenantConfig. Same shape as TenantConfig, plus:

| Field | Type | Description | | -------- | -------- | ------------------------------------------------ | | sender | string | The sender address the tenant config applies to. |

Config

Read shape returned by getConfig. Extends TenantConfig with:

| Field | Type | Description | | -------------------- | ---------- | ----------------------------------------------- | | frequencies | number[] | Allowed accrual frequencies in seconds. | | whitelisted_tokens | string[] | Token contract addresses permitted for streams. | | xlm_token | string | Canonical XLM (Stellar Asset Contract) address. |

CreateStreamParams

Full parameters for creating a stream.

| Field | Type | Description | | --------------------------- | --------- | ------------------------------------------------------------------------ | | amount | string | Total stream amount (human-readable; scaled by token decimals). | | start_time | string | Unix timestamp (seconds) when streaming begins (ignored if start_now). | | duration | string | Stream duration in seconds. | | cliff_percentage | string | Percent of total that vests at start (basis-point–like input). | | start_now | boolean | If true, streaming starts at on-chain submission time. | | payroll_run_id | string | Off-chain identifier linking the stream to a payroll run. | | accrual_frequency | string | Vesting tick interval, in seconds. Must be in the allowed frequencies. | | pausable | boolean | Whether the stream supports pause/resume. | | cancelable_by_sender | boolean | Whether the sender may cancel. | | cancelable_by_recipient | boolean | Whether the receiver may cancel. | | transferable_by_sender | boolean | Whether the sender may reassign the receiver. | | transferable_by_recipient | boolean | Whether the receiver may reassign themselves. | | automatic_withdrawal | boolean | If true, the configured withdraw_account performs withdrawals. | | initial_buffer_amount | string | Optional buffer amount (human-readable; scaled by token decimals). | | auto_topup | boolean | If true, the stream supports automated top-ups. | | sender_id | string | Off-chain identifier for the sender (e.g. employer ref). | | receiver_id | string | Off-chain identifier for the receiver (e.g. employee ref). |

CreateStreamEntry

One entry in a createMultipleStreams batch. Carries everything createStream would otherwise take as positional arguments.

| Field | Type | Description | | ----------- | -------------------- | --------------------------------------------------------------- | | stream_id | string | 32-byte hex-encoded stream identifier (BytesN). | | receiver | string | Receiver account address. | | token | string | Token contract address (SAC or custom) for this stream. | | params | CreateStreamParams | Full stream configuration; amounts are in human-readable units. |

TransactionPayload

All write methods return a TransactionPayload rather than submitting directly, giving consumers control over the signing/submission flow.

class TransactionPayload {
  readonly server: Server;
  readonly sourcePublicKey: string;
  readonly operations: xdr.Operation[];
  readonly networkPassphrase: string;
  readonly memo?: string;

  buildTransaction(
    options?: TransactionOptions,
  ): Promise<Transaction | FeeBumpTransaction>;
  buildTransactionXDR(options?: TransactionOptions): Promise<string>;
  signAndSubmit(
    options?: TransactionOptions,
  ): Promise<Api.GetTransactionResponse>;
}

Build + simulate work is deferred until one of these methods is called, so the source account's sequence number and Soroban resource fees stay fresh even if the user pauses between the SDK call and the wallet popup.

  • buildTransaction(options?) — Fetches a fresh sequence number, simulates the operation, applies the Soroban footprint and resource fees, and returns the prepared transaction (unsigned). Throws "Simulation failed: ..." if the RPC reports a simulation error.
  • buildTransactionXDR(options?) — Convenience wrapper that returns the prepared transaction's base64 XDR string. Useful for handing to an external signer (hardware wallet, multi-sig coordinator, browser wallet).
  • signAndSubmit(options?) — Builds, signs via the configured WalletAdapter, submits to the network, and polls until a final status is available.

options (TransactionOptions) shallow-merges on top of any construction-time options; per-call values win.

This separation lets you, for example, build the payload server-side and submit it client-side, or sign with multiple parties before submission.

Wallet Adapter

The SDK is signer-agnostic. Any object satisfying the WalletAdapter interface works — Freighter, Albedo, Rabet, a Keypair-backed local signer, or a custom remote signer.

Example with a local Keypair:

import { Keypair, Networks, TransactionBuilder } from "@stellar/stellar-sdk";

const keypair = Keypair.fromSecret(process.env.SECRET!);
const wallet: WalletAdapter = {
  getPublicKey: async () => keypair.publicKey(),
  signTransaction: async (txXDR) => {
    const tx = TransactionBuilder.fromXDR(txXDR, Networks.TESTNET);
    tx.sign(keypair);
    return tx.toXDR();
  },
  signAllTransactions: async (txXDRs) =>
    txXDRs.map((txXDR) => {
      const tx = TransactionBuilder.fromXDR(txXDR, Networks.TESTNET);
      tx.sign(keypair);
      return tx.toXDR();
    }),
};

Example with Freighter (browser):

import freighter from "@stellar/freighter-api";

const wallet: WalletAdapter = {
  getPublicKey: () => freighter.getPublicKey(),
  signTransaction: (txXDR) =>
    freighter.signTransaction(txXDR, { networkPassphrase: Networks.PUBLIC }),
  signAllTransactions: async (txXDRs) =>
    Promise.all(
      txXDRs.map((txXDR) =>
        freighter.signTransaction(txXDR, {
          networkPassphrase: Networks.PUBLIC,
        }),
      ),
    ),
};

Error Handling

The SDK throws plain Error instances for:

  • Wallet/caller mismatch — when the supplied admin/sender/caller does not match the wallet's public key.
  • Simulation failures — when Soroban simulation returns an error.
  • Invalid contract responses — when returned data does not match the expected shape (e.g. missing base_fee, malformed fee tiers).

Wrap calls in try/catch and surface the error message to your UI/logs:

try {
  const payload = await service.cancelStream(caller, streamId);
  await payload.signAndSubmit();
} catch (err) {
  console.error("Cancel failed:", (err as Error).message);
}

Project Structure

src/
├── constants.ts         # Network-keyed contract addresses
├── conversions.ts       # ScVal <-> native conversion helpers
├── index.ts             # Public exports
├── services.ts          # StellarStreamingService — main entrypoint
├── transactionPayload.ts# TransactionPayload class
├── types.ts             # All public type definitions
└── utils.ts             # getAssetDecimals, submitTransaction, etc.
test/                    # Mocha + ts-mocha integration tests

Development

Clone the repository and install dependencies:

git clone https://github.com/zebec-network/stellar-streaming-sdk.git
cd stellar-streaming-sdk
npm install

Scripts

| Script | Description | | ---------------- | ----------------------------------------- | | npm run build | Cleans dist/ and compiles TypeScript. | | npm run clean | Removes the dist/ directory. | | npm run format | Formats source files with Biome. | | npm test | Runs the Mocha test suite via ts-mocha. |

Running Tests

The test suite expects a .env file with the necessary keys (sender/receiver secrets, RPC URL, contract ID, stream ID). See the existing tests under test/ for required variables.

npm test

License

MIT © Zebec Network