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
- Solana CLI 3.1 (Agave) or newer
- Anchor 1.0 or newer
- A devnet keypair with ~2 SOL
- An existing Anchor program you want to gate
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.
[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.
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.
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.
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.What you just shipped
Your instruction now fails closed unless the caller is:
- A wallet backed by a human-bound registry entry (Poseidon nullifier on-chain)
- Operating under an unexpired, unrevoked delegation whose scope includes your program
- At a reputation floor of 500 out of 10,000
- Under the per-delegation budget cap for this call
No off-chain ping. No identity provider. No session token. Four state reads, one transaction, one CPI.