5.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
Build
# Build a single program (produces .so in target/deploy/)
cargo build-sbf --manifest-path programs/descro/Cargo.toml
cargo build-sbf --manifest-path programs/descro_ext_resolvers/Cargo.toml
# Build both (registry must be built first — descro depends on it)
cargo build-sbf --manifest-path programs/descro_ext_resolvers/Cargo.toml && \
cargo build-sbf --manifest-path programs/descro/Cargo.toml
Test
Tests use LiteSVM (in-process Solana runtime, no validator needed). They load the compiled .so from target/deploy/ via include_bytes!, so the relevant program(s) must be built before running tests.
# Run all tests for a program
cargo test --manifest-path programs/descro/Cargo.toml
cargo test --manifest-path programs/descro_ext_resolvers/Cargo.toml
# Run a single test by name
cargo test --manifest-path programs/descro/Cargo.toml -- test_create_escrow
cargo test --manifest-path programs/descro/Cargo.toml -- resolve_updates_stats_when_resolver_registered
# Run tests in a specific file
cargo test --manifest-path programs/descro/Cargo.toml --test test_resolve
Lint / Format
cargo clippy --manifest-path programs/descro/Cargo.toml
cargo clippy --manifest-path programs/descro_ext_resolvers/Cargo.toml
cargo fmt --manifest-path programs/descro/Cargo.toml
Architecture
Two-Program Protocol
programs/
├── descro/ — Escrow Program (Phase 1)
└── descro_ext_resolvers/ — Resolver Registry (Phase 2)
descro depends on descro_ext_resolvers (with features = ["cpi"]). The registry does NOT depend on the escrow — this is intentional to avoid a circular dependency. Communication is one-way: escrow → registry via CPI.
Escrow Program (descro)
State: A single EscrowAccount PDA holds all trade state. SOL is held separately in a Vault PDA (system-owned, data-free). The two-account split makes SOL transfers simpler: the vault is a pure lamport store.
PDA seeds:
- Escrow:
["escrow", seller_pubkey, escrow_id_le_bytes]— allows one seller to have multiple concurrent escrows - Vault:
["vault", escrow_account_pubkey]— uniquely derived from the escrow account
Instruction flow:
create_escrow (seller) → deposit (buyer) → complete (buyer)
→ dispute (buyer|seller) → resolve (resolver)
cancel (seller, only from AwaitingDeposit)
SOL transfers use raw invoke / invoke_signed (not Anchor's CPI helpers) because Anchor 1.0.x changed CpiContext::new to take Pubkey instead of AccountInfo, which conflicts with how system_program transfers work in practice.
Resolver Registry (descro_ext_resolvers)
State: ResolverEntry PDA at ["resolver", authority_pubkey]. Tracks name, type, fee, and on-chain resolution stats.
CPI authorization for update_stats: Only the escrow program may call update_stats. This is enforced via a ["escrow_authority"] PDA that only the escrow program can derive and sign for (invoke_signed). The registry verifies this signer has seeds::program = ESCROW_PROGRAM_ID (hardcoded constant in lib.rs).
update_stats is optional from the escrow side: In resolve(), the handler checks resolver_entry.data_is_empty() before attempting the CPI. Resolvers that are not registered in the registry still work — stats are simply not tracked.
Anchor 1.0.x Notes
CpiContext::new(program: Pubkey, accounts: T)— first arg isPubkey, notAccountInfoAccountInfo<'info>in#[derive(Accounts)]structs is deprecated; useUncheckedAccount<'info>with/// CHECK:doc comments#[derive(InitSpace)]with#[max_len(N)]onStringfields for automatic space calculation- The
cpiCargo feature (no-entrypoint) is required on the dependency when calling another Anchor program
Test Infrastructure
Each program has a tests/common/mod.rs with shared helpers:
setup()— creates LiteSVM, loads.so, airdrops to test keypairs- PDA derivation helpers (
escrow_pda,vault_pda,resolver_entry_pda, …) send()/try_send()— wraps instruction in aVersionedTransactionix_*builders — construct typedInstructionstructs via Anchor'sInstructionData+ToAccountMetasread_*helpers — deserialise on-chain accounts withAccountDeserialize::try_deserialize
The test_resolve_with_registry.rs test loads both .so files into one LiteSVM instance to test the full CPI flow end-to-end.
Program IDs
| Program | ID |
|---|---|
descro |
DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3 |
descro_ext_resolvers |
Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS (placeholder — update after first deployment) |
The descro_ext_resolvers ID is hardcoded as ESCROW_PROGRAM_ID inside the registry program. After deploying the registry for the first time, update declare_id! in programs/descro_ext_resolvers/src/lib.rs and ESCROW_PROGRAM_ID in programs/descro_ext_resolvers/src/lib.rs to match.