Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pinocchio Vault

Single-asset tokenized vault built with Pinocchio.

This repo implements the core vault instruction surface:

  • initialize_vault
  • deposit
  • withdraw
  • pause
  • unpause
  • transfer_admin
  • accept_admin

The vault is similar in spirit to ERC-4626, but intentionally smaller: one underlying SPL Token mint, one share mint, PDA custody, conservative share accounting, and authority controls.

Devnet

  • Program ID: not deployed yet

Current Status

Implemented and tested:

  • vault initialization with deterministic PDA seeds
  • PDA-controlled underlying token custody
  • PDA-controlled share mint authority
  • first-deposit 1:1 share minting
  • proportional deposits after bootstrap
  • proportional withdrawals by burning shares
  • conservative floor rounding for deposits and withdrawals
  • user slippage protection on deposit and withdraw
  • pause and unpause controls
  • two-step admin transfer
  • wrong user underlying mint rejection
  • non-admin admin-transfer rejection
  • SBF build loaded into LiteSVM tests

Not implemented yet:

  • yield strategy integration
  • Token-2022 support
  • protocol, management, or performance fees
  • governance
  • exact preview helpers
  • fuzz tests across many randomized users

Design

initialize_vault

flowchart LR
    A[Underlying SPL mint]
    B[Share SPL mint]
    C[Initialize Vault]
    D[Create Vault State PDA]
    E[Validate Vault Authority PDA]
    F[Validate Underlying Vault]

    A --> C
    B --> C
    C --> D
    C --> E
    C --> F
Loading

1. Vault Model

Each vault stores:

  • admin
  • pending_admin
  • pause_authority
  • underlying_mint
  • share_mint
  • underlying_vault
  • vault_authority
  • total_underlying
  • total_shares
  • PDA bumps

Vault state PDA:

[b"vault", underlying_mint, share_mint]

Vault authority PDA:

[b"vault_authority", vault_state]

This guarantees one deterministic vault state account per underlying/share mint pair.

deposit

flowchart LR
    A[User wallet<br/>Underlying tokens]
    B[Deposit]
    C[Underlying vault receives assets]
    D[User receives vault shares]
    E[Vault accounting increases]

    A --> B
    B --> C
    B --> D
    B --> E
Loading

2. Share Model

Vault shares represent proportional ownership of the vault's underlying balance.

Bootstrap deposit:

  • shares minted = amount_in

Non-bootstrap deposit:

  • shares minted = floor(amount_in * total_shares / total_underlying)

Deposits round down against the depositor. If rounding produces zero shares, the instruction fails.

withdraw

flowchart LR
    A[User holds vault shares]
    B[Withdraw]
    C[Burn user shares]
    D[Transfer underlying from vault]
    E[Vault accounting decreases]

    A --> B
    B --> C
    B --> D
    B --> E
Loading

3. Withdrawal Model

Withdrawals burn shares and return proportional underlying.

Underlying returned:

  • underlying_out = floor(shares_in * total_underlying / total_shares)

Withdrawals round down against the withdrawer. If rounding produces zero underlying, the instruction fails.

4. Authority Model

The vault separates operational and ownership controls:

  • vault_authority is a PDA used for SPL Token CPIs
  • pause_authority can pause and unpause deposits and withdrawals
  • admin can start an admin handoff
  • pending_admin must accept the handoff before admin changes

This keeps token custody program-controlled and avoids single-step admin replacement.

Architecture

flowchart TD
    A[User Wallet]
    B[Vault Instructions<br/>initialize_vault<br/>deposit<br/>withdraw<br/>pause<br/>unpause]
    C[Vault State PDA]
    D[Vault Authority PDA]
    E[Underlying Token Vault]
    F[Share Mint]
    G[User Share Account]

    A --> B
    B --> C
    C --> D
    D --> E
    D --> F
    F --> G
Loading

Math Layout

Vault math lives in math.rs:

  • checked_mul_div_floor
  • calculate_deposit_shares
  • calculate_withdraw_underlying

This keeps:

  • instruction orchestration
  • token CPIs
  • pure share accounting

separated cleanly.

Instruction Layout

Program entrypoint:

Processor and dispatch:

Vault state and PDA helpers:

Reusable token validation helpers:

Error definitions:

Instruction Data

All instruction data is fixed-size and little-endian.

Discriminant Instruction Payload
0 initialize_vault none
1 deposit amount_in: u64, minimum_shares_out: u64
2 withdraw shares_in: u64, minimum_underlying_out: u64
3 pause none
4 unpause none
5 transfer_admin new_admin: [u8; 32]
6 accept_admin none

Account Order

initialize_vault

  1. Payer signer, writable
  2. Admin signer
  3. Vault state PDA, writable
  4. Vault authority PDA
  5. Underlying mint
  6. Share mint
  7. Underlying vault token account
  8. System program
  9. Token program

The payer funds the new vault state account. The share mint and underlying vault token account must already exist and must be controlled by the vault authority PDA.

deposit

  1. Depositor signer
  2. Vault state, writable
  3. Vault authority PDA
  4. User underlying token account, writable
  5. Underlying vault token account, writable
  6. Share mint, writable
  7. User share token account, writable
  8. Token program

withdraw

  1. Withdrawer signer
  2. Vault state, writable
  3. Vault authority PDA
  4. User share token account, writable
  5. Share mint, writable
  6. Underlying vault token account, writable
  7. User underlying token account, writable
  8. Token program

pause And unpause

  1. Pause authority signer
  2. Vault state, writable

transfer_admin

  1. Admin signer
  2. Vault state, writable

accept_admin

  1. Pending admin signer
  2. Vault state, writable

Tests

This repo uses Rust unit tests and LiteSVM integration tests.

Test layers:

  1. Rust unit tests for math and instruction decoding
  2. LiteSVM integration tests that load the compiled SBF program

Current integration coverage includes:

  • initialize vault happy path
  • first deposit success
  • second deposit success
  • partial withdraw success
  • pause blocks deposit
  • pause blocks withdraw
  • unpause restores operations
  • two-step admin transfer success
  • wrong user underlying mint rejection
  • non-admin transfer-admin rejection

See:

Commands

Build the SBF program:

cargo build-sbf --features bpf-entrypoint

Run Rust unit tests and LiteSVM integration tests:

cargo test

Run formatting:

cargo fmt --all --check

Run Clippy:

cargo clippy --all-targets --all-features -- -D warnings

The LiteSVM tests load:

target/deploy/pinocchio_vault.so

So run the SBF build before the full test suite if the artifact is missing or stale.

Learning Note

The important part of a vault is not the number of instructions. It is the accounting invariant.

For this vault, the bar is:

  • shares cannot be minted without an underlying deposit
  • underlying cannot leave without burning shares
  • vault accounting tracks token balances and share supply
  • rounding is conservative
  • authority checks happen before value-moving CPIs

Once these rules are reliable, fees, strategies, Token-2022, and governance can be added without weakening the base primitive.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages