Skip to Content
Architecture

Architecture

Account Model

Veil uses three primary on-chain account types, each identified by an 8-byte discriminator in the first bytes of its data:

AccountDiscriminatorSizePurpose
LendingPoolVEILPOOL416 bytesOne per token market. Tracks indices, parameters, and oracle cache.
UserPositionVEILPOS!144 bytesOne per (user, pool) pair. Tracks deposit shares and borrow principal.
EncryptedPositionVEILENC!144 bytesOptional. Holds ciphertext account handles when privacy is enabled.
IkaDwalletPositionVEILIKA!128 bytesOne per (user, pool) pair for Ika dWallet collateral.

All accounts use #[repr(C)] zero-copy layout — no serialisation overhead. References are cast directly from raw account data pointers.

PDA Derivation

All seeds are byte literals (b"…" in Rust) — no string-encoded UTF-8 prefix.

LendingPool seeds: [b"pool", token_mint] program: Veil PoolAuthority seeds: [b"authority", pool] program: Veil UserPosition seeds: [b"position", pool, user] program: Veil EncryptedPos seeds: [b"enc_pos", owner, pool] program: Veil IkaDwalletPos seeds: [b"ika_pos", pool, user] program: Veil CPI Authority seeds: [b"__ika_cpi_authority"] program: Veil

Source: programs/src/instructions/initialize.rs, deposit.rs, ika_register.rs, programs/src/state/encrypted_position.rs, programs/src/ika/mod.rs:67.

LendingPool Layout (416 bytes)

Offset Size Field ────────────────────────────────────────────────────────────── 0 8 discriminator ("VEILPOOL") 8 32 authority ← protocol admin pubkey 40 32 token_mint 72 32 vault ← SPL token account (owned by PoolAuthority PDA) 104 8 total_deposits ← virtual (includes accrued interest) 112 8 total_borrows ← virtual 120 8 accumulated_fees 128 8 last_update_timestamp 136 1 authority_bump 137 1 pool_bump 138 1 vault_bump 139 1 paused ← 0 = active, 1 = paused 140 4 _pad 144 16 borrow_index ← WAD = 1e18 160 16 supply_index 176 16 base_rate ← WAD-scaled annual rate 192 16 optimal_utilization ← WAD-scaled 208 16 slope1 224 16 slope2 240 16 reserve_factor 256 16 ltv 272 16 liquidation_threshold 288 16 liquidation_bonus 304 16 protocol_liq_fee 320 16 close_factor 336 8 flash_loan_amount ← 0 = no active flash loan 344 8 flash_fee_bps ← default 9 (= 0.09%) 352 32 pyth_price_feed ← zero = not anchored 384 8 oracle_price ← raw aggregate price 392 8 oracle_conf ← raw confidence interval 400 4 oracle_expo ← price exponent (negative) 404 12 _oracle_pad 416 (end)

Instruction Dispatch

Every instruction is routed by the first byte of the instruction data (the discriminator). There is no Anchor anchor discriminator hash — just a raw u8.

0x00 Initialize 0x08 EnablePrivacy 0x10 CollectFees 0x01 Deposit 0x09 PrivateDeposit 0x11 IkaRegister 0x02 Withdraw 0x0A PrivateBorrow 0x12 IkaRelease 0x03 Borrow 0x0B PrivateRepay 0x13 IkaSign 0x04 Repay 0x0C PrivateWithdraw 0x14 UpdateOraclePrice 0x05 Liquidate 0x0D UpdatePool 0x06 FlashBorrow 0x0E PausePool 0x07 FlashRepay 0x0F ResumePool

The dispatch table is in programs/src/entrypoint.rs:28-50.

Interest Accrual

Interest is accrued lazily — at the start of every state-mutating instruction — by calling LendingPool::accrue_interest(current_timestamp). This advances two global indices:

  • borrow_index — cumulative growth of 1 unit of debt. A borrower’s current debt = principal × (borrow_index / snapshot_at_borrow_time).
  • supply_index — cumulative growth of 1 unit of supply. A depositor’s balance = shares × supply_index.

This is the same index-based model used by Aave v2 and Compound v3 — positions are always up to date without per-position storage.

Component Relationships

┌───────────────────────────────────────────────────────┐ │ Veil Program │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ LendingPool │ │ UserPosition │ │ │ │ (per token) │ │ (per user) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ accrue_interest │ deposit_shares │ │ │ oracle_price │ borrow_principal │ │ └───────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ Privacy Layer (optional per position) │ │ │ │ EncryptedPosition → Encrypt program (CPI) │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ Ika Layer (cross-chain collateral) │ │ │ │ IkaDwalletPosition → Ika program (CPI) │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ Oracle Layer │ │ │ │ LendingPool.oracle_* ← Pyth push account │ │ │ └─────────────────────────────────────────────────┘ │ └───────────────────────────────────────────────────────┘

External Programs

ProgramIDRole
Ika dWallet87W54kGYFQ1rgWqMeu4XTPHWXWmXSQCcjm8vCTfiq1oYCross-chain MPC signing
Encrypt4ebfzWdKnrnGseuQpezXdG8yCdHqwQ1SSBHD3bWArND8FHE ciphertext operations
SPL TokenTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DAToken transfers
System Program11111111111111111111111111111111Account creation

The Encrypt program CPI is stubbed pending an SDK update from pinocchio 0.10.x → 0.11.x. All five private instructions compile and route correctly; the graph execution call activates when the SDK ships the update.

Last updated on