GuidesPayroll Example

Payroll Example

Example: setting up recurring payroll payments for employees.

Use Case

A company wants to pay employees weekly without exposing:

  • who gets paid
  • each employee’s amount
  • the recipient-to-amount mapping

Reserved totals and per-execution totals are still visible on-chain, so Veil improves recipient privacy rather than hiding every aggregate number.

Setup

import { VeilClient } from "@veil-dev/sdk";
import { PublicKey } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";
 
const client = new VeilClient({ connection, wallet });
const USDC = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");

Step 1: Initialize Payroll Vault

await client.initVault(USDC);
 
const monthlyBudget = new BN(100_000_000); // 100 USDC
await client.deposit(monthlyBudget, USDC);

Step 2: Define Employee Recipients

const employees = [
  {
    name: "Alice",
    address: new PublicKey("AliceAddress..."),
    salary: 2_000_000n,
  },
  {
    name: "Bob",
    address: new PublicKey("BobAddress..."),
    salary: 1_500_000n,
  },
  {
    name: "Charlie",
    address: new PublicKey("CharlieAddress..."),
    salary: 1_800_000n,
  },
];
 
const recipients = employees.map((employee) => ({
  address: employee.address,
  amount: employee.salary,
}));

Step 3: Create Weekly Payroll Schedule

const weeklyInterval = 7 * 24 * 60 * 60; // 604800 seconds
const weeksToReserve = 4;
 
const totalWeeklyPayroll = recipients.reduce((sum, recipient) => sum + recipient.amount, 0n);
const reservedAmount = new BN(Number(totalWeeklyPayroll) * weeksToReserve);
 
const { scheduleId } = await client.createScheduleFromRecipients({
  tokenMint: USDC,
  recipients,
  intervalSecs: weeklyInterval,
  reservedAmount,
  perExecutionAmount: new BN(Number(totalWeeklyPayroll)),
});

Step 4: Monitor Execution

import { getVaultPda, getSchedulePda } from "@veil-dev/sdk";
 
const [vaultPda] = getVaultPda(wallet.publicKey, USDC);
const [schedulePda] = getSchedulePda(vaultPda, scheduleId);
 
const schedule = await client.getSchedule(schedulePda);
console.log("Next payment:", new Date(schedule.nextExecution.toNumber() * 1000));
console.log("Batches executed:", schedule.lastExecutedBatch.toString());

Step 5: Add a New Employee

To change the recipient set, create a new schedule with the updated list:

const updatedRecipients = [
  ...recipients,
  { address: new PublicKey("NewEmployee..."), amount: 1_200_000n },
];
 
const updatedWeeklyPayroll = updatedRecipients.reduce(
  (sum, recipient) => sum + recipient.amount,
  0n
);
 
const { scheduleId: newScheduleId } = await client.createScheduleFromRecipients({
  tokenMint: USDC,
  recipients: updatedRecipients,
  intervalSecs: weeklyInterval,
  reservedAmount: new BN(Number(updatedWeeklyPayroll) * 4),
  perExecutionAmount: new BN(Number(updatedWeeklyPayroll)),
});

Privacy Profile

Public:

  • vault address
  • schedule PDA
  • Merkle root
  • total recipients count
  • execution interval
  • reserved amount
  • per-execution total

Private:

  • individual employee addresses
  • individual salaries
  • recipient ordering
  • Merkle proofs

Complete Example

async function setupPayroll() {
  const client = new VeilClient({ connection, wallet });
  const USDC = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
 
  await client.initVault(USDC);
  await client.deposit(new BN(100_000_000), USDC);
 
  const employees = [
    { address: new PublicKey("..."), amount: 2_000_000n },
    { address: new PublicKey("..."), amount: 1_500_000n },
  ];
 
  const weeklyPayroll = employees.reduce((sum, employee) => sum + employee.amount, 0n);
 
  const { scheduleId } = await client.createScheduleFromRecipients({
    tokenMint: USDC,
    recipients: employees,
    intervalSecs: 604800,
    reservedAmount: new BN(Number(weeklyPayroll) * 4),
    perExecutionAmount: new BN(Number(weeklyPayroll)),
  });
 
  console.log("Payroll schedule created:", scheduleId);
}
 
setupPayroll();