Full lesson
Vetra
Chrome extension (MV3) that assesses Solana transactions before signing. Wraps window.solana, runs local heuristics, shows risk score and reasons. Optional on-chain attestations via Anchor with reputation/stake for signal providers. Privacy by default.
What it does
From the project README:
1. Open `chrome://extensions/` 2. Enable "Developer mode" 3. Click "Load unpacked" → Select `frontend/extension/dist`
- Original
- williamhnyohei/Vetra
- SuperStack fork
- Blockchains/Vetra @
c6b3071 - Stars
- 2
- Last push (original)
- 2026-09-24
- Languages
- TypeScript, JavaScript, Python, HTML, Rust, CSS
- Detected stack
- Anchorweb3.js
- Categories
- Infrastructure & Tooling
- Licence
- MIT
Architecture
143 source files. By extension: .js ×53, .ts ×46, .py ×18, .tsx ×16, .rs ×10.
| Top-level directory | Source files |
|---|---|
frontend | 64 |
backend | 59 |
vetra_mas | 18 |
(root) | 1 |
scripts | 1 |
On-chain programs: backend/programs/attestation
Dapp: devnet deploy pending
⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/vetra/. Until then, the lesson and step-by-step guide below are complete, and you can run it locally with the commands below.
Key code, explained
Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.
backend/programs/attestation/src/instructions/stake_reputation.rs L1–24 @ c6b3071 · MIT · originaluse anchor_lang::prelude::*;use anchor_lang::system_program;use crate::state::SignalProvider;use crate::errors::AttestationError; #[derive(Accounts)]pub struct StakeReputation<'info> { #[account(mut)] pub authority: Signer<'info>, #[account( init_if_needed, payer = authority, space = 8 + SignalProvider::MAX_SIZE, seeds = [b"provider", authority.key().as_ref()], bump )] pub provider: Account<'info, SignalProvider>, pub system_program: Program<'info, System>,} pub fn handler(ctx: Context<StakeReputation>, amount: u64) -> Result<()> { require!(amount >= SignalProvider::MIN_STAKE, AttestationError::InsufficientStake);- Line 9
pub authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 12
init_if_needed,: Creates the account only if it doesn't exist. Watch for re-initialisation bugs. - Line 15
seeds = [b"provider", authority.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 16
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 24
require!(amount >= SignalProvider::MIN_STAKE, AttestationError::InsufficientStake);: An explicit validation that aborts the instruction with an error.
backend/programs/attestation/src/instructions/create_attestation.rs L1–24 @ c6b3071 · MIT · originaluse anchor_lang::prelude::*;use crate::state::{Attestation, SignalProvider};use crate::errors::AttestationError; #[derive(Accounts)]#[instruction(transaction_hash: [u8; 32])]pub struct CreateAttestation<'info> { #[account(mut)] pub provider_authority: Signer<'info>, #[account( mut, seeds = [b"provider", provider_authority.key().as_ref()], bump = provider.bump, constraint = provider.stake >= SignalProvider::MIN_STAKE @ AttestationError::InsufficientStake )] pub provider: Account<'info, SignalProvider>, #[account( init, payer = provider_authority, space = 8 + Attestation::MAX_SIZE, seeds = [ b"attestation",- Line 9
pub provider_authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 13
seeds = [b"provider", provider_authority.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 14
bump = provider.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 15
constraint = provider.stake >= SignalProvider::MIN_STAKE @ AttestationError::InsufficientS: Touches staking state: the amount locked and when it was locked. - Line 20
init,: Creates and pays for a new account. Space and payer must be specified.
backend/programs/attestation/src/instructions/withdraw_stake.rs L1–23 @ c6b3071 · MIT · originaluse anchor_lang::prelude::*;use crate::state::SignalProvider;use crate::errors::AttestationError; #[derive(Accounts)]pub struct WithdrawStake<'info> { #[account(mut)] pub authority: Signer<'info>, #[account( mut, seeds = [b"provider", authority.key().as_ref()], bump = provider.bump, constraint = provider.authority == authority.key() @ AttestationError::Unauthorized )] pub provider: Account<'info, SignalProvider>, pub system_program: Program<'info, System>,} pub fn handler(ctx: Context<WithdrawStake>, amount: u64) -> Result<()> { let provider = &mut ctx.accounts.provider; let clock = Clock::get()?;- Line 8
pub authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 12
seeds = [b"provider", authority.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 13
bump = provider.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 23
let clock = Clock::get()?;: Reads on-chain time from the Clock sysvar.
backend/programs/attestation/src/instructions/vote_attestation.rs L1–24 @ c6b3071 · MIT · originaluse anchor_lang::prelude::*;use crate::state::{Attestation, SignalProvider}; #[derive(Accounts)]pub struct VoteAttestation<'info> { pub voter: Signer<'info>, #[account(mut)] pub attestation: Account<'info, Attestation>, #[account( mut, seeds = [b"provider", attestation.provider.as_ref()], bump = provider.bump )] pub provider: Account<'info, SignalProvider>,} pub fn handler(ctx: Context<VoteAttestation>, is_accurate: bool) -> Result<()> { let attestation = &mut ctx.accounts.attestation; let provider = &mut ctx.accounts.provider; // Update vote counts if is_accurate {- Line 6
pub voter: Signer<'info>,: Requires this account to have signed the transaction. - Line 13
seeds = [b"provider", attestation.provider.as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 14
bump = provider.bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
How to run, build and test
These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.
| Directory | Command | Runs |
|---|---|---|
. | npm run dev | pnpm --filter @vetra/extension dev |
. | npm run build | pnpm -r build |
. | npm run test | pnpm -r test |
. | npm run lint | pnpm -r lint |
frontend/extension | npm run dev | vite |
frontend/extension | npm run build | vite build && node copy-icons.cjs |
frontend/extension | npm run test | vitest run |
frontend/extension | npm run lint | eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 |
backend | npm run dev | nodemon src/server.js |
backend | npm run start | node src/server.js |
backend | npm run build | echo 'No build step required' |
backend | npm run test | jest |
backend | npm run lint | eslint src/ |
backend/programs | anchor build | compile programs and generate IDLs |
backend/programs | anchor test | local validator + test suite |
backend/programs | anchor run test | yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts |
What you could build from it
- Fork the SuperStack copy and extend it within the Infrastructure & Tooling gaps listed on the idea page.
- Learn the pattern first: Building and sending transactions from a client
- Learn the pattern first: Anchor account constraints