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();