Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

130 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Accord

Mechanize the verdict.

Accord is a general-purpose, Schelling-point arbitration primitive on Solana — the "Kleros of Solana." Any Solana program (the Arbitrable) files a subjective Dispute via two CPI calls; the Accord draws stake-weighted Jurors (VRF), collects commit-reveal votes, and emits a Ruling governed by game-theoretic incentives instead of trusted humans.

It is a standalone, reusable product: the Accord has no knowledge of the filing program's domain. Dispute resolution becomes composable infrastructure.

your program ──create_dispute()──► Accord ──draws jurors, runs commit/reveal──► Ruling
      ▲                                                                            │
      └────────────────────────────get_ruling()────────────────────────────────────┘

Key Features

  • Schelling Point = honesty. Jurors converge on the truthful answer because voting coherently with the majority is the profitable strategy. No central authority picks judges.
  • Party-agnostic Arbitrable interface. Integrate with two CPI calls: create_dispute()get_ruling(). The Accord never learns your domain.
  • Permissionless Subaccords. Specialized Juror pools (automotive, freelancing, NFTs, …). Anyone can register one; each defines its own staking token, min stake, windows, and slash factor.
  • Per-Subaccord staking token. Each pool picks the SPL token Jurors stake (USDC by default). Stake is the anti-sybil mechanism and the coherence-slashing substrate.
  • Verifiable sortition. Stake-weighted Juror draw over a bonded Merkle-Sum-Tree snapshot, seeded by committed VRF — manipulation-resistant and fraud-proofable on-chain (ADR-0008/0009).
  • Commit-reveal + exponential appeals. Secret votes prevent vote-copying so the Schelling Point forms independently; each appeal doubles the panel + 1 (3 → 7 → 15 → 31), making bribery prohibitively expensive.

Important

Project status. The on-chain program (programs/accord) implements the full v1 instruction set with a LiteSVM unit-test per instruction. The TypeScript SDK (packages/sdk) and the jest/ Surfpool integration suite (tests/) are scaffolded and under active development (Codama codegen — ADR-0010). See Project Status. This is pre-mainnet, unaudited software — do not secure real value with it yet.


Table of Contents


Tech Stack

  • Program language: Rust (Anchor framework)
  • Framework: Anchor 1.0.2
  • Runtime: Solana 3.1.10 (BPF; host Rust via rust-toolchain.toml = stable)
  • Randomness: Magicblock / Solana VRF (ephemeral-vrf-sdk 0.4.1)
  • Token layer: SPL Token + Associated Token (anchor-spl 1.0.2)
  • SDK: TypeScript (@solana/web3.js, @anchor-lang/core) — Codama + Solana Kit codegen pipeline (ADR-0010, in progress)
  • Docs: MkDocs Material (apps/docs/)
  • Package manager: pnpm 9.15.0 (workspaces) + Cargo (Rust workspace)
  • Lint/format: rustfmt, clippy, tsc --noEmit, Prettier, ESLint, markdownlint, gitleaks (via pre-commit)

Prerequisites

  • Rust (stable) — curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Node.js 20+ and pnpm 9.xcorepack enable && corepack prepare pnpm@9.15.0 --activate
  • Solana CLI + Anchor — installed automatically by make prep (below)
  • Poetry (only for the docs site) — curl -sSL https://install.python-poetry.dev | python3 -

Tip

make prep installs Solana 3.1.10 (via solana-install) and Anchor 1.0.2 (via avm) for you — you do not need to pin them manually.

Getting Started

1. Clone the repository

git clone https://github.com/xeroc/accord.git
cd accord

2. Install toolchains and dependencies

make prep

This runs solana-install init 3.1.10, installs Anchor 1.0.2 through avm, and runs pnpm install across the workspace. Re-run only when toolchain versions change.

3. Build the program and workspace

make build

make build runs anchor build (compiles programs/accord to target/deploy/accord.so and emits the IDL) followed by pnpm -r run build (the SDK and any apps). The first build downloads and compiles the Solana BPF toolchain — expect a few minutes.

4. Configure your wallet / cluster

The provider defaults to localnet with ~/.config/solana/id.json (see Anchor.toml). Generate a keypair if you don't have one:

solana-keygen new

Switch clusters with the Solana CLI:

solana config set --url localhost      # local validator / Surfpool
solana config set --url devnet         # devnet

5. Run the tests

Fast in-process unit tests (no validator needed):

make test_unit

Full end-to-end suite against a live validator (start Surfpool first):

make run_surfpool     # in a separate terminal
make test_surfpool

See Testing for the two-harness philosophy.


Architecture

Monorepo Layout

.
├── programs/
│   └── accord/                 # The on-chain arbitration program (Anchor)
│       ├── src/
│       │   ├── lib.rs          # #[program] instructions + account contexts
│       │   ├── state.rs        # Account structs, enums, PDA proof types
│       │   ├── constants.rs    # Size bounds, windows, PDA seed prefixes
│       │   ├── errors.rs       # AccordError codes
│       │   └── events.rs       # Emitted events for off-chain indexers
│       ├── tests/              # LiteSVM unit tests (one file per instruction)
│       ├── accord.qedspec      # Formal-verification spec (qedgen)
│       ├── SPEC.md             # v1 build spec (account model, state machine)
│       └── security-checklist.md
├── packages/
│   └── sdk/                    # @accord/sdk — TypeScript SDK (codegen, in progress)
├── tests/                      # jest + Surfpool integration suite
├── apps/
│   └── docs/                   # MkDocs Material docs site (domain TBD)
│       ├── docs/
│       │   ├── adr/            # Architecture Decision Records (0001–0010)
│       │   ├── integration/    # Arbitrable integration guide
│       │   ├── reference/      # Accounts, instructions, state machine, errors
│       │   └── security/       # Fraud proofs, sortition/VRF, circuit breaker
│       └── mkdocs.yml
├── formal_verification/        # Lean / qedgen harness
├── CONTEXT.md                  # Domain language (ubiquitous-language glossary)
├── PROJECT.md                  # Project rationale (the "why")
├── BRAND.md                    # Brand model
├── Cargo.toml                  # Rust workspace
├── Anchor.toml                 # Anchor workspace + provider + test script
├── Makefile                    # Build / test / lint orchestration
├── pnpm-workspace.yaml         # TS workspace globs (apps/*, packages/*, tests)
└── tsconfig.base.json          # Shared TS compiler options

Note

The root package.json intentionally has no scripts block. The Makefile orchestrates builds; lint/test fan out via pnpm's recursive filter. Don't add root scripts — they'd duplicate the Makefile.

How a Dispute Is Resolved

The dispute lifecycle is a state machine advanced by permissionless cranks (anyone can move it forward when a window elapses):

stateDiagram-v2
    [*] --> Created: create_dispute (Arbitrable CPI)
    Created --> SnapshotPosted: post_snapshot (bonded root)
    SnapshotPosted --> Drawable: finalize_snapshot (challenge window passes)
    Drawable --> Drawn: draw (VRF-seeded sortition)
    Drawn --> Committed: commit (hash(vote, salt, juror))
    Committed --> Revealed: reveal ({vote, salt})
    Revealed --> RoundResolved: finalize_round (tally)
    RoundResolved --> Final: finalize_dispute (no appeal / max reached)
    RoundResolved --> Drawn: appeal → new round (2N+1 jurors)
    Final --> [*]: get_ruling (lazy read by Arbitrable)
Loading

Odd Juror counts (3 / 7 / 15 / 31) make ties impossible.

Account & PDA Model

Every account stores its canonical bump so handlers reuse the same PDA without re-deriving. Large accounts (Round) are #[zero_copy] (AccountLoader) to fit BPF's stack.

Account Seeds Purpose
Subaccord ["subaccord", creator, risk_type] A specialized Juror pool: staking token, windows, alpha, authority
JurorStake ["stake", subaccord, juror] A Juror's staked capital + active_draws lock count
Dispute ["dispute", filer, nonce] A case: options, evidence hash, state, final_ruling
Round ["round", dispute, round_idx] Per-round jurors, commits, reveals, result (zero-copy)
Snapshot ["snapshot", dispute, round_idx] Bonded Merkle-Sum-Tree root over the Juror set
AppealBond ["bond", dispute, round_idx] Custody record for one appeal bond
PendingUpdate ["update", subaccord, nonce] Timelocked Subaccord parameter update (48h)
PauseState ["pause"] Singleton program-level circuit breaker
token vaults Subaccord-PDA-owned SPL accounts Stake pool + fee pool

Draw & Verifiable Sortition

The draw is the security-critical path (ADR-0003, 0008, 0009):

  1. Snapshot. An off-chain indexer posts a Merkle-Sum-Tree root over the Subaccord's Juror set + cumulative stakes, bonded at 1 × max-appeal-fee. anchor_slot freezes the Juror set at post time so the draw is provably fair.
  2. Challenge window (1 day). Anyone can challenge_snapshot with a fraud proof (duplicate leaf, wrong stake, omission, unsorted tree). A proven fraud voids the root and pays the poster's bond to the challenger; a false challenge pays the challenger's bond to the poster.
  3. VRF. request_vrf asks the VRF oracle for randomness; the oracle's identity-signed commit_vrf_callback lands the result. Only the VRF program can call the callback.
  4. Draw. A permissionless cranker submits the drawn Jurors' membership proofs. The program verifies each proof against the finalized root, checks the sortition criterion (cum_before ≤ r_i < cum_after), enforces the inflation guard (JurorStake.amount ≥ leaf.stake), and enforces distinctness.

Economics

Inherited from Kleros (live since 2019, 1000+ disputes):

  • Fee: filer pays N · fee_per_juror; appellant pays N_new · fee_per_juror + bond.
  • Slash: each Incoherent Juror loses α · min_stake (flat; ADR-0003).
  • Redistribution: forfeited fees + slashed stake → Coherent Jurors, equal split.
  • Non-reveal penalty: ≥ the Incoherent penalty (forces reveal).
  • Appeal bond: forfeited to Coherent Jurors of the final round if the appeal does not flip the prior Ruling; returned if it flips.
  • Cross-round settlement: every round is re-settled against the final Ruling.

Evidence Flow

The Accord stores only an evidence hash on-chain (ADR-0006). A Subaccord-designated Evidence Operator re-encrypts the evidence for the drawn Jurors off-chain:

claimant ──encrypt(evidence, operator_pubkey)──► encrypted blob ──► off-chain store
on-chain Accord: evidence_hash only
dispute filed + Jurors drawn
   ▼
evidence_operator service: decrypt → re-encrypt per drawn Juror (+ optional watermark)
   ▼
Juror decrypts, verifies cleartext vs on-chain evidence_hash

The Arbitrable Interface

Your program integrates with two CPI calls. The Accord handles everything else.

// 1. File the dispute
let dispute = accord::create_dispute(
    ctx.accounts.clone(),
    vec![option_a_hash, option_b_hash], // 2+ option hashes
    evidence_hash,                       // commitment to the evidence
    nonce,                               // caller-chosen, for PDA uniqueness
    fee,                                 // jurors_per_dispute * fee_per_juror
)?;

// 2. Read the ruling (lazy — call whenever, after finalization)
let ruling: Option<u8> = accord::get_ruling(ctx.accounts.dispute)?;
import { Accord } from "@accord/sdk";

// File a dispute
const { dispute } = await accord.createDispute({
  subaccord: subaccordAddress,
  options: [hashOption("Yes"), hashOption("No")],
  evidenceHash: evidenceCommitment,
  nonce: 1n,
  fee: requiredFee,
});

// Later: read the ruling (0 = option A, 1 = option B, null = not final)
const ruling = await accord.getRuling(dispute);

Note

The full instruction surface (24 instructions) is documented in the Protocol Reference and programs/accord/SPEC.md. Integrators normally only need create_dispute and get_ruling; the rest are permissionless cranks.


Environment Variables

The program is configured on-chain (per-Subaccord params), not via env vars. Local development needs only Solana CLI config:

Variable Description Example
ANCHOR_WALLET Path to the provider keypair (defaults to Solana CLI config) ~/.config/solana/id.json
RPC_URL Cluster RPC endpoint (or use solana config set --url) localhost:8899

Anchor.toml pins the provider:

[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"

Available Commands

All orchestration lives in the root Makefile. The root package.json has no scripts by design.

Command Description
make prep Install Solana 3.1.10 + Anchor 1.0.2 (via avm), then pnpm install
make build anchor build (programs) then pnpm -r run build (packages/apps)
make test Rust unit tests + jest suite against a local validator (anchor test)
make test_unit LiteSVM Rust unit/TDD tests (fast, no validator)
make run_surfpool Start a Surfpool local fork (separate terminal)
make test_surfpool Full suite against a running Surfpool instance
make lint Lint every workspace that declares a lint script
make clean Remove build artifacts and node_modules
cd programs/accord && cargo test Rust unit tests in isolation
cd packages/sdk && pnpm run build Build the SDK
cd tests && npx jest -t "<name>" Run a single integration test by name
cd apps/docs && poetry run mkdocs serve Serve the docs site locally (localhost:8000)

Per-package lint auto-fix (where defined):

pnpm --filter @accord/sdk run lint:fix

Testing

The project uses two complementary harnesses (decision veridao-8ys4):

LiteSVM — fast in-process unit tests

  • Location: programs/accord/tests/*_litesvm.rs
  • Run: make test_unit
  • What it is: anchor-litesvm 0.4.x runs the real compiled .so in-process — no validator. One fresh AnchorLiteSVM context per test. Each instruction has a test file covering happy-path, authority, reinit guard, timelock, arithmetic, and closure cases.

jest + Surfpool — full end-to-end

  • Location: tests/*.spec.ts
  • Run: make run_surfpool (start the fork), then make test_surfpool
  • What it is: the real validator behaviour — CPI chains, VRF, token transfers. Long-running (testTimeout: 120000).

TDD workflow

Every feature/instruction follows RED → GREEN → REFACTOR. The failing test ships first; no exceptions. A milestone is completed only when all its leaf tests are green.

Important

The no-entrypoint feature quirk. The program's entrypoint! symbol collides with a builtin when the crate is statically linked into the test binary. Rust tests therefore build accord with --features no-entrypoint (types only). The .so — built separately via cargo build-sbf / anchor build with the entrypoint — is what LiteSVM loads. All *_litesvm.rs files are gated with #![cfg(feature = "no-entrypoint")] so anchor build (which doesn't pass the feature) skips them during IDL gen. make test_unit handles both steps.


Project Status

Component Status Notes
programs/accord (on-chain) ✅ Implemented Full v1 instruction set + per-instruction LiteSVM tests
Formal verification (accord.qedspec) ⚠️ Declared Four economic invariants modeled; pending VRF/param-bounds binding
@accord/sdk (TypeScript) 🚧 Scaffolded Codama codegen pipeline in progress (ADR-0010); facade stub only
tests/ (jest/Surfpool) 🚧 Scaffolded Harness configured; integration specs in progress
apps/docs (MkDocs) ✅ Live Full integration guide, protocol reference, security docs, ADRs
Security audit ❌ Not started Pre-mainnet; do not secure real value yet

Deployment

Deploy the program

The program ID is RokLJyruq34Ubtaj8mFnQETKcZpNCbW6k6xsgrMoHEe, kept in sync across declare_id!, Anchor.toml, and target/deploy/accord-keypair.json via anchor keys sync.

# Devnet
solana config set --url devnet
anchor build
anchor deploy --provider.cluster devnet

# Verify the deployed program
solana program show RokLJyruq34Ubtaj8mFnQETKcZpNCbW6k6xsgrMoHEe

Upgrade authority (ADR-0007)

The upgrade authority is a Squads multisig at launch; after a sufficient audit it is set to None (frozen, immutable). The on-chain PauseState singleton (seeds ["pause"]) is a separate circuit breaker: pause() is instant and authority-gated; unpause() is timelocked (propose_unpauseexecute_unpause after UNPAUSE_TIMELOCK_SLOTS) so a freeze is always recoverable on a known schedule. While paused, create_dispute / stake / appeal revert; in-flight disputes resolve normally.

Initialize after deploy

Bundle the pause-singleton init with deploy (front-running is an ops concern):

# initialize_pause — the caller becomes the pause authority (the Squads multisig).
# Invoke it once, ideally bundled with the deploy tx (front-running is an ops
# concern). There is no Makefile target yet — call the instruction directly via
# the SDK / a small script, e.g.:
#   accord.methods.initializePause().accounts({...}).rpc()

Troubleshooting

cargo build-sbf fails on edition2024

Cause: Solana CLI < 3.x bundles platform-tools v1.48 / cargo 1.84, which can't parse edition2024 manifests.

Fix: make prep installs Solana 3.1.10, which drops the flag. If you must invoke cargo build-sbf directly on an older CLI, pass --tools-version v1.52. (anchor build manages its own toolchain and is unaffected.)

LiteSVM test fails: read …/accord.so — run cargo build-sbf first

The .so must be built before the unit tests load it. make test_unit does both; if you run cargo test directly, build first:

cargo build-sbf --manifest-path programs/accord/Cargo.toml
cargo test --manifest-path programs/accord/Cargo.toml --features no-entrypoint

Program ID mismatch / declare_id! out of sync

anchor keys sync

This rewrites declare_id! and Anchor.toml [programs.*] from target/deploy/accord-keypair.json.

anchor build IDL generation blocked

On Anchor 1.0.2 + Solana 3.x deps, IDL generation is unblocked end-to-end via the idl-build feature (see programs/accord/Cargo.toml). If you hit an older Anchor, ensure the crate declares idl-build in [features].

jest integration tests can't connect

Integration tests need a running validator. Start Surfpool first:

make run_surfpool     # keeps running; use a separate terminal
make test_surfpool

Native extension / build failures

Ensure the Solana BPF toolchain and system libs are present. make prep handles the Solana side; for host crates you need a working rustc (stable) and standard build essentials (build-essential / Xcode CLT).


Contributing

  1. Read first: CONTEXT.md (domain language) → this README → apps/docs/docs/adr/ (the why behind every locked decision).
  2. TDD only. Write the failing test first, then implement to pass.
  3. Lint is law. Run make lint (and the relevant test) before committing. Pre-commit hooks (fmt, cargo-check, markdownlint, gitleaks, detect-private-key) run automatically.
  4. Track work with beans. This repo uses the beans CLI for issue tracking. Check beans list --json --ready before assuming docs reflect reality — active milestones may supersede code state. Include relevant bean IDs in commit messages (the bean prefix is accord- per .beans.yml).
  5. ADRs are immutable once deployed. A superseded decision gets a new ADR that references the old one.

Install the git hooks:

pip install pre-commit
pre-commit install

License

UNLICENSED (private) — see package.json. The program crate (programs/accord) ships under the workspace license.


Further Reading

  • Docs site: docs (domain TBD) — Quickstart, Integration Guide, Protocol Reference, Security, ADRs
  • CONTEXT.md — domain language / ubiquitous-language glossary
  • PROJECT.md — project rationale (the "why")
  • BRAND.md — brand model
  • programs/accord/SPEC.md — v1 build spec (account model, state machine, economics, edge cases)
  • programs/accord/security-checklist.md — security audit authority (findings cite file:line)
  • ADRs (apps/docs/docs/adr/):
    • 0001 Schelling-point Accord replaces hired-judge committee
    • 0002 Per-Subaccord staking token, no Accord token in v1
    • 0003 Draw — Merkle snapshot, off-chain sortition, distinct Jurors
    • 0004 Party-agnostic; appeal is permissionless
    • 0005 Subaccord authority — pubkey, 48h timelock
    • 0006 Evidence — on-chain hash, trusted re-encryption operator
    • 0007 Upgrade authority — Squads multisig, then freeze
    • 0008 Snapshot trust hardening — anchor-slot, fraud predicates, sortition
    • 0009 Stake-weighted verifiable sortition — MST, committed VRF
    • 0010 SDK — Codama codegen + Solana Kit facade

ACCORD
An accord, not a committee.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages