SPB Git

Repository Public

adding-a-chain-family.md 4.9 KB 51 lines markdown

1# Adding a chain family (contract for implementers)23SP Wallet moves real funds. A chain integration is accepted only if the signer can rebuild the exact transaction from the4approved intent, refuses everything else, and the result is proven against an independent implementation.56## Read first7- `packages/core/src/families.ts` — the `FamilyPlugin` contract and `KeyHandle`.8- `packages/core/src/intent.ts` — the approved intent. Plugin families put their chain parameters in `x` (flat string map) and may use `memo`.9- `packages/core/src/build-sol.ts` + `services/api/src/chains/sol.ts` — a complete built-in example (deterministic builder, adapter).10- `services/api/src/chains/rpc.ts` — the `Adapter` interface, `rpcAny` (failover, display only), `rpcQuorum` (all sources must agree).11- `services/api/src/chains/btc.ts` — two-source agreement before preparing a transfer.12- `packages/core/test/core.test.ts`, `services/isolated-signer/test/signer.test.ts` — the style of tests expected.1314## What to deliver151. `packages/core/src/plugins/<family>.ts` exporting a `FamilyPlugin` object (pure module: no network access, no side effects).16   - `build()` rebuilds the transaction from the intent only and returns the exact bytes/digests to sign. It must throw on: wrong17     chain id / genesis, non-normalized or invalid addresses, an asset that is not the native coin or an allowlisted token, a fee18     above `intent.maxFee`, unknown or missing `x` keys, any amount ≤ 0, destination equal to source.19   - `sign()` uses only the `KeyHandle` (never raw private keys) and returns the broadcast-ready payload and the transaction id.20   - Only two shapes: native transfer, and (where listed in the task) one allowlisted token transfer. No arbitrary calls.212. Registration: add the network(s) to `DEFS` in `packages/core/src/networks.ts` (`family`, `coinType`, `chainRef` = the chain's22   genesis/network identifier, `memo`, `activation`, `rpc` with **two independent keyless public sources**, explorers, `minConf`),23   register the plugin in `packages/core/src/plugins/index.ts` (create it if missing; it must be imported by `packages/core/src/index.ts`),24   add the native symbol to `CAPS` (≈ 50 USD worth) and a fee ceiling to `STABLE_FEE` in `packages/core/src/policy.ts`,25   and register the API adapter factory in `services/api/src/chains/index.ts`.263. `services/api/src/chains/<family>.ts` exporting `<family>Adapter(n: NetworkDef): Adapter`:27   - `health()`: both sources up + chain identity verified (genesis hash / network id) → `ok`; at least one up → `readable`.28   - `balances()`, `deposits()` (incoming transfers, finalized only), `track()` (broadcast → confirming → finalized, with29     `failed` / expiry handled; a returned hash is never success), `broadcast()` (to all sources; "already known" is success).30   - `prepare()`: every value that feeds the signature (sequence/nonce, reference block, account existence, fee) must be read with31     agreement of both sources or fail closed with `UserError('rpc_degraded' | 'rpc_disagreement', …, 503)`; check balance covers32     amount + fee + any reserve the chain requires; simulate when the chain offers it; return `{ section: { x: {...} }, maxFee, feeEstimate, warnings, ttlSec, aux? }`.334. Tests (vitest) in `packages/core/test/<family>.test.ts`:34   - address derivation from the public BIP-39 test mnemonic `abandon ×11 about` against a published vector or the official SDK;35   - the built transaction decoded/re-encoded **by the chain's official SDK** (devDependency) must be byte-identical or field-identical;36   - the signature verifies with the official SDK or noble against the public key;37   - every fail-closed case above; changing any intent field changes the signed bytes or throws.38   Plus one signer-level test in `services/isolated-signer/test/` signing an intent through `Signer` (see existing tests).395. `scripts/smoke-<family>.mts`: live **read-only** check against mainnet (health, identity, balance of a well-known address,40   `prepare()` for a funded public address to prove the two sources agree). Run it and include the output in your report.4142## Rules43- Never broadcast, never use real keys, never touch any keystore or the production nodes. Tests use the public test mnemonic only.44- Money is `bigint` base units end to end. No floating point.45- Prefer `@noble/*` and `@scure/*` (already present) for primitives. A runtime dependency is acceptable only if it is the chain's46  official, maintained encoder, pure JavaScript (the services are bundled with esbuild; no WASM, no native addons). Pin exact versions.47- `pnpm install`, `pnpm typecheck` and `npx vitest run` must pass; `node scripts/build.mjs` must still bundle.48- Commit on a branch `wave3-<family>` in your worktree (do not push). Do not modify unrelated code; if the contract itself is49  insufficient, say so in the report instead of working around it.50- Report: what is implemented, the published vectors/SDK checks used, live smoke output, residual risks and anything unverified.51
Files main
Menu

Theme