Skip to main content
CONFIDENTIAL & PROPRIETARY © 2026 Inkwell Finance, Inc. All Rights Reserved. This document is for informational purposes only and does not constitute legal, tax, or investment advice, nor an offer to sell or a solicitation to buy any security or other financial instrument. Any examples, structures, or flows described here are design intent only and may change.
Pre-alpha. Encrypt’s pre-alpha devnet does no real encryption. Where this page says “ciphertext”, read “plaintext in a ciphertext account” until Encrypt Alpha 1. See Overview.
Six beats, from a salary leaving HR’s machine to a payslip landing with a lender.

00 · Setup (once)

Any payer calls create_company(company_id, authority), with authority set to the multisig’s vault PDA. This creates the Company PDA, the vault token account (owned by the Company PDA) and a treasury ciphertext holding 0. The multisig does not sign this step. Policy starts empty: no auditor, no operator, no salary cap. The multisig’s first proposal sets it with update_policy(auditor, salary_cap, paused) and, optionally, set_operator(operator).

01 · Hire

HR encrypts each new or changed salary off-chain with Encrypt CreateInput, authorized to the payroll program. Encrypt returns a 32-byte ciphertext handle. Only the handle goes into the proposal and on-chain. The plaintext salary never touches the chain or the proposal.

02 · Fund

The multisig sends the payroll mint from its treasury to the company’s vault token account. It is a plain SPL transfer, and it can ride in the same proposal as the run. Anyone then calls sync_treasury. It credits the difference between the vault’s token balance and the last recorded balance into the encrypted treasury, through the deposit graph. Payments refuse to run while any deposit sits unsynced.

03 · Approve

One multisig proposal per period:
  1. add_employee(wallet) × N, each carrying one salary handle. Use update_salary, approve_bonus or set_employee_active for changes.
  2. open_pay_run(period, budget). The budget is the run’s public total ceiling. The run snapshots the company’s current salary_cap (the per-payment cap) and its active headcount.
Put hires before open_pay_run. A hire made while a run is already open waits for the next run. Every instruction in this proposal takes fixed-width arguments and needs no extra signer. The vault can be both the authority and the rent payer. Signers see wallets, handles, the period and the budget. They never see a salary.

04 · Crank

Anyone drives the run, in five steps. If the authority set an operator, only that key may submit the payments in step 4.
  1. sync_treasury, if new funds arrived.
  2. init_employee_ledger for each new hire. It creates the encrypted wage balance from a public 0.
  3. prepare_pay_run. It turns the public budget and cap into ciphertexts and creates the encrypted flagged counter at 0.
  4. process_payment × N, plus process_bonus for each approved bonus.
  5. close_pay_run. Anyone may close once the paid count reaches the headcount snapshot. The authority may close earlier.
Each payment runs the pay graph inside FHE:
A payment that would overdraw the treasury, exceed the run budget or exceed the cap moves nothing and increments the encrypted flagged counter. Success is encrypted, so the employee is still marked paid for that run. See Guardrails and limitations. The crank never sees a plaintext amount. Its payer needs SOL and an Encrypt fee deposit.

05 · Withdraw

The employee calls employee_withdraw(amount) and names an SPL token account of the payroll mint as recipient. The withdraw graph debits the encrypted balance only if it covers amount, and writes paid ∈ {amount, 0} to a fresh ciphertext.
  1. request_withdrawal_decryption. For 150 slots after the debit only the initiator may call it. After that, anyone may.
  2. The Encrypt decryptor reveals paid, and only paid. The remaining balance stays encrypted.
  3. complete_withdrawal, permissionless. If paid == amount, the program transfers amount from the vault to the recipient. Otherwise it adds paid back to the balance inside FHE. A stale decryption answer is harmless: it adds back 0, or restores the debit.
Withdrawals keep working when the company is paused or the employee is deactivated. If the payout account is frozen or closed, the employee can swap it with retarget_withdrawal.

06 · Disclose

grant_view(target) copies one encrypted figure into a new ciphertext authorized to a named viewer. The viewer reads it privately through Encrypt ReadCiphertext, re-encrypted to their key. Nothing is decrypted on-chain. An employee proves income to a lender or landlord this way. An auditor pulls the treasury, a run’s remaining budget or its flagged count. The grant itself (grantor, viewer, source handle) is public; the value is not.
Returning excess treasury follows the same exit path: the multisig calls approve_treasury_withdrawal(amount, recipient) (amount = 0 revokes), and a crank runs begin_treasury_withdrawal, which is blocked while a run is open, then the same decrypt and complete steps.