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 / programs / reputation

reputation

A 0–10,000 integer score per writ, aggregated from on-chain behavior reports. Every report is program-signed. Disputes use staked resolution with a 72-hour challenge window. Age weighting caps at 1.5× after 180 days on-chain.

Program ID (devnet)
F8yFcvoXpupNahzJ2wSKDBErqKgmE7ws1gVVtdAq33FC
Layer
L3 — depends on delegation and writ_registry
Score range
0..=10,000 (u16)
Report buffer
last 64 reports, ring-structured
Dispute window
72 hours
Stake to dispute
0.1 SOL minimum

Scoring model

The score is not a weighted average of reports. It is a bounded running signal that rewards consistent good behavior and penalizes recent bad behavior asymmetrically. The update function is pure and deterministic:

score_new = clamp(
    score_prev + delta,
    0, 10_000,
)

where delta =
      + weight(report.kind) × age_bonus(writ)            if report.kind == POSITIVE
      - weight(report.kind) × severity(report.severity)  if report.kind == NEGATIVE

and age_bonus(writ) = min(1.0 + (age_days / 180) × 0.5, 1.5)

Report kinds

KindWeightTypical source
POS_COMPLETED+3successful swap, repaid loan
POS_LIQUIDITY+5LP sustained > 7 days
POS_LONGEVITY+1passive on-chain age tick (auto)
NEG_FAILED-10failed instruction, slipped past budget
NEG_DISPUTED-25lost a dispute
NEG_EXPLOIT-500confirmed MEV exploit, wash trading
NoteWeights are set in programs/reputation/src/constants.rs and are not upgradable in v0.4. They will become parameters governed by a reputation council in v0.5.

State

programs/reputation/src/state.rsrust
#[account]
#[derive(InitSpace)]
pub struct ReputationAccount {
    pub writ:          Pubkey,         // 32
    pub score:         u16,            //  2
    pub total_reports: u32,            //  4
    pub ring_head:     u8,             //  1
    pub recent:        [Report; 64],
    pub bump:          u8,             //  1
}

pub struct Report {
    pub reporter:     Pubkey,          // 32
    pub kind:         u8,              //  1
    pub severity:     u8,              //  1 — 0..=10 multiplier for NEG_*
    pub context_hash: [u8; 32],        // 32 — tx sig or caller-supplied key
    pub timestamp:    i64,             //  8
    pub disputed:     bool,            //  1
}

Disputes

A negative report can be challenged by any wallet (not just the writ owner) within 72 hours of submission. The challenger stakes at least 0.1 SOL. Resolution is by a quorum of reputation council validators in v0.4 (bootstrapped set); in v0.5 it moves to a reputation-weighted DAO vote.

72-hour dispute window. Uncontested reports become canonical after expiry.

Instructions

submit_report

Called by any program. The caller signs with its program authority via PDA. Reports about a writ you are not currently interacting with will still succeed, but can be contested more easily — the context_hash must be derivable from an on-chain event.

pub fn submit_report(
    ctx: Context<SubmitReport>,
    kind: u8,
    severity: u8,
    context_hash: [u8; 32],
) -> Result<()>

open_dispute

pub fn open_dispute(
    ctx: Context<OpenDispute>,
    report_index: u8,
    stake_lamports: u64,  // ≥ 100_000_000
) -> Result<()>

resolve_dispute

Callable only by the reputation council PDA. Commits the dispute outcome, reverts the report delta if upheld, slashes or returns stake, and emits a resolution event.

Reading the score

Callers almost never read ReputationAccount directly — writ_gate::verify returns score as part of AgentStatus. If you do need raw access (e.g. to build a leaderboard), the account is public and can be deserialized with any Anchor client.

TipFor most gating logic, branch on score >= threshold. Common thresholds: 500 (basic liveness), 2,500 (trusted LP), 5,000 (high-stakes perp), 8,000 (governance voting weight).