Skip to content

chore: drop @meshsdk/core-csl and implement TxParser on core-cst - #868

Open
lisicky wants to merge 6 commits into
mainfrom
chore/drop-core-csl
Open

lisicky wants to merge 6 commits into
mainfrom
chore/drop-core-csl

Conversation

@lisicky

@lisicky lisicky commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Summary

@meshsdk/core-cst becomes the only serializer backend: @meshsdk/core-csl (CSLSerializer, the whisky OfflineEvaluator and the whisky-based TxParser) is removed from the monorepo. The one thing core-csl still did that cst could not, parsing a transaction back into a MeshTxBuilderBody for TxParser / TxTester, is implemented on the cardano-sdk Serialization layer as CardanoSDKTxParser, so CardanoSDKSerializer.parser works instead of throwing. Reviewing that work turned up three serializer/builder bugs, which are fixed here as well.

Six commits, meant to be read in order:

  1. feat(core-cst): implement ITxParser for CardanoSDKSerializer — packages/mesh-core-cst/src/parser/. Inputs (key / Plutus / native, provided or reference scripts; inline, witness-set and hash-only datums), collaterals, reference inputs with script sizes, outputs with datums and reference scripts, mints grouped by policy, withdrawals, certificates incl. pool registration, votes and proposals of every governance action kind (incl. protocol parameter updates), metadata, validity range, required signers, total collateral, collateral return and donation. Redeemers are matched to their targets in ledger order (inputs by (txid, index), mints by policy id, withdrawals by reward account Ord, votes by voter), whatever order the transaction encodes them in. The TxParser / TxTester tests run on cst.
  2. chore: drop @meshsdk/core-csl — the package, its lockfile entries (whisky), the publish job, bump-version.sh, docs; tests migrated to CardanoSDKSerializer / OfflineEvaluatorScalus, duplicated CSL-vs-CST cases dropped. @meshsdk/core never depended on core-csl; only a doc comment changes there.
  3. fix: index voting redeemers by voter in ledger order
  4. fix(core-cst): keep native reference scripts on outputs
  5. feat: keep the deposit of Conway stake registration certificates
  6. test(core-cst): cover an invalid scriptRef on a resolved reference input

The three fixes

Voting redeemers were indexed per vote. The ledger runs a voter's script once for all of its votes and indexes the redeemer by the voter's position among the voting procedures (committee members, then DReps, then pools; script credentials before key hashes). The serializer pushed one redeemer per ScriptVote, indexed by the vote's position in the body, so a script DRep voting on two governance actions produced redeemers 0 and 1 and an invalid transaction. The serializer now keeps one redeemer per voter, indexes it by the sorted voters of the body and rejects different redeemers for one voter (The same voter must have the same redeemer). The builder evaluates and updates budgets per voter (votesByVoter), getRedeemerCosts prices one redeemer per voter and sortVotes shares the comparator. The voter ordering helpers live in core-cst/src/utils/vote.ts and are used by the parser too.

toCardanoOutput dropped native reference scripts. serializeTxBody builds every output through it, and it only handled V1 / V2 / V3; serializeOutput already treated any other version marker as a native script. The core path now does the same.

Conway reg_cert / unreg_cert lost their deposit. They were parsed into RegisterStake / DeregisterStake, which had no field for it, and rebuilt as the legacy certificates 0 and 1. RegisterStake / DeregisterStake take an optional coin: with it toCardanoCert emits the Conway forms, without it the legacy ones as before. registerStakeCertificate(rewardAddress, coin?) / deregisterStakeCertificate(rewardAddress, coin?) accept it, balancing uses coin ?? keyDeposit, the parser keeps it, and the tx-prototype conversion passes the coin on instead of building those certificates itself.

Affect components

  • @meshsdk/common — coin?: number on RegisterStake / DeregisterStake
  • @meshsdk/contract
  • @meshsdk/core — doc comment only
  • @meshsdk/core-cst — parser, serializer and certificate changes
  • @meshsdk/hydra
  • @meshsdk/provider
  • @meshsdk/react
  • @meshsdk/svelte
  • @meshsdk/transaction — votes per voter, stake certificate deposit
  • @meshsdk/wallet
  • Mesh playground (i.e. https://meshjs.dev/)
  • Mesh CLI

Type of Change

  • New feature (non-breaking change which adds functionality) — CardanoSDKTxParser; the deposit on stake certificates
  • Breaking change (fix or feature that would cause existing functionality to not work as expected) — @meshsdk/core-csl is no longer part of the monorepo and is not published from it any more; validateTx goes without a replacement. Consumers import CardanoSDKSerializer, OfflineEvaluatorScalus and CardanoSDKTxParser from @meshsdk/core-cst.
  • Bug fix (non-breaking change which fixes an issue) — the three fixes above
  • Code refactoring (improving code quality without changing its behavior)
  • Documentation update (adding or updating documentation related to the project) — README package table and diagram, core README, PR template, copilot instructions, tx-prototype design note

Behaviour changes to be aware of

  • A transaction with a script voter casting several votes carries one voting redeemer (it used to carry one per vote and be rejected by the ledger). An evaluator's VOTE result at index i is applied to every vote of the i-th voter. Two ScriptVotes of one voter with different redeemer data or budgets are rejected at serialization.
  • Native reference scripts on outputs reach the transaction when it is built through serializeTxBody (they were silently dropped).
  • registerStakeCertificate / deregisterStakeCertificate behave as before when called with one argument; only a stated deposit changes the certificate form and the balance. Parsed Conway stake certificates now round-trip.
  • OfflineEvaluatorScalus is the only offline evaluator; the tests that asserted whisky's error text assert Scalus's.

Parser limitations

Documented on CardanoSDKTxParser:

  • fee of the parsed body is left at the body default "0". MeshTxBuilder.complete() takes any non-empty fee as fixed, so drop or override it before rebuilding (the round-trip tests do).
  • An output datum hash whose datum is not in the witness set comes back as { type: "Hash", data: { type: "CBOR", content: <hash> } }, since Output.datum has no hash-only form.
  • Output.referenceScript is typed PlutusScript; a native reference script comes back as { code, version: "Native" } behind a cast, which is what the serializer expects.
  • Not changed here: reward redeemer indexes in the serializer still follow the body order and rely on MeshTxBuilder.sortWithdrawals.

Related Issues

None.

Checklist

  • My code is appropriately commented and includes relevant documentation, if necessary
  • I have added tests to cover my changes, if necessary — test/parser/tx-parser.test.ts (42 cases: every parsed part, ledger-order redeemer matching, round trips), vote-redeemers.test.ts and stake-certificates.test.ts in both core-cst and mesh-transaction, the native reference script case in ref-script-output.test.ts
  • I have updated the documentation, if necessary
  • All new and existing tests pass (i.e. npm run test)
  • The build is pass (i.e. npm run build:mesh)

Verification

Run locally after npm run build:mesh:

package tests
mesh-core-cst 231 passed, plus 16 under the Scalus (ESM) config
mesh-transaction 308 passed
mesh-common 200 passed
mesh-wallet, mesh-x402, mesh-scalus-emulator, mesh-contract, mesh-core 34, 98, 24, 1, 1 passed

Root npm test (turbo): 8 of 8 tasks successful.

The tests added with the fixes fail on the code before them (7 in core-cst, 4 in mesh-transaction); each intermediate commit type-checks on its own.

Additional Information

  • The lockfile regeneration also picks up the stale packages/mesh-x402 version (1.9.1 → 0.0.1).
  • _backup/apps/** still mentions CSLSerializer; it is not built and is left alone.
  • Historical comments in tx-prototype-to-cbor/* that cite whisky's Rust sources as the reference for the port are kept.

Replace the throwing `parser` stub of CardanoSDKSerializer with
CardanoSDKTxParser, built on the cardano-sdk Serialization layer, so that
TxParser and TxTester no longer need @meshsdk/core-csl.

The parser rebuilds a MeshTxBuilderBody from a transaction and its resolved
UTxOs:
- inputs (pub key / Plutus / native script; provided or reference scripts;
  inline, witness-set and hash-only datums; spend redeemers by ledger order),
  collaterals and reference inputs with script sizes
- outputs with hash / inline / embedded datums and Plutus or native
  reference scripts; change address = last output
- mints (Plutus and native, grouped by policy), withdrawals (reward account
  order), certificates incl. pool registration, votes and proposals of every
  governance action kind incl. protocol parameter updates
- metadata (nested maps, integer keys, byte strings, big integers), script
  metadata, validity range, required signers, total collateral, collateral
  return address and donation
- getRequiredInputs lists inputs, collateral and reference inputs

Switch the TxParser and TxTester tests to CardanoSDKSerializer.
Remove the core-csl package (CSLSerializer, the whisky OfflineEvaluator and
the whisky-based TxParser) now that core-cst covers everything it provided:
CardanoSDKSerializer is the only IMeshTxSerializer, OfflineEvaluatorScalus
the only offline evaluator and CardanoSDKTxParser backs TxParser/TxTester.

- delete packages/mesh-core-csl and its lockfile entries
  (@sidan-lab/whisky-js-browser, @sidan-lab/whisky-js-nodejs,
  whisky-evaluator); the lockfile also picks up the stale
  packages/mesh-x402 version (1.9.1 -> 0.0.1)
- tests: migrate to CardanoSDKSerializer / OfflineEvaluatorScalus, drop the
  duplicated CSL-vs-CST builds and the CSL-only cases, assert the Scalus
  error message in script-tx-fail; the serializeData helper and the
  serializer-utils test use core-cst (fromBuilderToPlutusData,
  normalizePlutusScript)
- CI: remove the publish-meshsdk-core-csl job and the bump-version entry
- docs: README package table and diagram, core README, PR template,
  copilot instructions, tx-prototype design note, code comments

validateTx (unused) is removed without a replacement.
The ledger runs a voter's script once for all of its votes and indexes
the voting redeemer by the position of the voter in the ordering of the
voting procedures (committee members, then DReps, then pools; script
credentials before key hashes). The serializer declared one redeemer per
vote, indexed by the vote's position, so a script DRep voting on two
actions produced redeemers 0 and 1 and an invalid transaction.

- serializer: collect one redeemer per voter, reject conflicting
  redeemers for the same voter, index by the sorted voters of the body
- builder: evaluate and update budgets per voter (`votesByVoter`), fee
  estimation prices one redeemer per voter, `sortVotes` shares the
  comparator
- parser: reuse the voter ordering helpers now in `utils/vote.ts`
`toCardanoOutput`, which `serializeTxBody` uses for every output, only
handled Plutus reference scripts and silently dropped a native one;
`serializeOutput` already treated any other version marker as a native
script. Do the same in the core path.
`reg_cert` / `unreg_cert` (Conway certificates 7 and 8) state their
deposit. Parsing them folded the deposit away and rebuilding produced
the legacy certificates 0 and 1, so a transaction did not round trip.

- `RegisterStake` / `DeregisterStake` take an optional `coin`; with it
  `toCardanoCert` emits the Conway forms, without it the legacy ones
- `registerStakeCertificate` / `deregisterStakeCertificate` accept the
  deposit, and balancing uses it instead of the protocol `keyDeposit`
- the parser keeps the deposit; the tx prototype conversion passes the
  coin on instead of building the certificates itself

This branch has not been deployed

No deployments
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.

1 participant