From 84a2ec4200c48fb87b1bae5d23d083d6204c7a30 Mon Sep 17 00:00:00 2001 From: thesn10 <38666407+thesn10@users.noreply.github.com> Date: Mon, 18 May 2026 20:07:52 +0200 Subject: [PATCH] claude md --- CLAUDE.md | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..103df11 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,106 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +### Build + +```bash +# 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. + +```bash +# 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 + +```bash +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.