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 / quickstart

Quickstart

Gate one instruction of an existing Anchor program with WRIT in about five minutes. Assumes you already have an Anchor workspace on Solana devnet.

Prerequisites

NoteEvery WRIT program is already deployed to devnet. You do not build or deploy anything from the WRIT source tree to use it — only add a dependency and a CPI.

1. Add the crate

Add writ-gate to your program's Cargo.toml. The cpi feature pulls in the CPI builder without compiling the program's entrypoint.

programs/your_program/Cargo.tomltoml
[dependencies]
anchor-lang = "1.0.0"
writ-gate = { version = "0.4", features = ["cpi"] }

2. Pass the accounts

Add two accounts to the instruction you want to gate: the agent wallet (the signer), and the WRIT gate state PDA.

programs/your_program/src/instructions/swap.rsrust
use anchor_lang::prelude::*;
use writ_gate::program::WritGate;
use writ_gate::state::GateState;

#[derive(Accounts)]
pub struct SensitiveSwap<'info> {
    #[account(mut)]
    pub agent: Signer<'info>,

    /// CHECK: verified by writ_gate via CPI
    pub writ_gate_state: UncheckedAccount<'info>,

    pub writ_gate_program: Program<'info, WritGate>,

    // ... your existing accounts
}

3. Call the verifier

One CPI returns an AgentStatus struct. Branch on its fields before proceeding.

programs/your_program/src/instructions/swap.rsrust
pub fn handler(ctx: Context<SensitiveSwap>, amount: u64) -> Result<()> {
    let status = writ_gate::cpi::verify(
        CpiContext::new(
            ctx.accounts.writ_gate_program.to_account_info(),
            writ_gate::cpi::accounts::Verify {
                agent: ctx.accounts.agent.to_account_info(),
                gate_state: ctx.accounts.writ_gate_state.to_account_info(),
            },
        ),
    )?.get();

    require!(status.is_valid,        Err::NotHumanBacked);
    require!(!status.expired(),      Err::DelegationExpired);
    require!(status.score >= 500,    Err::LowReputation);
    require!(
        status.scope.allows_program(ctx.program_id) &&
        status.scope.within_budget(amount),
        Err::OutOfScope,
    );

    do_swap(ctx, amount)
}

4. Derive the gate state PDA

The gate state PDA is deterministic per agent. Derive it client-side from the agent's public key.

app/integrate.tstypescript
import { PublicKey } from "@solana/web3.js";
import { WRIT_GATE } from "@writnetwork/sdk";

export function deriveGateState(agent: PublicKey): PublicKey {
    const [pda] = PublicKey.findProgramAddressSync(
        [Buffer.from("gate"), agent.toBuffer()],
        WRIT_GATE,
    );
    return pda;
}

5. Verify against devnet

Run your tests against the live devnet deployment. No local validator setup, no additional program deployment.

$ anchor test --provider.cluster devnet

# writ_gate is already deployed at
#   3tpfhT2m1vF7FCLsGazbEPFRiRnjgwk2CnC3yeonas7M
# You only need to fund your own test authority.
TipFor local iteration speed, you can point Anchor at devnet for reads but use a local validator for your own program's tests. The SDK supports both.

What you just shipped

Your instruction now fails closed unless the caller is:

No off-chain ping. No identity provider. No session token. Four state reads, one transaction, one CPI.