Privacy (FHE) Instructions
Veil’s privacy layer uses Fully Homomorphic Encryption (FHE) via Encrypt . Privacy is opt-in per position. The plaintext UserPosition is never removed — it remains the authoritative source for health factor enforcement.
Implementation status: The Encrypt SDK (encrypt-pinocchio) currently targets pinocchio 0.10.x. Veil’s program uses 0.11.x. All five private instructions are fully implemented and routed. The FHE graph execution CPI (execute_graph) is stubbed and will activate when the SDK ships the version update.
Encrypt program ID (devnet): 4ebfzWdKnrnGseuQpezXdG8yCdHqwQ1SSBHD3bWArND8
EncryptedPosition Account
Discriminator: VEILENC! · Size: 144 bytes
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 8 | discriminator | VEILENC! |
| 8 | 32 | owner | User wallet |
| 40 | 32 | pool | Associated lending pool |
| 72 | 32 | enc_deposit | Pubkey of the EUint64 ciphertext for deposit balance |
| 104 | 32 | enc_debt | Pubkey of the EUint64 ciphertext for debt balance |
| 136 | 1 | bump | PDA bump |
| 137 | 7 | _pad | — |
PDA: seeds ["enc_pos", user, pool]
The ciphertext account pubkeys are opaque 32-byte handles. To any RPC observer the position reveals nothing about the amounts. Only the position owner (with their FHE secret key) can decrypt.
EnablePrivacy
Discriminator: 0x08 (8)
Create an EncryptedPosition for an existing UserPosition. Initialises two ciphertext accounts (enc_deposit, enc_debt) seeded with the current plaintext values.
Accounts
| # | Name | Flags | Description |
|---|---|---|---|
| 0 | user | signer, writable | — |
| 1 | user_position | readonly | Must already exist |
| 2 | encrypted_position | writable | New PDA — seeds: ["enc_pos", user, pool] |
| 3 | enc_deposit_ct | writable | New EUint64 ciphertext account (Encrypt program) |
| 4 | enc_debt_ct | writable | New EUint64 ciphertext account (Encrypt program) |
| 5 | pool | readonly | — |
| 6 | encrypt_program | readonly | Encrypt program |
Instruction Data
| Offset | Size | Field |
|---|---|---|
| 0 | 1 | discriminator 0x08 |
| 1 | 1 | enc_position_bump |
PrivateDeposit
Discriminator: 0x09 (9)
Identical to Deposit plus an FHE add_deposit computation on enc_deposit.
Accounts
| # | Name | Flags | Description |
|---|---|---|---|
| 0 | user | signer, writable | — |
| 1 | user_token | writable | Source token account |
| 2 | vault | writable | Pool vault |
| 3 | pool | writable | — |
| 4 | user_position | writable | Plaintext position (updated) |
| 5 | encrypted_position | writable | — |
| 6 | enc_deposit_ct | writable | EUint64 deposit ciphertext (updated) |
| 7 | amount_ct | writable | Fresh EUint64 holding the plaintext amount |
| 8 | system_program | readonly | — |
| 9 | token_program | readonly | — |
| 10 | encrypt_program | readonly | — |
Instruction Data
| Offset | Size | Field |
|---|---|---|
| 0 | 1 | discriminator 0x09 |
| 1 | 8 | amount (u64 LE) |
| 9 | 1 | position_bump |
PrivateBorrow
Discriminator: 0x0A (10)
Borrow tokens (same checks as Borrow) plus FHE add_debt on enc_debt.
Instruction Data
| Offset | Size | Field |
|---|---|---|
| 0 | 1 | discriminator 0x0A |
| 1 | 8 | amount (u64 LE) |
PrivateRepay
Discriminator: 0x0B (11)
Repay debt (same as Repay) plus FHE sub_debt on enc_debt.
Instruction Data
| Offset | Size | Field |
|---|---|---|
| 0 | 1 | discriminator 0x0B |
| 1 | 8 | amount (u64 LE) |
PrivateWithdraw
Discriminator: 0x0C (12)
Withdraw deposit (same as Withdraw) plus FHE sub_deposit on enc_deposit.
Instruction Data
| Offset | Size | Field |
|---|---|---|
| 0 | 1 | discriminator 0x0C |
| 1 | 8 | shares (u64 LE) |
Privacy Model
┌──────────────────────────────────────────────────────────┐
│ What is on-chain │
│ │
│ UserPosition.deposit_shares ← plaintext (authoritative)│
│ UserPosition.borrow_principal← plaintext (authoritative)│
│ │
│ EncryptedPosition.enc_deposit ← opaque 32-byte pubkey │
│ EncryptedPosition.enc_debt ← opaque 32-byte pubkey │
│ │
│ ciphertext account (Encrypt) ← FHE-encrypted amount │
└──────────────────────────────────────────────────────────┘Health factor checks always use the plaintext UserPosition. The encrypted ciphertexts provide observer confidentiality — an RPC caller cannot infer position size from on-chain data. The protocol itself never operates on encrypted values for solvency decisions.