WRIT Protocol on X@writnetwork on Twitterx.com/writnetworkwrit.networkWRIT Protocol official X account: @writnetworkFollow WRIT Protocol on X: https://x.com/writnetworkTwitter: @writnetwork · Website: writ.network
docs·WRIT Protocol
docs / architecture

System Design

Four Anchor programs arranged as a strict DAG. External protocols only ever interact with the top layer. The layers below it form the state machine the top layer reads from.

Layering

Dependencies flow downward. writ_gate reads from reputation,delegation, and writ_registry. reputation reads from delegation and writ_registry. delegation reads from writ_registry. writ_registry has no upstream dependencies — it is the root of trust.

There are no upward CPIs. writ_registry does not call delegation. The crate graph is acyclic, enforced at build time.

external solana programsCPI verifyL4writ_gateverify · verify_strict · verify_and_recordreturns AgentStatus (64 bytes)readsreadsreadsL3reputation0 – 10,000 scorering buffer × 64delegation5 scope PDAs / writbudget · expiry · actionsL1writ_registryGroth16 verify · Poseidon nullifier · Token-2022 SBT mintroot of trust — no upstream dependenciesexternal CPIonly surfacepolicy stateindependentroot of trustZK verificationcalls flow top → bottom · no circular dependencies · crate graph acyclic at build

Account model

Registry entries (L1)

One WritAccount per human. Created once via register, never closed. Keyed by the authority wallet; seeds ["writ", authority].

writ_registry/state/writ_account.rsrust
#[account]
#[derive(InitSpace)]
pub struct WritAccount {
    pub authority:    Pubkey,      // 32  — the human's wallet
    pub nullifier:    [u8; 32],    // 32  — Poseidon commitment
    pub sbt_mint:     Pubkey,      // 32  — Token-2022 NonTransferable mint
    pub created_slot: u64,         //  8  — for age bonus
    pub bump:         u8,          //  1
}

Delegations (L2)

Up to five per WritAccount. Keyed by ["delegation", writ, index] where index is a u8 in [0, 5). Each delegation is independent; revoking one does not affect the others.

delegation/state/scope.rsrust
#[account]
#[derive(InitSpace)]
pub struct Scope {
    pub writ:            Pubkey,      // 32  — parent WritAccount
    pub agent:           Pubkey,      // 32  — the delegated wallet
    pub programs:        [Pubkey; 8], // 256 — whitelisted programs (zero = slot empty)
    pub budget_lamports: u64,         //  8  — SOL ceiling for this delegation
    pub spent_lamports:  u64,         //  8  — cumulative drawn
    pub expires_at:      i64,         //  8  — unix seconds
    pub actions:         u16,         //  2  — bitmask of ActionKind
    pub revoked:         bool,        //  1  — set on revoke_delegation
    pub index:           u8,          //  1  — slot position in [0, 5)
    pub bump:            u8,          //  1
}

Reputation ledger (L3)

One ReputationAccount per writ. Score is recomputed on every submit_report or resolve_dispute; intermediate reports are stored in a ring buffer of the last 64 entries.

reputation/state/rep.rsrust
#[account]
#[derive(InitSpace)]
pub struct ReputationAccount {
    pub writ:          Pubkey,        // 32
    pub score:         u16,           //  2  — 0..=10_000
    pub total_reports: u32,           //  4
    pub ring_head:     u8,            //  1  — index into recent[]
    pub recent:        [Report; 64],  // 64 × sizeof(Report) = 4_096
    pub bump:          u8,            //  1
}

Gate state (L4)

Gate state is not a long-lived account. It is a transient PDA derived from the agent pubkey, read-only, computed on each verify CPI.

CPI boundaries

Each program exposes a narrow, typed CPI surface. The tables below list the external instructions (callable via CPI) for each program.

writ_gate

InstructionMutatesReturns
verifynoneAgentStatus
verify_strictnoneerr on any fail condition
verify_and_recorddelegation.Scope.spent_lamportsAgentStatus

delegation

InstructionMutatesAuthority
create_delegationScopehuman
revoke_delegationScope.revokedhuman
record_spendScope.spent_lamportsgate (CPI)

reputation

InstructionMutatesAuthority
submit_reportReputationAccountany program
open_disputeDisputestaker
resolve_disputeDisputeresolver quorum

writ_registry

InstructionMutatesAuthority
registerWritAccount + SBT minthuman (with ZK proof)
rotate_authorityWritAccount.authorityhuman

Data flow: one verify call

The steps below describe what happens inside a single writ_gate::verify CPI. The entire sequence runs in one slot, no clock skew, no network RTT.

  1. Caller passes agent and gate_state to writ_gate.
  2. writ_gate derives the writ PDA from agent via the delegation lookup table.
  3. It loads the WritAccount and verifies the SBT mint is still held.
  4. It scans the five Scope PDAs; picks the first unrevoked, unexpired one matching ctx.program_id.
  5. It loads ReputationAccount.score.
  6. It packs AgentStatus and returns.

Total reads: 3–8 accounts. Estimated compute: 18–42k CU depending on which delegation slot matches.

Compute budget

verify (happy path)
~18,000 CU
verify (last delegation slot)
~42,000 CU
register (ZK proof verify)
~165,000 CU
create_delegation
~12,000 CU
submit_report
~8,000 CU
NoteSolana's default compute budget is 200,000 CU per instruction. The only WRIT instruction that approaches this is register, which runs once per human per lifetime. Prepend a ComputeBudgetProgram.setComputeUnitLimit(220_000) to be safe.

Failure modes

CodeConditionRaised by
WritNotFoundagent has no registry entrywrit_gate
SbtMissingSBT mint account closed or transferredwrit_gate
NoActiveDelegationall 5 slots revoked or expiredwrit_gate
ProgramNotAllowedcaller not in scope.programswrit_gate
BudgetExceededscope.spent + amount > scope.budgetwrit_gate
ActionNotPermittedaction bit unset in scope.actionswrit_gate
NullifierCollisionregister with a nullifier already on chainwrit_registry
ProofInvalidGroth16 verification failedwrit_registry

Program references