diff --git a/.github/workflows/snippets.yml b/.github/workflows/snippets.yml index 64b071e..fe279db 100644 --- a/.github/workflows/snippets.yml +++ b/.github/workflows/snippets.yml @@ -98,12 +98,15 @@ jobs: uses: actions/checkout@v4 # ----------------------------------------------------------------------- - # Rust — pinned stable toolchain for reproducibility. + # Rust — pinned toolchain for reproducibility. 1.89 is the floor for the + # Solana workspace's dependency tree (anchor-lang 0.32 pulls crates that + # require edition 2024 and rustc 1.87+); rustfmt gates formatting. # ----------------------------------------------------------------------- - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.78.0" + toolchain: "1.89.0" + components: rustfmt # ----------------------------------------------------------------------- # Python — pinned minor version; patch resolved by setup-python. @@ -147,6 +150,27 @@ jobs: # 1 – content regression → always a hard failure # 2 – provider outage → warning annotation, does NOT block merge # --------------------------------------------------------------------------- + # ----------------------------------------------------------------------- + # Solana Anchor workspace — the structural reference behind + # contracts/solana.mdx. `cargo check` type-checks every documented + # instruction, account context, event, account layout, and error against + # the pinned anchor-lang; the TS check compares each fence against the + # workspace and compares program IDs, layouts, and discriminators with + # the canonical program sources and the chain-generated deployment + # record in wraith-protocol/contracts. These are structural checks — + # they do not execute or deploy the programs. + # ----------------------------------------------------------------------- + - name: Check Solana program workspace + working-directory: snippets/solana-contracts + run: cargo check --workspace --locked + + - name: Check Solana workspace formatting + working-directory: snippets/solana-contracts + run: cargo fmt --check --all + + - name: Compare Solana program IDs and account layouts + run: pnpm run check:solana-contracts + stellar-testnet-snippets: name: Stellar snippet testnet validation runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 5379ce5..d8087aa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ node_modules/ .npm/ test-results/ playwright-report/ + +# Rust build artifacts +snippets/*/target/ diff --git a/contracts/solana.mdx b/contracts/solana.mdx index b251703..e307f88 100644 --- a/contracts/solana.mdx +++ b/contracts/solana.mdx @@ -5,6 +5,29 @@ description: "Anchor program specs and deployment" Solana programs (smart contracts) for stealth address operations, written in Rust with the Anchor framework. +Every Rust block on this page is a verbatim excerpt from the reference +workspace in [`snippets/solana-contracts`](https://github.com/wraith-protocol/docs/tree/develop/snippets/solana-contracts), +which CI type-checks with `cargo check --workspace` against the pinned +`anchor-lang` release and gates with `cargo fmt --check`. A second CI step +compares the workspace's program IDs, instruction signatures, account +layouts, events, and error codes against the canonical program sources and +the chain-generated deployment record in the `wraith-protocol/contracts` +repository. + +Those checks are **structural**. They confirm that the documented API shape +matches the canonical program and that the program IDs match the deployment +record. They do not execute the program, deploy it, or verify runtime +on-chain behaviour; the workspace is a documentation fixture, not a deployment +artifact. + +Two things are intentionally abbreviated here: + +- Instruction fences show the public signature only; the transfer, CPI, and + event-emission bodies live in the workspace crates. +- The bare PDA `seeds = ...` fragment under [wraith-names](#pda-derivation) is + an Anchor account-attribute fragment, not a standalone Rust item, so it is + marked `no-check`. + ## Program Set | Program | Purpose | @@ -21,7 +44,7 @@ Emits announcement events via Anchor's `emit!()` macro. Stateless — no on-chai ### Instruction -```rust no-check +```rust pub fn announce( ctx: Context, scheme_id: u32, @@ -33,7 +56,7 @@ pub fn announce( ### Accounts -```rust no-check +```rust #[derive(Accounts)] pub struct Announce<'info> { #[account(mut)] @@ -43,7 +66,7 @@ pub struct Announce<'info> { ### Event -```rust no-check +```rust #[event] pub struct AnnouncementEvent { pub scheme_id: u32, @@ -85,7 +108,7 @@ Atomic SOL or SPL token transfer + announcement in one instruction. Sends funds Transfer SOL to a stealth address and emit an announcement. -```rust no-check +```rust pub fn send_sol( ctx: Context, amount: u64, @@ -98,7 +121,7 @@ pub fn send_sol( **Accounts:** -```rust no-check +```rust #[derive(Accounts)] pub struct SendSol<'info> { #[account(mut)] @@ -114,7 +137,7 @@ pub struct SendSol<'info> { Transfer SPL tokens to a stealth address's associated token account and emit an announcement. -```rust no-check +```rust pub fn send_spl( ctx: Context, amount: u64, @@ -127,7 +150,7 @@ pub fn send_spl( **Accounts:** -```rust no-check +```rust #[derive(Accounts)] pub struct SendSpl<'info> { #[account(mut)] @@ -140,6 +163,23 @@ pub struct SendSpl<'info> { } ``` +### Event + +Both instructions emit the same `AnnouncementEvent` that the announcer +program emits, so indexers can subscribe to either program. The reference +crate re-declares it rather than importing across programs: + +```rust +#[event] +pub struct AnnouncementEvent { + pub scheme_id: u32, + pub stealth_address: Pubkey, + pub caller: Pubkey, + pub ephemeral_pub_key: [u8; 32], + pub metadata: Vec, +} +``` + ### Usage ```typescript @@ -178,7 +218,7 @@ PDA-based name to meta-address mapping. Names are stored in Program Derived Addr Register a new `.wraith` name. -```rust no-check +```rust pub fn register( ctx: Context, name: String, @@ -190,18 +230,15 @@ pub fn register( Update the meta-address for a name you own. -```rust no-check -pub fn update( - ctx: Context, - new_meta_address: [u8; 64], -) -> Result<()> +```rust +pub fn update(ctx: Context, new_meta_address: [u8; 64]) -> Result<()> ``` #### `release` Release a name. Closes the PDA account and returns rent to the owner. -```rust no-check +```rust pub fn release(ctx: Context) -> Result<()> ``` @@ -209,13 +246,62 @@ pub fn release(ctx: Context) -> Result<()> Look up a name's meta-address. Read-only. -```rust no-check +```rust pub fn resolve(ctx: Context) -> Result<[u8; 64]> ``` +### Account Contexts + +The account layouts each instruction validates against. Only `Register` +creates the PDA; the other three reference it through the same seeds: + +```rust +#[derive(Accounts)] +#[instruction(name: String)] +pub struct Register<'info> { + #[account( + init, + payer = owner, + space = 8 + 4 + MAX_NAME_LEN + 64 + 32 + 8, + seeds = [b"name", name.as_bytes()], + bump, + )] + pub name_record: Account<'info, NameRecord>, + #[account(mut)] + pub owner: Signer<'info>, + pub system_program: Program<'info, System>, +} +``` + +```rust +#[derive(Accounts)] +pub struct Update<'info> { + #[account(mut)] + pub name_record: Account<'info, NameRecord>, + pub owner: Signer<'info>, +} +``` + +```rust +#[derive(Accounts)] +pub struct Release<'info> { + #[account(mut, close = owner)] + pub name_record: Account<'info, NameRecord>, + #[account(mut)] + pub owner: Signer<'info>, +} +``` + +```rust +#[derive(Accounts)] +pub struct Resolve<'info> { + pub name_record: Account<'info, NameRecord>, +} +``` + ### Account Structure -```rust no-check +```rust #[account] pub struct NameRecord { pub name: String, // max 32 bytes @@ -230,6 +316,9 @@ pub struct NameRecord { Name records are stored at PDAs derived from the name: ```rust no-check +// Anchor account-attribute fragment, not a standalone Rust item, so it is +// not compiled on its own. The full `Register` account context above shows +// this seed in place. seeds = [b"name", name.as_bytes()] ``` @@ -241,9 +330,14 @@ This means names are globally unique and can be resolved without knowing the own - Meta-address: must be exactly 64 bytes (two 32-byte ed25519 public keys) - Only the owner can update or release a name +The meta-address is stored exactly as supplied. The program does not verify a +spending-key signature over it: registration binds the record to the +registrant's Solana wallet, and later updates are gated on that wallet being +the record owner. + ### Error Codes -```rust no-check +```rust #[error_code] pub enum WraithError { #[msg("Name must be 3-32 characters")] @@ -329,7 +423,7 @@ anchor deploy --provider.cluster devnet anchor test ``` -Tests cover: +The canonical program's `anchor test` suite covers: - **wraith-announcer** — event emission, multiple callers, metadata preservation - **wraith-sender** — `send_sol` transfers + emits, `send_spl` token transfer + emits, insufficient funds rejection - **wraith-names** — register/resolve, name validation (too short, invalid chars), update by owner, update by non-owner (rejected), release and re-register @@ -343,7 +437,7 @@ Tests cover: | Language | Solidity | Rust | Rust (Anchor) | | Contract model | Bytecode deployment | WASM deployment | BPF program deployment | | Name storage | Contract storage | Contract storage | PDAs (Program Derived Addresses) | -| Name sig verification | On-chain ECDSA recovery | Caller auth | Signer constraint | +| Name sig verification | On-chain ECDSA recovery | Caller auth | Wallet signer constraint (no meta-address signature check) | | Event indexing | Subgraph / The Graph | Soroban RPC `getEvents` | Program transaction logs | | Account model | Address always exists | Must `createAccount` first | Address always valid, no deployment | | Token transfers | `msg.value` / `safeTransferFrom` | Soroban token contract | SystemProgram / SPL Token | diff --git a/package.json b/package.json index f9b40e0..c6a4966 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "check:snippets": "tsx scripts/check-snippets.ts && tsx scripts/check-api-examples.ts", "check:api-examples": "tsx scripts/check-api-examples.ts", "check:integration-guides": "tsx scripts/check-integration-guides.ts", + "check:solana-contracts": "tsx scripts/check-solana-contracts.ts", "check:snippets-extended": "tsx scripts/check-snippets-extended.ts && npm run check:python-quickstart", "check:python-quickstart": "tsx scripts/python-quickstart/generate-vectors.ts --check && python3 scripts/python-quickstart/run_quickstart.py --bootstrap", "generate:python-quickstart-vectors": "tsx scripts/python-quickstart/generate-vectors.ts", diff --git a/scripts/check-snippets-extended.ts b/scripts/check-snippets-extended.ts index dd32073..43b9977 100644 --- a/scripts/check-snippets-extended.ts +++ b/scripts/check-snippets-extended.ts @@ -27,8 +27,23 @@ import { mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { spawn } from "node:child_process"; +import { createRequire } from "node:module"; import process from "node:process"; +// --------------------------------------------------------------------------- +// Configuration +// --------------------------------------------------------------------------- + +const repoRoot = process.cwd(); + +// Resolve a YAML parser from the project's node_modules. `require` is used +// instead of `import()` because absolute paths break ESM on Windows. +const require = createRequire(path.join(repoRoot, "package.json")); + +const ignoredDirs = new Set([ + ".git", ".github", "node_modules", ".next", "dist", "build", +]); + // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- @@ -51,15 +66,9 @@ type CheckResult = { }; // --------------------------------------------------------------------------- -// Configuration +// Language dispatch // --------------------------------------------------------------------------- -const repoRoot = process.cwd(); - -const ignoredDirs = new Set([ - ".git", ".github", "node_modules", ".next", "dist", "build", -]); - /** Map from raw fence language tag (lower-case) to canonical Lang value. */ const langMap: Record = { rust: "rust", @@ -74,6 +83,19 @@ const langMap: Record = { yml: "yaml", }; +/** + * MDX pages whose Rust fences cannot be compiled by bare `rustc` (they are + * Anchor program snippets that need `anchor-lang`). Each fence is instead + * compared verbatim against a reference workspace by the named script, and + * that workspace is type-checked with cargo in CI. + */ +const crateChecked: Record = { + "contracts/solana.mdx": "check:solana-contracts (snippets/solana-contracts workspace)", +}; + +/** Normalise a repo-relative path to forward slashes for map lookups. */ +const relPath = (p: string): string => p.replace(/\\/g, "/"); + // --------------------------------------------------------------------------- // Entry point // --------------------------------------------------------------------------- @@ -83,7 +105,17 @@ async function main() { const allSnippets = await collectSnippets(files); const skipped = allSnippets.filter((s) => /\bno-check\b/.test(s.attrs)); - const checkable = allSnippets.filter((s) => !/\bno-check\b/.test(s.attrs)); + // Rust fences in crate-checked pages are verified by their workspace script + // (see `crateChecked`), never by bare rustc — they need anchor-lang etc. + // Other languages in the same page (bash, JSON, …) are still checked here. + const crateCheckedSnippets = allSnippets.filter( + (s) => s.lang === "rust" && !/\bno-check\b/.test(s.attrs) && crateChecked[relPath(s.file)] !== undefined, + ); + const checkable = allSnippets.filter( + (s) => + !/\bno-check\b/.test(s.attrs) && + !(s.lang === "rust" && crateChecked[relPath(s.file)] !== undefined), + ); const byLang = groupBy(checkable, (s) => s.lang); @@ -114,6 +146,7 @@ async function main() { ` toml : ${counts["toml"] ?? 0}`, ` yaml : ${counts["yaml"] ?? 0}`, `Skipped (no-check) : ${skipped.length}`, + `Crate-checked : ${crateCheckedSnippets.length}`, `Checked : ${checkable.length}`, `Failures : ${failures.length}`, ].join("\n"); @@ -337,7 +370,7 @@ function checkToml(snippets: Snippet[]): Promise { * header, not a key-value pair, not a value continuation). */ function lintToml(src: string): string | null { - const lines = src.split("\n"); + const lines = src.replace(/\r\n/g, "\n").split("\n"); for (let i = 0; i < lines.length; i++) { const raw = lines[i]; const line = raw.replace(/#.*$/, "").trim(); // strip inline comments @@ -363,7 +396,8 @@ function lintToml(src: string): string | null { // --------------------------------------------------------------------------- async function checkYaml(snippets: Snippet[]): Promise { - // Try to resolve a YAML parser from the project's node_modules. + // Resolve a YAML parser from the project's node_modules. `require` is used + // instead of `import()` because absolute paths break ESM on Windows. let parse: ((src: string) => unknown) | null = null; for (const [pkg, exported] of [ @@ -371,8 +405,7 @@ async function checkYaml(snippets: Snippet[]): Promise { ["yaml", "parse"], ] as const) { try { - const modPath = path.join(repoRoot, "node_modules", pkg); - const mod = await import(modPath) as Record; + const mod = require(`${pkg}`) as Record; const fn = mod[exported] ?? mod["default"]; if (typeof fn === "function") { parse = fn as (src: string) => unknown; @@ -502,7 +535,9 @@ function countBy(arr: T[], key: (item: T) => string): Record } async function commandExists(cmd: string): Promise { - const r = await run("which", [cmd]); + // `which` is POSIX; Windows ships `where`. + const locator = process.platform === "win32" ? "where" : "which"; + const r = await run(locator, [cmd]); return r.exitCode === 0; } @@ -516,6 +551,9 @@ function run(command: string, args: string[]): Promise<{ exitCode: number; outpu let output = ""; child.stdout.on("data", (c: Buffer) => { output += c.toString(); }); child.stderr.on("data", (c: Buffer) => { output += c.toString(); }); + // Without this listener a failed spawn (e.g. a missing command on Windows) + // emits an unhandled 'error' event and kills the process mid-run. + child.on("error", () => resolve({ exitCode: 1, output })); child.on("close", (code) => { resolve({ exitCode: code ?? 1, output }); }); }); } diff --git a/scripts/check-solana-contracts.ts b/scripts/check-solana-contracts.ts new file mode 100644 index 0000000..f765993 --- /dev/null +++ b/scripts/check-solana-contracts.ts @@ -0,0 +1,861 @@ +/** + * check-solana-contracts.ts + * + * Compares the Solana contract guide (`contracts/solana.mdx`) and its + * reference workspace (`snippets/solana-contracts/`) against the canonical + * program sources and the chain-generated deployment record in the + * `wraith-protocol/contracts` repository — the same sources the SDK's + * `getDeployment("solana")` manifest is generated from. + * + * These checks are STRUCTURAL: they compare published API shape (instruction + * signatures, account contexts, account layouts, events, error codes) and + * program IDs. They do not execute the programs, deploy them, or verify + * runtime on-chain behaviour, and they do not compare instruction bodies — + * the workspace crates are documentation fixtures whose bodies are abbreviated. + * + * Run it after editing either the guide or the workspace: + * + * pnpm run check:solana-contracts + * pnpm run check:solana-contracts -- --offline # skip the network fetches + * + * Checks performed: + * + * 1. guide — every checked `rust` fence in contracts/solana.mdx is a + * verbatim excerpt of its program's crate in the workspace; + * `no-check` fences must be allowlisted with a reason; + * 2. coverage — every instruction, account context, event, account + * layout, and error enum in the workspace is documented by + * a fence in the matching guide section; + * 3. program id — each crate's `declare_id!` is a valid canonical base58 + * pubkey and equals BOTH the pinned SDK's + * `getDeployment("solana")` manifest AND the chain-generated + * deployment record `deployments/solana/solana-devnet.json` + * in the canonical contracts repository; + * 4. layout — instructions (names, arguments, return types), account + * contexts (field order, field types, account constraints), + * account layouts, events, and error codes in the workspace + * match the canonical program sources pinned below; + * 5. discriminator — each instruction's Anchor sighash + * (`sha256("global:")[..8]`) matches the *_DISC + * constant the pinned SDK ships for it. + * + * Checks 3 (chain manifest) and 4 need network access to fetch the pinned + * canonical sources. Skip them locally with --offline; CI always runs the + * full comparison. + * + * Wired into CI via the "Compile Rust / Python / shell / config snippets" job + * in .github/workflows/snippets.yml. + */ +import { createHash } from "node:crypto"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import process from "node:process"; +import { PublicKey } from "@solana/web3.js"; + +const repoRoot = process.cwd(); + +const OFFLINE = process.argv.includes("--offline"); + +const GUIDE_REL = "contracts/solana.mdx"; +const WORKSPACE_REL = "snippets/solana-contracts"; + +/** + * The canonical Solana programs and the chain-generated deployment record are + * fetched from `wraith-protocol/contracts` at this exact commit, mirroring how + * check-contract-registry.mjs pins its contracts-repo manifest. The pin tracks + * the `develop` branch (the repo's default branch). Bump it when the programs + * or the deployment record change. + */ +const CONTRACTS_REPO = "wraith-protocol/contracts"; +const PINNED_CONTRACTS_COMMIT = "4f6189e24746852287d245b42a0cc324caf53d4e"; +const FETCH_TIMEOUT_MS = 15_000; +const FETCH_RETRIES = 2; + +/** Workspace crate <-> guide section <-> SDK deployment key. */ +const PROGRAMS = [ + { crate: "wraith-announcer", sdkKey: "announcer" }, + { crate: "wraith-sender", sdkKey: "sender" }, + { crate: "wraith-names", sdkKey: "names" }, +] as const; + +/** + * `rust no-check` fences that are intentionally not compiled or extracted. + * + * Every entry must carry a reason; a `no-check` fence outside this list fails + * the check. Keys are a distinctive substring of the fence body. + */ +const NO_CHECK_ALLOWLIST: Record = { + 'seeds = [b"name", name.as_bytes()]': + "Anchor account-attribute fragment, not a standalone Rust item. " + + "The full Register account context shows the seed in place.", +}; + +// ---------------------------------------------------------------------- +// Types +// ---------------------------------------------------------------------- + +type RustField = { name: string; type: string; attr: string }; + +type Instruction = { + name: string; + args: Array<{ name: string; type: string }>; + ret: string; +}; + +type AccountsStruct = { + name: string; + instructionAttr: string | null; + fields: RustField[]; + body: string; // normalised text of the whole item +}; + +type PlainStruct = { name: string; fields: RustField[]; body: string }; + +type ErrorEnum = { + name: string; + variants: Array<{ name: string; msg: string | null }>; + body: string; +}; + +type ProgramModel = { + programId: string | null; + instructions: Instruction[]; + accounts: AccountsStruct[]; + events: PlainStruct[]; + accountData: PlainStruct[]; + errors: ErrorEnum[]; +}; + +type Fence = { + attrs: string; + code: string; + line: number; + section: string | null; // "wraith-announcer" etc. +}; + +type Issue = { file: string; line: number; message: string }; + +// ---------------------------------------------------------------------- +// Normalisation helpers +// ---------------------------------------------------------------------- + +/** + * Whitespace- and comma-free text, for verbatim containment comparisons. + * Dropping commas lets multi-line signatures (which rustfmt ends with a + * trailing comma) match their one-line forms and vice versa. + */ +function norm(text: string): string { + return text.replace(/\s+/g, "").replace(/,/g, ""); +} + +/** Strip line and block comments (none of the compared code embeds `//` in strings). */ +function stripComments(src: string): string { + return src.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, ""); +} + +function lineNumberAt(text: string, index: number): number { + return text.slice(0, index).split("\n").length; +} + +/** `pub fn name(args) -> ret` in its whitespace/comma-free form. */ +function signatureOf(ix: Instruction): string { + return norm(`pub fn ${ix.name}(${ix.args.map((a) => `${a.name}: ${a.type}`).join(", ")}) -> ${ix.ret}`); +} + +// ---------------------------------------------------------------------- +// Rust parsing (line-based; the sources are rustfmt-formatted) +// ---------------------------------------------------------------------- + +function parseProgram(src: string): ProgramModel { + const code = stripComments(src); + const lines = code.split("\n"); + + const model: ProgramModel = { + programId: /declare_id!\("([^"]+)"\)/.exec(code)?.[1] ?? null, + instructions: [], + accounts: [], + events: [], + accountData: [], + errors: [], + }; + + let pending: string[] = []; // item-level attributes collected before an item + let attrStart = 0; // line index where `pending` started + let i = 0; + + /** Collect a brace-balanced block starting at `start`; returns it plus the next line index. */ + const takeBalanced = (start: number, open: string, close: string): { text: string; next: number } => { + let depth = 0; + let text = ""; + let j = start; + for (; j < lines.length; j++) { + const line = lines[j]; + text += line + "\n"; + for (const ch of line) { + if (ch === open) depth++; + else if (ch === close) depth--; + } + if (depth === 0 && text.trim() !== "") return { text, next: j + 1 }; + } + return { text, next: j }; + }; + + const ITEM_ATTRS = ["#[derive(Accounts)]", "#[event]", "#[account]", "#[error_code]"]; + + while (i < lines.length) { + const line = lines[i].trim(); + + // Instructions inside `#[program] pub mod { ... }`. + if (line === "#[program]" && /^pub mod \w+ \{/.test(lines[i + 1]?.trim() ?? "")) { + const modBody = takeBalanced(i + 1, "{", "}"); + const fnRe = /pub fn (\w+)\s*\(([\s\S]*?)\)\s*->\s*([^{]+)\{/g; + let m: RegExpExecArray | null; + while ((m = fnRe.exec(modBody.text)) !== null) { + model.instructions.push({ + name: m[1], + args: m[2] + .split(",") + .map((a) => a.trim()) + .filter((a) => a.length > 0) + .map((a) => { + const at = a.indexOf(":"); + return { name: a.slice(0, at).trim(), type: a.slice(at + 1).trim() }; + }), + ret: m[3].trim(), + }); + } + i = modBody.next; + pending = []; + continue; + } + + if (line.startsWith("#[")) { + if (ITEM_ATTRS.includes(line)) { + pending = [line]; + attrStart = i; + } else if (line.startsWith("#[instruction(") && pending.includes("#[derive(Accounts)]")) { + pending.push(line); + } else { + pending = []; + } + i++; + continue; + } + + const structMatch = /^pub struct (\w+)/.exec(line); + if (structMatch && pending.length > 0) { + const name = structMatch[1]; + const attrs = pending; + pending = []; + const body = takeBalanced(i, "{", "}"); + const fields = parseFields(body.text); + + if (attrs.includes("#[derive(Accounts)]")) { + model.accounts.push({ + name, + instructionAttr: attrs.find((a) => a.startsWith("#[instruction(")) ?? null, + fields, + body: norm(lines.slice(attrStart, body.next).join("\n")), + }); + } else if (attrs.includes("#[event]")) { + model.events.push({ name, fields, body: norm(lines.slice(attrStart, body.next).join("\n")) }); + } else if (attrs.includes("#[account]")) { + model.accountData.push({ name, fields, body: norm(lines.slice(attrStart, body.next).join("\n")) }); + } + i = body.next; + continue; + } + + const enumMatch = /^pub enum (\w+)/.exec(line); + if (enumMatch && pending.includes("#[error_code]")) { + const name = enumMatch[1]; + pending = []; + const body = takeBalanced(i, "{", "}"); + const variants: Array<{ name: string; msg: string | null }> = []; + let msg: string | null = null; + for (const raw of body.text.split("\n")) { + const t = raw.trim(); + const msgMatch = /^#\[msg\("([^"]*)"\)\]$/.exec(t); + if (msgMatch) { + msg = msgMatch[1]; + continue; + } + const variant = /^(\w+),?$/.exec(t); + if (variant && variant[1] !== "pub") { + variants.push({ name: variant[1], msg }); + msg = null; + } + } + model.errors.push({ name, variants, body: norm(lines.slice(attrStart, body.next).join("\n")) }); + i = body.next; + continue; + } + + pending = []; + i++; + } + + return model; +} + +/** Parse `pub name: Type,` fields (with a possible preceding `#[account(...)]`) from a struct body. */ +function parseFields(structBody: string): RustField[] { + const fields: RustField[] = []; + let attr: string | null = null; + let depth = 0; + let collecting: string[] = []; + + for (const raw of structBody.split("\n")) { + const t = raw.trim(); + if (collecting.length > 0 || t.startsWith("#[account(")) { + collecting.push(t); + for (const ch of t) { + if (ch === "(") depth++; + else if (ch === ")") depth--; + } + if (depth === 0) { + attr = collecting.join(" "); + collecting = []; + } + continue; + } + const field = /^pub (\w+):\s*(.+?),?$/.exec(t); + if (field) { + fields.push({ name: field[1], type: field[2].replace(/,$/, "").trim(), attr: attr ?? "" }); + attr = null; + } else if (t.startsWith("#[") || t === "") { + attr = null; + } + } + return fields; +} + +// ---------------------------------------------------------------------- +// Guide parsing +// ---------------------------------------------------------------------- + +async function parseGuide(): Promise { + const markdown = await readFile(path.join(repoRoot, GUIDE_REL), "utf8"); + const fencePattern = /^```([A-Za-z0-9_-]+)([^\n]*)\n([\s\S]*?)^```/gm; + const headings = [...markdown.matchAll(/^##\s+(wraith-[a-z-]+)\s*$/gm)]; + const fences: Fence[] = []; + let match: RegExpExecArray | null; + + const sectionAt = (index: number): string | null => { + let current: string | null = null; + for (const h of headings) { + if (h.index! > index) break; + current = h[1]; + } + return current; + }; + + while ((match = fencePattern.exec(markdown)) !== null) { + if (match[1].toLowerCase() !== "rust") continue; + fences.push({ + attrs: match[2] ?? "", + code: match[3], + line: lineNumberAt(markdown, match.index), + section: sectionAt(match.index), + }); + } + return fences; +} + +// ---------------------------------------------------------------------- +// Canonical + SDK artifacts +// ---------------------------------------------------------------------- + +async function fetchText(url: string): Promise { + for (let attempt = 1; attempt <= FETCH_RETRIES; attempt++) { + try { + const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) }); + if (response.ok) return await response.text(); + if (response.status === 404) return null; + } catch { + // transient — retry + } + } + return null; +} + +async function fetchCanonicalPrograms(): Promise | null> { + const programs = new Map(); + for (const { crate } of PROGRAMS) { + const url = `https://raw.githubusercontent.com/${CONTRACTS_REPO}/${PINNED_CONTRACTS_COMMIT}/solana/programs/${crate}/src/lib.rs`; + const src = await fetchText(url); + if (src === null) return null; + programs.set(crate, parseProgram(src)); + } + return programs; +} + +async function fetchChainManifest(): Promise | null> { + const url = `https://raw.githubusercontent.com/${CONTRACTS_REPO}/${PINNED_CONTRACTS_COMMIT}/deployments/solana/solana-devnet.json`; + const text = await fetchText(url); + if (text === null) return null; + try { + const json = JSON.parse(text); + const ids = new Map(); + for (const contract of json.contracts ?? []) { + if (contract?.name && contract?.id) ids.set(contract.name, contract.id); + } + return ids; + } catch { + return null; + } +} + +type SdkArtifacts = { contracts: Record; discriminators: Map }; + +async function loadSdkArtifacts(failures: Issue[]): Promise { + const bundlePath = path.join( + repoRoot, + "node_modules", + "@wraith-protocol", + "sdk", + "dist", + "chains", + "solana", + "index.js", + ); + let source: string; + try { + source = await readFile(bundlePath, "utf8"); + } catch { + failures.push({ + file: "node_modules/@wraith-protocol/sdk", + line: 1, + message: + "Could not read @wraith-protocol/sdk from node_modules.\n" + + "CI installs dependencies before this check; locally run `pnpm install` first.", + }); + return null; + } + + const manifestMatch = /var DEPLOYMENTS = (\{[\s\S]*?\n\});/.exec(source); + let contracts: Record = {}; + try { + const deployments = new Function(`return (${manifestMatch?.[1]});`)(); + contracts = deployments?.solana?.contracts ?? {}; + } catch { + /* fall through to the emptiness check below */ + } + if (Object.keys(contracts).length === 0) { + failures.push({ + file: "node_modules/@wraith-protocol/sdk", + line: 1, + message: "Could not locate the Solana DEPLOYMENTS manifest in the installed SDK bundle.", + }); + return null; + } + + const discriminators = new Map(); + for (const m of source.matchAll(/var (\w+)_DISC = new Uint8Array\(\[([\d,\s]+)\]\)/g)) { + discriminators.set(m[1].toLowerCase(), m[2].split(",").map((n) => Number(n.trim()))); + } + + return { contracts, discriminators }; +} + +// ---------------------------------------------------------------------- +// Check 1 + 2: guide fences against the workspace crates +// ---------------------------------------------------------------------- + +function checkGuide(fences: Fence[], models: Map, sources: Map): Issue[] { + const issues: Issue[] = []; + const crateNorm = new Map(); + for (const { crate } of PROGRAMS) { + crateNorm.set(crate, norm(stripComments(sources.get(crate)!))); + } + + for (const fence of fences) { + const noCheck = /\bno-check\b/.test(fence.attrs); + const body = norm(stripComments(fence.code)); + + if (noCheck) { + const allowlisted = Object.keys(NO_CHECK_ALLOWLIST).some((k) => norm(body).includes(norm(k))); + if (!allowlisted) { + issues.push({ + file: GUIDE_REL, + line: fence.line, + message: + "Rust fence is marked `no-check` but is not allowlisted.\n" + + "Either remove `no-check` so it is verified against the workspace, or add it\n" + + "to NO_CHECK_ALLOWLIST in scripts/check-solana-contracts.ts with a reason.", + }); + } + continue; + } + + if (!fence.section || !crateNorm.has(fence.section)) { + issues.push({ + file: GUIDE_REL, + line: fence.line, + message: + "Checked Rust fences must sit under a `## wraith-…` program section so this\n" + + "checker knows which workspace crate verifies them.", + }); + continue; + } + + const crate = fence.section; + if (!crateNorm.get(crate)!.includes(norm(body))) { + issues.push({ + file: GUIDE_REL, + line: fence.line, + message: + `This fence is not a verbatim excerpt of ${WORKSPACE_REL}/programs/${crate}/src/lib.rs.\n` + + "Update the guide or the crate so the two agree — CI compiles the crate, so the\n" + + "guide must show exactly what compiles.", + }); + continue; + } + + const model = models.get(crate)!; + const isItem = [ + ...model.accounts.map((a) => a.body), + ...model.events.map((e) => e.body), + ...model.accountData.map((a) => a.body), + ...model.errors.map((e) => e.body), + ...model.instructions.map((ix) => signatureOf(ix)), + ].some((item) => item.includes(norm(body))); + if (!isItem) { + issues.push({ + file: GUIDE_REL, + line: fence.line, + message: + `This fence matches ${crate} source text but not a documented item\n` + + "(instruction, account context, event, account layout, or error enum).", + }); + } + } + + // Coverage: every workspace item must be documented by a fence in its section. + for (const { crate } of PROGRAMS) { + const model = models.get(crate)!; + const sectionFences = fences + .filter((f) => f.section === crate && !/\bno-check\b/.test(f.attrs)) + .map((f) => norm(stripComments(f.code))); + + for (const ix of model.instructions) { + if (!sectionFences.includes(signatureOf(ix))) { + issues.push({ + file: GUIDE_REL, + line: 1, + message: `${crate}: instruction \`${ix.name}\` is not documented by a fence in the "## ${crate}" section.`, + }); + } + } + for (const item of [...model.accounts, ...model.events, ...model.accountData]) { + if (!sectionFences.some((f) => item.body.includes(f))) { + issues.push({ + file: GUIDE_REL, + line: 1, + message: `${crate}: \`${item.name}\` is not documented by a fence in the "## ${crate}" section.`, + }); + } + } + for (const err of model.errors) { + if (!sectionFences.some((f) => f.includes(norm(`pub enum ${err.name}`)))) { + issues.push({ + file: GUIDE_REL, + line: 1, + message: `${crate}: error enum \`${err.name}\` is not documented by a fence in the "## ${crate}" section.`, + }); + } + } + } + + return issues; +} + +// ---------------------------------------------------------------------- +// Check 3: program IDs against generated artifacts +// ---------------------------------------------------------------------- + +function checkProgramIds( + models: Map, + sdk: SdkArtifacts | null, + chainIds: Map | null, + failures: Issue[], +): void { + for (const { crate, sdkKey } of PROGRAMS) { + const id = models.get(crate)!.programId; + const file = `${WORKSPACE_REL}/programs/${crate}/src/lib.rs`; + if (!id) { + failures.push({ file, line: 1, message: `${crate}: no declare_id! found.` }); + continue; + } + + try { + if (new PublicKey(id).toBase58() !== id) { + failures.push({ file, line: 1, message: `${crate}: declare_id! "${id}" is not canonical base58.` }); + } + } catch { + failures.push({ file, line: 1, message: `${crate}: declare_id! "${id}" is not a valid 32-byte Solana pubkey.` }); + continue; + } + + if (sdk) { + const sdkId = sdk.contracts[sdkKey]; + if (sdkId !== id) { + failures.push({ + file, + line: 1, + message: + `${crate}: declare_id! does not match the pinned SDK's getDeployment("solana") manifest.\n` + + ` crate: ${id}\n sdk: ${sdkId ?? "(not published)"}`, + }); + } + } + + if (chainIds) { + const chainId = chainIds.get(crate.replace(/-/g, "_")); + if (chainId !== id) { + failures.push({ + file, + line: 1, + message: + `${crate}: declare_id! does not match the chain-generated deployment record\n` + + `(${CONTRACTS_REPO}@${PINNED_CONTRACTS_COMMIT.slice(0, 8)} deployments/solana/solana-devnet.json).\n` + + ` crate: ${id}\n record: ${chainId ?? "(missing)"}`, + }); + } + } + } +} + +// ---------------------------------------------------------------------- +// Check 5: instruction discriminators vs the SDK's sighash constants +// ---------------------------------------------------------------------- + +function checkDiscriminators(models: Map, sdk: SdkArtifacts | null, failures: Issue[]): void { + if (!sdk) return; + const known = new Set(); + for (const { crate } of PROGRAMS) { + for (const ix of models.get(crate)!.instructions) { + known.add(ix.name); + const shipped = sdk.discriminators.get(ix.name); + if (!shipped) continue; // The SDK does not build every instruction client-side. + const expected = Array.from(createHash("sha256").update(`global:${ix.name}`).digest().subarray(0, 8)); + if (shipped.join(",") !== expected.join(",")) { + failures.push({ + file: "node_modules/@wraith-protocol/sdk", + line: 1, + message: + `SDK ${ix.name.toUpperCase()}_DISC does not match Anchor's sighash for \`${ix.name}\`.\n` + + ` sdk: ${shipped.join(", ")}\n sighash: ${expected.join(", ")}`, + }); + } + } + } + for (const name of sdk.discriminators.keys()) { + if (!known.has(name)) { + failures.push({ + file: "node_modules/@wraith-protocol/sdk", + line: 1, + message: `SDK ships a discriminator for unknown instruction \`${name}\`.`, + }); + } + } +} + +// ---------------------------------------------------------------------- +// Check 4: workspace layouts vs the canonical program sources +// ---------------------------------------------------------------------- + +function compareModels(crate: string, docs: ProgramModel, canonical: ProgramModel, failures: Issue[]): void { + const file = `${WORKSPACE_REL}/programs/${crate}/src/lib.rs`; + const fail = (message: string) => failures.push({ file, line: 1, message }); + + if (docs.programId === null) { + fail(`${crate}: no declare_id! in the workspace crate.`); + } else if (docs.programId !== canonical.programId) { + fail( + `${crate}: declare_id! does not match the canonical program.\n` + + ` workspace: ${docs.programId}\n canonical: ${canonical.programId}`, + ); + } + + const byName = (items: T[]) => new Map(items.map((i) => [i.name, i])); + + const ixDocs = byName(docs.instructions); + const ixCanonical = byName(canonical.instructions); + for (const [name, ix] of ixCanonical) { + const other = ixDocs.get(name); + if (!other) { + fail(`${crate}: workspace crate is missing instruction \`${name}\`.`); + continue; + } + if (signatureOf(ix) !== signatureOf(other)) { + fail( + `${crate}: instruction \`${name}\` signature differs from the canonical program.\n` + + ` canonical: ${ix.args.map((a) => `${a.name}: ${a.type}`).join(", ")} -> ${ix.ret}\n` + + ` workspace: ${other.args.map((a) => `${a.name}: ${a.type}`).join(", ")} -> ${other.ret}`, + ); + } + } + for (const name of ixDocs.keys()) { + if (!ixCanonical.has(name)) fail(`${crate}: workspace crate has extra instruction \`${name}\`.`); + } + + const compareStructs = ( + kind: string, + docs2: Array<{ name: string; fields: RustField[] }>, + canonical2: Array<{ name: string; fields: RustField[]; instructionAttr?: string | null }>, + ) => { + const cBy = byName(canonical2); + const dBy = byName(docs2); + for (const [name, cItem] of cBy) { + const dItem = dBy.get(name); + if (!dItem) { + fail(`${crate}: workspace crate is missing ${kind} \`${name}\`.`); + continue; + } + const layout = (s: { fields: RustField[] }) => + s.fields.map((f) => `${f.name}:${norm(f.type)}#${norm(f.attr)}`).join("|"); + if (layout(cItem) !== layout(dItem)) { + fail( + `${crate}: ${kind} \`${name}\` layout differs from the canonical program.\n` + + ` canonical: ${cItem.fields.map((f) => `${f.name}: ${f.type}${f.attr ? ` [${f.attr}]` : ""}`).join(", ")}\n` + + ` workspace: ${dItem.fields.map((f) => `${f.name}: ${f.type}${f.attr ? ` [${f.attr}]` : ""}`).join(", ")}`, + ); + } + if ("instructionAttr" in cItem || "instructionAttr" in dItem) { + const cAttr = norm((cItem as AccountsStruct).instructionAttr ?? ""); + const dAttr = norm((dItem as AccountsStruct & { instructionAttr?: string | null }).instructionAttr ?? ""); + if (cAttr !== dAttr) { + fail( + `${crate}: ${kind} \`${name}\` #[instruction(...)] attribute differs from the canonical program.\n` + + ` canonical: ${(cItem as AccountsStruct).instructionAttr ?? "(none)"}\n` + + ` workspace: ${(dItem as AccountsStruct).instructionAttr ?? "(none)"}`, + ); + } + } + } + for (const name of dBy.keys()) { + if (!cBy.has(name)) fail(`${crate}: workspace crate has extra ${kind} \`${name}\`.`); + } + }; + + compareStructs("account context", docs.accounts, canonical.accounts); + compareStructs("event", docs.events, canonical.events); + compareStructs("account layout", docs.accountData, canonical.accountData); + + const eCanonical = byName(canonical.errors); + const eDocs = byName(docs.errors); + const errSig = (e: ErrorEnum) => e.variants.map((v) => `${v.name}:${v.msg ?? ""}`).join("|"); + for (const [name, err] of eCanonical) { + const other = eDocs.get(name); + if (!other) { + fail(`${crate}: workspace crate is missing error enum \`${name}\`.`); + continue; + } + if (errSig(err) !== errSig(other)) { + fail( + `${crate}: error enum \`${name}\` differs from the canonical program.\n` + + ` canonical: ${err.variants.map((v) => `${v.name} ("${v.msg ?? ""}")`).join(", ")}\n` + + ` workspace: ${other.variants.map((v) => `${v.name} ("${v.msg ?? ""}")`).join(", ")}`, + ); + } + } + for (const name of eDocs.keys()) { + if (!eCanonical.has(name)) fail(`${crate}: workspace crate has extra error enum \`${name}\`.`); + } +} + +// ---------------------------------------------------------------------- +// Main +// ---------------------------------------------------------------------- + +async function main() { + const failures: Issue[] = []; + const warnings: string[] = []; + + const models = new Map(); + const sources = new Map(); + for (const { crate } of PROGRAMS) { + const rel = `${WORKSPACE_REL}/programs/${crate}/src/lib.rs`; + let src: string; + try { + src = await readFile(path.join(repoRoot, rel), "utf8"); + } catch { + console.error(`Missing ${rel}. The Solana reference workspace is not checked out.`); + process.exit(1); + } + sources.set(crate, src); + models.set(crate, parseProgram(src)); + } + + const fences = await parseGuide(); + failures.push(...checkGuide(fences, models, sources)); + + const sdk = await loadSdkArtifacts(failures); + checkDiscriminators(models, sdk, failures); + + let chainIds: Map | null = null; + if (OFFLINE) { + warnings.push( + "Remote verification skipped (--offline): canonical program layouts and the chain-generated deployment record were not checked.", + ); + } else { + const canonical = await fetchCanonicalPrograms(); + if (!canonical) { + failures.push({ + file: GUIDE_REL, + line: 1, + message: + `Could not fetch the canonical Solana programs from ${CONTRACTS_REPO}@${PINNED_CONTRACTS_COMMIT.slice(0, 8)}.\n` + + "Re-run with --offline to skip this verification locally, or retry later.", + }); + } else { + for (const { crate } of PROGRAMS) { + compareModels(crate, models.get(crate)!, canonical.get(crate)!, failures); + } + } + + chainIds = await fetchChainManifest(); + if (!chainIds) { + failures.push({ + file: GUIDE_REL, + line: 1, + message: + `Could not fetch deployments/solana/solana-devnet.json from ${CONTRACTS_REPO}@${PINNED_CONTRACTS_COMMIT.slice(0, 8)}.\n` + + "Re-run with --offline to skip this verification locally, or retry later.", + }); + } + } + + checkProgramIds(models, sdk, chainIds, failures); + + if (warnings.length > 0) { + console.warn(["Solana contract check warnings:", ...warnings.map((w) => ` - ${w}`)].join("\n")); + } + + if (failures.length > 0) { + console.error( + [ + "Solana contract check failed.", + "", + ...failures.map((f) => `${f.file}:${f.line}\n${"-".repeat(40)}\n${f.message}`), + "", + `${GUIDE_REL} and ${WORKSPACE_REL} must mirror the canonical programs in`, + `${CONTRACTS_REPO} and the program IDs the SDK publishes.`, + ].join("\n\n"), + ); + process.exit(1); + } + + const compared = OFFLINE + ? "guide, workspace, SDK manifest (offline)" + : "guide, workspace, SDK manifest, canonical programs, chain-generated deployment record"; + console.log( + `Solana contract structure check passed: ${fences.length} guide fence(s), ${PROGRAMS.length} program(s) compared against: ${compared}.`, + ); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/snippets/solana-contracts/Cargo.lock b/snippets/solana-contracts/Cargo.lock new file mode 100644 index 0000000..9775b30 --- /dev/null +++ b/snippets/solana-contracts/Cargo.lock @@ -0,0 +1,3000 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b938c8b4988a78a4d2203bee59f6727883ddefc551bebbc31dc39bce0cad9b" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31140e3f7622f0d5c519941210a4cd8cbc8b7c159bfbe3c5e18033b54afcf3d" +dependencies = [ + "anchor-syn", + "bs58", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "072af3e841c04fc330da06ae4ce21d64f82126ee29735dc3b38463d4df429ab7" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "118ab229afbc4d263c27fdc21198309212e82eba26b09d8810d65898ceceb706" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fb6b19b701c057b7844b41e01ebdd802b8b0e06be39c0b4a163e8ef5f54f0e" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a43a3b862af82a9a32644b71a85ddd7c1aff2c2378d4e973c979c1637672ac2c" +dependencies = [ + "anchor-lang-idl", + "anchor-syn", + "anyhow", + "bs58", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b63581912522eeaf2ef1e1efd0cdc5ffc376d7dd2e3e9fcd99dc309f8a13b9" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030232abc6f00f427b50a8b3f8d1e10fbb1c961245fd2353ec129c3594c15f1e" +dependencies = [ + "anchor-syn", + "borsh-derive-internal", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f96c7183104c29e48ffd0daab154a602e1da765d0f8c3d45b0a48f12e8af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-lang" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7469ba047dd8d26cee94878db458cc024a26a191310e72d76646fc0b969382d" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-program", + "anchor-derive-accounts", + "anchor-derive-serde", + "anchor-derive-space", + "base64 0.21.7", + "bincode", + "borsh 0.10.4", + "bytemuck", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-define-syscall", + "solana-feature-gate-interface", + "solana-instruction", + "solana-instructions-sysvar", + "solana-invoke", + "solana-loader-v3-interface 3.0.0", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "thiserror 1.0.69", +] + +[[package]] +name = "anchor-lang-idl" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47914b4290ae2bdf4ec203aa821e6eba86d7c78ef497918938038dcc6919f953" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck", + "serde", + "serde_json", + "sha2 0.10.9", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdf143115440fe621bdac3a29a1f7472e09f6cd82b2aa569429a0c13f103838" +dependencies = [ + "anyhow", + "serde", +] + +[[package]] +name = "anchor-spl" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9436d0ba1c4784fc040ee1e50e7d76f02399f3f976ba0c5b843ff9c17a6d35" +dependencies = [ + "anchor-lang", + "spl-associated-token-account", + "spl-pod", + "spl-token", + "spl-token-2022", + "spl-token-group-interface", + "spl-token-metadata-interface", +] + +[[package]] +name = "anchor-syn" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bfc8f646330093d9413fff5b471d71046511fac50d52ccc22d9ab07af8c416" +dependencies = [ + "anyhow", + "bs58", + "heck", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "syn 1.0.109", + "thiserror 1.0.69", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "blake3" +version = "1.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" +dependencies = [ + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "borsh" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" +dependencies = [ + "borsh-derive 0.10.4", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" +dependencies = [ + "borsh-derive 1.8.1", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cdfe656708a01f89b451a7d36466e6fe6c414de0aa18fc54f864f6f9ca9f56" +dependencies = [ + "once_cell", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" +dependencies = [ + "cmov", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "five8" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75b8549488b4715defcb0d8a8a1c1c76a80661b5fa106b4ca0e7fce59d7d875" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_const" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26dec3da8bc3ef08f2c04f61eab298c3ab334523e55f076354d6d6f613799a7b" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2551bf44bc5f776c15044b9b94153a00198be06743e262afaaa61f11ac7523a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "lazy_static" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb" + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" + +[[package]] +name = "solana-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f949fe4edaeaea78c844023bfc1c898e0b1f5a100f8a8d2d0f85d0a7b090258" +dependencies = [ + "solana-account-info", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-account-info" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f5152a288ef1912300fc6efa6c2d1f9bb55d9398eb6c72326360b8063987da" +dependencies = [ + "bincode", + "serde", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", +] + +[[package]] +name = "solana-address-lookup-table-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673f67efe870b64a65cb39e6194be5b26527691ce5922909939961a6e6b395" +dependencies = [ + "bincode", + "bytemuck", + "serde", + "serde_derive", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-slot-hashes", +] + +[[package]] +name = "solana-atomic-u64" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52e52720efe60465b052b9e7445a01c17550666beec855cce66f44766697bc2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "solana-big-mod-exp" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" +dependencies = [ + "num-bigint", + "num-traits", + "solana-define-syscall", +] + +[[package]] +name = "solana-bincode" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a3787b8cf9c9fe3dd360800e8b70982b9e5a8af9e11c354b6665dd4a003adc" +dependencies = [ + "bincode", + "serde", + "solana-instruction", +] + +[[package]] +name = "solana-blake3-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" +dependencies = [ + "blake3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-borsh" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718333bcd0a1a7aed6655aa66bef8d7fb047944922b2d3a18f49cbc13e73d004" +dependencies = [ + "borsh 0.10.4", + "borsh 1.8.1", +] + +[[package]] +name = "solana-clock" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8584296123df8fe229b95e2ebfd37ae637fe9db9b7d4dd677ac5a78e80dbfce" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-cpi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +dependencies = [ + "solana-account-info", + "solana-define-syscall", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-stable-layout", +] + +[[package]] +name = "solana-curve25519" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae4261b9a8613d10e77ac831a8fa60b6fa52b9b103df46d641deff9f9812a23" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "solana-define-syscall", + "subtle", + "thiserror 2.0.21", +] + +[[package]] +name = "solana-decode-error" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c781686a18db2f942e70913f7ca15dc120ec38dcab42ff7557db2c70c625a35" +dependencies = [ + "num-traits", +] + +[[package]] +name = "solana-define-syscall" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae3e2abcf541c8122eafe9a625d4d194b4023c20adde1e251f94e056bb1aee2" + +[[package]] +name = "solana-derivation-path" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939756d798b25c5ec3cca10e06212bdca3b1443cb9bb740a38124f58b258737b" +dependencies = [ + "derivation-path", + "qstring", + "uriparse", +] + +[[package]] +name = "solana-epoch-rewards" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b575d3dd323b9ea10bb6fe89bf6bf93e249b215ba8ed7f68f1a3633f384db7" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-epoch-schedule" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce071fbddecc55d727b1d7ed16a629afe4f6e4c217bc8d00af3b785f6f67ed" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-example-mocks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84461d56cbb8bb8d539347151e0525b53910102e4bced875d49d5139708e39d3" +dependencies = [ + "serde", + "serde_derive", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "thiserror 2.0.21", +] + +[[package]] +name = "solana-feature-gate-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f5c5382b449e8e4e3016fb05e418c53d57782d8b5c30aa372fc265654b956d" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-fee-calculator" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89bc408da0fb3812bc3008189d148b4d3e08252c79ad810b245482a3f70cd8d" +dependencies = [ + "log", + "serde", + "serde_derive", +] + +[[package]] +name = "solana-hash" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b96e9f0300fa287b545613f007dfe20043d7812bee255f418c1eb649c93b63" +dependencies = [ + "borsh 1.8.1", + "bytemuck", + "bytemuck_derive", + "five8", + "js-sys", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-sanitize", + "wasm-bindgen", +] + +[[package]] +name = "solana-instruction" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab5682934bd1f65f8d2c16f21cb532526fcc1a09f796e2cacdb091eee5774ad" +dependencies = [ + "bincode", + "borsh 1.8.1", + "getrandom 0.2.17", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "serde_json", + "solana-define-syscall", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-instructions-sysvar" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" +dependencies = [ + "bitflags", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-serialize-utils", + "solana-sysvar-id", +] + +[[package]] +name = "solana-invoke" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f5693c6de226b3626658377168b0184e94e8292ff16e3d31d4766e65627565" +dependencies = [ + "solana-account-info", + "solana-define-syscall", + "solana-instruction", + "solana-program-entrypoint", + "solana-stable-layout", +] + +[[package]] +name = "solana-keccak-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" +dependencies = [ + "sha3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-last-restart-slot" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6360ac2fdc72e7463565cd256eedcf10d7ef0c28a1249d261ec168c1b55cdd" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-loader-v2-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ab08006dad78ae7cd30df8eea0539e207d08d91eaefb3e1d49a446e1c49654" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4be76cfa9afd84ca2f35ebc09f0da0f0092935ccdac0595d98447f259538c2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7162a05b8b0773156b443bccd674ea78bb9aa406325b467ea78c06c99a63a2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v4-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706a777242f1f39a83e2a96a2a6cb034cb41169c6ecbee2cf09cb873d9659e7e" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-message" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1796aabce376ff74bf89b78d268fa5e683d7d7a96a0a4e4813ec34de49d5314b" +dependencies = [ + "bincode", + "blake3", + "lazy_static", + "serde", + "serde_derive", + "solana-bincode", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-system-interface", + "solana-transaction-error", + "wasm-bindgen", +] + +[[package]] +name = "solana-msg" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-native-token" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61515b880c36974053dd499c0510066783f0cc6ac17def0c7ef2a244874cf4a9" + +[[package]] +name = "solana-nonce" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703e22eb185537e06204a5bd9d509b948f0066f2d1d814a6f475dafb3ddf1325" +dependencies = [ + "serde", + "serde_derive", + "solana-fee-calculator", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-program" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98eca145bd3545e2fbb07166e895370576e47a00a7d824e325390d33bf467210" +dependencies = [ + "bincode", + "blake3", + "borsh 0.10.4", + "borsh 1.8.1", + "bs58", + "bytemuck", + "console_error_panic_hook", + "console_log", + "getrandom 0.2.17", + "lazy_static", + "log", + "memoffset", + "num-bigint", + "num-derive", + "num-traits", + "rand 0.8.8", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-address-lookup-table-interface", + "solana-atomic-u64", + "solana-big-mod-exp", + "solana-bincode", + "solana-blake3-hasher", + "solana-borsh", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-example-mocks", + "solana-feature-gate-interface", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-keccak-hasher", + "solana-last-restart-slot", + "solana-loader-v2-interface", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-message", + "solana-msg", + "solana-native-token", + "solana-nonce", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-secp256k1-recover", + "solana-serde-varint", + "solana-serialize-utils", + "solana-sha256-hasher", + "solana-short-vec", + "solana-slot-hashes", + "solana-slot-history", + "solana-stable-layout", + "solana-stake-interface", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "solana-vote-interface", + "thiserror 2.0.21", + "wasm-bindgen", +] + +[[package]] +name = "solana-program-entrypoint" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ce041b1a0ed275290a5008ee1a4a6c48f5054c8a3d78d313c08958a06aedbd" +dependencies = [ + "solana-account-info", + "solana-msg", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "solana-program-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee2e0217d642e2ea4bee237f37bd61bb02aec60da3647c48ff88f6556ade775" +dependencies = [ + "borsh 1.8.1", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-pubkey", +] + +[[package]] +name = "solana-program-memory" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-program-option" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc677a2e9bc616eda6dbdab834d463372b92848b2bfe4a1ed4e4b4adba3397d0" + +[[package]] +name = "solana-program-pack" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319f0ef15e6e12dc37c597faccb7d62525a509fec5f6975ecb9419efddeb277b" +dependencies = [ + "solana-program-error", +] + +[[package]] +name = "solana-pubkey" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b62adb9c3261a052ca1f999398c388f1daf558a1b492f60a6d9e64857db4ff1" +dependencies = [ + "borsh 0.10.4", + "borsh 1.8.1", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "five8", + "five8_const", + "getrandom 0.2.17", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-decode-error", + "solana-define-syscall", + "solana-sanitize", + "solana-sha256-hasher", + "wasm-bindgen", +] + +[[package]] +name = "solana-rent" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1aea8fdea9de98ca6e8c2da5827707fb3842833521b528a713810ca685d2480" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sanitize" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f1bc1357b8188d9c4a3af3fc55276e56987265eb7ad073ae6f8180ee54cecf" + +[[package]] +name = "solana-sdk-ids" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5d8b9cc68d5c88b062a33e23a6466722467dde0035152d8fb1afbcdf350a5f" +dependencies = [ + "solana-pubkey", +] + +[[package]] +name = "solana-sdk-macro" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86280da8b99d03560f6ab5aca9de2e38805681df34e0bb8f238e69b29433b9df" +dependencies = [ + "bs58", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "solana-secp256k1-recover" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" +dependencies = [ + "libsecp256k1", + "solana-define-syscall", + "thiserror 2.0.21", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94a02d486b28f219a4f8f5d7dd93cbfbb93c9f466cb7871c22e50cd5ae9a7a2" + +[[package]] +name = "solana-seed-derivable" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beb82b5adb266c6ea90e5cf3967235644848eac476c5a1f2f9283a143b7c97f" +dependencies = [ + "solana-derivation-path", +] + +[[package]] +name = "solana-seed-phrase" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36187af2324f079f65a675ec22b31c24919cb4ac22c79472e85d819db9bbbc15" +dependencies = [ + "hmac", + "pbkdf2", + "sha2 0.10.9", +] + +[[package]] +name = "solana-serde-varint" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7e155eba458ecfb0107b98236088c3764a09ddf0201ec29e52a0be40857113" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serialize-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "817a284b63197d2b27afdba829c5ab34231da4a9b4e763466a003c40ca4f535e" +dependencies = [ + "solana-instruction", + "solana-pubkey", + "solana-sanitize", +] + +[[package]] +name = "solana-sha256-hasher" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa3feb32c28765f6aa1ce8f3feac30936f16c5c3f7eb73d63a5b8f6f8ecdc44" +dependencies = [ + "sha2 0.10.9", + "solana-define-syscall", + "solana-hash", +] + +[[package]] +name = "solana-short-vec" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c54c66f19b9766a56fa0057d060de8378676cb64987533fa088861858fc5a69" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-signature" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c8ec8e657aecfc187522fc67495142c12f35e55ddeca8698edbb738b8dbd8c" +dependencies = [ + "five8", + "solana-sanitize", +] + +[[package]] +name = "solana-signer" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c41991508a4b02f021c1342ba00bcfa098630b213726ceadc7cb032e051975b" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-transaction-error", +] + +[[package]] +name = "solana-slot-hashes" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8691982114513763e88d04094c9caa0376b867a29577939011331134c301ce" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-slot-history" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ccc1b2067ca22754d5283afb2b0126d61eae734fc616d23871b0943b0d935e" +dependencies = [ + "bv", + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-stable-layout" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f14f7d02af8f2bc1b5efeeae71bc1c2b7f0f65cd75bcc7d8180f2c762a57f54" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "solana-stake-interface" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5269e89fde216b4d7e1d1739cf5303f8398a1ff372a81232abbee80e554a838c" +dependencies = [ + "borsh 0.10.4", + "borsh 1.8.1", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-system-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-system-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7c18cb1a91c6be5f5a8ac9276a1d7c737e39a21beba9ea710ab4b9c63bc90" +dependencies = [ + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-sysvar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c3595f95069f3d90f275bb9bd235a1973c4d059028b0a7f81baca2703815db" +dependencies = [ + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "lazy_static", + "serde", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-last-restart-slot", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-slot-hashes", + "solana-slot-history", + "solana-stake-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sysvar-id" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5762b273d3325b047cfda250787f8d796d781746860d5d0a746ee29f3e8812c1" +dependencies = [ + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-transaction-error" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a9dc8fdb61c6088baab34fc3a8b8473a03a7a5fd404ed8dd502fa79b67cb1" +dependencies = [ + "solana-instruction", + "solana-sanitize", +] + +[[package]] +name = "solana-vote-interface" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b80d57478d6599d30acc31cc5ae7f93ec2361a06aefe8ea79bc81739a08af4c3" +dependencies = [ + "bincode", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-decode-error", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-serde-varint", + "solana-serialize-utils", + "solana-short-vec", + "solana-system-interface", +] + +[[package]] +name = "solana-zk-sdk" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b9fc6ec37d16d0dccff708ed1dd6ea9ba61796700c3bb7c3b401973f10f63b" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "itertools", + "js-sys", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.8", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-derivation-path", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "subtle", + "thiserror 2.0.21", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "spl-associated-token-account" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae179d4a26b3c7a20c839898e6aed84cb4477adf108a366c95532f058aea041b" +dependencies = [ + "borsh 1.8.1", + "num-derive", + "num-traits", + "solana-program", + "spl-associated-token-account-client", + "spl-token", + "spl-token-2022", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-associated-token-account-client" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f8349dbcbe575f354f9a533a21f272f3eb3808a49e2fdc1c34393b88ba76cb" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "spl-discriminator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7398da23554a31660f17718164e31d31900956054f54f52d5ec1be51cb4f4b3" +dependencies = [ + "bytemuck", + "solana-program-error", + "solana-sha256-hasher", + "spl-discriminator-derive", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn", + "syn 2.0.119", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1dbc82ab91422345b6df40a79e2b78c7bce1ebb366da323572dd60b7076b67" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.119", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-elgamal-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65edfeed09cd4231e595616aa96022214f9c9d2be02dea62c2b30d5695a6833a" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-cpi", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", + "solana-sysvar", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction", +] + +[[package]] +name = "spl-memo" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" +dependencies = [ + "solana-account-info", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "spl-pod" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d994afaf86b779104b4a95ba9ca75b8ced3fdb17ee934e38cb69e72afbe17799" +dependencies = [ + "borsh 1.8.1", + "bytemuck", + "bytemuck_derive", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-program-error" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdebc8b42553070b75aa5106f071fef2eb798c64a7ec63375da4b1f058688c6" +dependencies = [ + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-program-error-derive", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2539e259c66910d78593475540e8072f0b10f0f61d7607bbf7593899ed52d0" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.119", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1408e961215688715d5a1063cbdcf982de225c45f99c82b4f7d7e1dd22b998d7" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-token" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053067c6a82c705004f91dae058b11b4780407e9ccd6799dc9e7d0fab5f242da" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-sysvar", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-token-2022" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f0dfbb079eebaee55e793e92ca5f433744f4b71ee04880bfd6beefba5973e5" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-native-token", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-security-txt", + "solana-system-interface", + "solana-sysvar", + "solana-zk-sdk", + "spl-elgamal-registry", + "spl-memo", + "spl-pod", + "spl-token", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction", + "spl-token-confidential-transfer-proof-generation", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-transfer-hook-interface", + "spl-type-length-value", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddd52bfc0f1c677b41493dafa3f2dbbb4b47cf0990f08905429e19dc8289b35" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2629860ff04c17bafa9ba4bed8850a404ecac81074113e1f840dbd0ebb7bd6" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-curve25519", + "solana-instruction", + "solana-instructions-sysvar", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa27b9174bea869a7ebf31e0be6890bce90b1a4288bc2bbf24bd413f80ae3fde" +dependencies = [ + "curve25519-dalek", + "solana-zk-sdk", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5597b4cd76f85ce7cd206045b7dc22da8c25516573d42d267c8d1fd128db5129" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304d6e06f0de0c13a621464b1fd5d4b1bebf60d15ca71a44d3839958e0da16ee" +dependencies = [ + "borsh 1.8.1", + "num-derive", + "num-traits", + "solana-borsh", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-type-length-value", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e905b849b6aba63bde8c4badac944ebb6c8e6e14817029cbe1bc16829133bd" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-tlv-account-resolution", + "spl-type-length-value", + "thiserror 2.0.21", +] + +[[package]] +name = "spl-type-length-value" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d417eb548214fa822d93f84444024b4e57c13ed6719d4dcc68eec24fb481e9f5" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.21", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.17+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" +dependencies = [ + "winnow", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "wraith-announcer" +version = "0.1.0" +dependencies = [ + "anchor-lang", +] + +[[package]] +name = "wraith-names" +version = "0.1.0" +dependencies = [ + "anchor-lang", +] + +[[package]] +name = "wraith-sender" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "anchor-spl", +] + +[[package]] +name = "zerocopy" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/snippets/solana-contracts/Cargo.toml b/snippets/solana-contracts/Cargo.toml new file mode 100644 index 0000000..c42926b --- /dev/null +++ b/snippets/solana-contracts/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "programs/wraith-announcer", + "programs/wraith-sender", + "programs/wraith-names", +] +resolver = "2" diff --git a/snippets/solana-contracts/programs/wraith-announcer/Cargo.toml b/snippets/solana-contracts/programs/wraith-announcer/Cargo.toml new file mode 100644 index 0000000..c65c66c --- /dev/null +++ b/snippets/solana-contracts/programs/wraith-announcer/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "wraith-announcer" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +anchor-lang = { version = "0.32.2" } + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = [] } diff --git a/snippets/solana-contracts/programs/wraith-announcer/src/lib.rs b/snippets/solana-contracts/programs/wraith-announcer/src/lib.rs new file mode 100644 index 0000000..f44419c --- /dev/null +++ b/snippets/solana-contracts/programs/wraith-announcer/src/lib.rs @@ -0,0 +1,71 @@ +// programs/wraith-announcer/src/lib.rs +// +// Structural fixture for contracts/solana.mdx § wraith-announcer. +// +// Emits announcement events via Anchor's emit!() macro. +// Stateless — no on-chain storage. +// +// This crate mirrors the *shape* of the canonical program — instruction +// signatures, account contexts, account layouts, events, and error codes — so +// the documented snippets can be type-checked in CI. It is a documentation +// fixture, not the program that runs on chain: it is not deployed, and its +// instruction bodies are abbreviated or omitted. +// +// Program ID matches the deployed devnet program recorded in the canonical +// deployment manifest (wraith-protocol/contracts deployments/solana) and in +// `getDeployment("solana")` from @wraith-protocol/sdk. + +use anchor_lang::prelude::*; + +declare_id!("9Ko7TuXHpLUH1ZsZWQEpeA9Tv7hX325ooWk5SD7Y9nuq"); + +#[program] +pub mod wraith_announcer { + use super::*; + + /// Emit a stealth address announcement. + /// + /// scheme_id — stealth scheme identifier (use SCHEME_ID from SDK) + /// stealth_address — the one-time stealth Pubkey + /// ephemeral_pub_key — 32-byte ephemeral public key + /// metadata — view tag + optional bytes (first byte = view tag) + pub fn announce( + ctx: Context, + scheme_id: u32, + stealth_address: Pubkey, + ephemeral_pub_key: [u8; 32], + metadata: Vec, + ) -> Result<()> { + emit!(AnnouncementEvent { + scheme_id, + stealth_address, + caller: ctx.accounts.caller.key(), + ephemeral_pub_key, + metadata, + }); + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Accounts +// --------------------------------------------------------------------------- + +#[derive(Accounts)] +pub struct Announce<'info> { + #[account(mut)] + pub caller: Signer<'info>, +} + +// --------------------------------------------------------------------------- +// Event +// --------------------------------------------------------------------------- + +#[event] +pub struct AnnouncementEvent { + pub scheme_id: u32, + pub stealth_address: Pubkey, + pub caller: Pubkey, + pub ephemeral_pub_key: [u8; 32], + pub metadata: Vec, +} diff --git a/snippets/solana-contracts/programs/wraith-names/Cargo.toml b/snippets/solana-contracts/programs/wraith-names/Cargo.toml new file mode 100644 index 0000000..1ec8746 --- /dev/null +++ b/snippets/solana-contracts/programs/wraith-names/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "wraith-names" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +anchor-lang = { version = "0.32.2" } + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = [] } diff --git a/snippets/solana-contracts/programs/wraith-names/src/lib.rs b/snippets/solana-contracts/programs/wraith-names/src/lib.rs new file mode 100644 index 0000000..3b928a7 --- /dev/null +++ b/snippets/solana-contracts/programs/wraith-names/src/lib.rs @@ -0,0 +1,160 @@ +// programs/wraith-names/src/lib.rs +// +// Structural fixture for contracts/solana.mdx § wraith-names. +// +// PDA-based name to meta-address mapping. +// Names are stored in Program Derived Addresses seeded by ["name", nameBytes]. +// +// This crate mirrors the *shape* of the canonical program — instruction +// signatures, account contexts, account layouts, events, and error codes — so +// the documented snippets can be type-checked in CI. It is a documentation +// fixture, not the program that runs on chain: it is not deployed, and its +// instruction bodies are abbreviated or omitted. +// +// Program ID matches the deployed devnet program recorded in the canonical +// deployment manifest (wraith-protocol/contracts deployments/solana) and in +// `getDeployment("solana")` from @wraith-protocol/sdk. + +use anchor_lang::prelude::*; + +declare_id!("4JrrQh5aK7iLvx6MgtEQk7K7X3SsWfTLxVJu1jXEwNjD"); + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +/// Maximum byte length of the stored name. +pub const MAX_NAME_LEN: usize = 32; + +// --------------------------------------------------------------------------- +// Program +// --------------------------------------------------------------------------- + +#[program] +pub mod wraith_names { + use super::*; + + /// Register a new `.wraith` name. + /// + /// The name format is validated and the supplied meta-address is stored + /// as-is. Record ownership is tied to the registrant's Solana wallet + /// (`owner`); the meta-address itself is not authenticated on chain. + pub fn register(ctx: Context, name: String, meta_address: [u8; 64]) -> Result<()> { + require!( + name.len() >= 3 && name.len() <= MAX_NAME_LEN, + WraithError::InvalidNameLength + ); + require!( + name.chars() + .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-'), + WraithError::InvalidNameCharacter + ); + + let record = &mut ctx.accounts.name_record; + record.name = name; + record.meta_address = meta_address; + record.owner = ctx.accounts.owner.key(); + record.created_at = Clock::get()?.unix_timestamp; + Ok(()) + } + + /// Update the meta-address for a name you own. + pub fn update(ctx: Context, new_meta_address: [u8; 64]) -> Result<()> { + require!( + ctx.accounts.name_record.owner == ctx.accounts.owner.key(), + WraithError::NotOwner + ); + ctx.accounts.name_record.meta_address = new_meta_address; + Ok(()) + } + + /// Release a name. Closes the PDA account and returns rent to the owner. + pub fn release(ctx: Context) -> Result<()> { + require!( + ctx.accounts.name_record.owner == ctx.accounts.owner.key(), + WraithError::NotOwner + ); + // Account closure is handled by the `close = owner` constraint below. + Ok(()) + } + + /// Look up a name's meta-address. Read-only. + /// + /// ABBREVIATED: production clients call + /// `program.account.nameRecord.fetch(pda)` directly; this instruction + /// exists to document the on-chain interface shape. + pub fn resolve(ctx: Context) -> Result<[u8; 64]> { + Ok(ctx.accounts.name_record.meta_address) + } +} + +// --------------------------------------------------------------------------- +// Account data +// --------------------------------------------------------------------------- + +/// On-chain storage for a single `.wraith` name registration. +/// +/// PDA seeds: [b"name", name.as_bytes()] +/// Global uniqueness is enforced by the PDA derivation — only one live +/// account can exist per (program_id, seeds) combination. +#[account] +pub struct NameRecord { + pub name: String, // max 32 bytes + pub meta_address: [u8; 64], // spending pub (32) + viewing pub (32) + pub owner: Pubkey, + pub created_at: i64, +} + +// --------------------------------------------------------------------------- +// Accounts contexts +// --------------------------------------------------------------------------- + +#[derive(Accounts)] +#[instruction(name: String)] +pub struct Register<'info> { + #[account( + init, + payer = owner, + space = 8 + 4 + MAX_NAME_LEN + 64 + 32 + 8, + seeds = [b"name", name.as_bytes()], + bump, + )] + pub name_record: Account<'info, NameRecord>, + #[account(mut)] + pub owner: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct Update<'info> { + #[account(mut)] + pub name_record: Account<'info, NameRecord>, + pub owner: Signer<'info>, +} + +#[derive(Accounts)] +pub struct Release<'info> { + #[account(mut, close = owner)] + pub name_record: Account<'info, NameRecord>, + #[account(mut)] + pub owner: Signer<'info>, +} + +#[derive(Accounts)] +pub struct Resolve<'info> { + pub name_record: Account<'info, NameRecord>, +} + +// --------------------------------------------------------------------------- +// Errors +// --------------------------------------------------------------------------- + +#[error_code] +pub enum WraithError { + #[msg("Name must be 3-32 characters")] + InvalidNameLength, + #[msg("Name must be lowercase alphanumeric or hyphens")] + InvalidNameCharacter, + #[msg("Only the owner can modify this name")] + NotOwner, +} diff --git a/snippets/solana-contracts/programs/wraith-sender/Cargo.toml b/snippets/solana-contracts/programs/wraith-sender/Cargo.toml new file mode 100644 index 0000000..12493b3 --- /dev/null +++ b/snippets/solana-contracts/programs/wraith-sender/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "wraith-sender" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +anchor-lang = { version = "0.32.2" } +anchor-spl = { version = "0.32.2", features = ["token"] } + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = [] } diff --git a/snippets/solana-contracts/programs/wraith-sender/src/lib.rs b/snippets/solana-contracts/programs/wraith-sender/src/lib.rs new file mode 100644 index 0000000..49357d0 --- /dev/null +++ b/snippets/solana-contracts/programs/wraith-sender/src/lib.rs @@ -0,0 +1,131 @@ +// programs/wraith-sender/src/lib.rs +// +// Structural fixture for contracts/solana.mdx § wraith-sender. +// +// Atomic SOL or SPL token transfer + announcement in one instruction. +// Sends funds to the stealth address and emits an announcement event. +// +// This crate mirrors the *shape* of the canonical program — instruction +// signatures, account contexts, account layouts, events, and error codes — so +// the documented snippets can be type-checked in CI. It is a documentation +// fixture, not the program that runs on chain: it is not deployed, and its +// instruction bodies are abbreviated or omitted. +// +// Program ID matches the deployed devnet program recorded in the canonical +// deployment manifest (wraith-protocol/contracts deployments/solana) and in +// `getDeployment("solana")` from @wraith-protocol/sdk. + +use anchor_lang::prelude::*; +use anchor_spl::token::{Token, TokenAccount}; + +declare_id!("E6J7GBSTjKbYANWjfTo5HfnXZ4Tg3LAasN7NrvCwn5Dq"); + +// --------------------------------------------------------------------------- +// Announcement event (re-declared here; in production import from a shared +// crate or CPI into wraith-announcer). +// --------------------------------------------------------------------------- + +#[event] +pub struct AnnouncementEvent { + pub scheme_id: u32, + pub stealth_address: Pubkey, + pub caller: Pubkey, + pub ephemeral_pub_key: [u8; 32], + pub metadata: Vec, +} + +// --------------------------------------------------------------------------- +// Program +// --------------------------------------------------------------------------- + +#[program] +pub mod wraith_sender { + use super::*; + + /// Transfer SOL to a stealth address and emit an announcement. + pub fn send_sol( + ctx: Context, + amount: u64, + scheme_id: u32, + stealth_address: Pubkey, + ephemeral_pub_key: [u8; 32], + metadata: Vec, + ) -> Result<()> { + // Transfer SOL via system program CPI. + let ix = anchor_lang::solana_program::system_instruction::transfer( + &ctx.accounts.sender.key(), + &ctx.accounts.stealth_account.key(), + amount, + ); + anchor_lang::solana_program::program::invoke( + &ix, + &[ + ctx.accounts.sender.to_account_info(), + ctx.accounts.stealth_account.to_account_info(), + ctx.accounts.system_program.to_account_info(), + ], + )?; + + emit!(AnnouncementEvent { + scheme_id, + stealth_address, + caller: ctx.accounts.sender.key(), + ephemeral_pub_key, + metadata, + }); + Ok(()) + } + + /// Transfer SPL tokens to a stealth address's associated token account + /// and emit an announcement. + pub fn send_spl( + ctx: Context, + amount: u64, + scheme_id: u32, + stealth_address: Pubkey, + ephemeral_pub_key: [u8; 32], + metadata: Vec, + ) -> Result<()> { + let cpi_accounts = anchor_spl::token::Transfer { + from: ctx.accounts.sender_token_account.to_account_info(), + to: ctx.accounts.stealth_token_account.to_account_info(), + authority: ctx.accounts.sender.to_account_info(), + }; + let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts); + anchor_spl::token::transfer(cpi_ctx, amount)?; + + emit!(AnnouncementEvent { + scheme_id, + stealth_address, + caller: ctx.accounts.sender.key(), + ephemeral_pub_key, + metadata, + }); + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Accounts +// --------------------------------------------------------------------------- + +#[derive(Accounts)] +pub struct SendSol<'info> { + #[account(mut)] + pub sender: Signer<'info>, + /// CHECK: stealth address, receives SOL + #[account(mut)] + pub stealth_account: UncheckedAccount<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct SendSpl<'info> { + #[account(mut)] + pub sender: Signer<'info>, + #[account(mut)] + pub sender_token_account: Account<'info, TokenAccount>, + #[account(mut)] + pub stealth_token_account: Account<'info, TokenAccount>, + pub token_program: Program<'info, Token>, +}