@zebec-network/proxy-stream-sdk
v1.6.0
Published
TypeScript SDK for interacting with the Zebec Proxy Stream program on Solana. Enables cross-chain token streaming via proxy — minting/burning Z-tokens, managing user PDAs, and creating/managing payment streams.
Readme
Zebec Proxy Stream Sdk
TypeScript SDK for interacting with the Zebec Proxy Stream program on Solana. Enables cross-chain token streaming via proxy — minting/burning Z-tokens, managing user PDAs, and creating/managing payment streams.
Table of Contents
Installation
npm install @zebec-network/proxy-stream-sdkyarn add @zebec-network/proxy-stream-sdkpnpm add @zebec-network/proxy-stream-sdkQuick Setup
import { Connection } from "@solana/web3.js";
import { Wallet } from "@coral-xyz/anchor";
import {
createAnchorProvider,
createReadonlyProvider,
ProxyStreamService,
} from "@zebec-network/proxy-stream-sdk";
const connection = new Connection(process.env.DEVNET_RPC_URL!, "confirmed");
// For read-only operations (no wallet required)
const readonlyProvider = createReadonlyProvider(connection);
const readService = ProxyStreamService.create(readonlyProvider, "devnet");
// For write operations (wallet required)
const wallet = new Wallet(keypair); // Anchor Wallet wrapping a Keypair
const provider = createAnchorProvider(connection, wallet);
const service = ProxyStreamService.create(provider, "devnet");Environment Variables
Create a .env file in the project root. Required for tests and local usage.
# Mainnet RPC endpoint
RPC_URL=https://api.mainnet-beta.solana.com
# Devnet RPC endpoint
DEVNET_RPC_URL=https://api.devnet.solana.com
# JSON array of base58-encoded secret keys for mainnet
MAINNET_SECRET_KEYS=["<base58-secret-key-1>"]
# JSON array of base58-encoded secret keys for devnet
DEVNET_SECRET_KEYS=["<base58-secret-key-1>","<base58-secret-key-2>"]API Reference
Providers
createAnchorProvider(connection, wallet, options?)
Creates an Anchor provider for signing and submitting transactions.
| Parameter | Type | Description |
| ------------ | ----------------- | --------------------------------- |
| connection | Connection | Solana RPC connection |
| wallet | AnchorWallet | Wallet with signing capability |
| options | ConfirmOptions? | Optional confirmation options |
Returns: AnchorProvider
createReadonlyProvider(connection, walletAddress?)
Creates a read-only provider for querying on-chain data without a signer.
| Parameter | Type | Description |
| --------------- | ------------ | ----------------------------- |
| connection | Connection | Solana RPC connection |
| walletAddress | Address? | Optional wallet public key |
Returns: ReadonlyProvider
ProxyStreamService
ProxyStreamService.create(provider, network)
Factory method — the recommended way to instantiate the service.
| Parameter | Type | Description |
| ---------- | ------------------------------ | --------------------------- |
| provider | Provider \| ReadonlyProvider | Anchor or readonly provider |
| network | "mainnet-beta" \| "devnet" | Target Solana network |
Returns: ProxyStreamService
service.initProxyConfig(params)
Initializes the on-chain proxy configuration for a chain. Called once by the admin.
| Field | Type | Description |
| ----------- | --------- | ------------------------------------ |
| admin | Address | Admin public key |
| feeVault | Address | Fee vault public key |
| chainId | string | Unique chain identifier |
| fee | number | Fee percentage (e.g., 0.5 = 0.5%) |
| minAmount | number | Minimum token amount |
Returns: Promise<TransactionPayload>
service.updateProxyConfig(params)
Updates fee or admin of an existing proxy config.
| Field | Type | Description |
| ---------- | --------- | ------------------------ |
| admin | Address | Current admin public key |
| chainId | string | Chain identifier |
| fee | number | New fee percentage |
| newAdmin | Address | New admin public key |
Returns: Promise<TransactionPayload>
service.getProxyConfig(chainId)
Fetches the on-chain proxy configuration. Works with a readonly provider.
| Parameter | Type | Description |
| --------- | -------- | ---------------- |
| chainId | string | Chain identifier |
Returns: Promise<ProxyConfigInfo>
service.createToken(params)
Creates a new Z-token (SPL mint), attaches Metaplex metadata, transfers mint authority to the proxy PDA, and whitelists the token in the stream config.
| Field | Type | Description |
| ------------------------ | --------- | ---------------------------------------- |
| admin | Address | Admin public key |
| chainId | string | Chain identifier |
| streamConfigName | string | Name of the stream config PDA |
| tokenInfo.tokenName | string | Full token name |
| tokenInfo.symbol | string | Token symbol |
| tokenInfo.metadataUri | string | URI for token metadata JSON |
| tokenInfo.decimals | number | Token decimals |
| tokenInfo.mintKeypair | Keypair?| Optional mint keypair (auto-generated if omitted) |
Returns: Promise<TransactionPayload>
service.updateZTokenMetadata(params)
Updates Metaplex metadata for an existing Z-token.
| Field | Type | Description |
| ----------------------- | --------- | ------------------------ |
| admin | Address | Admin public key |
| mint | Address | Mint address |
| tokenInfo.tokenName | string | New token name |
| tokenInfo.symbol | string | New symbol |
| tokenInfo.metadataUri | string | New metadata URI |
Returns: Promise<TransactionPayload>
service.createUserPda(params)
Creates an on-chain user PDA and vault for a given L1 wallet hash. Must be called before minting or streaming for a new user.
| Field | Type | Description |
| -------------- | ---------------------------------- | ---------------------------------------- |
| admin | Address | Fee payer / admin |
| chainId | string | Chain identifier |
| l1WalletHash | number[] \| Buffer \| Uint8Array | 32-byte SHA-256 hash of the L1 address |
Returns: Promise<TransactionPayload>
service.mintZToken(params)
Mints Z-tokens into a user vault. The signatureHash is a 32-byte SHA-256 hash of the L1 deposit signature, used for replay protection.
| Field | Type | Description |
| --------------- | ---------------------------------- | ----------------------------------- |
| admin | Address | Admin public key |
| chainId | string | Chain identifier |
| destination | Address | Destination user vault public key |
| mint | Address | Z-token mint address |
| amount | Numeric | Amount to mint (in human units) |
| signatureHash | number[] \| Buffer \| Uint8Array | 32-byte hash for replay protection |
Returns: Promise<TransactionPayload>
service.burnZToken(params)
Burns Z-tokens from a user's vault (used when bridging back to L1).
| Field | Type | Description |
| ---------------------- | ---------------------------------- | ----------------------------------------- |
| proxyAdmin | Address | Admin public key |
| chainId | string | Chain identifier |
| mint | Address | Z-token mint address |
| receiverL1WalletHash | number[] \| Buffer \| Uint8Array | 32-byte hash of receiver's L1 address |
| amount | Numeric | Amount to burn (in human units) |
Returns: Promise<TransactionPayload>
service.createStream(params)
Creates a payment stream from a sender's user vault to a receiver's user vault.
See CreateStreamParams for the full parameter list.
Returns: Promise<TransactionPayload>
service.pauseResumeStream(params)
Toggles pause/resume state on an existing pausable stream (same method for both).
| Field | Type | Description |
| ------------------- | ---------------------------------- | --------------------------------------- |
| proxyAdmin | Address | Admin public key |
| chainId | string | Chain identifier |
| senderL1WalletHash| number[] \| Buffer \| Uint8Array | 32-byte hash of sender's L1 address |
| streamMetadata | Address | Stream metadata account public key |
Returns: Promise<TransactionPayload>
service.cancelStream(params)
Cancels an existing stream. Remaining tokens are returned to the sender's vault.
| Field | Type | Description |
| ---------------------- | ---------------------------------- | --------------------------------------- |
| proxyAdmin | Address | Admin public key |
| chainId | string | Chain identifier |
| senderL1WalletHash | number[] \| Buffer \| Uint8Array | 32-byte hash of sender's L1 address |
| receiverL1WalletHash | number[] \| Buffer \| Uint8Array | 32-byte hash of receiver's L1 address |
| streamMetadata | Address | Stream metadata account public key |
Returns: Promise<TransactionPayload>
service.withdrawStream(params)
Withdraws unlocked tokens from a stream into the receiver's user vault.
| Field | Type | Description |
| ---------------------- | ---------------------------------- | --------------------------------------- |
| proxyAdmin | Address | Admin public key |
| chainId | string | Chain identifier |
| streamConfigName | string | Name of the stream config PDA |
| receiverL1WalletHash | number[] \| Buffer \| Uint8Array | 32-byte hash of receiver's L1 address |
| streamMetadata | Address | Stream metadata account public key |
Returns: Promise<TransactionPayload>
service.emergencyPause(params)
Triggers an emergency pause on all proxy operations.
| Field | Type | Description |
| ------- | --------- | ----------------- |
| admin | Address | Admin public key |
Returns: Promise<TransactionPayload>
PDA Utilities
import {
deriveProxyConfigPda,
deriveMintAuthorityPda,
deriveCheckAccountPda,
deriveUserPda,
deriveUserVaultPda,
} from "@zebec-network/proxy-stream-sdk";
// Proxy config PDA for a chain
const [proxyConfig, bump] = deriveProxyConfigPda(chainId, programId);
// Mint authority PDA — controls Z-token minting
const [mintAuthority] = deriveMintAuthorityPda(programId);
// Check account PDA — replay protection for mint operations
const [checkAccount] = deriveCheckAccountPda(signatureHash, programId);
// User PDA for an L1 wallet hash
const [userPda] = deriveUserPda(chainId, l1WalletHash, programId);
// User vault PDA — holds user tokens
const [userVault] = deriveUserVaultPda(userPda, programId);Types
CreateStreamParams
| Field | Type | Required | Description |
| ------------------------- | ---------------------------------- | -------- | ------------------------------------------ |
| proxyAdmin | Address | yes | Admin / fee payer |
| chainId | string | yes | Chain identifier |
| streamConfigName | string | yes | Name of the stream config PDA |
| senderL1WalletHash | number[] \| Buffer \| Uint8Array | yes | 32-byte SHA-256 hash of sender's L1 address |
| receiverL1WalletHash | number[] \| Buffer \| Uint8Array | yes | 32-byte SHA-256 hash of receiver's L1 address |
| streamToken | Address | yes | Token mint address |
| streamName | string | yes | Human-readable stream name |
| amount | Numeric | yes | Token amount (in human units) |
| duration | number | yes | Stream duration in seconds |
| startNow | boolean | yes | Start immediately |
| startTime | number | yes | Unix timestamp (used when startNow is false) |
| automaticWithdrawal | boolean | yes | Enable auto-withdrawal |
| autoWithdrawFrequency | number | no | Withdrawal interval in seconds |
| cancelableByRecipient | boolean | yes | Allow recipient to cancel |
| cancelableBySender | boolean | yes | Allow sender to cancel |
| cliffPercentage | Numeric | yes | Cliff as percentage (0–100) |
| isPausable | boolean | yes | Allow pausing |
| rateUpdatable | boolean | yes | Allow rate updates |
| transferableByRecipient | boolean | yes | Allow recipient to transfer |
| transferableBySender | boolean | yes | Allow sender to transfer |
| canTopup | boolean | yes | Allow topping up |
| streamMetadataKeypair | Keypair | no | Keypair for metadata account (auto-generated if omitted) |
ProxyConfigInfo
type ProxyConfigInfo = {
address: PublicKey;
fee: string; // e.g. "0.5" (percentage)
minAmount: string;
proxyAdmin: PublicKey;
mintableTokens: PublicKey[];
feeVault: PublicKey;
emergencyPaused: boolean;
chainId: string;
};Usage Examples
Initialize proxy config
import { createAnchorProvider, ProxyStreamService } from "@zebec-network/proxy-stream-sdk";
const provider = createAnchorProvider(connection, wallet);
const service = ProxyStreamService.create(provider, "devnet");
const txPayload = await service.initProxyConfig({
admin: wallet.publicKey,
feeVault: "A8TFmgg65YmA4LG7aFKLA1Fqg1Rb3kxSLYgio5YmNUgJ",
chainId: "DASH",
fee: 0.5, // 0.5%
minAmount: 1000,
});
const signature = await txPayload.execute({ commitment: "confirmed" });
console.log("Tx:", signature);Get proxy config
import { createReadonlyProvider, ProxyStreamService } from "@zebec-network/proxy-stream-sdk";
const provider = createReadonlyProvider(connection);
const service = ProxyStreamService.create(provider, "devnet");
const config = await service.getProxyConfig("DASH");
console.log(config);Create Z token
import { Keypair } from "@solana/web3.js";
const mintKeypair = Keypair.generate();
console.log("New mint:", mintKeypair.publicKey.toString());
const txPayload = await service.createToken({
admin: wallet.publicKey,
chainId: "DASH",
streamConfigName: "Config-001",
tokenInfo: {
tokenName: "Z Dash USDC",
symbol: "ZDUSDC",
metadataUri: "https://example.com/zdusdc.json",
decimals: 9,
mintKeypair,
},
});
const signature = await txPayload.execute({ commitment: "confirmed" });Mint Z token
import { hashSHA256ToBuffer } from "@zebec-network/core-utils";
import { deriveUserPda, deriveUserVaultPda } from "@zebec-network/proxy-stream-sdk";
const chainId = "DASH";
const l1WalletHash = await hashSHA256ToBuffer("0xc0ffee254729296a45a3885639AC7E10F9d54979");
// Ensure user PDA exists first
const [userPda] = deriveUserPda(chainId, l1WalletHash, service.proxyStreamProgramId);
if (!(await connection.getAccountInfo(userPda))) {
const tx = await service.createUserPda({ admin: wallet.publicKey, chainId, l1WalletHash });
await tx.execute({ commitment: "confirmed" });
}
const [destination] = deriveUserVaultPda(userPda, service.proxyStreamProgramId);
const signatureHash = await hashSHA256ToBuffer(`deposit-${Date.now()}`);
const txPayload = await service.mintZToken({
admin: wallet.publicKey,
chainId,
destination,
mint: "De31sBPcDejCVpZZh1fq8SNs7AcuWcBKuU3k2jqnkmKc",
amount: 5000,
signatureHash,
});
const signature = await txPayload.execute({ commitment: "confirmed" });Burn Z token
const txPayload = await service.burnZToken({
mint: "De31sBPcDejCVpZZh1fq8SNs7AcuWcBKuU3k2jqnkmKc",
chainId: "DASH",
proxyAdmin: wallet.publicKey,
receiverL1WalletHash: l1WalletHash,
amount: 2000,
});
const signature = await txPayload.execute({ commitment: "confirmed" });Create stream
import { Keypair } from "@solana/web3.js";
import { hashSHA256ToBuffer } from "@zebec-network/core-utils";
const senderL1WalletHash = await hashSHA256ToBuffer("0xSenderEthAddress");
const receiverL1WalletHash = await hashSHA256ToBuffer("0xReceiverEthAddress");
const streamMetadataKeypair = Keypair.generate();
const txPayload = await service.createStream({
proxyAdmin: wallet.publicKey,
chainId: "DASH",
streamConfigName: "Config-001",
senderL1WalletHash,
receiverL1WalletHash,
streamToken: "De31sBPcDejCVpZZh1fq8SNs7AcuWcBKuU3k2jqnkmKc",
streamName: "My Payment Stream",
amount: 200,
duration: 200,
startNow: true,
startTime: 0,
automaticWithdrawal: false,
autoWithdrawFrequency: 0,
cancelableByRecipient: false,
cancelableBySender: true,
cliffPercentage: 0,
isPausable: true,
rateUpdatable: false,
transferableByRecipient: false,
transferableBySender: false,
canTopup: false,
streamMetadataKeypair,
});
const signature = await txPayload.execute({ commitment: "confirmed" });
console.log("Stream metadata:", streamMetadataKeypair.publicKey.toBase58());Pause / Resume stream
// Same method toggles between paused and active
const txPayload = await service.pauseResumeStream({
proxyAdmin: wallet.publicKey,
chainId: "DASH",
senderL1WalletHash,
streamMetadata: streamMetadataKeypair.publicKey,
});
const signature = await txPayload.execute({ commitment: "confirmed" });Cancel stream
const txPayload = await service.cancelStream({
proxyAdmin: wallet.publicKey,
chainId: "DASH",
senderL1WalletHash,
receiverL1WalletHash,
streamMetadata: streamMetadataKeypair.publicKey,
});
const signature = await txPayload.execute({ commitment: "confirmed" });Withdraw stream
const txPayload = await service.withdrawStream({
proxyAdmin: wallet.publicKey,
chainId: "DASH",
streamConfigName: "Config-001",
receiverL1WalletHash,
streamMetadata: streamMetadataKeypair.publicKey,
});
const signature = await txPayload.execute({ commitment: "confirmed" });Development
Prerequisites
- Node.js >= 18
- npm / yarn / pnpm
Install dependencies
npm installBuild
Compiles TypeScript to dist/.
npm run buildClean
Removes the dist/ directory.
npm run cleanFormat
Formats source files using Biome.
npm run formatRun all tests
Runs the full e2e test suite. Requires .env to be configured with devnet credentials.
npm testRun a single test file
npm run test:single test/e2e/getProxyConfig.test.ts
npm run test:single test/e2e/initProxyconfig.test.ts
npm run test:single test/e2e/updateProxyConfig.test.ts
npm run test:single test/e2e/createToken.test.ts
npm run test:single test/e2e/updateZTokenMetadata.test.ts
npm run test:single test/e2e/createUserPda.test.ts
npm run test:single test/e2e/mintZtoken.test.ts
npm run test:single test/e2e/burnZtoken.test.ts
npm run test:single test/e2e/stream/createStream.test.ts
npm run test:single test/e2e/stream/pauseResumeStream.test.ts
npm run test:single test/e2e/stream/cancelStream.test.ts
npm run test:single test/e2e/stream/withdrawStream.test.tsProgram IDs
| Network | Program ID |
| ------------ | ----------------------------------------------- |
| Devnet | 5SaauzPGka7m7pTis7BXGwTh1zFYpGnU3wSQrehZDgJH |
| Mainnet-Beta | TBD |
License
MIT — see LICENSE for details.
Author
Zebec Network | Ashish Sapkota
