# descro A decentralized escrow protocol on Solana for physical goods trading. Fully permissionless and resolver-agnostic: any wallet or program that implements the resolver interface can adjudicate disputes. ## 🌍 Vision & Concept Buying and selling physical goods peer-to-peer runs on trust. Today that trust is either placed in a centralized platform that takes a cut and can freeze your funds, or in nothing at all, leaving buyers and sellers to hope the other side follows through. descro removes that single point of control: funds sit in an on-chain vault that only releases according to rules enforced by the protocol itself, not by a company. Instead of building one monolithic marketplace, descro splits the problem into a minimal **protocol layer** (escrow logic + a resolver registry) and lets anyone build the **application layer** on top, from a slick, KYC'd consumer app to a raw wallet-to-wallet trade between two people who've never met. - βš–οΈ **Resolver-agnostic by design**: disputes aren't adjudicated by descro itself. Any wallet or program that implements the resolver interface can be plugged in, whether that's a single trusted authority, a multisig, a jury DAO, or an algorithmic (MAD-style) resolver. - πŸ”“ **Fully permissionless**: no whitelist, no gatekeeping. Anyone can create an escrow, register as a resolver, or build a client against the protocol. - πŸ”’ **Non-custodial by construction**: SOL lives in a program-owned vault, not a company's balance sheet. Nobody, including descro itself, can move funds without satisfying the on-chain rules. - 🧩 **Progressive decentralization**: a central platform (own resolver, KYC, familiar UX) can onboard mainstream users first, while the same trades quietly build up a decentralized backend anyone can tap into directly, with free choice of resolver, once critical mass is reached. - ⚑ **Built for Solana**: sub-cent transaction fees and ~400ms finality mean the on-chain plumbing can stay invisible to end users while still being fully verifiable. - 🧱 **Minimal, composable core**: the escrow program only knows about escrow state and SOL; dispute resolution is entirely external via CPI, so the protocol doesn't need to be trusted to "get justice right," only to enforce the interface correctly. - πŸ›‘οΈ **Trust-minimized stats**: resolver reputation (fee, resolution history) lives in an on-chain registry that only the escrow program can update, so it can't be gamed by a resolver inflating its own track record. ## Overview ``` APPLICATION LAYER Central Platform (own resolver, KYC, normal UX) Decentralized Clients (raw protocol access, free resolver choice) β”‚ PROTOCOL LAYER β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Escrow Program │────▢│ Resolver Registry β”‚ β”‚ (descro) β”‚ CPI β”‚ (descro_ext_res.) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` The protocol only defines the interface. Any resolver that implements it is compatible, from a single authority wallet to a jury DAO voting contract. ## Programs | Program | ID | |---|---| | `descro` | `DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3` | | `descro_ext_resolvers` | `GwUPAKs3HHzCpj8uhet4NAnxk9GWNwfrYbpihu5DyFp` | ### Escrow Program (`descro`) Holds trade state and SOL. Two PDAs per escrow: - **EscrowAccount** `["escrow", seller_pubkey, escrow_id_le_bytes]`: trade state - **Vault** `["vault", escrow_account_pubkey]`: system-owned, holds SOL only **Instruction flow:** ``` create_escrow (seller) └─▢ deposit (buyer) β”œβ”€β–Ά complete (buyer) β†’ SOL released to seller └─▢ dispute (buyer | seller) └─▢ resolve (resolver) β†’ SOL released to winner cancel (seller, only from AwaitingDeposit) ``` The `dispute_resolver` field in the escrow can be any pubkey, whether that's a wallet signing directly or a program calling `resolve()` via CPI. The check is identical either way. **Emergency resolve:** After 14 days in `Disputed` state with no resolution, either party can call `emergency_resolve` to unlock funds. ### Resolver Registry (`descro_ext_resolvers`) An on-chain directory for resolvers. Resolvers register with metadata (name, type, fee, URI); resolution stats are auto-incremented by the escrow program via CPI and cannot be manipulated externally. `update_stats` is callable only by the escrow program, enforced via an `["escrow_authority"]` PDA that only the escrow program can sign for. Resolver types: `CentralAuthority`, `JuryDAO`, `MAD`, `Algorithmic`, `Multisig` ## Repository Structure ``` programs/ β”œβ”€β”€ descro/ β€” Escrow Program (Rust/Anchor) └── descro_ext_resolvers/ β€” Resolver Registry (Rust/Anchor) sdk/ β€” @descro/sdk TypeScript client β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ idl/ β€” Anchor-generated IDL JSON β”‚ β”œβ”€β”€ escrow.ts β€” EscrowClient (instruction builders + fetchers) β”‚ β”œβ”€β”€ registry.ts β€” RegistryClient β”‚ β”œβ”€β”€ listener.ts β€” WebSocket subscriptions β”‚ β”œβ”€β”€ pda.ts β€” PDA derivation helpers β”‚ β”œβ”€β”€ types.ts β€” TypeScript types matching on-chain state β”‚ └── index.ts β€” Re-exports + DescroSdk convenience class app/ β€” Next.js + Tamagui playground β”œβ”€β”€ src/app/ β€” App Router pages + providers β”œβ”€β”€ src/components/ β€” EscrowDetail, ResolverPanel, … └── src/hooks/ β€” useEscrows, useEscrowDetail ``` ## Getting Started ### Prerequisites - Rust + `cargo-build-sbf` - Solana CLI - Node.js + Yarn ### Build Programs must be built in order, since the escrow program depends on the registry: ```bash cargo build-sbf --manifest-path programs/descro_ext_resolvers/Cargo.toml cargo build-sbf --manifest-path programs/descro/Cargo.toml ``` Install JS dependencies from the repo root: ```bash yarn install ``` ### Test Tests load the compiled `.so` files via `include_bytes!`, so programs must be built first: ```bash cargo test --manifest-path programs/descro/Cargo.toml cargo test --manifest-path programs/descro_ext_resolvers/Cargo.toml ``` Run a single test: ```bash cargo test --manifest-path programs/descro/Cargo.toml -- test_create_escrow cargo test --manifest-path programs/descro/Cargo.toml --test test_resolve ``` ### App (dev server) ```bash yarn workspace descro-app run dev ``` ## SDK Usage The SDK is consumed as raw TypeScript via `transpilePackages` in the Next.js config, so no build step is needed. ```ts import { DescroSdk } from "@descro/sdk"; const sdk = new DescroSdk(provider); // Create escrow await sdk.escrow.createEscrow({ amount, disputeResolver, escrowId }); // Fetch all escrows for a seller const escrows = await sdk.escrow.fetchEscrowsBySeller(sellerPubkey); // Subscribe to state changes sdk.listener.onEscrowUpdate(escrowPubkey, (account) => { ... }); ``` ## Security Properties | Property | Mechanism | |---|---| | Nobody can take SOL without consent | Network enforces signature requirement at protocol level | | Vault can only be drained by the escrow program | Vault owner = system_program, only accessible via `invoke_signed` with correct seeds | | Wrong resolver cannot call `resolve()` | `require!(signer.key() == escrow.dispute_resolver)` | | PDAs cannot be spoofed | Program verifies seeds on-chain for every instruction | | Registry stats cannot be manipulated | `update_stats` only callable via CPI from the escrow program | ## Architecture Notes - **Anchor 1.0.x:** `UncheckedAccount<'info>` with `/// CHECK:` doc comments; `#[derive(InitSpace)]` + `#[max_len(N)]` for strings. - **`@coral-xyz/anchor@0.32.x`:** Use `new Program(idl, provider)`. The IDL's `address` field provides the program ID. The 3-argument form is broken in 0.32.x. - **Yarn workspaces:** `nodeLinker: node-modules` (not PnP), required for Next.js Turbopack compatibility. - **LiteSVM tests:** each `tests/common/mod.rs` provides `setup()`, PDA helpers, `send()`/`try_send()`, `ix_*` builders, and `read_*` deserializers. `test_resolve_with_registry.rs` loads both `.so` files to test the full CPI flow.