Files
descro/README.md
2026-07-08 16:28:21 +02:00

173 lines
8.3 KiB
Markdown

# 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.