154 lines
5.7 KiB
Markdown
154 lines
5.7 KiB
Markdown
# 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).
|