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. Not deployed, not externally audited. The guardrails below
are enforced by the program and covered by its integration tests. The
confidentiality they rely on holds only from Encrypt Alpha 1.
Run budget
open_pay_run(period, budget) sets a public total ceiling for the run.
prepare_pay_run turns it into an encrypted remaining accumulator. Every
payment and bonus in the run is checked against it inside the pay graph
and subtracted from it on success. A payment that would exceed the
remaining budget moves nothing.
budget set to u64::MAX (the SDK’s UNLIMITED) means no ceiling.
Salary cap
update_policy(auditor, salary_cap, paused) sets a public per-payment cap.
Each run snapshots the cap when it opens. Inside the pay graph, any single
payment above the cap moves nothing. This bounds the damage from a bad
salary ciphertext.
The cap starts at u64::MAX (no cap) when the company is created.
The flagged counter
Each run carries an encryptedflagged counter, starting at 0. The pay
graph increments it whenever a payment fails any check:
grant_view (target: run flagged) and
fixes shortfalls with a make-up run or a bonus.
Solvency invariant
Every graph conserves value:Withdrawals survive a pause
employee_withdraw deliberately has no paused or active check. Earned
wages belong to the employee and stay withdrawable when the company is
paused or the employee is deactivated. Pausing stops new runs and new
payments; it does not stop exits.
Other guards
Known limitations
- Failed payments consume the slot. A payment the graph flags (treasury
short, budget exhausted, over cap) still marks the employee paid for that
run, because success is encrypted. The authority sees the encrypted
flaggedcount viagrant_viewand fixes it with a make-up run or a bonus. The sync guard, the no-withdrawal-mid-run rule and the optional operator remove the ways an outsider could trigger this. - Without an operator, the crank picks the order. If the treasury is
short, whoever cranks decides who is paid first. Set an
operatorif that matters. - Salary and bonus inputs are trusted from the authority. The ingress
check can’t tell a fresh
CreateInputciphertext from another program-authorized handle, such as a run accumulator. This is authority-only, so it is not exploitable by outsiders, but the SDK should create fresh inputs. - Scratch-ciphertext rent.
sync_treasury’s amount ciphertext and the no-payoutpaidciphertext are inputs to graphs still pending when the instruction ends, so they aren’t closed. The loss is roughly 0.001 SOL per occurrence, paid by the caller. - Ordering. Consecutive graphs on the same ciphertext rely on Encrypt’s executor applying them in order, which is its documented behavior.
- Deactivating an employee mid-run. The run’s headcount is a snapshot
taken at
open_pay_run, andprocess_paymentrejects inactive employees. If an employee is deactivated before being paid in an open run, the paid count never reaches the headcount, so only the authority can close that run. grant_viewis not multisig-shaped. It creates a ciphertext, so it needs a fresh keypair and an Encrypt CPI, unlike the twelve authority instructions. See Multisig integration.