Skip to content

feat(ows-signer): add Cardano chain support (CIP-1852, Ed25519-BIP32, bech32) - #186

Open
adacapo21 wants to merge 9 commits into
open-wallet-standard:mainfrom
adacapo21:feat/cardano-chain-support
Open

feat(ows-signer): add Cardano chain support (CIP-1852, Ed25519-BIP32, bech32)#186
adacapo21 wants to merge 9 commits into
open-wallet-standard:mainfrom
adacapo21:feat/cardano-chain-support

Conversation

@adacapo21

Copy link
Copy Markdown

Summary

  • Adds ChainType::Cardano to ows-core with CAIP-2 identifiers cardano:mainnet, cardano:preprod, cardano:preview
  • Implements CardanoSigner using CIP-1852 HD derivation (m/1852'/1815'/account'/0/index) via ed25519-bip32
  • Enterprise address encoding: bech32 with addr/addr_test HRP and blake2b-224 payment key hash
  • Transaction signing: blake2b-256 of CBOR-encoded tx body, signed with Ed25519-BIP32; witness set injected via ciborium
  • Adds Curve::Ed25519Bip32 variant (64-byte extended keys) and updates KeyPair in ows-lib
  • Coin type 1815, derivation path m/1852'/1815'/0'/0/0

Known limitations

Root key derivation uses the BIP-39 seed (PBKDF2 output) via HMAC-SHA512, not Icarus-style entropy-based derivation. Derived addresses are internally consistent and deterministic but will not match addresses generated by Lucid Evolution / Eternl (which use the Byron/Icarus root key from raw entropy).

Test plan

  • cargo test -p ows-signer — all Cardano unit tests pass (address format, signing roundtrip, CBOR tx sign, blake2b vectors, staking path)
  • cargo test -p ows-coretest_parse_chain_cardano, test_serde_all_variants, test_all_chain_types pass
  • cargo test -p ows-lib — all import/sign tests pass including Cardano
  • cargo test --workspace — 540 tests pass, 0 failures
  • cargo clippy --workspace -- -D warnings — no warnings
  • cargo fmt --all --check — no formatting issues

@adacapo21
adacapo21 requested a review from njdawn as a code owner April 4, 2026 11:11
@vercel

vercel Bot commented Apr 4, 2026

Copy link
Copy Markdown

@adacapo21 is attempting to deploy a commit to the MoonPay Team on Vercel.

A member of the Team first needs to authorize it.

@socket-security

socket-security Bot commented Apr 4, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​ciborium@​0.2.210010093100100
Addedcargo/​ed25519-bip32@​0.4.110010093100100

View full report

… bech32)

- Add Cardano variant to ChainType enum in ows-core
- Implement CardanoSigner with CIP-1852 HD derivation (m/1852'/1815'/0'/0/0)
- Enterprise address encoding: bech32 with blake2b-224 payment key hash
- Transaction signing: blake2b-256(tx_body) signed with ed25519-bip32
- CBOR witness set injection via ciborium
- Support cardano:mainnet, cardano:preprod, cardano:preview CAIP-2 identifiers
- Tests: address derivation from known vectors, message signing, tx signing
- Implement broadcast_cardano() using Koios submittx endpoint
  (POST /submittx, Content-Type: application/cbor, no API key required)
- Add default RPC endpoints for cardano:mainnet, cardano:preprod, cardano:preview
- Wire Cardano into the broadcast() dispatcher
- ows sign send-tx --chain cardano now works end-to-end
@adacapo21
adacapo21 force-pushed the feat/cardano-chain-support branch from 0cf1b8b to 87b9446 Compare July 27, 2026 07:58
Comment thread ows/crates/ows-lib/src/ops.rs Outdated
…_bip32 key

Loading a wallet created before Cardano support silently substituted an
all-zero 64-byte key for the missing ed25519_bip32 field. Any Cardano
address derived from it would be identical across all legacy wallets and
publicly predictable, so funds sent there could be stolen by anyone.

The missing key is now kept empty: account derivation skips Cardano for
such wallets, and explicit Cardano operations fail with an error telling
the user to re-import the wallet. Adds a regression test.
@adacapo21
adacapo21 force-pushed the feat/cardano-chain-support branch from de97e4f to 8bfa8f5 Compare July 27, 2026 08:53
@adacapo21

Copy link
Copy Markdown
Author

@njdawn this is ready for another look: rebased onto latest main (NEAR/Nano conflicts resolved), the HIGH finding from the automated review is fixed in 8bfa8f5 with a regression test, and the full workspace test suite passes. The PR is mergeable — one workflow is awaiting maintainer approval to run. Context: Cardano support is also being requested in #222, where this PR is referenced as the base for follow-up Midnight work.

Comment thread ows/crates/ows-lib/src/ops.rs Outdated
Comment thread ows/crates/ows-signer/src/chains/cardano.rs Outdated
- reward_address and derive_child_soft now build the XPrv through the
  shared clamping helper, matching the signing path so a malformed
  extended key can neither panic nor derive an inconsistent stake
  address. All raw-byte XPrv construction goes through one helper.
- import_wallet_private_key no longer silently ignores a getrandom
  failure for the ed25519 fallback key; RNG errors now abort the import
  like every other key-generation call in the function.
Comment thread ows/crates/ows-signer/src/chains/cardano.rs
BIP32-Ed25519 child derivation is keyed by the chain code; deriving with
a hardcoded zero chain code makes sibling public keys enumerable from a
single exposed public key, breaking the HD wallet privacy guarantee. The
function had no callers — real path derivation happens in HdDeriver,
which threads the proper chain code — so the unsound public API is
removed rather than patched.
Comment thread ows/crates/ows-lib/src/ops.rs Outdated
Comment thread ows/crates/ows-signer/src/chains/cardano.rs Outdated
@TechRiderWR

Copy link
Copy Markdown

Hi,

We are the WingRiders team, and we are currently developing the official Cardano support for OWS in cooperation with IOG. Our implementation is almost complete, and we are addressing the final review comments before it is ready to be merged.

Compared with our implementation, this PR currently appears to have several gaps:

  • It derives only enterprise addresses rather than base addresses.
  • The key derivation does not match the derivation used by other Cardano software wallets, so the resulting addresses will differ.
  • It does not support signing transactions or messages with the stake key.
  • The signMessage implementation does not follow the CIP-8 standard.
  • It does not include Policy Engine support.
  • It supports only Koios and does not provide support for multiple RPC providers, such as Koios and Blockfrost.
  • Balance fetching has not been implemented.

Since our implementation is already close to completion and covers these areas, we kindly ask that you do not move forward with this PR to avoid duplicating work and introducing two competing implementations.

You can follow our work here:
https://github.com/WingRiders/ows-core/pulls?q=is%3Apr+label%3Acardano

@adacapo21

adacapo21 commented Jul 28, 2026

Copy link
Copy Markdown
Author

Hi @TechRiderWR, thanks for the technical feedback — I'm Aggelos leading engineer of Indigo Protocol. Concrete points like these are genuinely useful, and inline review comments on this PR are the right place for them.

That said, I'd ask that we respect the open contribution process here. This PR has been open since April 4th — nearly four months — publicly visible, referenced in #222 as the reference Cardano implementation, and actively maintained.

On the substance: everything you list — base addresses, stake-key signing, CIP-8 signMessage, Policy Engine integration, multiple RPC providers, balance fetching — is additive. None of it conflicts with the signer, derivation, and broadcast foundation this PR establishes; it layers on top of it. That's exactly the sequencing already discussed in #222 for Midnight: merge the base, then build on it. If your team's work covers those areas, the constructive path is to review this PR and contribute those capabilities as follow-up PRs based on it — I'd welcome that and will gladly review.

Having looked at your fork's PR series, that split is already visible in your own structure: [Cardano 1] (cryptography) and [Cardano 2] (register chain) cover roughly the same ground as this PR — the same Icarus/CIP-3 entropy-based derivation and the same CIP-1852 paths — while [Cardano 3] through [Cardano 7] (signing extensions, policy engine, balance fetching, Blockfrost, docs/bindings) are precisely the additive follow-up layers described above. Rebasing those onto this merged foundation would avoid duplicating the base and get your feature work upstream faster.

@njdawn An open four-month-old PR should be reviewed on its merits.

- sign_message now prepends a domain-separation prefix, so a signed
  message can never be byte-identical to a blake2b-256 transaction hash
  and message signatures are structurally unusable as transaction
  witnesses. Full CIP-8/COSE_Sign1 enveloping is left as a follow-up.
- import_wallet_private_key no longer fabricates a random ed25519_bip32
  key for secp256k1/ed25519 imports. Such a key has no derivation
  relationship to any user-held secret, so ADA sent to its address would
  be unrecoverable if the vault were lost. Cardano accounts are now
  simply skipped for these wallets (same path as legacy wallets).
… compatibility

The previous root key construction hashed the BIP-39 seed, which is
deterministic but derives different addresses than mainstream Cardano
wallets. Cardano software wallets (Daedalus/Shelley, Yoroi, Eternl,
Lucid) use the Icarus master key generation from CIP-3: PBKDF2-HMAC-
SHA512 keyed on the mnemonic entropy (4096 iterations, 96-byte output).

- Mnemonic gains to_entropy(), recovering the BIP-39 entropy bits
- Ed25519Bip32 derivation now runs Icarus from entropy + passphrase;
  deriving Cardano keys from a bare seed is refused, since entropy
  cannot be recovered from the seed
- Verified against the official CIP-3 Icarus test vectors (master key
  with and without passphrase)
- End-to-end vectors cross-checked against
  @emurgo/cardano-serialization-lib: payment/enterprise and reward
  addresses now match byte-for-byte on mainnet and testnet for two
  mnemonics

A mnemonic imported into OWS now yields the same Cardano addresses as
any standard wallet, and vice versa.
@adacapo21

Copy link
Copy Markdown
Author

Following up on the derivation-compatibility point raised above, since it's the only item on that list that concerns correctness rather than scope: as of 3a5210e it is settled, verifiably.

Key derivation now uses the Icarus master key generation from CIP-3 — the scheme used by Daedalus (Shelley), Yoroi, Eternl, and Lucid — with CIP-1852 paths on top. Two layers of proof are in the test suite, reproducible by anyone:

  • test_cip3_icarus_master_key_no_passphrase / _with_passphrase assert the exact master keys from the official CIP-3 test vectors.
  • test_addresses_match_cardano_serialization_lib asserts byte-for-byte equality of enterprise (mainnet + testnet) and reward addresses against @emurgo/cardano-serialization-lib for two independent mnemonics at m/1852'/1815'/0'/0/0 and m/1852'/1815'/0'/2/0.

A mnemonic imported into OWS yields the same Cardano addresses as any mainstream wallet, and vice versa.

The remaining items (base addresses, stake-key transaction signing, CIP-8 COSE enveloping for signMessage, multi-RPC, balance fetching) are additive scope on top of this foundation — as noted above, contributions layering them on this PR are welcome.

Comment thread ows/crates/ows-lib/src/ops.rs
Adds --proto/--proto-redir '-all,http,https' so a hostile rpc_url cannot
route the spawned curl to non-HTTP schemes (file://, ftp://, dict://,
...), including via redirects. Spawning curl itself matches the existing
broadcast convention (curl_post_json) used by every other chain.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants