Skip to content

feat: wallet-owned types + ledger-native builders (drop-cardano-api foundation)#5270

Merged
paolino merged 15 commits into
masterfrom
feat/drop-cardano-api-foundation
Apr 25, 2026
Merged

feat: wallet-owned types + ledger-native builders (drop-cardano-api foundation)#5270
paolino merged 15 commits into
masterfrom
feat/drop-cardano-api-foundation

Conversation

@paolino
Copy link
Copy Markdown
Collaborator

@paolino paolino commented Apr 23, 2026

Summary

Foundation layer of the cardano-api removal (phase A of the split of #5236). Adds wallet-owned types and ledger-native builders without switching any existing call site — the new code paths exist alongside the cardano-api paths and are only exercised by the new test duplicate. Low risk, reviewable in one sitting.

Companion PR (call-site migration) will be opened against this branch once merged. The review-response commit also tightens one existing address-network-tag helper to delegate the mainnet/testnet bit to ledger, with unchanged semantics.

What lands here (15 commits, all bisect-safe)

# SHA Purpose
1–4 docs spec / plan / tasks artifacts (speckit) under specs/001-drop-cardano-api/
5 7307a0fe Add the initial SNetworkId n -> Ledger.Network conversion, now exposed as sNetworkIdToLedger after review feedback
6 e1a1acaf Rewrite SealedTx internals to EraValue Read.Tx (previous cardano-api reconstruction helpers retained as temporary bridges)
7 fd5fe22f Replace Cardano.Api.NetworkId with wallet-owned NetworkId in 4 files
8 393eaa28 Add wallet-owned TxMetadata module (lib/primitive/.../Types/Tx/TxMetadata.hs) with JSON schema conversion ported from cardano-api
9 92a7699d Add Transaction.Build.mkLedgerTx — builds a ledger Tx era from its components using mkBasicTxBody + lenses
10 726656ab Add Transaction.Ledger — ledger-native tx construction (mkTransactionLedger, constructUnsignedTxLedger, buildLedgerTx, TxPayload, mkRewardAccount)
11 68e441c5 Complete Transaction.Ledger exports
12 13e76caa Duplicate TransactionSpec.hsTransactionLedgerSpec.hs as baseline for exercising the new builder in tests
13 984e8177 Add ledger-native cert helpers: certificateFromDelegationActionLedger, certificateFromVotingActionLedger, toLedgerStakeCred, toLedgerCoin
14 d8e74e41 Wire ledger-native witness construction (mkShelleyWitnessLedger, mkByronWitnessLedger) into TransactionLedgerSpec
15 2e210821 Address reviewer feedback: use Ledger.networkToWord8 for address network tags; document wallet NetworkId vs ledger Network; try Dijkstra in ledger-native SealedTx decode; document why TxMetadata keeps the wallet/cardano-api-compatible surface while converting to ledger metadata internally

Ledger 1.19/1.20 adaptations applied

  • Ledger.Tx eraWrite.Tx era (balance-tx's type Tx era = Core.Tx Core.TopTx era alias for the 1.19 TxLevel kind lift)
  • Ledger.TxBody eraWrite.TxBody era
  • Ledger.RewardAccount pattern synonym → Ledger.AccountAddress (Ledger.AccountId …) (1.20 rename)
  • KeyRole (Witness) import → Keys.Witness qualified (1.20 reorg — Witness is a standalone type, not a KeyRole data constructor)
  • hashAnnotated bodyhashAnnotated @_ @EraIndependentTxBody body (1.20 HashAnnotated x i | x -> i functional dep can't resolve through Write.TxBody era type synonym)

What is NOT in this PR

  • No transaction construction, balanceTx, signing, cert construction, or TxMetadata consumer migration lands here. Those paths still go through cardano-api; the new ledger-native paths sit alongside for now. The only existing-path tweak is the equivalent postAnyAddress network tag helper change from manual 1/0 matching to Ledger.networkToWord8.
  • No bridges are added or removed: Cardano.Api.Extra shim, sealWriteTx, and certToLedger/unCert helpers are untouched.
  • No cabal changes to drop cardano-api (the dependency is still pulled in by the unchanged call sites).

Per-commit CI gate

The original 14 commits were gated through fourmolu --mode check + hlint lib + nix build .#cardano-wallet and PASS individually.

The review-response commit 2e210821 was checked with direct Fourmolu on the touched Haskell files and a library-only Cabal build of cardano-wallet-primitive, cardano-wallet-api, and cardano-wallet inside nix develop. The stricter just build path currently stops in pre-existing cardano-wallet-read warnings promoted to errors before reaching the changed modules.

Test plan

Review response:

  • nix develop --command fourmolu --mode check on touched Haskell files
  • nix develop --command cabal build lib:cardano-wallet-primitive lib:cardano-wallet-api lib:cardano-wallet -O0 -v0
  • nix develop --command just build 'cardano-wallet-primitive cardano-wallet-api cardano-wallet' with -Werror is blocked by pre-existing cardano-wallet-read warnings

Original branch:

  • nix build .#cardano-wallet succeeds at every commit (bisect-safe)
  • fourmolu --mode check clean
  • hlint lib no hints
  • Full build-gate (Linux) CI on the previous tip before review-response commit
  • Full CI on the new review-response tip
  • unit-cardano-wallet-unit test suite (deferred to CI)

paolino added 5 commits April 23, 2026 14:28
Spec covers the full removal across 6 phases: ledger re-exports,
TxMetadata, NetworkId, certificates, SealedTx, and tx submission.
Rewrite spec starting from current master state where
cardano-balance-transaction and cardano-coin-selection are already
extracted. Focus on the remaining 71 imports across 45 files.
Added context section, test generator migration story (US5),
Byron support requirement (FR-013).
The pinned cardano-balance-transaction (98e7f41) still depends on
cardano-api. Add US1 as a gate: pin the cardano-api-free version
first. Document the breaking API changes (CardanoApiEra removal,
toCardanoApiTx/fromCardanoApiTx removal) that cascade into wallet
transaction, delegation, and voting code.
Additive-then-remove strategy: add ledger-native paths alongside
cardano-api, migrate consumers one module at a time (each commit
compiles), then atomically remove cardano-api in a single commit.

51 tasks across 5 phases (A-E). Covers pin bump, new type paths,
consumer migration, atomic removal, and verification.
@paolino paolino added the Refactoring No functional changes label Apr 23, 2026
@paolino paolino self-assigned this Apr 23, 2026
@paolino paolino force-pushed the feat/drop-cardano-api-foundation branch from ad2ce97 to 21ded9a Compare April 23, 2026 14:26
paolino added 8 commits April 23, 2026 16:00
Replace InAnyCardanoEra Cardano.Tx with EraValue Read.Tx as the
decoded transaction field. The CBOR bytes remain the canonical
representation.

Temporary cardano-api reconstruction functions kept for callers
that haven't migrated yet (cardanoTxIdeallyNoLaterThan,
getSealedTxBody, getSealedTxWitnesses). These deserialize from
bytes on demand.

fromSealedTx in Sealed.hs becomes trivial: just reads the field.
Alternative to Transaction.hs that builds transactions using
mkLedgerTx (ledger lenses) instead of Cardano.Api.TxBodyContent.

Implements mkTransactionLedger and constructUnsignedTxLedger with
key-witnessed withdrawals and certificates. Minting, script
witnesses, and reference scripts are TODO.

Remaining cardano-api dependencies (with TODOs):
- signTransaction takes Cardano.NetworkId
- sealWriteTx roundtrips through cardano-api
- certificateFromDelegationAction returns Cardano.Certificate
@paolino paolino force-pushed the feat/drop-cardano-api-foundation branch from 21ded9a to 161391a Compare April 23, 2026 15:32
Replace mkUnsignedTx + mkByronWitness/mkShelleyWitness with
mkLedgerTx + mkByronWitnessLedger/mkShelleyWitnessLedger in
the duplicated test module.

All 8 golden hex string tests pass — ledger-native construction
produces byte-identical CBOR to the cardano-api construction.
@paolino paolino force-pushed the feat/drop-cardano-api-foundation branch from 161391a to 8d511cc Compare April 23, 2026 17:23
@@ -802,8 +799,8 @@ postAnyAddress net addrData = do
fromXPub = fromJust . CA.xpubFromBytes
fromPub = fromJust . CA.pubFromBytes
netTag = case net of
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would bet we have this 1/0 conversion in ledger

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I switched this to use Ledger.networkToWord8 via networkIdToLedger, so we do not keep the manual 1/0 mapping here.

, deserialise MaryEra Cardano.AsMaryEra
, deserialise AllegraEra Cardano.AsAllegraEra
, deserialise ShelleyEra Cardano.AsShelleyEra
[ deserialise
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no Dijkstra here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The ledger-native byte reader now tries Dijkstra first. The remaining cardanoTxFromBytes branch is still limited by the temporary CardanoApiEra bridge, so I left a note there.

-- TxMetadata types
--

newtype TxMetadata
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed this should converge toward ledger metadata. For this PR I kept the wallet module because it preserves the current cardano-api-shaped constructors and JSON surface, while conversions go through ledger metadata internally.

networkMagic =
Cardano.NetworkMagic . fromIntegral $ fromSNat snat

-- | Convert a 'SNetworkId' to a ledger 'Network' value.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not to use only Ledger.Netwok in the codebase?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be nicer long term. I kept wallet NetworkId for now because it still carries the testnet magic, while Ledger.Network only has Mainnet/Testnet. I added helpers to keep that boundary explicit.

Copy link
Copy Markdown
Collaborator

@paweljakubas paweljakubas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice first step. Probably a lot could be simplified and replaced in tx building also

@paolino paolino merged commit b1e16db into master Apr 25, 2026
110 of 113 checks passed
@paolino paolino deleted the feat/drop-cardano-api-foundation branch April 25, 2026 09:39
paolino added a commit that referenced this pull request May 8, 2026
…5236)

## Summary

Remaining call-site migration after #5270 merged. This branch is rebased
directly onto current `master` and contains the commits that switch
existing wallet call sites onto the ledger-native surfaces introduced by
#5270.

Base branch: `master`.
Follow-ups: #5271, then #5272.

## What is in this PR

| # | SHA | Purpose |
|---|---|---|
| 1 | `84bd668` | Use `constructUnsignedTxLedger` in `balanceTx` paths |
| 2 | `4c8b61b` | Replace `AnyCardanoEra` with `Read.EraValue` in
NetworkLayer |
| 3 | `55097e6` | Build delegation and voting certificates in
`balanceTx` paths |
| 4 | `f5b8ed2` | Replace `StakeAddress` with wallet-owned
`RewardAccount` |
| 5 | `26bad3e` | Migrate `TxMetadata` end-to-end to wallet-owned types
|
| 6 | `d543a6b` | Adapt singleton network conversions to #5270's
`sNetworkIdToLedger` helper |

The foundation work previously described here landed in #5270. In
particular, the wallet-owned `NetworkId`, `TxMetadata`, `SealedTx`,
ledger-native transaction builders, certificate helpers, and witness
helpers are now part of `master`.

## What this PR does not do

- It does not delete the remaining cardano-api bridges.
- It does not remove `cardano-api` from cabal files.
- It does not delete `lib/cardano-api-extra/`.
- It does not finish the `SealedTx` decommission; that remains split
across #5271 and #5272.

## Test plan

- [x] Rebased cleanly onto `origin/master` after #5270 merged
(2026-04-25).
- [x] `nix develop --command cabal build cardano-wallet
cardano-wallet-api cardano-wallet-unit:unit --enable-benchmarks
--enable-tests --minimize-conflict-set -O0 -v0`
- [ ] CI green on the rebased tip.

Note: `just build 'cardano-wallet cardano-wallet-unit:unit'` reaches
unrelated `cardano-wallet-read` deprecation/unused warnings promoted by
`-Werror` on this branch; the same target without `-Werror` passes.
paolino added a commit that referenced this pull request May 8, 2026
…5271)

## Summary

Progress toward removing `Cardano.Api*` from
`Cardano.Wallet.Primitive.Types.Tx.SealedTx`. Moves the first set of
production and test call sites off the deprecated surface. Follow-up
#5272 now finishes the remaining SealedTx cleanup.

Base branch: `001-drop-cardano-api` (#5236).
Rebased after #5270 merged and #5236 was rebased onto `master`.

## What this PR does

Eight bisect-safe commits:

| # | SHA | Effect |
|---|---|---|
| 1 | `04d65b2` | `docs`: plan document for the decommission series |
| 2 | `b4b87af` | Test helper `compareOnCBOR` -> raw `serialisedTx`
bytes, avoiding a cardano-api roundtrip |
| 3 | `282807d` | Submission path `_postSealedTx` -> `EraValue Read.Tx`
+ `consensusGenTxFromTxRecent`; deletes `unsealShelleyTx` /
`UnsealException` |
| 4 | `a6d0c9d` | `parsePartialTx` -> `deserializeTx :: Either
DecoderError (Read.Tx era)` per era |
| 5 | `f1ba908` | Delete `cardanoTxInExactEra` after the callers are
gone |
| 6 | `55f2bd9` | Add `sealedTxFromLedgerTx :: Read.IsEra era => Read.Tx
era -> SealedTx`; migrate `sealWriteTx` off
`toCardanoApiTx`/`sealedTxFromCardano'` |
| 7 | `e6feabc` | Add `sealedTxWitnessCount :: SealedTx -> Int` via
ledger witnesses; migrate Server.hs away from `getSealedTxWitnesses` |
| 8 | `c2ca434` | Integration test `getMetadataFromTx` ->
`Meta.getMetadata . Meta.getEraMetadata` on `EraValue Read.Tx` |

New ledger-native surface added to `SealedTx`:

- `sealedTxFromLedgerTx :: Read.IsEra era => Read.Tx era -> SealedTx`
- `sealedTxWitnessCount :: SealedTx -> Int`

Deleted:

- `cardanoTxInExactEra`
- `Cardano.Wallet.Primitive.Ledger.Shelley.unsealShelleyTx` /
`UnsealException`

## What this PR does not do

The deprecated SealedTx cardano-api exports are removed in #5272, not
here.

Out of scope:

- Removing `cardano-api` from `cardano-wallet-primitive.cabal`.
- Removing `lib/cardano-api-extra/`.

## Test plan

- [x] Rebased cleanly onto updated #5236 after #5270 merged
(2026-04-25).
- [ ] CI green on the rebased tip.
paolino added a commit that referenced this pull request May 8, 2026
…5272)

## Summary

Follow-up to #5271. This PR finishes the
`Cardano.Wallet.Primitive.Types.Tx.SealedTx` cardano-api decommission:
the primitive `SealedTx` module no longer imports `Cardano.Api`, and the
deprecated cardano-api-facing SealedTx exports are removed.

Base branch: `feat/drop-cardano-api-sealedtx` (#5271).
Rebased after #5270 merged, #5236 was rebased onto `master`, and #5271
was rebased onto #5236.

## What changed

| # | SHA | Purpose |
|---|---|---|
| 1 | `734f624` | Plan for finishing the SealedTx cardano-api
decommission |
| 2 | `80a3de4` | Port metadata extraction in `Shared/Transactions` to
ledger-native |
| 3 | `8faf76e` | Drop the dead cardano-api body in `addRequiredSigners`
|
| 4 | `a929e8e` | Migrate `buildAndSignTransactionPure` to ledger-native
seal |
| 5 | `e2e72af` | Drop the local `sealWriteTx` cardano-api bridge in
Server.hs |
| 6 | `0b619dd` | Remove the `SealedTx` cardano-api surface and move
remaining unit-test adapters local to tests |
| 7 | `b4da418` | Keep default sealed transaction byte decoding capped
at the supported Conway era, and make signing/decoding re-read sealed
bytes with the caller's preferred latest era |

## SealedTx surface removed

Removed from `Cardano.Wallet.Primitive.Types.Tx.SealedTx` and the
umbrella `Cardano.Wallet.Primitive.Types.Tx` export list:

- `cardanoTxIdeallyNoLaterThan`
- `sealedTxFromCardano`
- `sealedTxFromCardano'`
- `sealedTxFromCardanoBody`
- `getSealedTxBody`
- `getSealedTxWitnesses`
- internal `cardanoTxFromBytes`
- internal `cardanoApiTxToReadTx`

`sealedTxFromBytes'` now takes `Read.EraValue Read.Era` instead of
`AnyCardanoEra`, which lets the primitive module avoid `Cardano.Api`
entirely.

## CI fix after rerun

The first rerun of #5272 exposed a real Conway integration failure:
sealed Conway transaction bytes could be decoded as Dijkstra by the new
ledger-native default path, after which downstream code hit the
still-incomplete Dijkstra transaction support.

This PR now keeps the public `sealedTxFromBytes` default capped at
Conway, and the transaction layer re-decodes sealed bytes with the
preferred latest era supplied by the caller before signing or decoding.
That preserves the old "no later than current era" behavior without
restoring the cardano-api bridge.

## Remaining out of scope

- Removing `cardano-api` from `cardano-wallet-primitive.cabal`; other
primitive modules still import it.
- Removing `lib/cardano-api-extra/`.
- Full deletion of the old `Shelley/Transaction.hs` module.

## Test plan

- [x] `nix develop --command fourmolu --mode inplace ...` on touched
Haskell files
- [x] `git diff --check`
- [x] `nix develop --command just build 'cardano-wallet-primitive
cardano-wallet cardano-wallet-unit:unit'`
- [x] `nix develop --command cabal test cardano-wallet-unit:unit -O0 -v0
--test-options '--match="TransactionSpec"'`
- [x] `nix develop --command cabal test cardano-wallet-unit:unit -O0 -v0
--test-options '--match="SealedTx serialisation/deserialisation"'`
- [x] `nix develop --command cabal build cardano-wallet
cardano-wallet-api cardano-wallet-unit:unit --enable-tests
--enable-benchmarks --minimize-conflict-set -O0 -v0`
- [x] `rg 'Cardano\.Api|qualified Cardano.Api'
lib/primitive/lib/Cardano/Wallet/Primitive/Types/Tx/SealedTx.hs` returns
no matches
- [x] CI green on the rebased tip
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Refactoring No functional changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants