readme
This commit is contained in:
158
README.md
Normal file
158
README.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
## 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 — 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 — 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 — no build step 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.
|
||||||
133
docs/local-dev.md
Normal file
133
docs/local-dev.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Descro — Local Development & Testing
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Rust](https://rustup.rs/) with the `solana` toolchain (`rust-toolchain.toml` pins the version)
|
||||||
|
- [Solana CLI](https://docs.solana.com/cli/install-solana-cli-tools) ≥ 1.18
|
||||||
|
- [Anchor CLI](https://www.anchor-lang.com/docs/installation) (used for `build-sbf`)
|
||||||
|
- Node.js + [Yarn](https://yarnpkg.com/)
|
||||||
|
- A browser wallet extension (Phantom, Backpack, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 — Start a local validator
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana-test-validator --reset
|
||||||
|
```
|
||||||
|
|
||||||
|
Leave this running in a dedicated terminal. The validator listens on:
|
||||||
|
|
||||||
|
| Endpoint | Address |
|
||||||
|
|---|---|
|
||||||
|
| RPC | `http://localhost:8899` |
|
||||||
|
| WebSocket | `ws://localhost:8900` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 — Configure the Solana CLI to use localhost
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana config set --url localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana config get
|
||||||
|
# RPC URL: http://localhost:8899
|
||||||
|
```
|
||||||
|
|
||||||
|
If you do not yet have a local keypair:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana-keygen new --outfile ~/.config/solana/id.json
|
||||||
|
solana airdrop 10
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 — Build and deploy the programs
|
||||||
|
|
||||||
|
Build order matters — `descro` depends on `descro_ext_resolvers`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the resolver registry first
|
||||||
|
cargo build-sbf --manifest-path programs/descro_ext_resolvers/Cargo.toml
|
||||||
|
|
||||||
|
# Then build the escrow program
|
||||||
|
cargo build-sbf --manifest-path programs/descro/Cargo.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy both to the local validator:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana program deploy \
|
||||||
|
--program-id programs/descro_ext_resolvers/keypair.json \
|
||||||
|
target/deploy/descro_ext_resolvers.so
|
||||||
|
|
||||||
|
solana program deploy \
|
||||||
|
--program-id programs/descro/keypair.json \
|
||||||
|
target/deploy/descro.so
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** if you don't have per-program keypair files, omit `--program-id` and note
|
||||||
|
> the deployed address; then update the IDs in `sdk/src/pda.ts` and the IDL `address`
|
||||||
|
> fields accordingly.
|
||||||
|
|
||||||
|
Expected program IDs (as declared in `Anchor.toml`):
|
||||||
|
|
||||||
|
| Program | ID |
|
||||||
|
|---|---|
|
||||||
|
| `descro` | `DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3` |
|
||||||
|
| `descro_ext_resolvers` | `GwUPAKs3HHzCpj8uhet4NAnxk9GWNwfrYbpihu5DyFp` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 — Configure your browser wallet for localhost
|
||||||
|
|
||||||
|
**Phantom:**
|
||||||
|
1. Settings → Developer Settings → Testnet Mode (or add a custom RPC)
|
||||||
|
2. Network → Custom → `http://localhost:8899`
|
||||||
|
|
||||||
|
**Backpack:**
|
||||||
|
1. Settings → Solana → RPC Connection → Custom → `http://localhost:8899`
|
||||||
|
|
||||||
|
Airdrop yourself some SOL from the CLI or from the Solana faucet panel inside the wallet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 — Start the app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install # from repo root, only needed once
|
||||||
|
yarn workspace descro-app run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000).
|
||||||
|
|
||||||
|
In the top-right **Network** dropdown, select **Localnet**. The app will connect to
|
||||||
|
`http://localhost:8899`. Connect your wallet and you can create escrows, deposit funds,
|
||||||
|
and test the full instruction flow against the locally-deployed programs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
Tests load the compiled `.so` files via `include_bytes!`, so always build before testing.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All tests for the escrow program (includes CPI flow test)
|
||||||
|
cargo test --manifest-path programs/descro/Cargo.toml
|
||||||
|
|
||||||
|
# All tests for the resolver registry
|
||||||
|
cargo test --manifest-path programs/descro_ext_resolvers/Cargo.toml
|
||||||
|
|
||||||
|
# Single test by name
|
||||||
|
cargo test --manifest-path programs/descro/Cargo.toml -- test_create_escrow
|
||||||
|
|
||||||
|
# Specific test file
|
||||||
|
cargo test --manifest-path programs/descro/Cargo.toml --test test_resolve
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests use **LiteSVM** (in-process) and do not require a running validator.
|
||||||
Reference in New Issue
Block a user