@zebec-network/zebec-stream-sdk
v3.3.0
Published
This is an SDK for interacting with ZEBEC Stream Program in solana
Readme
Zebec Stream SDK
A TypeScript SDK for interacting with the Zebec Stream protocol on Solana. This SDK provides a comprehensive interface for creating, managing, and interacting with payment streams on the Zebec Network.
Features
- Stream Management: Create, cancel, pause, resume, and withdraw from payment streams
- Token Streaming: SPL token streaming with configurable cliff, frequency, and duration
- Tiered Fee System: Dynamic fee calculation based on stream amount via on-chain fee tiers
- Token Whitelisting: Admin-controlled token whitelist with full metadata resolution via Metaplex
- Admin Operations: Initialize and update global stream configuration, fee tiers, and fee vaults
- Flexible Permissions: Granular per-stream control — cancellation, transfer, topup, pause, and rate update permissions
- PDA Utilities: Helpers for deriving stream config and stream vault program-derived addresses
- Provider Abstractions: Separate read-only and read-write provider helpers
- Type Safety: Full TypeScript support with comprehensive type definitions
Installation
npm install @zebec-network/zebec-stream-sdkyarn add @zebec-network/zebec-stream-sdkQuick Start
Setting Up the Service
import { Connection } from "@solana/web3.js";
import { ZebecStreamService, createAnchorProvider } from "@zebec-network/zebec-stream-sdk";
// Create a connection to devnet or mainnet-beta
const connection = new Connection("https://api.devnet.solana.com");
// Create an AnchorProvider using your wallet
const provider = createAnchorProvider(connection, wallet);
// Initialize the service with a config name and network
const streamService = ZebecStreamService.create("my-app-config", provider, "devnet");Creating a Stream
const tx = await streamService.createStream({
sender: "SenderPublicKeyHere",
receiver: "ReceiverPublicKeyHere",
streamToken: "TokenMintAddressHere",
amount: "1000", // Amount in human-readable token units
duration: 86400, // Total stream duration in seconds (1 day)
autoWithdrawFrequency: 3600, // Auto-withdrawal interval in seconds (1 hour)
streamName: "Monthly Salary",
startNow: true,
startTime: Math.floor(Date.now() / 1000),
cliffPercentage: 0,
automaticWithdrawal: true,
cancelableByRecipient: true,
cancelableBySender: true,
isPausable: true,
transferableByRecipient: false,
transferableBySender: false,
canTopup: true,
rateUpdatable: false,
});
const signature = await tx.execute();
console.log("Stream created:", signature);API Reference
ZebecStreamService
The main service class for interacting with Zebec streams.
ZebecStreamService.create(streamConfigName, provider, network)
Creates a new service instance. Validates that the provider connection network matches the specified network.
static create(
streamConfigName: string,
provider: Provider,
network: "mainnet-beta" | "devnet"
): ZebecStreamService| Parameter | Type | Description |
| --- | --- | --- |
| streamConfigName | string | Unique name identifying the stream config PDA |
| provider | Provider | Anchor or readonly provider |
| network | RpcNetwork | "mainnet-beta" or "devnet" |
Stream Operations
createStream(params)
Creates a new payment stream. Automatically fetches the fee quote from the Zebec backend and includes the fee transfer instruction in the transaction.
Parameters (CreateStreamParams):
| Field | Type | Description |
| --- | --- | --- |
| sender | Address | Sender's public key |
| receiver | Address | Recipient's public key |
| streamToken | Address | SPL token mint address |
| amount | Numeric | Amount to stream in human-readable token units |
| duration | number | Stream duration in seconds |
| autoWithdrawFrequency | number | Auto-withdrawal interval in seconds (must be in config's allowed frequencies when automaticWithdrawal is true) |
| streamName | string | Human-readable stream name (max 128 bytes) |
| startNow | boolean | Whether to start immediately |
| startTime | number | Unix timestamp for scheduled start |
| cliffPercentage | Numeric | Percentage of amount locked until cliff expires |
| automaticWithdrawal | boolean | Enable automatic withdrawals |
| cancelableByRecipient | boolean | Allow recipient to cancel |
| cancelableBySender | boolean | Allow sender to cancel |
| isPausable | boolean | Allow stream to be paused |
| transferableByRecipient | boolean | Allow recipient to transfer |
| transferableBySender | boolean | Allow sender to transfer |
| canTopup | boolean | Allow adding funds to stream |
| rateUpdatable | boolean | Allow rate modifications |
| feePayer? | Address | Optional custom fee payer (defaults to sender) |
| streamMetadataKeypair? | Keypair | Optional custom metadata keypair |
Returns: Promise<TransactionPayload>
cancelStream(params)
Cancels an existing stream. Either sender or receiver can cancel (if allowed by stream permissions).
Parameters (CancelStreamParams):
| Field | Type | Description |
| --- | --- | --- |
| streamMetadata | Address | Stream metadata account address |
| user | Address | User canceling the stream (must be sender or receiver) |
| feePayer? | Address | Optional custom fee payer |
Returns: Promise<TransactionPayload>
pauseResumeStream(params)
Toggles a stream between paused and active states. Only the sender can call this.
Parameters:
| Field | Type | Description |
| --- | --- | --- |
| streamMetadata | Address | Stream metadata account address |
Returns: Promise<TransactionPayload>
withdrawStream(params)
Withdraws vested tokens from a stream to the receiver.
Parameters (WithdrawStreamParams):
| Field | Type | Description |
| --- | --- | --- |
| streamMetadata | Address | Stream metadata account address |
| receiver | Address | Recipient's public key |
| withdrawer? | Address | Optional custom withdrawer (defaults to receiver) |
| feePayer? | Address | Optional custom fee payer |
Returns: Promise<TransactionPayload>
changeStreamReceiver(params)
Transfers stream ownership to a new recipient.
Parameters (ChangeStreamReceiverParams):
| Field | Type | Description |
| --- | --- | --- |
| streamMetadata | Address | Stream metadata account address |
| newRecipient | Address | New recipient's public key |
| signer | Address | Currently authorized signer |
Returns: Promise<TransactionPayload>
Information Retrieval
getStreamMetadataInfo(streamMetadata, commitment?)
Retrieves detailed information about a stream.
Returns: Promise<StreamMetadataInfo>
const info = await streamService.getStreamMetadataInfo("StreamMetadataAddressHere");
console.log(info.parties.sender.toBase58());
console.log(info.financials.depositedAmount); // human-readable
console.log(info.schedule.startTime);
console.log(info.permissions.isPausable);getStreamConfigInfo(configName, commitment?)
Retrieves the global stream configuration for a given config name.
Returns: Promise<StreamConfigInfo>
const config = await streamService.getStreamConfigInfo("my-app-config");
console.log(config.frequencies); // allowed auto-withdraw intervals
console.log(config.feeTiers); // tiered fee schedule
console.log(config.feeVault.toBase58());getWhitelistedTokens(configName, commitment?)
Fetches the list of whitelisted tokens with full on-chain mint info and Metaplex metadata.
Returns: Promise<TokenMetadata[]>
const tokens = await streamService.getWhitelistedTokens("my-app-config");
tokens.forEach(t => {
console.log(t.metadata?.symbol, t.mint.toBase58());
});Admin Operations
initializeStreamConfig(params)
Initializes the global stream configuration (admin only, one-time setup).
Parameters (InitializeStreamConfigParams):
await streamService.initializeStreamConfig({
admin: adminAddress, // optional, defaults to provider wallet
config: {
baseFeePercent: "0.1",
platformFeePercent: "0.05",
frequencies: [3600, 86400, 604800], // hourly, daily, weekly
withdrawAccount: "WithdrawAccountAddressHere",
feeVault: "FeeVaultAddressHere",
feeTiers: [
{ minThreshold: "0", maxThreshold: "1000", feeRateInPercent: "1.0" },
{ minThreshold: "1000", maxThreshold: "10000", feeRateInPercent: "0.75" },
{ minThreshold: "10000", maxThreshold: "99999999", feeRateInPercent: "0.5" },
],
},
});updateStreamConfig(params)
Updates an existing stream configuration (admin only).
Parameters (UpdateStreamConfigParams): Same shape as InitializeStreamConfigParams.
Returns: Promise<TransactionPayload>
whiteListTokens(params)
Adds token mint addresses to the whitelist (admin only).
Parameters (WhiteListTokensParams):
| Field | Type | Description |
| --- | --- | --- |
| admin | Address | Admin public key |
| tokens | Address[] | Array of token mint addresses to whitelist |
Returns: Promise<TransactionPayload>
Low-Level Instruction Builders
Each high-level method has a corresponding instruction builder for composing custom transactions:
| Method | Returns |
| --- | --- |
| getCreateStreamInstruction(...) | Promise<TransactionInstruction> |
| getCancelStreamInstruction(...) | Promise<TransactionInstruction> |
| getPauseResumeStreamInstruction(...) | Promise<TransactionInstruction> |
| getWithdrawStreamInstruction(...) | Promise<TransactionInstruction> |
| getChangeStreamReceiverInstruction(...) | Promise<TransactionInstruction> |
| getWhitelistTokensInstruction(...) | Promise<TransactionInstruction> |
| getInitializeStreamConfigInstruction(...) | Promise<TransactionInstruction> |
| getUpdateStreamConfigInstruction(...) | Promise<TransactionInstruction> |
Provider Setup
AnchorProvider (Read/Write)
Use for operations that sign transactions:
import { createAnchorProvider } from "@zebec-network/zebec-stream-sdk";
const provider = createAnchorProvider(connection, wallet, {
commitment: "confirmed",
preflightCommitment: "confirmed",
});ReadonlyProvider (Read-Only)
Use for read-only queries without a wallet:
import { createReadonlyProvider } from "@zebec-network/zebec-stream-sdk";
const provider = createReadonlyProvider(connection, optionalWalletAddress);PDA Utilities
import { deriveStreamConfigPda, deriveStreamVaultPda } from "@zebec-network/zebec-stream-sdk";
// Derive the stream config PDA
const [configPda] = deriveStreamConfigPda("my-app-config", programId);
// Derive the stream vault PDA for a given stream metadata address
const [vaultPda] = deriveStreamVaultPda(streamMetadataAddress, programId);Types
StreamMetadataInfo
type StreamMetadataInfo = {
address: PublicKey;
parties: {
sender: PublicKey;
receiver: PublicKey;
};
financials: {
streamToken: PublicKey;
cliffPercentage: number;
depositedAmount: string; // human-readable
withdrawnAmount: string; // human-readable
};
schedule: {
startTime: number;
endTime: number;
lastWithdrawTime: number;
frequency: number;
duration: number;
pausedTimestamp: number;
pausedInterval: number;
canceledTimestamp: number;
};
permissions: {
cancelableBySender: boolean;
cancelableByRecipient: boolean;
automaticWithdrawal: boolean;
transferableBySender: boolean;
transferableByRecipient: boolean;
canTopup: boolean;
isPausable: boolean;
rateUpdatable: boolean;
};
streamName: string;
};StreamConfigInfo
type StreamConfigInfo = {
address: PublicKey;
admin: PublicKey;
withdrawerAccount: PublicKey;
whitelistedTokens: PublicKey[];
platformFee: number;
baseFee: number;
frequencies: number[];
feeTiers: FeeTier[];
feeVault: PublicKey;
};FeeTier
type FeeTier = {
minThreshold: Numeric; // minimum USD amount for this tier
maxThreshold: Numeric; // maximum USD amount for this tier
feeRateInPercent: Numeric;
};StreamFeeInfo
Returned by the Zebec backend fee quote API and used internally during stream creation:
type StreamFeeInfo = {
tokenSymbol: string;
mintAddress: string;
chain: string;
streamAmount: string;
streamAmountUi: string;
tokenPriceUsd: number;
streamAmountUsd: number;
feeTier: { tier: number; range: string; feeRatePercent: number };
feeRatePercent: number;
feeAmountUsd: number;
feeToken: { symbol: string; decimals: number; priceUsd: number; mintAddress: string };
feeAmount: number;
feeAmountRaw: string;
};TokenMetadata
type TokenMetadata = {
mint: PublicKey;
decimals: number;
freezeAuthority: PublicKey | null;
supply: string;
isInitialized: boolean;
mintAuthority: PublicKey | null;
metadata: {
address: PublicKey;
updateAuthority: PublicKey;
name: string;
symbol: string;
uri: string;
} | null;
};Usage Examples
Complete Stream Lifecycle
import { Connection } from "@solana/web3.js";
import { ZebecStreamService, createAnchorProvider } from "@zebec-network/zebec-stream-sdk";
const connection = new Connection("https://api.devnet.solana.com");
const provider = createAnchorProvider(connection, wallet);
const streamService = ZebecStreamService.create("my-app-config", provider, "devnet");
// 1. Create a stream
const createTx = await streamService.createStream({
sender: senderAddress,
receiver: receiverAddress,
streamToken: tokenMintAddress,
amount: "1000",
duration: 86400,
autoWithdrawFrequency: 3600,
streamName: "Test Stream",
startNow: true,
startTime: Math.floor(Date.now() / 1000),
cliffPercentage: 0,
automaticWithdrawal: true,
cancelableByRecipient: false,
cancelableBySender: true,
isPausable: true,
transferableByRecipient: false,
transferableBySender: false,
canTopup: false,
rateUpdatable: false,
});
const streamSignature = await createTx.execute();
console.log("Stream created:", streamSignature);
// 2. Fetch stream info
const streamInfo = await streamService.getStreamMetadataInfo(streamMetadataAddress);
console.log("Deposited:", streamInfo.financials.depositedAmount);
// 3. Withdraw vested tokens
const withdrawTx = await streamService.withdrawStream({
streamMetadata: streamMetadataAddress,
receiver: receiverAddress,
});
await withdrawTx.execute();
// 4. Cancel the stream
const cancelTx = await streamService.cancelStream({
streamMetadata: streamMetadataAddress,
user: senderAddress,
});
await cancelTx.execute();Pause and Resume a Stream
const pauseTx = await streamService.pauseResumeStream({
streamMetadata: streamMetadataAddress,
});
await pauseTx.execute(); // toggles between paused and activeChange Stream Receiver
const changeTx = await streamService.changeStreamReceiver({
streamMetadata: streamMetadataAddress,
newRecipient: newRecipientAddress,
signer: currentRecipientAddress,
});
await changeTx.execute();Fetch Whitelisted Tokens
const tokens = await streamService.getWhitelistedTokens("my-app-config");
tokens.forEach((token) => {
console.log(token.metadata?.name, token.metadata?.symbol);
console.log("Mint:", token.mint.toBase58());
console.log("Decimals:", token.decimals);
});Error Handling
try {
const tx = await streamService.createStream(params);
const signature = await tx.execute();
console.log("Success:", signature);
} catch (error) {
if (error.message.includes("Invalid stream frequency")) {
console.error("autoWithdrawFrequency must be one of the config's allowed frequencies");
} else {
console.error("Stream creation failed:", error);
}
}Constants
| Constant | Description |
| --- | --- |
| STREAM_PROGRAM_ID | Program ID per network (zSTRMmYcFF8SPdHmsAmAUjBnx4zDHvnqqGz2mPcc5QC) |
| STREAM_PROGRAM_LOOKUP_TABLE_ADDRESS | Address lookup table per network |
| SUPERAPP_BACKEND_URL | Zebec backend URL per network (used for fee quotes) |
| STREAM_NAME_BUFFER_SIZE | Fixed buffer size for stream names (128 bytes) |
Network Support
| Network | Status | | --- | --- | | Mainnet Beta | Supported | | Devnet | Supported | | Testnet | Not supported |
Dependencies
| Package | Purpose |
| --- | --- |
| @coral-xyz/anchor | Anchor framework for Solana program interaction |
| @solana/web3.js | Solana Web3 JavaScript API |
| @solana/spl-token | SPL token utilities |
| @metaplex-foundation/mpl-token-metadata | On-chain token metadata resolution |
| @metaplex-foundation/umi | Metaplex UMI framework |
| @metaplex-foundation/umi-bundle-defaults | UMI default bundle |
| @metaplex-foundation/umi-web3js-adapters | UMI ↔ web3.js adapters |
| @zebec-network/core-utils | Zebec core utility functions (BPS conversions, etc.) |
| @zebec-network/solana-common | Common Solana helpers (ATAs, transaction payload, etc.) |
| bignumber.js | Arbitrary-precision arithmetic for token amounts |
Development
Install Dependencies
npm installBuild
npm run buildClean Build Artifacts
npm run cleanRun Tests
npm testRun a Single Test File
npm run test:single -- test/e2e/stream/createStream.test.tsFormat Code
npm run formatPublishing
Bump the version in package.json.
Build the package:
npm run buildPublish to npm:
npm publish --access public
Only the dist/ directory is included in the published package (as specified by the files field in package.json).
License
This project is licensed under the MIT License.
Support
For issues and questions:
- GitHub Issues: Repository Issues
