feat: wallet-owned types + ledger-native builders (drop-cardano-api foundation)#5270
Conversation
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.
ad2ce97 to
21ded9a
Compare
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
21ded9a to
161391a
Compare
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.
161391a to
8d511cc
Compare
| @@ -802,8 +799,8 @@ postAnyAddress net addrData = do | |||
| fromXPub = fromJust . CA.xpubFromBytes | |||
| fromPub = fromJust . CA.pubFromBytes | |||
| netTag = case net of | |||
There was a problem hiding this comment.
I would bet we have this 1/0 conversion in ledger
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
I wonder if we cannot replace this module with https://github.com/IntersectMBO/cardano-ledger/blob/b5bd21e777e0a1791c12bdc5e4a522ed0e214e3a/libs/cardano-ledger-core/src/Cardano/Ledger/Metadata.hs#L12
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
why not to use only Ledger.Netwok in the codebase?
There was a problem hiding this comment.
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.
paweljakubas
left a comment
There was a problem hiding this comment.
nice first step. Probably a lot could be simplified and replaced in tx building also
…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.
…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.
…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
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)
specs/001-drop-cardano-api/7307a0feSNetworkId n -> Ledger.Networkconversion, now exposed assNetworkIdToLedgerafter review feedbacke1a1acafSealedTxinternals toEraValue Read.Tx(previous cardano-api reconstruction helpers retained as temporary bridges)fd5fe22fCardano.Api.NetworkIdwith wallet-ownedNetworkIdin 4 files393eaa28TxMetadatamodule (lib/primitive/.../Types/Tx/TxMetadata.hs) with JSON schema conversion ported from cardano-api92a7699dTransaction.Build.mkLedgerTx— builds a ledgerTx erafrom its components usingmkBasicTxBody+ lenses726656abTransaction.Ledger— ledger-native tx construction (mkTransactionLedger,constructUnsignedTxLedger,buildLedgerTx,TxPayload,mkRewardAccount)68e441c5Transaction.Ledgerexports13e76caaTransactionSpec.hs→TransactionLedgerSpec.hsas baseline for exercising the new builder in tests984e8177certificateFromDelegationActionLedger,certificateFromVotingActionLedger,toLedgerStakeCred,toLedgerCoind8e74e41mkShelleyWitnessLedger,mkByronWitnessLedger) intoTransactionLedgerSpec2e210821Ledger.networkToWord8for address network tags; document walletNetworkIdvs ledgerNetwork; try Dijkstra in ledger-nativeSealedTxdecode; document whyTxMetadatakeeps the wallet/cardano-api-compatible surface while converting to ledger metadata internallyLedger 1.19/1.20 adaptations applied
Ledger.Tx era→Write.Tx era(balance-tx'stype Tx era = Core.Tx Core.TopTx eraalias for the 1.19TxLevelkind lift)Ledger.TxBody era→Write.TxBody eraLedger.RewardAccountpattern synonym →Ledger.AccountAddress (Ledger.AccountId …)(1.20 rename)KeyRole (Witness)import →Keys.Witnessqualified (1.20 reorg —Witnessis a standalone type, not aKeyRoledata constructor)hashAnnotated body→hashAnnotated @_ @EraIndependentTxBody body(1.20HashAnnotated x i | x -> ifunctional dep can't resolve throughWrite.TxBody eratype synonym)What is NOT in this PR
balanceTx, signing, cert construction, orTxMetadataconsumer 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 equivalentpostAnyAddressnetwork tag helper change from manual 1/0 matching toLedger.networkToWord8.Cardano.Api.Extrashim,sealWriteTx, andcertToLedger/unCerthelpers are untouched.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-walletand PASS individually.The review-response commit
2e210821was checked with direct Fourmolu on the touched Haskell files and a library-only Cabal build ofcardano-wallet-primitive,cardano-wallet-api, andcardano-walletinsidenix develop. The stricterjust buildpath currently stops in pre-existingcardano-wallet-readwarnings promoted to errors before reaching the changed modules.Test plan
Review response:
nix develop --command fourmolu --mode checkon touched Haskell filesnix develop --command cabal build lib:cardano-wallet-primitive lib:cardano-wallet-api lib:cardano-wallet -O0 -v0nix develop --command just build 'cardano-wallet-primitive cardano-wallet-api cardano-wallet'with-Werroris blocked by pre-existingcardano-wallet-readwarningsOriginal branch:
nix build .#cardano-walletsucceeds at every commit (bisect-safe)fourmolu --mode checkcleanhlint libno hintsbuild-gate (Linux)CI on the previous tip before review-response commitunit-cardano-wallet-unittest suite (deferred to CI)