Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Samizdat – Copilot Instructions

## Project Overview

Samizdat is a **DePIN (Decentralized Physical Infrastructure) protocol on Solana** connecting content publishers with display-node operators. It's a monorepo with three major components:

| Component | Path | Stack |
|-----------|------|-------|
| Solana program | `programs/samizdat/` | Rust, Anchor 0.32 |
| Integration tests | `tests/` | TypeScript, `node:test`, `solana-kite`, `@solana/kit` v6 |
| Web frontend | `web/` | React 19, Vite, Tailwind CSS v4, React Aria Components |

Content lives off-chain (Arweave/IPFS CIDs); Solana handles campaign matching, payments, and proof-of-play.

## Build & Test Commands

```bash
bun install # root deps (tests + codama client gen)
anchor build # compile the Solana program → target/deploy/, target/idl/
anchor test # build + start localnet + run tests (or: bun test)
bun run generate-client # anchor build → codama → generate TS client into web/client/samizdat/
cd web && bun install && bun run dev # start web frontend at localhost:5173
```

- **Package manager**: Bun everywhere (configured in `Anchor.toml` and `bunfig.toml`).
- **Rust toolchain**: 1.89.0 pinned in `rust-toolchain.toml`.
- **Deployments** use Surfpool runbooks (`runbooks/deployment/main.tx`), not `anchor deploy`.

## Solana Program Architecture (`programs/samizdat/src/`)

### Module layout

- `lib.rs` – Program entrypoint; thin dispatch to `instructions::process_*` functions.
- `state/` – Account structs (`CampaignAccount`, `NodeAccount`, `PublisherAccount`, `PlayRecord`, `ClaimCooldown`) and shared types/enums/constants.
- `instructions/` – One file per instruction (e.g., `create_campaign.rs`). Each exports a `#[derive(Accounts)]` context struct and a `process_*` function.
- `definitions/` – Proof-related types.
- `errors.rs` – All error variants in `SamizdatError`.

### Key conventions

- **PDA seeds** are defined as constants in `state/shared.rs` (`PUBLISHER_SEED`, `CAMPAIGN_SEED`, `NODE_ACCOUNT_SEED`, `PLAY_RECORD_SEED`, `COOLDOWN_SEED`).
- All accounts use `#[derive(InitSpace)]` for automatic space calculation; account init uses `space = 8 + T::INIT_SPACE`.
- Content tags use a **bitmask** (`tag_mask` / `blocked_tag_mask`); overlap check: `campaign.tag_mask & node.blocked_tag_mask != 0` → content blocked.
- Geo coordinates are **fixed-point i64** (degrees × 1e7).
- Campaign funding is **upfront** (`bounty_per_play × total_plays` transferred at creation). The campaign account itself acts as the SOL vault.
- Play cycle: `claim_campaign` → 5-min timeout window → `confirm_play` (pays operator) or `timeout_play` (restores play count).

### When adding a new instruction

1. Create `instructions/<name>.rs` with an `Accounts` struct and `process_<name>` function.
2. Re-export in `instructions/mod.rs`.
3. Add the dispatch entry in `lib.rs` under `#[program] mod samizdat`.
4. Run `bun run generate-client` to regenerate the TS client.

## TypeScript Client (`web/client/samizdat/`)

Auto-generated by **Codama** from the Anchor IDL (`createCodamaClient.ts`). **Never hand-edit** files under `web/client/samizdat/src/generated/`. To update after program changes:

```bash
bun run generate-client
```

The root `tsconfig.json` maps `@client/*` → `web/client/samizdat/src/generated/*`.

## Tests (`tests/samizdat.test.ts`)

- Uses **`node:test`** (not Jest/Mocha) with `node:assert`.
- Uses **`solana-kite`** for connection management and `@solana/kit` v6 for types.
- Test wallets are created via `connection.createWallets(n)`.
- PDAs are derived with `getPDAAndBump(SAMIZDAT_PROGRAM_ADDRESS, [...seeds])`.
- Instructions come from the generated client (`getXxxInstructionAsync` / `getXxxInstruction`).
- Timeout testing requires clock-warping (not available on localnet), so those tests only assert pre-timeout state.

## Web Frontend (`web/`)

### Critical conventions (from `web/CLAUDE.md`)

- **File naming**: All files use **kebab-case** (`date-picker.tsx`, not `DatePicker.tsx`).
- **React Aria imports**: Always prefix with `Aria*`:
```typescript
import { Button as AriaButton } from 'react-aria-components';
```
- **Path alias**: `@/` → `web/src/` (configured in `web/vite.config.ts`).
- **Component structure**: Base components in `components/base/`, complex app components in `components/application/`. Uses compound component pattern with sub-components.
- **Styling**: Tailwind CSS v4 with `sortCx()` utility for organized style objects. Brand colors via `--color-brand-*` CSS variables in `src/styles/theme.css`.
- **Icons**: `@untitledui/icons` (line-style, free).
- **Routing**: React Router v7 integrated through React Aria's `RouterProvider` (see `providers/router-provider.tsx`).

## Project-Specific Patterns

- The `@solana/kit` v6 API is **not** the legacy `@solana/web3.js` — use `Address` (not `PublicKey`), `TransactionSigner`, `lamports()`, etc.
- `solana-kite` wraps `@solana/kit` for ergonomics (`connect()`, `sendTransactionFromInstructions()`).
- Campaigns use `campaign_id: u64` as a publisher-chosen identifier; PDA = `[CAMPAIGN_SEED, publisher_account_pda, campaign_id_le_bytes]`.
- Nodes use `node_id: u64`; PDA = `[NODE_ACCOUNT_SEED, authority, node_id_le_bytes]`.
Loading
Loading