Accepted
The cardano-api library provides two main API layers
for constructing transactions and certificates:
-
Legacy API (
Cardano.Api.Tx,Cardano.Api.Certificate.Internal) — The original transaction construction interface. It covers all Shelley-based eras and supports the full feature set including governance operations (DRep registration, committee certificates, proposal procedures). However, it uses era-specific GADT constructors (e.g.,StakeAddressRequirements era,DRepRegistrationRequirements era) that carry era witnesses at construction time. As the number of Cardano eras has grown, maintaining correct GADT coverage across all eras has become increasingly difficult, and several functions do not compile for all Shelley-based eras. -
Experimental API (
Cardano.Api.Experimental.Tx,Cardano.Api.Experimental.Tx.Internal.Certificate) — A newer API built around theIsEratype class/Eradata type, which is intentionally restricted to only the current mainnet era and the upcoming era (currently Conway and Dijkstra). After a hardfork, the previous era is deprecated and eventually removed. This forward-looking design avoids the combinatorial complexity of supporting all historical eras but means Experimental functions cannot be used for pre-Conway eras.
Neither API fully satisfies the need for simple, uniform transaction and certificate construction across all Shelley-based eras (Shelley through Conway and beyond). The Legacy API covers all eras but its GADT-heavy design makes writing truly era-polymorphic code difficult. The Experimental API has a cleaner design but intentionally excludes historical eras.
The Compatible API was introduced specifically to support QA's hardfork traversal tests, which need to construct valid transactions in every Shelley-based era in order to hardfork across all boundaries within a single test run.
A key challenge is that hardfork boundaries introduce breaking semantic changes:
- Stake registration:
Pre-Conway eras require only a
StakeCredential; Conway and later require aStakeCredentialand a deposit (Coin). - Delegation targets:
Pre-Conway eras delegate to stake pool key hashes (
Hash StakePoolKey); Conway and later support richer delegation targets including DReps (Ledger.Delegatee). - Protocol governance:
Pre-Conway eras use
UpdateProposalfor parameter changes; Conway and later useProposalProceduresandVotingProcedures.
These differences need to be captured in a type-safe way without burdening callers with era-specific logic at every use site.
We introduce the Cardano.Api.Compatible module family
as a bridge layer that provides uniform transaction
and certificate construction across all Shelley-based eras.
The Compatible API is organised into two sub-modules:
Cardano.Api.Compatible— Top-level re-export module.Cardano.Api.Compatible.Tx— Transaction construction functions.Cardano.Api.Compatible.Certificate— Certificate construction functions (re-exports fromCardano.Api.Experimental.Tx.Internal.Certificate.Compatible).
The implementation of certificate functions lives under
Cardano.Api.Experimental.Tx.Internal.Certificate.Compatible
because these functions build on the Experimental API's
internal certificate types.
Cardano.Api.Compatible.Certificate re-exports them
for discoverability under the Compatible namespace.
Type families capture era-dependent semantics at the type level:
-- Delegation targets differ across the Conway boundary
type family Delegatee era where
Delegatee ConwayEra = Ledger.Delegatee -- DRep/pool/abstain
Delegatee BabbageEra = Hash StakePoolKey -- Pool only
Delegatee AlonzoEra = Hash StakePoolKey
-- ... (all pre-Conway eras map to Hash StakePoolKey)
-- Stake registration requirements differ across the Conway boundary
type family StakeRegistrationRequirements era where
StakeRegistrationRequirements ConwayEra = StakeCredentialAndDeposit
StakeRegistrationRequirements BabbageEra = StakeCredential
-- ... (all pre-Conway eras map to StakeCredential)This design means callers provide the correct data for each era
without needing to pattern-match on era witnesses themselves.
Note that IsShelleyBasedEra and the type families are distinct concerns:
the type families determine the types of arguments at the call site (compile time),
while the certificate functions also carry an IsShelleyBasedEra era constraint
that provides the era singleton (ShelleyBasedEra era) used at runtime
to dispatch to the correct ledger operations for each era.
Callers in a polymorphic-era context must therefore include
IsShelleyBasedEra era (or a stronger constraint that implies it,
such as IsBabbageBasedEra era) in their own type signatures.
For transaction-level governance features that exist only in certain eras, GADTs provide runtime era witnesses:
data AnyProtocolUpdate era where
ProtocolUpdate
:: ShelleyToBabbageEra era
-> UpdateProposal
-> AnyProtocolUpdate era
ProposalProcedures
:: ConwayEraOnwards era
-> TxProposalProcedures (...)
-> AnyProtocolUpdate era
NoPParamsUpdate
:: ShelleyBasedEra era
-> AnyProtocolUpdate era
data AnyVote era where
VotingProcedures
:: ConwayEraOnwards era
-> TxVotingProcedures (...)
-> AnyVote era
NoVotes :: AnyVote eraTransaction construction:
createCompatibleTx— Constructs an unbalanced transaction in any Shelley-based era, given inputs, outputs, fees, protocol updates, votes, and certificates. ReturnsEither ProtocolParametersConversionError (Tx era).addWitnesses— Adds key witnesses (both Shelley address and Byron bootstrap) to a transaction post-construction.
Certificate construction:
makeStakeAddressDelegationCertificate— Era-polymorphic stake delegation (pool key pre-Conway,Delegateepost-Conway).makeStakeAddressRegistrationCertificate— Era-polymorphic stake registration (no deposit pre-Conway, deposit required post-Conway).makeStakeAddressUnregistrationCertificate— Stake deregistration across all eras.makeStakePoolRegistrationCertificate/makeStakePoolRetirementCertificate— Pool lifecycle certificates.makeMIRCertificate/makeGenesisKeyDelegationCertificate— QA-only certificates hardcoded to BabbageEra (these certificate types do not exist post-Conway).selectStakeCredentialWitness/getTxCertWitness— Extract the stake credential that must witness a given certificate.
- Built on the Experimental API —
Compatible uses the Experimental API's internal types
(
TxCertificates,TxProposalProcedures,TxVotingProcedures,AnyWitness,Certificate) rather than duplicating ledger integration. - Deprecates Legacy API functions —
Legacy certificate functions in
Cardano.Api.Certificate.InternalcarryDEPRECATEDpragmas directing users to the Compatible equivalents for cross-era support, or to the Experimental equivalents for era-specific use. - Not re-exported from
Cardano.Api— The Compatible module is deliberately not exposed through the main API entry point, keeping it clearly scoped as a testing/internal API.
- Uniform cross-era coverage: A single set of functions works across all Shelley-based eras, eliminating the need for era-specific branching at call sites in tests and QA tooling.
- Type-safe hardfork boundaries:
Type families make era-dependent differences explicit at the type level.
Callers cannot accidentally pass a
StakeCredentialwhere aStakeCredentialAndDepositis required in Conway. - Clear deprecation path: Legacy API functions now have a concrete migration target, reducing API surface over time.
- Layered on Experimental: By building on the Experimental API's internals, Compatible avoids duplicating ledger integration logic and benefits from improvements to the Experimental layer.
- Third API surface: Maintaining three API layers (Legacy, Experimental, Compatible) increases the overall API surface area and cognitive load for contributors.
- Testing-scoped by design:
The Compatible API is intentionally not exposed via
Cardano.Api, meaning downstream consumers must importCardano.Api.Compatibledirectly if they want to use it. This is a deliberate trade-off to keep the main API surface clean. - Partial coverage of governance features: The Compatible API focuses on common transaction and certificate operations. Advanced governance operations (DRep registration, committee certificates) remain exclusive to the Experimental API.