Initial Commit
This commit is contained in:
153
openspec/changes/enterprise-cryptocurrency/proposal.md
Normal file
153
openspec/changes/enterprise-cryptocurrency/proposal.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# OpenSpec Change: enterprise-cryptocurrency
|
||||
|
||||
- **Change ID**: enterprise-cryptocurrency
|
||||
- **Package**: `@suaveplan/cryptocurrency`
|
||||
- **Category**: finance
|
||||
- **Target path**: `finance/crypto/cryptocurrency`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/cryptocurrency.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/cryptocurrency` on the genesis branch from scratch at enterprise quality. The package is a zero-runtime-dep (beyond `tslib`) pure utility library covering: wallet address validation and auto-detection across 15+ chains; EIP-55 checksumming; unit conversion (wei/gwei/ether, satoshi/bitcoin, lamport/sol, plus generic `convertUnits`); a pluggable `MemoryChainRegistry`; chain explorer URL generators; transaction hash validation; EIP-1559 + EIP-4844 blob-gas estimation helpers; a `TokenRegistry` with common tokens and balance formatting; an ABI-aware calldata/function-selector decoder; testnet chain entries; and property-based tests for round-trip unit conversions. Typed `CryptoError` hierarchy mirrors `@suaveplan/error` shape without importing it (preserving zero runtime deps).
|
||||
|
||||
---
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
Does not exist on genesis — fresh build.
|
||||
|
||||
---
|
||||
|
||||
## Workspace deps
|
||||
|
||||
No production workspace deps (zero-dep design). Dev deps only:
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `zod` (`catalog:forms`) | Address validation / chain info schemas (runtime dep by exception — tiny) |
|
||||
| `tslib` (`catalog:build`) | Runtime helpers |
|
||||
| `fast-check` (`catalog:testing`) | Property-based tests |
|
||||
| `@suaveplan/testing workspace:^` | Test utilities |
|
||||
|
||||
---
|
||||
|
||||
## Public API surface (from main branch, fully preserved + genesis additions)
|
||||
|
||||
```ts
|
||||
// Validation
|
||||
function validateAddress(address: string): AddressValidation
|
||||
function validateAddressForChain(address: string, chainId: ChainId): AddressValidation
|
||||
function validateTxHash(hash: string): TxHashValidation
|
||||
|
||||
// Chain registry
|
||||
const CHAINS: Record<ChainId, ChainInfo>
|
||||
class ChainRegistry { ... }
|
||||
class MemoryChainRegistry implements ChainRegistryAdapter { ... }
|
||||
function getAllChainIds(): ChainId[]
|
||||
function getChain(id: ChainId): ChainInfo | undefined
|
||||
function getChainByEvmId(evmId: number): ChainInfo | undefined
|
||||
function getEvmChains(): ChainInfo[]
|
||||
function getExplorerAddressUrl(chainId: ChainId, address: string): string
|
||||
function getExplorerTxUrl(chainId: ChainId, tx: string): string
|
||||
|
||||
// Gas estimation (EIP-1559 + EIP-4844)
|
||||
function calculateEffectiveGasPrice(baseFee: bigint, maxFee: bigint, maxPriorityFee: bigint): bigint
|
||||
function calculateMaxFeePerGas(baseFee: bigint, multiplier?: number): bigint
|
||||
function estimateGas(params: GasEstimateParams): GasEstimate
|
||||
function estimateTxCostEth(params: GasEstimateParams): string
|
||||
function formatGwei(wei: bigint): string
|
||||
function calculateBlobGasPrice(excessBlobGas: bigint): bigint // EIP-4844 genesis addition
|
||||
function estimateBlobTxCost(params: BlobGasEstimateParams): GasEstimate // genesis addition
|
||||
|
||||
// Tokens
|
||||
const COMMON_TOKENS: TokenInfo[]
|
||||
class TokenRegistry { ... }
|
||||
function formatTokenBalance(amount: bigint, decimals: number, options?: FormatTokenOptions): string
|
||||
|
||||
// Transaction decode (ABI-aware — genesis improvement)
|
||||
function decodeCalldata(calldata: string, abi: FunctionSignature): DecodedCall
|
||||
function decodeTxHash(hash: string): TxMetadata
|
||||
function extractSelector(calldata: string): string
|
||||
function lookupSelector(selector: string, resolver?: SelectorResolver): Promise<FunctionSignature | undefined>
|
||||
const KNOWN_SELECTORS: Record<string, FunctionSignature>
|
||||
|
||||
// Units
|
||||
function etherToWei(ether: number | string): bigint
|
||||
function weiToEther(wei: bigint): string
|
||||
function gweiToEther(gwei: bigint): string
|
||||
function bitcoinToSatoshi(btc: number | string): bigint
|
||||
function satoshiToBitcoin(satoshi: bigint): string
|
||||
function lamportToSol(lamport: bigint): string
|
||||
function convertUnits(value: bigint, fromDecimals: number, toDecimals: number): bigint
|
||||
function getUnits(chainId: ChainId): CryptoUnit
|
||||
|
||||
// Testnet chains (genesis addition — sub-path export)
|
||||
// import { TESTNET_CHAINS } from '@suaveplan/cryptocurrency/chains/testnet-data'
|
||||
|
||||
// Error hierarchy (mirrors @suaveplan/error shape, no import)
|
||||
class CryptoError extends Error { code: CryptoErrorCode }
|
||||
class UnknownChainError extends CryptoError { }
|
||||
class InvalidAddressError extends CryptoError { }
|
||||
class InvalidTxHashError extends CryptoError { }
|
||||
class UnsupportedUnitError extends CryptoError { }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
finance/crypto/cryptocurrency/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
errors/
|
||||
crypto-error/
|
||||
crypto-error.ts / .md / .test.ts / index.ts
|
||||
address/
|
||||
validator/
|
||||
validator.ts / validator.md / validator.test.ts / index.ts
|
||||
chains/
|
||||
chain-data/
|
||||
chain-data.ts / chain-data.md / chain-data.test.ts / index.ts
|
||||
testnet-data/
|
||||
testnet-data.ts / testnet-data.md / testnet-data.test.ts / index.ts
|
||||
chain-registry/
|
||||
chain-registry.ts / .md / .test.ts / index.ts
|
||||
registry/
|
||||
registry.ts / registry.md / registry.test.ts / index.ts
|
||||
gas/
|
||||
gas-estimator/
|
||||
gas-estimator.ts / .md / .test.ts / index.ts
|
||||
blob-gas-estimator/
|
||||
blob-gas-estimator.ts / .md / .test.ts / index.ts
|
||||
tokens/
|
||||
token-data/ ...
|
||||
token-registry/ ...
|
||||
transaction/
|
||||
tx-decoder/ ...
|
||||
selector-resolver/ ...
|
||||
units/
|
||||
converter/ ...
|
||||
types/
|
||||
crypto.types.ts
|
||||
crypto.types.md
|
||||
tests/
|
||||
property.test.ts
|
||||
src/__benchmarks__/
|
||||
hot-paths.bench.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Impact
|
||||
|
||||
Downstream: `@suaveplan/currency-providers` (on-chain oracle adapters), `@suaveplan/money` (token unit interop).
|
||||
|
||||
**Prerequisites on Verdaccio**: `zod`, `tslib` (only external catalog deps).
|
||||
96
openspec/changes/enterprise-cryptocurrency/tasks.md
Normal file
96
openspec/changes/enterprise-cryptocurrency/tasks.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Tasks: enterprise-cryptocurrency
|
||||
|
||||
**Change ID**: enterprise-cryptocurrency
|
||||
**Package**: `@suaveplan/cryptocurrency`
|
||||
**Target path**: `finance/crypto/cryptocurrency`
|
||||
**Prerequisites on Verdaccio**: `zod`, `tslib`
|
||||
|
||||
---
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [ ] Create full directory tree per `proposal.md`.
|
||||
- [ ] `package.json` at `0.1.0`, `"sideEffects": false`, exports `"."` + per-feature subpaths + `"./chains/testnet-data"`.
|
||||
- [ ] Deps: `zod catalog:forms`, `tslib catalog:build`. No workspace runtime deps.
|
||||
- [ ] DevDeps: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `fast-check catalog:testing`, `mitata catalog:testing`, `@suaveplan/testing workspace:^`.
|
||||
- [ ] `tsconfig.json` extending `library.json`; `vitest.config.ts` with `createTestConfig()`.
|
||||
- [ ] Scripts: `lint`, `typecheck`, `test`, `test:coverage`, `build`, `bench`, `docs:check`.
|
||||
|
||||
## 2. Error hierarchy
|
||||
|
||||
- [ ] Implement `CryptoError`, `UnknownChainError`, `InvalidAddressError`, `InvalidTxHashError`, `UnsupportedUnitError` in `src/errors/crypto-error/crypto-error.ts`.
|
||||
- [ ] Mirror `@suaveplan/error` shape (`.code`, `.message`, `.cause`) without importing it.
|
||||
- [ ] Write `crypto-error.test.ts`: instanceof chain, code discrimination.
|
||||
|
||||
## 3. Address validator
|
||||
|
||||
- [ ] Implement `validateAddress` and `validateAddressForChain` in `src/address/validator/validator.ts`:
|
||||
- EVM: EIP-55 checksum; hex + 0x prefix + 40 chars.
|
||||
- Bitcoin: Base58Check (P2PKH/P2SH) + Bech32 (P2WPKH/P2WSH) + Bech32m (P2TR).
|
||||
- Solana: Base58, 32–44 chars.
|
||||
- Cardano: Bech32 `addr1` prefix.
|
||||
- Others: per-chain regex patterns from `CHAINS` data.
|
||||
- Auto-detect chain when not specified.
|
||||
- [ ] Write `validator.test.ts` (≤500 lines): valid + invalid per chain; EIP-55 case sensitivity; auto-detect.
|
||||
|
||||
## 4. Chain data + testnet data
|
||||
|
||||
- [ ] Implement `src/chains/chain-data/chain-data.ts` with 15+ mainnet chain entries.
|
||||
- [ ] Implement `src/chains/testnet-data/testnet-data.ts` with Sepolia, Holesky, Base Sepolia, Arbitrum Sepolia, OP Sepolia.
|
||||
- [ ] `src/chains/chain-registry/chain-registry.ts`: `MemoryChainRegistry` with add/remove/list.
|
||||
- [ ] `src/chains/registry/registry.ts`: pure getter helpers using global registry.
|
||||
- [ ] Write tests for all chain registry operations.
|
||||
|
||||
## 5. Gas estimators
|
||||
|
||||
- [ ] Implement EIP-1559 helpers in `src/gas/gas-estimator/gas-estimator.ts`.
|
||||
- [ ] Implement EIP-4844 blob-gas helpers in `src/gas/blob-gas-estimator/blob-gas-estimator.ts`:
|
||||
- `calculateBlobGasPrice(excessBlobGas)`: `MIN_BLOB_GWEI_PER_GAS * e^(excessBlobGas / BLOB_GAS_UPDATE_FRACTION)`.
|
||||
- `estimateBlobTxCost(params)`: include blob-fee component.
|
||||
- [ ] Write tests against known EIP-4844 spec test vectors.
|
||||
|
||||
## 6. Tokens
|
||||
|
||||
- [ ] `src/tokens/token-data/token-data.ts`: common ERC-20 + native tokens.
|
||||
- [ ] `src/tokens/token-registry/token-registry.ts`: `TokenRegistry` with get/register/list.
|
||||
- [ ] `formatTokenBalance` with locale option.
|
||||
- [ ] Write tests.
|
||||
|
||||
## 7. Transaction decode
|
||||
|
||||
- [ ] Implement ABI-aware `decodeCalldata` in `src/transaction/tx-decoder/tx-decoder.ts`:
|
||||
- Accept `FunctionSignature` with typed parameters; decode fixed types (uint, int, bool, address, bytes, bytesN) and dynamic types (string, bytes, array).
|
||||
- Keep `decodeTxHash` / `extractSelector` / `lookupSelector` / `KNOWN_SELECTORS` from main.
|
||||
- [ ] `src/transaction/selector-resolver/selector-resolver.ts`: `SelectorResolver` interface with injectable async lookup.
|
||||
- [ ] Write `tx-decoder.test.ts`: each ABI type decoded correctly against known calldata fixtures.
|
||||
|
||||
## 8. Unit converter
|
||||
|
||||
- [ ] Implement all unit helpers in `src/units/converter/converter.ts`.
|
||||
- [ ] Ensure no float-precision loss for large values; use BigInt throughout for integer types.
|
||||
- [ ] Write `converter.test.ts`.
|
||||
|
||||
## 9. Property-based + benchmarks
|
||||
|
||||
- [ ] `tests/property.test.ts` (fast-check, `{ timeout: 30000 }`):
|
||||
- `etherToWei` / `weiToEther` round-trip for arbitrary ether values (string decimal).
|
||||
- `bitcoinToSatoshi` / `satoshiToBitcoin` round-trip.
|
||||
- `validateAddress(addr).chain` is consistent with `validateAddressForChain(addr, detected)`.
|
||||
- EIP-55 checksum is idempotent.
|
||||
- [ ] `src/__benchmarks__/hot-paths.bench.ts`: EIP-55 checksum throughput, bech32 decode, address validation throughput.
|
||||
|
||||
## 10. Docs + verify + publish
|
||||
|
||||
- [ ] All `.md` files per layout; root `README.md` with chain table, quick-start validation + unit conversion + ABI decode, API table.
|
||||
- [ ] `bunx turbo lint --filter=@suaveplan/cryptocurrency` — zero errors.
|
||||
- [ ] `bunx turbo typecheck --filter=@suaveplan/cryptocurrency` — zero errors.
|
||||
- [ ] `bunx turbo test --filter=@suaveplan/cryptocurrency` — all pass.
|
||||
- [ ] `bunx turbo test:coverage --filter=@suaveplan/cryptocurrency` — 100%.
|
||||
- [ ] `bun scripts/publish.ts --package @suaveplan/cryptocurrency`.
|
||||
- [ ] Update `CLAUDE.md`; archive to `openspec/archive/2026-04-24-enterprise-cryptocurrency/`.
|
||||
Reference in New Issue
Block a user