feat(cardano): spec-exact CIP-30 adapter, conformance suite, and CI#9
Open
jinglescode wants to merge 6 commits into
Open
feat(cardano): spec-exact CIP-30 adapter, conformance suite, and CI#9jinglescode wants to merge 6 commits into
jinglescode wants to merge 6 commits into
Conversation
- Add .github/workflows/test.yml running build + jest on every PR and push to main (repo previously had publish-only CI) - Move blakejs to dependencies (used by src/cardano/signer/cip-8.ts, was only available transitively) - Regenerate package-lock.json: it had drifted from package.json (jest-environment-jsdom tree missing), breaking npm ci - Add a CBOR round-trip spike test pinning @cardano-sdk serialization against known-good CIP-30 vectors Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
getPublicKeyFromCoseKey, getPublicKey, getAddress and hashPayload compared CBOR nodes via serialized-JSON equality, which never matches nodes produced by Cbor.parse (they carry subCborRef/addInfos metadata that constructed nodes lack). Parsing a COSE key produced by our own getCoseKeyFromPublicKey threw 'Public key not found'. Replace all six comparison sites with semantic helpers (cborKeyIsText, cborKeyIsInt, cborIsSimpleTrue) that compare decoded values via instanceof + value equality. Caught by the new CIP-30 conformance suite; behavior otherwise unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New src/cardano/cip30/ module wrapping any ICardanoWallet as a
spec-exact CIP-30 surface, without changing existing wallet APIs:
- errors.ts: full CIP-30 error taxonomy — APIError(-1..-4),
PaginateError{maxSize}, TxSignError(1,2), DataSignError(1,2,3),
TxSendError(1,2) — with {code, info} wire shapes and type guards
- types.ts: ICip30Api, ICip30InitialApi, Paginate, Cip30Extension
- cip30-api.ts:
- createCip30Api(wallet): getUtxos(amount?, paginate?) with
amount-coverage selection (coin + every multiasset) and null
semantics, paginated getUsedAddresses, getCollateral({amount})
over pure-ADA utxos with null semantics, and every wallet error
mapped to the spec taxonomy
- createCip30Wallet({wallet, name, ...}): simulated initial API —
apiVersion "1", isEnabled(), enable(extensions?) with
requested-intersect-supported negotiation and a configurable
approval hook for the Refused(-3) path
Surgical wallet improvements behind the adapter:
- signData now accepts bech32 or hex addresses (CIP-30 Address type)
- getUsedAddresses derives used addresses from fetcher utxos when a
fetcher is attached, falling back to the single derived address
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- One describe per CIP-30 spec endpoint (initial API included):
CBOR round-trips, pagination boundaries + PaginateError{maxSize},
per-failure-mode error codes, null semantics, extension negotiation
- Real cryptography, not shape checks: Ed25519 witness signatures
verified against the tx body hash; COSE_Sign1 verified via
CoseSign1.verifySignature; COSE_Key structure (kty/alg/crv/x)
asserted per CIP-8; hex and bech32 signData parity
- Provider attachment (A3): identical results from OfflineFetcher and
a hand-rolled array-backed IFetcher, plus ISubmitter swap
- Shared fixtures extracted to test/cardano/cip30/fixtures.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Conformance matrix (initial + full API) with documented deviations: stateless single-address semantics, auto-approve enable(), spec- deprecated getCollateral, unreachable decline/refusal codes - New 'Simulated CIP-30 API (headless)' section with usage examples and provider-attachment (A3) notes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README documented an 'InMemoryBip32' export that doesn't exist — the class was renamed CardanoInMemoryBip32 when the Bitcoin SDK landed its BitcoinInMemoryBip32 counterpart. Import example would not compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the CIP-30 conformance gap for the Cardano headless wallet (Catalyst milestone: Develop and deploy a CIP-30 headless wallet on Cardano). The existing
CardanoHeadlessWalletalready covered derivation, addresses, signing, and provider attachment — this PR adds the last mile: a spec-exact CIP-30 adapter layer, the full error taxonomy, a simulated initial API, and a conformance test suite, with zero breaking changes to existing APIs.New:
src/cardano/cip30/createCip30Wallet({ wallet, name, ... })→ simulatedwindow.cardano.{name}-shaped initial API:apiVersion "1",isEnabled(),enable(extensions?)with requested∩supported negotiation and a configurable approval hook (Refused(-3))createCip30Api(wallet)→ spec-exact endpoints:getUtxos(amount?, paginate?)with amount-coverage selection (coin + every multiasset) andnullsemantics, paginatedgetUsedAddresses,getCollateral({amount})withnullsemantics, and every failure mapped to the CIP-30 error taxonomy (APIError−1…−4,PaginateError{maxSize},TxSignError,DataSignError,TxSendError)Surgical wallet improvements (non-breaking)
signDataaccepts bech32 or hex addresses (CIP-30Addresstype)getUsedAddressesderives used addresses from fetcher UTxOs when attached (resolves a TODO), with single-address fallbackBug fixes
subCborRefmetadata) — our own COSE keys failed to re-parse. Replaced with semantic comparisons. Found by the new conformance suite.blakejsmoved todependencies(was transitive-only)package-lock.jsonregenerated — it had drifted and brokenpm ciTests & CI
test/cardano/cip30/): one describe per spec endpoint incl. initial API; CBOR round-trips; real Ed25519 signature verification against tx body hashes; COSE_Sign1/COSE_Key structural + cryptographic checks; pagination boundaries; per-failure-mode error codes; extension negotiation; provider-swap proof (two independentIFetcherimpls +ISubmitterswap).github/workflows/test.yml: build + jest on every PR (repo previously had publish-only CI)Docs
enable(), spec-deprecatedgetCollateral, unreachable decline codes), plus a "Simulated CIP-30 API (headless)" usage sectionTest plan
pnpm test— 305/305 across 24 suitespnpm build— ESM/CJS/DTS clean, no export collisionsnpm ci --dry-run— lockfile in sync (new CI workflow will run green)🤖 Generated with Claude Code