chore: scaffold PYRE MVP monorepo (structure + docs)

pnpm + TypeScript workspace per design doc §13:
- apps/{web,api,worker} skeletons (Next.js 16, Fastify 5, BullMQ)
- packages/{core,solana,prometheus,db,config} (core has real types/DTOs;
  solana/prometheus are stubs)
- programs/pyre-core placeholder (future Anchor, v1.0)
- docs/: PYRE_MVP_DESIGN (canonical), ARCHITECTURE, SECURITY, TOKEN_CLASSIFICATION
- CLAUDE.md, README, .env.example (no private-key var by design)

Skeleton + docs only — no Solana/business logic yet. All workspaces typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 02:20:55 +00:00
parent e86b57e00f
commit c20094ab56
65 changed files with 13834 additions and 1 deletions

View File

@@ -0,0 +1,94 @@
/**
* @pyre/solana — Solana transaction helpers (STUBS ONLY).
*
* Responsibilities (§13): token-account parsing, close-account tx builder, burn
* tx builder, simulation helpers, and a transaction decoder.
*
* IMPORTANT (trust rules, §3/§7/§16):
* - PYRE never holds private keys; these helpers build **unsigned** transactions
* only. Signing happens client-side in the user's wallet.
* - Recovered ATA rent must default to the user's own wallet.
* - Every transaction must be simulated and **decoded**, then matched against the
* preview shown to the user before any signature is requested.
* - Classic SPL only in the MVP. Skip Token-2022 / NFTs / unsupported layouts.
*
* Nothing here is implemented yet — every function throws "not implemented".
*/
import type { Connection, PublicKey } from "@solana/web3.js";
import type {
TokenAccountDto,
BuildCloseEmptyPreview,
BuildBurnPreview,
BurnItem,
} from "@pyre/core";
const NOT_IMPLEMENTED = "not implemented";
/**
* Parse a wallet's SPL token accounts into classified DTOs.
*
* TODO: fetch token accounts via RPC, decode account state (balance, decimals,
* frozen/delegated, token program), resolve metadata, and hand off to the
* classifier in `@pyre/core`. Classic SPL only.
*/
export function parseTokenAccounts(
_connection: Connection,
_wallet: PublicKey,
): Promise<TokenAccountDto[]> {
throw new Error(NOT_IMPLEMENTED);
}
/**
* Build an UNSIGNED transaction that closes the given empty ATAs, returning rent
* to the user wallet.
*
* TODO: assemble CloseAccount instructions, set fee payer + rent destination to
* the user, return a base64 transaction plus a matching preview.
*/
export function buildCloseEmptyAccountsTx(
_connection: Connection,
_wallet: PublicKey,
_accountAddresses: PublicKey[],
): Promise<{ transactionBase64: string; preview: BuildCloseEmptyPreview }> {
throw new Error(NOT_IMPLEMENTED);
}
/**
* Build an UNSIGNED transaction that burns the given token balances (optionally
* closing accounts that become empty).
*
* TODO: assemble Burn (and optional CloseAccount) instructions, return a base64
* transaction plus a matching preview.
*/
export function buildBurnTx(
_connection: Connection,
_wallet: PublicKey,
_items: BurnItem[],
): Promise<{ transactionBase64: string; preview: BuildBurnPreview }> {
throw new Error(NOT_IMPLEMENTED);
}
/**
* Simulate a transaction before signing.
*
* TODO: run `connection.simulateTransaction`, surface logs/errors, and confirm
* the simulation succeeds. All transactions must be simulated before signing.
*/
export function simulateTransaction(
_connection: Connection,
_transactionBase64: string,
): Promise<unknown> {
throw new Error(NOT_IMPLEMENTED);
}
/**
* Decode an unsigned transaction so its contents can be matched against the
* preview shown to the user (accounts to close, tokens to burn, rent amount,
* rent destination, fees, warnings).
*
* TODO: deserialize the transaction, decode SPL instructions, and return a
* structured, human-comparable summary.
*/
export function decodeTransaction(_transactionBase64: string): unknown {
throw new Error(NOT_IMPLEMENTED);
}