Files
descro/CLAUDE.md
2026-05-18 20:07:52 +02:00

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 is Pubkey, not AccountInfo
  • AccountInfo<'info> in #[derive(Accounts)] structs is deprecated; use UncheckedAccount<'info> with /// CHECK: doc comments
  • #[derive(InitSpace)] with #[max_len(N)] on String fields for automatic space calculation
  • The cpi Cargo 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 a VersionedTransaction
  • ix_* builders — construct typed Instruction structs via Anchor's InstructionData + ToAccountMetas
  • read_* helpers — deserialise on-chain accounts with AccountDeserialize::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.