@zebec-network/solana-payroll-sdk
v3.1.0
Published
[](https://www.npmjs.com/package/@zebec-network/solana-payroll-sdk) [](https://opensource.org/licenses/MI
Keywords
Readme
Solana Payroll SDK
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
- Installation
- Quick Start
- Usage
- Creating Providers and Programs
- Creating a Payroll Stream
- Withdrawing from a Payroll
- Cancelling a Payroll
- Pausing / Resuming a Payroll
- Topping Up a Payroll
- Setting a Topup Delegate
- Changing the Receiver
- Batched (Multi) Operations
- Instant Disbursement
- Yield Deposits and Unlocks
- Withdraw Rewards
- Enterprise Cards
- Payroll + Yield Compound
- Fetching On-Chain Data
- Building, Signing and Broadcasting Transactions
- API Reference
- Running Tests
- Building from Source
- License
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/MultiTransactionPayloadobjects 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-sdkyarn add @zebec-network/solana-payroll-sdkpnpm add @zebec-network/solana-payroll-sdkPeer 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:
createMultiplePayrollcancelMultiplePayrollpauseResumeMultiplePayrollwithdrawMultiplePayrolltopupMultiplePayrollsetMultipleTopupDelegate
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 } | nullPayroll + 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.sendRawTransactionAPI 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.tsThe 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 formatnpm 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.
