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/solana-payroll-sdk

v3.1.0

Published

[![npm version](https://img.shields.io/npm/v/@zebec-network/solana-payroll-sdk.svg)](https://www.npmjs.com/package/@zebec-network/solana-payroll-sdk) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MI

Readme

Solana Payroll SDK

npm version License: MIT

The official TypeScript SDK for interacting with the Zebec Network Payroll program on Solana. It provides a high-level, ergonomic interface for building, signing and broadcasting payroll-related transactions — including continuous token streaming, multi-employee payroll runs, yield deposits and reward distribution.

This SDK wraps the on-chain Zebec Payroll and Zebec Configs Anchor programs and exposes typed helpers for every supported workflow.

Table of Contents

Features

  • Create single or multi-recipient payroll streams with cliff, duration, frequency and topup support
  • Cancel, pause, resume, top-up and withdraw from payrolls
  • Reassign payroll recipients and configure topup delegates
  • Instant disbursement: batch SPL token and native SOL transfers to multiple recipients
  • Yield management: deposit, request/cancel/complete unlocks, immediate unlocks and reward distribution
  • Enterprise cards: create virtual cards via USDC or swap-and-create via Jupiter DEX aggregator
  • Read helpers for payroll config, payroll metadata, whitelisted tokens, company nonces, deposits and card data
  • Built-in support for Anchor and read-only providers
  • Returns ready-to-broadcast TransactionPayload / MultiTransactionPayload objects that can be serialized for off-chain signing flows (e.g. wallet adapters, custodial signers)

Installation

Install the SDK with your package manager of choice:

npm install @zebec-network/solana-payroll-sdk
yarn add @zebec-network/solana-payroll-sdk
pnpm add @zebec-network/solana-payroll-sdk

Peer dependencies

The SDK depends on @solana/web3.js and the @coral-xyz/anchor toolchain. These are installed automatically as direct dependencies, but if your project already pins versions make sure they are compatible with the versions listed in package.json.

Requirements

  • Node.js 22+ (ESM is required — the package is shipped as "type": "module")
  • A Solana RPC endpoint (devnet or mainnet-beta)
  • A funded wallet for signing transactions

Quick Start

import { Connection, Keypair } from "@solana/web3.js";
import { Wallet } from "@coral-xyz/anchor";
import {
  createAnchorProvider,
  createPayrollProgram,
  createZebecConfigProgram,
  createPayroll,
} from "@zebec-network/solana-payroll-sdk";
import { sha256HashBuffer } from "@zebec-network/core-utils";

const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const wallet = new Wallet(Keypair.generate()); // for node
// or
// const wallet = useAnchorWallet(); // for browsers

const provider = createAnchorProvider(connection, wallet);
const payrollProgram = createPayrollProgram(provider);
const configProgram = createZebecConfigProgram(provider);

// A signed price attestation obtained from Zebec's price-oracle service,
// used on-chain to compute the dynamic fee for the run. See `PayrollTokenPriceData`.
const priceData = await fetchSignedPriceData("<spl-token-mint-address>");

const payload = await createPayroll(payrollProgram, configProgram, {
  payrollConfigName: "Payroll-Config-002",
  sender: wallet.publicKey,
  senderId: sha256HashBuffer("sender-001"),
  receiver: "<receiver-public-key>",
  receiverId: sha256HashBuffer("receiver-001"),
  payrollToken: "<spl-token-mint-address>",
  amount: 1_000,
  initialBufferAmount: 100,
  duration: 60 * 60, // 1 hour stream
  startNow: true,
  startTime: 0,
  cliffPercentage: 0,
  canTopup: true,
  delegateAutoTopup: true,
  isPausable: true,
  rateUpdatable: false,
  automaticWithdrawal: false,
  autoWithdrawFrequency: 0,
  cancelableBySender: false,
  cancelableByRecipient: false,
  transferableBySender: false,
  transferableByRecipient: false,
  payrollRunId: crypto.randomUUID(),
  priceData,
});

const signature = await payload.execute();
console.log("Payroll created:", signature);

Usage

Creating Providers and Programs

The SDK ships two provider helpers:

  • createAnchorProvider(connection, wallet, options?) — full read/write provider used for signing transactions.
  • createReadonlyProvider(connection, walletAddress?) — read-only provider for fetching on-chain state when no signer is available.
import { Connection } from "@solana/web3.js";
import {
  createAnchorProvider,
  createReadonlyProvider,
  createPayrollProgram,
  createZebecConfigProgram,
} from "@zebec-network/solana-payroll-sdk";

const connection = new Connection(process.env.RPC_URL!, "confirmed");

const writableProvider = createAnchorProvider(connection, wallet);
const readonlyProvider = createReadonlyProvider(connection);

const payrollProgram = createPayrollProgram(writableProvider);
const configProgram = createZebecConfigProgram(writableProvider);

Creating a Payroll Stream

createPayroll returns a TransactionPayload containing the fee-transfer instruction and the create-payroll instruction. params.priceData must be a signed price attestation (see PayrollTokenPriceData) obtained from Zebec's price-oracle service, used on-chain to compute the dynamic fee for the run.

const payload = await createPayroll(payrollProgram, configProgram, params);

See CreatePayrollParams for the full parameter list.

Withdrawing from a Payroll

import { withdrawPayroll } from "@zebec-network/solana-payroll-sdk";

const payload = await withdrawPayroll(payrollProgram, configProgram, {
  payrollConfigName: "Payroll-Config-002",
  payrollMetadata: payrollMetadataPubkey,
  receiver: receiverPubkey,
  amount: 100, // amount to withdraw, in UI token units
  withdrawAll: false, // set true to withdraw the full vested balance instead
});

await payload.execute();

Cancelling a Payroll

A payroll can be cancelled by either the sender or the receiver (subject to the permissions set at creation).

import { cancelPayroll } from "@zebec-network/solana-payroll-sdk";

const payload = await cancelPayroll(payrollProgram, {
  payrollMetadata: payrollMetadataPubkey,
  user: senderPubkey, // or receiver
});

Pausing / Resuming a Payroll

import { pauseResumePayroll } from "@zebec-network/solana-payroll-sdk";

const payload = await pauseResumePayroll(payrollProgram, {
  payrollMetadata: payrollMetadataPubkey,
});

The same instruction toggles between paused and resumed states.

Topping Up a Payroll

import { topupPayroll } from "@zebec-network/solana-payroll-sdk";

const payload = await topupPayroll(payrollProgram, configProgram, {
  payrollConfigName: "Payroll-Config-002",
  caller: callerPubkey,
  sender: senderPubkey,
  payrollMetadata: payrollMetadataPubkey,
  extra: 500, // additional UI-amount of the payroll token
});

Setting a Topup Delegate

Grants the program permission to pull additional tokens from the sender's account when the stream is low. Internally, this delegates tokens to a shared sharedTopupDelegate PDA (derived per sender + payrollToken), so the same delegation is reused across every payroll stream that sender has for that token. Repeated calls accumulate onto the existing delegated amount rather than replacing it.

import { setTopupDelegate } from "@zebec-network/solana-payroll-sdk";

const payload = await setTopupDelegate(payrollProgram, {
  payrollConfigName: "Payroll-Config-002",
  sender: senderPubkey,
  payrollMetadata: payrollMetadataPubkey,
  amount: 10_000,
});

await payload.execute();

Use getTopupDelegateAmount to check how much is currently delegated. It throws if the sender's token account delegate isn't set to the shared PDA (e.g. setTopupDelegate was never called for that sender/token pair):

import { getTopupDelegateAmount } from "@zebec-network/solana-payroll-sdk";

const delegatedAmount = await getTopupDelegateAmount(
  payrollProgram,
  senderPubkey,
  payrollTokenMintPubkey,
);
// => "10000" (UI decimal string)

Changing the Receiver

import { changePayrollReceiver } from "@zebec-network/solana-payroll-sdk";

const payload = await changePayrollReceiver(payrollProgram, {
  payrollMetadata: payrollMetadataPubkey,
  newRecipient: newReceiverPubkey,
  signer: currentSignerPubkey,
});

Batched (Multi) Operations

Every single-payroll method has a multi-payroll counterpart that returns a MultiTransactionPayload, which packs as many instructions as fit per Solana transaction and chains them for atomic-per-tx execution:

  • createMultiplePayroll
  • cancelMultiplePayroll
  • pauseResumeMultiplePayroll
  • withdrawMultiplePayroll
  • topupMultiplePayroll
  • setMultipleTopupDelegate
import { createMultiplePayroll } from "@zebec-network/solana-payroll-sdk";

const payload = await createMultiplePayroll(payrollProgram, configProgram, {
  payrollConfigName: "Payroll-Config-002",
  sender: senderPubkey,
  senderId: sha256HashBuffer("sender-001"),
  payrollInfo: [
    /* one entry per recipient, each including its own `priceData` */
  ],
});

const result = await payload.execute();

result.forEach((item, i) => {
  console.log(`Payroll ${i} created with tx signature: ${item.signature}`);
  if (item.status === "fulfilled") {
    console.log("success:", item.value); // here item.value also returns a signature
  } else {
    console.error("failed:", item.reason);
  }
});

Instant Disbursement

instantDisbursement provides a direct, non-streaming batch transfer of SOL or SPL tokens to multiple recipients. It is useful for one-off payroll runs, bonus payouts, or any scenario where tokens need to be sent immediately rather than streamed over time.

The method chunks recipients into a single transaction (default: 5 recipients per tx for SPL tokens, 20 for native SOL) and returns a MultiTransactionPayload.

import {
  createAnchorProvider,
  createPayrollProgram,
  createZebecConfigProgram,
  instantDisbursement,
} from "@zebec-network/solana-payroll-sdk";

const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const wallet = new Wallet(senderKeypair);
const provider = createAnchorProvider(connection, wallet);
const payrollProgram = createPayrollProgram(provider);

// SPL token disbursement
const payload = await instantDisbursement(payrollProgram, {
  sender: senderPubkey,
  feePayer: senderPubkey, // optional, defaults to sender
  token: splTokenMintPubkey, // SPL token mint address
  isNative: false,
  recipients: [
    { address: recipient1Pubkey, amount: 100 },
    { address: recipient2Pubkey, amount: 250 },
    { address: recipient3Pubkey, amount: 50 },
  ],
  chunkSize: 5, // optional, defaults to 5 for SPL, 20 for native SOL
});

const result = await payload.execute();
result.forEach((item, i) => {
  if (item.status === "fulfilled") {
    console.log(`Recipient ${i} paid: ${item.value}`);
  } else {
    console.error(`Recipient ${i} failed:`, item.reason);
  }
});

For native SOL transfers, set isNative: true and omit the token field:

const payload = await instantDisbursement(payrollProgram, {
  sender: senderPubkey,
  token: systemProgramId, // ignored when isNative is true
  isNative: true,
  recipients: [
    { address: recipient1Pubkey, amount: 1.5 }, // amounts in SOL
    { address: recipient2Pubkey, amount: 0.75 },
  ],
});

await payload.execute();

See InstantDisbursementParams for the full parameter list.

Yield Deposits and Unlocks

The SDK exposes the company-yield flow used by Zebec's payroll yield product:

import {
  yieldDeposit,
  requestUnlock,
  cancelUnlock,
  completeUnlock,
  distributeRewards,
} from "@zebec-network/solana-payroll-sdk";

// Deposit company funds for yield
const deposit = await yieldDeposit(payrollProgram, configProgram, {
  companyId: companyIdBuffer,
  company: companyPubkey,
  nonce: 0n,
  depositMint: tokenMintPubkey,
  amount: 1_000,
});

// Two-step unlock (delayed)
await requestUnlock(payrollProgram, configProgram, params);
await completeUnlock(payrollProgram, configProgram, params);

// Or cancel a pending request
await cancelUnlock(payrollProgram, params);

// Admin distributes rewards across many company deposits in one transaction
await distributeRewards(payrollProgram, configProgram, {
  admin: adminPubkey,
  feePayer: adminPubkey,
  checkId: checkIdBuffer, // used to derive a check account that prevents duplicate distributions
  distributions: [{ companyId, nonce, amount }],
});

Withdraw Rewards

After rewards have been distributed to a company deposit via distributeRewards, the company can withdraw the accumulated USX tokens using withdrawRewards. This transfers USX from the central vault to the company's token account.

import { withdrawRewards } from "@zebec-network/solana-payroll-sdk";

const payload = await withdrawRewards(payrollProgram, configProgram, {
  company: companyPubkey,
  companyId: companyIdBuffer,
  nonce: 0n,
  feePayer: companyPubkey, // optional, defaults to company
  admin: adminPubkey, // optional, defaults to yield config admin
  centralVault: centralVaultPubkey, // optional, defaults to yield config central vault
});

await payload.execute();

See WithdrawRewardsParams for the full parameter list.

Enterprise Cards

The SDK supports enterprise virtual card purchases. Cards can be created with USDC directly, or by swapping another SPL token via Jupiter DEX aggregator before purchasing.

Creating a Card

Purchase an enterprise card using USDC directly. The method validates daily purchase limits and provider card amount ranges before building the transaction.

import {
  createAnchorProvider,
  createPayrollProgram,
  createZebecConfigProgram,
  createCard,
} from "@zebec-network/solana-payroll-sdk";

const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const wallet = new Wallet(userKeypair);
const provider = createAnchorProvider(connection, wallet);
const payrollProgram = createPayrollProgram(provider);
const configProgram = createZebecConfigProgram(provider);

const emailHash = await sha256HashBuffer("[email protected]");

const payload = await createCard(payrollProgram, configProgram, {
  entCardConfigName: "Enterprise-Card-Config-001",
  user: userPubkey,
  feePayer: userPubkey,
  emailHash: emailHash,
  amount: 50, // USDC amount
  currency: "USD",
  reloadCardId: "", // empty string for new card
  nextCardIndex: 0n, // use getNextCardIndex() to fetch the correct value
});

await payload.execute();

See CreateCardParams for the full parameter list.

Swap and Create a Card

Swap an SPL token (e.g., SOL, USDC, or any whitelisted token) via Jupiter DEX aggregator and use the swap output to purchase a card. When wrapAndUnwrapWsol is true and the input token is SOL, the method automatically handles wrapping SOL to WSOL and unwrapping it after the swap.

The quoteInfo parameter must be a valid Jupiter quote response (or error object). Obtain it from the Jupiter Swap API.

import {
  swapAndCreateCard,
} from "@zebec-network/solana-payroll-sdk";

// Fetch a Jupiter quote first
const quoteResponse = await fetch("https://quote-api.jup.ag/v6/quote", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    inputMint: so11111111111111111111111111111111111111112, // SOL
    outputMint: usdcMintPubkey,
    amount: 1_500_000_000, // 1.5 SOL in lamports
    slippageBps: 50,
  }),
});
const quoteInfo = await quoteResponse.json();

const payload = await swapAndCreateCard(payrollProgram, configProgram, {
  entCardConfigName: "Enterprise-Card-Config-001",
  user: userPubkey,
  feePayer: userPubkey,
  emailHash: emailHash,
  currency: "USD",
  reloadCardId: "", // empty string for new card
  nextCardIndex: 0n,
  quoteInfo: quoteInfo, // Jupiter quote response
  wrapAndUnwrapWsol: true, // optional, handles SOL wrapping automatically
});

await payload.execute();

See SwapAndCreateCardParams and QuoteInfo for the full parameter types.

Reading Card Data

Several read helpers are available for querying card index and purchase state:

import {
  getCardIndexInfo,
  getNextCardIndex,
  getUserPurchaseRecordInfo,
  getCardPurchaseInfo,
} from "@zebec-network/solana-payroll-sdk";

// Get the current card index and total cards sold for a config
const cardIndex = await getCardIndexInfo(
  payrollProgram,
  "Enterprise-Card-Config-001",
);
// => { index: 42n, totoalCardSold: "15000.00" } | null

// Get the next card index to use when creating a new card
const nextIndex = await getNextCardIndex(
  payrollProgram,
  "Enterprise-Card-Config-001",
);
// => 43n (or 1n if no cards exist yet)

// Get a user's daily purchase record for a card config
const purchaseRecord = await getUserPurchaseRecordInfo(
  payrollProgram,
  "Enterprise-Card-Config-001",
  userPubkey,
);
// => { user: <PublicKey>, lastCardBoughtPerDay: "50.00", lastCardBoughtTimestamp: 1700000000 } | null

// Get details of a specific card purchase
const cardPurchase = await getCardPurchaseInfo(
  payrollProgram,
  "Enterprise-Card-Config-001",
  userPubkey,
  0n, // card index
);
// => { index: 0n, buyer: <PublicKey>, amount: "50.00", purchasedAt: 1700000000 } | null

Payroll + Yield Compound

withdrawPayrollAndYieldDeposit combines a payroll withdrawal and a yield deposit into a single atomic transaction. The withdrawn payroll tokens are immediately deposited into the company's yield vault. The method validates that the withdrawable (vested) amount is sufficient for the requested deposit before building the transaction.

import { withdrawPayrollAndYieldDeposit } from "@zebec-network/solana-payroll-sdk";

const payload = await withdrawPayrollAndYieldDeposit(
  payrollProgram,
  configProgram,
  {
    payrollConfigName: "Payroll-Config-002",
    payrollMetadata: payrollMetadataPubkey,
    company: companyPubkey,
    companyId: companyIdBuffer,
    nonce: 0n,
    depositAmount: 500, // amount to deposit into yield (in UI token units)
    withdrawer: companyPubkey, // optional, defaults to company
    feePayer: companyPubkey, // optional
  },
);

await payload.execute();

See WithdrawPayrollAndYieldDepositParams for the full parameter list.

Fetching On-Chain Data

import {
  getPayrollConfig,
  getPayrollMetadataInfo,
  getWhitelistedTokens,
  getCompanyNonceInfo,
  getCompanyDeposit,
} from "@zebec-network/solana-payroll-sdk";

const config = await getPayrollConfig(configProgram, "Payroll-Config-002");
const metadata = await getPayrollMetadataInfo(payrollProgram, metadataPubkey);
const tokens = await getWhitelistedTokens(configProgram, "Payroll-Config-002");
const nonce = await getCompanyNonceInfo(payrollProgram, companyIdBuffer);
const deposit = await getCompanyDeposit(payrollProgram, depositPubkey);

Building, Signing and Broadcasting Transactions

Every write method returns a TransactionPayload (single) or MultiTransactionPayload (batched). These wrap the raw instructions and let you choose how to send them:

// 1. Execute end-to-end (signs + sends + confirms)
const signature = await payload.execute();

// 2. Build a versioned transaction in backend and sends the serialized transaction to frontend for signing and broadcasting.
const { blockhash, lastValidBlockHeight } =
  await connection.getLatestBlockhash();
const tx = payload.buildVersionTransaction(blockhash);
const serialized = Buffer.from(tx.serialize()).toString("base64");
// send `serialized` to the client, have it signed, then re-broadcast via connection.sendRawTransaction

API Reference

Providers and Programs

| Symbol | Description | | ---------------------------------------------------- | ---------------------------------------------------- | | createAnchorProvider(connection, wallet, options?) | Returns a writable AnchorProvider. | | createReadonlyProvider(connection, walletAddress?) | Returns a read-only provider for fetch-only flows. | | createPayrollProgram(provider) | Returns the Zebec Payroll Anchor Program instance. | | createZebecConfigProgram(provider) | Returns the Zebec Configs Anchor Program instance. |

Payroll Service Methods

| Function | Returns | Purpose | | ---------------------------- | ------------------------- | ------------------------------------------------ | | createPayroll | TransactionPayload | Create a single payroll stream. | | createMultiplePayroll | MultiTransactionPayload | Create many payroll streams under one sender. | | cancelPayroll | TransactionPayload | Cancel a payroll (sender or receiver). | | cancelMultiplePayroll | MultiTransactionPayload | Cancel many payrolls. | | pauseResumePayroll | TransactionPayload | Toggle pause/resume on a payroll. | | pauseResumeMultiplePayroll | MultiTransactionPayload | Toggle pause/resume on many payrolls. | | withdrawPayroll | TransactionPayload | Withdraw vested tokens. | | withdrawMultiplePayroll | MultiTransactionPayload | Withdraw from many payrolls. | | topupPayroll | TransactionPayload | Add tokens to an existing payroll. | | topupMultiplePayroll | MultiTransactionPayload | Top up many payrolls. | | setTopupDelegate | TransactionPayload | Authorize automated topups. | | setMultipleTopupDelegate | MultiTransactionPayload | Authorize many topup delegations. | | changePayrollReceiver | TransactionPayload | Reassign a payroll to a new receiver. | | instantDisbursement | MultiTransactionPayload | Direct SPL/SOL batch transfer to recipients. | | withdrawPayrollAndYieldDeposit | TransactionPayload | Withdraw vested payroll and deposit into yield in one tx. |

Yield Service Methods

| Function | Returns | Purpose | | ------------------- | -------------------- | ------------------------------------------------ | | yieldDeposit | TransactionPayload | Deposit company funds for yield. | | requestUnlock | TransactionPayload | Request the delayed unlock of a company deposit. | | cancelUnlock | TransactionPayload | Cancel a pending unlock request. | | completeUnlock | TransactionPayload | Finalize a delayed unlock. | | distributeRewards | TransactionPayload | Admin distributes USX rewards to many deposits. | | withdrawRewards | TransactionPayload | Withdraw accumulated USX rewards from a deposit. |

Card Service Methods

| Function | Returns | Purpose | | -------------------- | -------------------- | --------------------------------------------- | | createCard | TransactionPayload | Purchase an enterprise card with USDC. | | swapAndCreateCard | TransactionPayload | Swap a token via Jupiter then create a card. |

Read Methods

| Function | Returns | Purpose | | ------------------------ | --------------------------------- | -------------------------------------------------- | | getPayrollConfig | PayrollConfigInfo | Fetch a named payroll config (admin, fees, tiers). | | getPayrollMetadataInfo | PayrollMetadataInfo | Fetch a payroll stream's full state. | | getWhitelistedTokens | TokenMetadata[] | Fetch whitelisted SPL token metadata for a config. | | getCompanyNonceInfo | CompanyNonceInfo \| null | Fetch the latest nonce for a company id. | | getCompanyDeposit | CompanyDepositInfo \| null | Fetch a specific yield deposit account. | | getTopupDelegateAmount | string | Fetch the amount delegated to the shared topup delegate PDA; throws if not set. | | getCardIndexInfo | { index, totalCardSold } \| null| Fetch the enterprise card index for a config. | | getNextCardIndex | bigint | Get the next card index (current + 1). | | getUserPurchaseRecordInfo | UserPurchaseRecordInfo \| null | Fetch a user's daily card purchase record. | | getCardPurchaseInfo | CardPurchaseInfo \| null | Fetch a specific card purchase details. |

PDA Helpers

| Function | Description | | ---------------------------------------------------------------------------------------- | --------------------------------------------------- | | derivePayrollConfigPda(configName, configProgramId) | Derive the payroll config PDA. | | derivePayrollVaultPda(metadata, payrollProgramId) | Derive the per-stream vault PDA. | | deriveYieldConfigPda(configProgramId) | Derive the yield config PDA. | | deriveCompanyDepositPda(companyId, nonce, payrollProgramId) | Derive a yield deposit PDA. | | deriveCompanyNoncePda(companyId, payrollProgramId) | Derive the per-company nonce PDA. | | deriveCheckAccountPda(checkId, payrollProgramId) | Derive the check account PDA used by distributeRewards. | | deriveEnterpriseCardIndexPda(entCardConfigName, payrollProgramId) | Derive the enterprise card index PDA. | | deriveEnterpriseCardPurchasePda(userAddress, entCardConfigName, cardIndex, payrollProgramId) | Derive a specific card purchase PDA. | | deriveEnterpriseUserPurchaseRecordPda(userAddress, entCardConfigName, payrollProgramId) | Derive a user's daily card purchase record PDA. | | deriveEnterpriseCardConfigPda(entCardConfigName, zebecConfigProgramId) | Derive the enterprise card config PDA. |

Utilities

| Function | Description | | ------------------------------------------------ | -------------------------------------------------------- | | getFeeInfoForPayroll(baseUrl, mint, rawAmount) | Query the Zebec fee oracle for a payroll's expected fee. |

Constants

| Constant | Description | | ------------------------------- | ----------------------------------------------------------------------------- | | PAYROLL_NAME_BUFFER_SIZE | Fixed-size buffer length used to encode the payroll run id. | | PAYROLL_PROGRAM_ID | Program id per network (devnet, mainnet-beta). | | USX_DECIMALS | Decimals used by the USX reward token. | | PAYROLL_TOKEN_PRICE_MESSAGE_SIZE | Expected byte length of priceData.message (the signed price attestation). | | ED25519_SIGNATURE_SIZE | Expected byte length of priceData.signature. |

Full type definitions for every parameter and return value live in src/types.ts.

Running Tests

The repository ships unit and end-to-end tests under test/. End-to-end tests run against Solana devnet using the Zebec devnet program deployment.

Environment variables

Tests require a .env file at the repo root with:

DEVNET_RPC_URL=<your-devnet-rpc-endpoint>
DEVNET_SECRET_KEYS=["<base58-secret-key-1>","<base58-secret-key-2>", ...]

DEVNET_SECRET_KEYS is a JSON-encoded array of base58 secret keys; the e2e suites pick specific indices for sender / receiver / fee payer roles. Make sure those wallets are funded with devnet SOL and the whitelisted SPL token used by the tests.

Commands

# Run the full test suite
npm test

# Run a single test file
npm run test:single -- test/e2e/payroll/createPayroll.test.ts

The Mocha timeout is set very high (-t 1000000000) because e2e tests wait for on-chain confirmation.

Building from Source

# Install dependencies
npm install

# Clean and build to ./dist
npm run build

# Format with Biome
npm run format

npm run build compiles TypeScript to dist/ and is also triggered automatically via the prepare script when the package is installed from a git source.

License

Released under the MIT License. Copyright © 2025 Zebec Network.