Privacy-Proxy: Solana Protocol Architecture Design
This document provides a comprehensive architecture design for Privacy-Proxy, a ZK-powered private wallet protocol on Solana. It follows the Solana Protocol Architecture Diagram Construction Guide, covering program structure, account mappings, external dependencies, and detailed interaction flows.
High-Level System Architecture
Program Structure Visualization
Account Structure Mapping
Cross-Program Invocations (CPIs)
User Interaction Flows
External Dependencies
Security Architecture
Error Handling & Decision Points
Deployment Architecture
1. High-Level System Architecture
1.1 Complete System Overview
flowchart TB
subgraph ClientLayer["Client Layer"]
direction LR
UI[Privacy-Proxy dApp<br/>Next.js/React]
Phantom[Phantom Wallet<br/>Browser Extension]
ZKClient[ZK Proof Generator<br/>WASM Module]
end
subgraph PrivacyLayer["Privacy Infrastructure"]
direction LR
Relayer[Transaction Relayer<br/>Tor Hidden Service]
StealthGen[Stealth Address<br/>Generator]
end
subgraph SolanaPrograms["Solana On-Chain Programs"]
direction TB
subgraph CoreProgram["Privacy-Proxy Core Program"]
ProxyMgr[Proxy Wallet Manager]
DepositMgr[Deposit Pool Manager]
WithdrawMgr[Withdrawal Manager]
SessionMgr[Session Key Manager]
end
subgraph ZKProgram["ZK Verifier Program"]
PasswordVerifier[Password Proof Verifier]
WithdrawVerifier[Withdrawal Proof Verifier]
end
end
subgraph ExternalPrograms["External Solana Programs"]
SystemProgram[System Program]
Token2022[Token-2022 Program<br/>Confidential Transfers]
Bonsol[Bonsol Verifier<br/>RISC-0 ZK Proofs]
end
subgraph DataLayer["On-Chain Accounts"]
GlobalConfig[(Global Config)]
ProxyWallets[(Proxy Wallets)]
DepositPools[(Deposit Pools)]
PendingTxs[(Pending Transactions)]
Sessions[(Session Tokens)]
Nullifiers[(Nullifier Registry)]
end
%% Client connections
UI <-->|Connect| Phantom
UI --> ZKClient
UI -->|Submit via| Relayer
%% Privacy layer
Relayer -->|Anonymous Submit| SolanaPrograms
StealthGen -.->|Generate| UI
%% Program interactions
CoreProgram -->|CPI| ZKProgram
CoreProgram -->|CPI| ExternalPrograms
ZKProgram -->|CPI| Bonsol
%% Data access
CoreProgram <-->|Read/Write| DataLayer
ZKProgram -->|Read| DataLayer
Loading
1.2 Layer Responsibilities
Layer
Components
Responsibility
Client
dApp UI, Phantom, ZK Generator
User interaction, proof generation, wallet signing
Privacy
Relayer, Stealth Generator
IP hiding, recipient anonymity
On-Chain Core
Proxy/Deposit/Withdraw/Session Managers
State management, business logic
On-Chain ZK
Password/Withdrawal Verifiers
Cryptographic proof verification
External
System, Token2022, Bonsol
Native Solana functionality, ZK infrastructure
Data
PDAs
Persistent state storage
2. Program Structure Visualization
2.1 Privacy-Proxy Core Program
flowchart TB
subgraph PrivacyProxyProgram["privacy_proxy (Program ID: PPxy...)]"]
direction TB
subgraph Instructions["Instructions"]
I1[initialize_global_config]
I2[initialize_proxy_wallet]
I3[set_password]
I4[deposit]
I5[request_withdrawal]
I6[execute_withdrawal]
I7[cancel_withdrawal]
I8[create_session]
I9[revoke_session]
I10[withdraw_to_public]
I11[pause_system]
I12[update_config]
end
subgraph Handlers["Instruction Handlers"]
H1[config_handler.rs]
H2[proxy_handler.rs]
H3[deposit_handler.rs]
H4[withdrawal_handler.rs]
H5[session_handler.rs]
end
subgraph State["State Definitions"]
S1[GlobalConfig]
S2[ProxyWallet]
S3[DepositPool]
S4[PendingTransaction]
S5[SessionToken]
S6[NullifierRegistry]
end
I1 & I11 & I12 --> H1
I2 & I3 --> H2
I4 --> H3
I5 & I6 & I7 & I10 --> H4
I8 & I9 --> H5
H1 --> S1
H2 --> S2
H3 --> S3
H4 --> S4 & S6
H5 --> S5
end
Loading
flowchart TB
subgraph ZKVerifierProgram["zk_verifier (Program ID: ZKvf...)]"]
direction TB
subgraph ZKInstructions["Instructions"]
ZI1[verify_password_proof]
ZI2[verify_withdrawal_proof]
ZI3[verify_deposit_commitment]
end
subgraph Circuits["ZK Circuits"]
C1[Password Circuit<br/>Poseidon Hash Preimage]
C2[Withdrawal Circuit<br/>Merkle Inclusion + Nullifier]
C3[Commitment Circuit<br/>Pedersen Commitment]
end
subgraph Verifiers["Proof Verifiers"]
V1[Groth16 Verifier]
V2[RISC-0 STARK Verifier<br/>via Bonsol CPI]
end
ZI1 --> C1 --> V1
ZI2 --> C2 --> V2
ZI3 --> C3 --> V1
end
Loading
2.3 Program Interaction Matrix
flowchart LR
subgraph Programs["Solana Programs"]
PP[Privacy-Proxy<br/>Core]
ZK[ZK Verifier]
T22[Token-2022]
SYS[System Program]
BON[Bonsol]
end
PP -->|verify_password_proof| ZK
PP -->|verify_withdrawal_proof| ZK
PP -->|transfer| SYS
PP -->|confidential_transfer| T22
ZK -->|verify_stark| BON
Loading
3. Account Structure Mapping
3.1 Complete Account Hierarchy
flowchart TB
subgraph GlobalAccounts["Global Accounts (1 per deployment)"]
GC[GlobalConfig PDA<br/>Seeds: b'config']
end
subgraph UserAccounts["Per-User Accounts"]
PW[ProxyWallet PDA<br/>Seeds: b'proxy', user_pubkey]
ST[SessionToken PDA<br/>Seeds: b'session', proxy_wallet, nonce]
end
subgraph PoolAccounts["Pool Accounts (1 per bucket)"]
DP01[DepositPool 0.1 SOL<br/>Seeds: b'pool', bucket_id]
DP1[DepositPool 1 SOL]
DP10[DepositPool 10 SOL]
DP100[DepositPool 100 SOL]
end
subgraph TransactionAccounts["Transaction Accounts"]
PT[PendingTransaction PDA<br/>Seeds: b'pending', proxy_wallet, tx_id]
end
subgraph RegistryAccounts["Registry Accounts"]
NR[NullifierRegistry PDA<br/>Seeds: b'nullifier', nullifier_hash]
EK[EphemeralKeyRegistry PDA<br/>Seeds: b'ephemeral', ephemeral_pubkey]
end
GC -.->|references| PoolAccounts
PW -->|creates| ST
PW -->|creates| PT
PT -->|registers| NR
Loading
3.2 Detailed Account Schemas
classDiagram
class GlobalConfig {
<<PDA: seeds = [b"config"]>>
+admin: Pubkey
+fee_min_bps: u16
+fee_max_bps: u16
+default_timelock_seconds: i64
+paused: bool
+total_deposits: u64
+total_withdrawals: u64
+treasury: Pubkey
+bump: u8
---
Space: 8 + 32 + 2 + 2 + 8 + 1 + 8 + 8 + 32 + 1 = 102 bytes
}
Loading
classDiagram
class ProxyWallet {
<<PDA: seeds = [b"proxy", owner]>>
+owner: Pubkey
+password_commitment: bytes32
+per_tx_limit: u64
+daily_limit: u64
+daily_spent: u64
+last_reset_day: i64
+timelock_override: Option of i64
+nonce: u64
+created_at: i64
+bump: u8
---
Space: 8 + 32 + 32 + 8 + 8 + 8 + 8 + 9 + 8 + 8 + 1 = 130 bytes
}
class ProxyWalletConstraints {
<<Validation Rules>>
+owner must sign for modifications
+password_commitment not equal zero for withdrawals
+daily_spent resets when day changes
+per_tx_limit within daily_limit
}
ProxyWallet ..> ProxyWalletConstraints : validates
Loading
classDiagram
class DepositPool {
<<PDA: seeds = [b"pool", bucket_id]>>
+bucket_id: u8
+bucket_amount_lamports: u64
+total_deposits: u64
+anonymity_set_size: u64
+merkle_root: bytes32
+deposit_index: u64
+bump: u8
---
Space: 8 + 1 + 8 + 8 + 8 + 32 + 8 + 1 = 74 bytes
}
class BucketConfig {
<<Constants>>
BUCKET_0: 0.1 SOL = 100_000_000 lamports
BUCKET_1: 0.5 SOL = 500_000_000 lamports
BUCKET_2: 1 SOL = 1_000_000_000 lamports
BUCKET_3: 5 SOL = 5_000_000_000 lamports
BUCKET_4: 10 SOL = 10_000_000_000 lamports
BUCKET_5: 50 SOL = 50_000_000_000 lamports
BUCKET_6: 100 SOL = 100_000_000_000 lamports
}
DepositPool ..> BucketConfig : uses
Loading
PendingTransaction Account
classDiagram
class PendingTransaction {
<<PDA: seeds = [b"pending", proxy_wallet, tx_id]>>
+tx_id: u64
+proxy_wallet: Pubkey
+recipient_stealth: Pubkey
+amount_lamports: u64
+execute_after: i64
+created_at: i64
+status: TransactionStatus
+nullifier: bytes32
+zk_proof_hash: bytes32
+bump: u8
---
Space: 8 + 8 + 32 + 32 + 8 + 8 + 8 + 1 + 32 + 32 + 1 = 170 bytes
}
class TransactionStatus {
<<Enum: 1 byte>>
Pending = 0
Executed = 1
Cancelled = 2
Expired = 3
}
PendingTransaction --> TransactionStatus
Loading
classDiagram
class SessionToken {
<<PDA: seeds = [b"session", proxy_wallet, session_nonce]>>
+proxy_wallet: Pubkey
+session_pubkey: Pubkey
+valid_until: i64
+max_amount: u64
+spent_amount: u64
+allowed_programs: Vec of Pubkey
+created_at: i64
+bump: u8
---
Space: 8 + 32 + 32 + 8 + 8 + 8 + (4 + 32*5) + 8 + 1 = 269 bytes (max 5 programs)
}
class SessionConstraints {
<<Validation Rules>>
+valid_until > current_timestamp
+spent_amount + tx_amount within max_amount
+target_program in allowed_programs
+session_pubkey must sign
}
SessionToken ..> SessionConstraints : validates
Loading
3.3 PDA Derivation Diagram
flowchart LR
subgraph Seeds["Seeds"]
S1["b'config'"]
S2["b'proxy' + owner_pubkey"]
S3["b'pool' + bucket_id"]
S4["b'pending' + proxy_wallet + tx_id"]
S5["b'session' + proxy_wallet + nonce"]
S6["b'nullifier' + nullifier_hash"]
end
subgraph Derivation["find_program_address()"]
D1[SHA256 + bump search]
end
subgraph PDAs["Program Derived Addresses"]
P1[GlobalConfig PDA]
P2[ProxyWallet PDA]
P3[DepositPool PDA]
P4[PendingTransaction PDA]
P5[SessionToken PDA]
P6[NullifierRegistry PDA]
end
S1 --> D1 --> P1
S2 --> D1 --> P2
S3 --> D1 --> P3
S4 --> D1 --> P4
S5 --> D1 --> P5
S6 --> D1 --> P6
Loading
4. Cross-Program Invocations (CPIs)
flowchart TB
subgraph Caller["Privacy-Proxy Core Program"]
REQ[request_withdrawal instruction]
EXEC[execute_withdrawal instruction]
DEP[deposit instruction]
end
subgraph ZKVerifier["ZK Verifier Program"]
VPW[verify_password_proof]
VWD[verify_withdrawal_proof]
end
subgraph Bonsol["Bonsol Program"]
STARK[verify_stark_proof]
end
subgraph SystemProgram["System Program"]
TRANSFER[transfer]
CREATE[create_account]
end
subgraph Token2022["Token-2022 Program"]
CT[confidential_transfer]
MINT[mint_to]
end
REQ -->|1. CPI| VPW
VPW -->|2. CPI| STARK
STARK -->|3. Return| VPW
VPW -->|4. Return bool| REQ
EXEC -->|1. CPI| VWD
VWD -->|2. CPI| STARK
EXEC -->|3. CPI| TRANSFER
EXEC -->|4. CPI| CT
DEP -->|1. CPI| TRANSFER
DEP -->|2. Update| MerkleTree[(Merkle Tree)]
Loading
4.2 CPI Security Considerations
flowchart TB
subgraph CPISecurity["CPI Security Checks"]
direction TB
C1[Verify program_id matches expected]
C2[Check account ownership]
C3[Validate signer privileges]
C4[Prevent reentrancy]
C5[Limit CPI depth]
end
subgraph Vulnerabilities["Common CPI Vulnerabilities"]
V1[Arbitrary CPI - calling untrusted programs]
V2[Missing owner checks]
V3[Signer privilege escalation]
V4[Account confusion attacks]
end
subgraph Mitigations["Our Mitigations"]
M1[Hardcoded program IDs]
M2[has_one constraints in Anchor]
M3[Explicit signer requirements]
M4[Account type discriminators]
end
V1 --> M1
V2 --> M2
V3 --> M3
V4 --> M4
Loading
4.3 CPI Call Specifications
Source Instruction
Target Program
Target Instruction
Accounts Passed
Signer Seeds
request_withdrawal
ZK Verifier
verify_password_proof
proof_data, commitment
None
execute_withdrawal
ZK Verifier
verify_withdrawal_proof
proof_data, merkle_root, nullifier
None
execute_withdrawal
System Program
transfer
pool_pda, recipient
pool_seeds
deposit
System Program
transfer
user, pool_pda
None
verify_*_proof
Bonsol
verify_stark
proof_data, vk
None
5. User Interaction Flows
sequenceDiagram
autonumber
participant User as User
participant UI as dApp UI
participant Phantom as Phantom
participant Relayer as Relayer
participant Core as Core Program
participant Pool as Deposit Pool
User->>UI: Select deposit amount (10 SOL)
UI->>UI: Validate bucket exists
UI->>UI: Generate deposit commitment<br/>commitment = Poseidon(nullifier, amount, randomness)
UI->>UI: Store nullifier + randomness locally (encrypted)
UI->>Phantom: Request transaction signature
Phantom->>User: "Deposit 10 SOL to Privacy Pool?"
User->>Phantom: Approve
Phantom->>UI: Signed transaction
UI->>Relayer: Submit signed transaction
Relayer->>Core: deposit(bucket_id=4, commitment)
Core->>Core: Validate bucket_id
Core->>Core: Verify amount = 10 SOL
Core->>Pool: CPI Transfer 10 SOL from user
Core->>Pool: Insert commitment into Merkle tree
Core->>Pool: Increment anonymity_set_size
Core->>Core: Emit DepositEvent(commitment, bucket_id)
Core-->>Relayer: Transaction confirmed
Relayer-->>UI: Success + tx signature
UI-->>User: "Deposit complete! Anonymity set: 47"
Loading
5.2 Private Withdrawal Flow
sequenceDiagram
autonumber
participant User as User
participant UI as dApp UI
participant ZKGen as ZK Generator
participant Phantom as Phantom
participant Relayer as Relayer
participant Core as Core Program
participant ZKV as ZK Verifier
participant Pool as Pool
User->>UI: Enter recipient stealth address
User->>UI: Enter ZK password
UI->>ZKGen: Generate password proof
Note over ZKGen: Proves knowledge of password where Poseidon hash equals commitment
ZKGen-->>UI: password_proof p1
UI->>ZKGen: Generate withdrawal proof
Note over ZKGen: Prove commitment in Merkle tree AND nullifier is fresh
ZKGen-->>UI: withdrawal_proof p2
UI->>Phantom: Request session signature
Phantom-->>UI: Signed
UI->>Relayer: Submit withdrawal request (encrypted)
Relayer->>Core: request_withdrawal p1, p2, stealth_addr, amount
Core->>ZKV: CPI verify_password_proof p1
ZKV-->>Core: Valid
Core->>ZKV: CPI verify_withdrawal_proof p2
ZKV-->>Core: Valid
Core->>Core: Check spending limits
Core->>Core: Create PendingTransaction
Core->>Core: Set execute_after = now + 30min
Core->>Core: Emit WithdrawalRequestedEvent
Core-->>Relayer: Pending TX created
Relayer-->>UI: TX ID + execute_after
UI-->>User: "Withdrawal pending. Cancel within 30 min."
Loading
5.3 Timelock Execution Flow
sequenceDiagram
autonumber
participant Anyone as Anyone (Permissionless)
participant Core as Core Program
participant ZKV as ZK Verifier
participant Pool as Pool
participant Stealth as Stealth Address
participant NR as Nullifier Registry
Note over Anyone: After timelock expires...
Anyone->>Core: execute_withdrawal(tx_id)
Core->>Core: Load PendingTransaction
Core->>Core: Check: current_time >= execute_after
Core->>Core: Check: status == Pending
Core->>NR: Check nullifier not used
NR-->>Core: Fresh nullifier
Core->>NR: Mark nullifier as used
Core->>Core: Calculate random fee (0.5-3%)
Note over Core: fee calculated using VRF with block_hash and tx_id
Core->>Pool: CPI Transfer amount minus fee to stealth_addr
Core->>Core: Transfer fee to treasury
Core->>Core: Update status = Executed
Core->>Core: Emit WithdrawalExecutedEvent
Core-->>Anyone: Success
Note over Stealth: Recipient scans ephemeral keys<br/>to detect incoming funds
Loading
sequenceDiagram
autonumber
participant User as User
participant UI as dApp UI
participant Phantom as Phantom
participant Core as Core Program
participant PT as PendingTx
Note over User: User receives notification<br/>of pending withdrawal
User->>UI: Click "Cancel Transaction"
UI->>Phantom: Request signature
Phantom->>User: "Cancel withdrawal #42?"
User->>Phantom: Approve
UI->>Core: cancel_withdrawal(tx_id=42)
Core->>PT: Load PendingTransaction
Core->>Core: Verify: caller == proxy_wallet.owner
Core->>Core: Verify: current_time < execute_after
Core->>Core: Verify: status == Pending
Core->>PT: Update status = Cancelled
Core->>Core: Refund daily_spent counter
Core->>Core: Close PendingTransaction account
Core->>Core: Return rent to user
Core->>Core: Emit WithdrawalCancelledEvent
Core-->>UI: Cancelled successfully
UI-->>User: "Transaction cancelled. Funds safe."
Loading
sequenceDiagram
autonumber
participant User as User
participant UI as dApp UI
participant Phantom as Phantom
participant Core as Core Program
participant DeFi as DeFi Protocol
Note over User: User wants to interact with DeFi<br/>without approving every tx
User->>UI: "Create session: 1 hour, max 5 SOL"
UI->>UI: Generate ephemeral session keypair
UI->>Phantom: Request signature for session creation
Phantom->>User: Create session? Duration 1hr, Max 5 SOL
User->>Phantom: Approve
UI->>Core: create_session(session_pubkey, valid_until, max_amount, allowed_programs)
Core->>Core: Create SessionToken PDA
Core-->>UI: Session created
loop DeFi Interactions (within session)
User->>UI: Execute swap
UI->>UI: Sign with session key (no Phantom popup)
UI->>Core: execute_with_session(session_signature, swap_params)
Core->>Core: Verify session_pubkey signed
Core->>Core: Verify valid_until > now
Core->>Core: Verify spent + amount within max
Core->>Core: Verify target in allowed_programs
Core->>DeFi: CPI Execute swap
Core->>Core: Update spent_amount
end
User->>UI: "Revoke session"
UI->>Phantom: Request owner signature
UI->>Core: revoke_session(session_id)
Core->>Core: Close SessionToken, return rent
Loading
6.1 External System Integration Diagram
flowchart TB
subgraph PrivacyProxy["Privacy-Proxy Protocol"]
Core[Core Program]
ZKV[ZK Verifier]
end
subgraph SolanaRuntime["Solana Runtime"]
SYS[System Program<br/>11111111111111111111111111111111]
T22[Token-2022 Program<br/>TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb]
CLOCK[Sysvar: Clock<br/>SysvarC1ock11111111111111111111111111111111]
RENT[Sysvar: Rent<br/>SysvarRent111111111111111111111111111111111]
end
subgraph ZKInfra["ZK Infrastructure"]
BON[Bonsol Verifier<br/>RISC-0 STARK Proofs]
VK[(Verification Keys<br/>On-chain or hardcoded)]
end
subgraph Oracles["Oracles (Optional)"]
VRF[Switchboard VRF<br/>Random Fee Generation]
PRICE[Pyth Price Feed<br/>USD Value Limits]
end
subgraph OffChain["Off-Chain Services"]
RELAY[Relayer Service<br/>Tor Hidden Service]
IPFS[IPFS<br/>Frontend Hosting]
HELIUS[Helius<br/>Webhooks & RPC]
end
Core -->|transfer, create_account| SYS
Core -->|confidential_transfer| T22
Core -->|get_clock| CLOCK
Core -->|get_rent| RENT
ZKV -->|verify_stark| BON
ZKV -->|load| VK
Core -.->|random| VRF
Core -.->|price| PRICE
RELAY -->|submit_tx| Core
HELIUS -->|events| RELAY
Loading
6.2 Dependency Specifications
Dependency
Type
Purpose
Integration Method
System Program
Native
SOL transfers, account creation
CPI with system_instruction
Token-2022
Native
Confidential token transfers
CPI with spl_token_2022
Clock Sysvar
Native
Timestamp for timelocks
Sysvar account read
Rent Sysvar
Native
Rent-exempt calculations
Sysvar account read
Bonsol
On-chain
RISC-0 STARK verification
CPI
Switchboard VRF
On-chain
Verifiable randomness for fees
CPI (optional)
Helius
Off-chain
RPC, webhooks, indexing
HTTP/WebSocket
IPFS
Off-chain
Frontend hosting
Content addressing
6.3 Token-2022 Confidential Transfer Integration
flowchart TB
subgraph UserFlow["User Confidential Balance Flow"]
U1[User deposits SOL]
U2[Mint wrapped SOL with CT extension]
U3[Balance encrypted with ElGamal]
U4[Transfer with ZK range proofs]
U5[Recipient decrypts with viewing key]
end
subgraph T22Accounts["Token-2022 Account Structure"]
MINT[Mint Account<br/>+ ConfidentialTransferMint extension]
TOKEN[Token Account<br/>+ ConfidentialTransferAccount extension]
PROOF[Proof Account<br/>ZK proof data]
end
subgraph ZKProofs["Required ZK Proofs"]
P1[Equality Proof<br/>encrypted = committed]
P2[Range Proof<br/>0 to 2 pow 64]
P3[Ciphertext Validity<br/>proper encryption]
end
U1 --> U2 --> U3 --> U4 --> U5
U2 --> MINT
U3 --> TOKEN
U4 --> PROOF
PROOF --> P1 & P2 & P3
Loading
7.1 Defense in Depth Layers
flowchart TB
subgraph Layer1[Layer 1 - Wallet Access]
L1A[Phantom Biometric]
L1B[Hardware Wallet Support]
end
subgraph Layer2[Layer 2 - Session Authorization]
L2A[Time-limited Sessions]
L2B[Scope-limited Permissions]
L2C[Program Whitelisting]
end
subgraph Layer3[Layer 3 - ZK Password]
L3A[Poseidon Hash Commitment]
L3B[Client-side Proof Generation]
L3C[On-chain Verification]
end
subgraph Layer4[Layer 4 - Spending Limits]
L4A[Per-Transaction Limit]
L4B[Daily Limit]
L4C[Automatic Reset]
end
subgraph Layer5[Layer 5 - Timelock]
L5A[30-min Default Delay]
L5B[Cancellation Window]
L5C[Notification System]
end
subgraph Layer6[Layer 6 - Privacy]
L6A[Stealth Addresses]
L6B[Relayer IP Hiding]
L6C[Fixed Denominations]
end
Attacker[Attacker] --> Layer1
Layer1 -->|Bypassed| Layer2
Layer2 -->|Bypassed| Layer3
Layer3 -->|Bypassed| Layer4
Layer4 -->|Bypassed| Layer5
Layer5 -->|User Cancels| Safe1[Funds Safe]
Layer5 -->|Timeout| Layer6
Layer6 --> Limited[Limited Loss]
Loading
7.2 Attack Scenario Decision Tree
flowchart TB
START[Attacker gains<br/>wallet access]
Q1{Session key<br/>active?}
Q2{Knows ZK<br/>password?}
Q3{Within spending<br/>limits?}
Q4{User online to<br/>cancel?}
BLOCK1[BLOCKED<br/>Cannot create session<br/>without Phantom approval]
BLOCK2[BLOCKED<br/>Cannot generate<br/>valid ZK proof]
BLOCK3[BLOCKED<br/>Transaction rejected<br/>by on-chain limits]
SAFE[SAFE<br/>User cancels<br/>during timelock]
LOSS[PARTIAL LOSS<br/>Limited by daily cap]
START --> Q1
Q1 -->|No| BLOCK1
Q1 -->|Yes| Q2
Q2 -->|No| BLOCK2
Q2 -->|Yes - Guessed| Q3
Q3 -->|Exceeds| BLOCK3
Q3 -->|Within| Q4
Q4 -->|Yes| SAFE
Q4 -->|No - Offline| LOSS
Loading
7.3 Nullifier Double-Spend Prevention
flowchart LR
subgraph Deposit["Deposit Phase"]
D1[User deposits 10 SOL]
D2[Generate nullifier from secret and index]
D3[Store commitment in Merkle tree]
end
subgraph Withdraw1["First Withdrawal Attempt"]
W1A[Submit withdrawal proof]
W1B[Reveal nullifier]
W1C{Nullifier in<br/>registry?}
W1D[Process withdrawal]
W1E[Add nullifier to registry]
end
subgraph Withdraw2["Second Withdrawal Attempt"]
W2A[Submit same proof]
W2B[Reveal same nullifier]
W2C{Nullifier in<br/>registry?}
W2D[REJECT<br/>Double-spend detected]
end
D1 --> D2 --> D3
D3 --> W1A --> W1B --> W1C
W1C -->|No| W1D --> W1E
W1E -.-> W2C
D3 --> W2A --> W2B --> W2C
W2C -->|Yes| W2D
Loading
8. Error Handling & Decision Points
8.1 Deposit Instruction Flow with Error Handling
flowchart TB
START([deposit instruction called])
V1{System<br/>paused?}
V2{Valid bucket<br/>ID?}
V3{Amount matches<br/>bucket?}
V4{User has<br/>sufficient SOL?}
V5{Commitment<br/>unique?}
E1[Error: SystemPaused]
E2[Error: InvalidBucket]
E3[Error: AmountMismatch]
E4[Error: InsufficientFunds]
E5[Error: DuplicateCommitment]
A1[Transfer SOL to pool]
A2[Insert commitment to Merkle tree]
A3[Increment anonymity set]
A4[Emit DepositEvent]
SUCCESS([Return success])
START --> V1
V1 -->|Yes| E1
V1 -->|No| V2
V2 -->|No| E2
V2 -->|Yes| V3
V3 -->|No| E3
V3 -->|Yes| V4
V4 -->|No| E4
V4 -->|Yes| V5
V5 -->|No| E5
V5 -->|Yes| A1 --> A2 --> A3 --> A4 --> SUCCESS
Loading
8.2 Withdrawal Request Flow with Decision Points
flowchart TB
START([request_withdrawal called])
V1{System<br/>paused?}
V2{Password<br/>set?}
V3{ZK password<br/>proof valid?}
V4{ZK withdrawal<br/>proof valid?}
V5{amount within<br/>per_tx_limit?}
V6{daily_spent + amount<br/>within daily_limit?}
V7{Nullifier<br/>fresh?}
E1[SystemPaused]
E2[PasswordNotSet]
E3[InvalidPasswordProof]
E4[InvalidWithdrawalProof]
E5[ExceedsPerTxLimit]
E6[ExceedsDailyLimit]
E7[NullifierAlreadyUsed]
D1{New day since<br/>last_reset?}
A0[Reset daily_spent = 0]
A1[Create PendingTransaction PDA]
A2[Set execute_after = now + timelock]
A3[Update daily_spent]
A4[Add nullifier to pending set]
A5[Emit WithdrawalRequestedEvent]
SUCCESS([Return tx_id])
START --> V1
V1 -->|Yes| E1
V1 -->|No| V2
V2 -->|No| E2
V2 -->|Yes| V3
V3 -->|Invalid| E3
V3 -->|Valid| V4
V4 -->|Invalid| E4
V4 -->|Valid| D1
D1 -->|Yes| A0 --> V5
D1 -->|No| V5
V5 -->|No| E5
V5 -->|Yes| V6
V6 -->|No| E6
V6 -->|Yes| V7
V7 -->|No| E7
V7 -->|Yes| A1 --> A2 --> A3 --> A4 --> A5 --> SUCCESS
Loading
Error Code
Name
Description
Resolution
6000
SystemPaused
Protocol is in emergency pause
Wait for admin to unpause
6001
InvalidBucket
Bucket ID not in valid range [0-6]
Use valid bucket ID
6002
AmountMismatch
Deposit amount doesn't match bucket
Send exact bucket amount
6003
InsufficientFunds
User doesn't have enough SOL
Add more SOL to wallet
6004
DuplicateCommitment
Commitment already exists
Generate new randomness
6005
PasswordNotSet
Proxy wallet has no password
Call set_password first
6006
InvalidPasswordProof
ZK proof verification failed
Regenerate proof with correct password
6007
InvalidWithdrawalProof
Merkle/nullifier proof invalid
Check deposit exists
6008
ExceedsPerTxLimit
Amount > per_tx_limit
Reduce amount or increase limit
6009
ExceedsDailyLimit
Would exceed daily spending cap
Wait for daily reset
6010
NullifierAlreadyUsed
Double-spend attempt detected
Cannot withdraw same deposit twice
6011
TimelockNotExpired
Trying to execute before timelock
Wait for execute_after
6012
TransactionNotPending
TX already executed/cancelled
No action needed
6013
NotOwner
Caller is not proxy wallet owner
Sign with owner wallet
6014
SessionExpired
Session token past valid_until
Create new session
6015
SessionLimitExceeded
Would exceed session max_amount
Create new session with higher limit
6016
ProgramNotAllowed
Target program not in whitelist
Add program to session whitelist
9. Deployment Architecture
9.1 Multi-Environment Setup
flowchart TB
subgraph Development["Development (Localnet)"]
DEV_PROG[Programs<br/>Local Validator]
DEV_UI[Frontend<br/>localhost:3000]
DEV_RELAY[Relayer<br/>localhost:8080]
end
subgraph Staging["Staging (Devnet)"]
STG_PROG[Programs<br/>Devnet Deployment]
STG_UI[Frontend<br/>staging.privacy-proxy.io]
STG_RELAY[Relayer<br/>relay-staging.privacy-proxy.io]
STG_HELIUS[Helius Devnet RPC]
end
subgraph Production["Production (Mainnet)"]
PROD_PROG[Programs<br/>Mainnet Deployment]
PROD_UI[Frontend<br/>app.privacy-proxy.io<br/>+ IPFS CID]
PROD_RELAY[Relayer Cluster<br/>relay.privacy-proxy.io<br/>+ Tor .onion]
PROD_HELIUS[Helius Mainnet RPC]
PROD_BACKUP[Backup RPC<br/>Triton, QuickNode]
end
DEV_UI --> DEV_PROG
DEV_UI --> DEV_RELAY --> DEV_PROG
STG_UI --> STG_HELIUS --> STG_PROG
STG_UI --> STG_RELAY --> STG_PROG
PROD_UI --> PROD_HELIUS --> PROD_PROG
PROD_UI --> PROD_RELAY --> PROD_PROG
PROD_RELAY --> PROD_BACKUP --> PROD_PROG
Loading
9.2 Program Deployment Addresses
Program
Devnet
Mainnet
Privacy-Proxy Core
PPxyDev...
PPxyMain...
ZK Verifier
ZKvfDev...
ZKvfMain...
Global Config PDA
Derived
Derived
9.3 Upgrade Authority Flow
flowchart LR
subgraph Governance["Governance"]
MULTI[Multisig<br/>3-of-5 Signers]
TIMELOCK[Upgrade Timelock<br/>48 hours]
end
subgraph Programs["Programs"]
CORE[Core Program<br/>Upgradeable]
ZKV[ZK Verifier<br/>Upgradeable]
CONFIG[Global Config<br/>Admin-controlled]
end
subgraph Actions["Upgrade Actions"]
PROPOSE[Propose Upgrade]
APPROVE[Approve with 3 sigs]
EXECUTE[Execute After Timelock]
end
MULTI --> PROPOSE --> APPROVE --> TIMELOCK --> EXECUTE
EXECUTE --> CORE
EXECUTE --> ZKV
MULTI --> CONFIG
Loading
Appendix A: Account Size Calculations
GlobalConfig: 8 (discriminator) + 32 + 2 + 2 + 8 + 1 + 8 + 8 + 32 + 1 = 102 bytes
ProxyWallet: 8 + 32 + 32 + 8 + 8 + 8 + 8 + 9 + 8 + 8 + 1 = 130 bytes
DepositPool: 8 + 1 + 8 + 8 + 8 + 32 + 8 + 1 = 74 bytes
PendingTx: 8 + 8 + 32 + 32 + 8 + 8 + 8 + 1 + 32 + 32 + 1 = 170 bytes
SessionToken: 8 + 32 + 32 + 8 + 8 + 8 + (4 + 32*5) + 8 + 1 = 269 bytes
NullifierReg: 8 + 32 + 8 + 1 = 49 bytes
EphemeralKey: 8 + 32 + 8 + 64 + 1 = 113 bytes
Appendix B: Instruction Discriminators
Instruction
Discriminator (first 8 bytes of SHA256)
initialize_global_config
[175, 175, 109, 31, 13, 152, 155, 237]
initialize_proxy_wallet
[132, 66, 89, 45, 201, 78, 90, 12]
set_password
[45, 178, 90, 12, 234, 56, 78, 90]
deposit
[242, 35, 198, 137, 82, 225, 242, 182]
request_withdrawal
[156, 89, 45, 201, 78, 90, 12, 34]
execute_withdrawal
[78, 90, 12, 34, 156, 89, 45, 201]
cancel_withdrawal
[90, 12, 34, 156, 89, 45, 201, 78]
create_session
[12, 34, 156, 89, 45, 201, 78, 90]
revoke_session
[34, 156, 89, 45, 201, 78, 90, 12]