Skip to content

feat: add @meshsdk/tx-emulator for simulating transactions on a local ledger - #858

Open
lisicky wants to merge 1 commit into
feat/governance-fetcher-interfacesfrom
feat/tx-emulator
Open

lisicky wants to merge 1 commit into
feat/governance-fetcher-interfacesfrom
feat/tx-emulator

Conversation

@lisicky

@lisicky lisicky commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Merge #864 first: this PR builds on its IGovernanceFetcher and IStakePoolFetcher interfaces.

What

Adds @meshsdk/tx-emulator, a local Cardano ledger for trying transactions out, alone or in chains.

  • simulate(tx) reports what a transaction would do without changing anything. submit(tx) applies it, and every later transaction is checked against the result.
  • UTxOs are kept by the Scalus ledger through @meshsdk/scalus-emulator. Transactions are validated by the validator the caller picks: "scalus" (built in) or "cquisitor" (@cardananium/cquisitor-lib, an optional peer dependency).
  • The emulator is itself a Mesh IFetcher, IGovernanceFetcher, IStakePoolFetcher, ISubmitter and IEvaluator, so transactions are built and submitted against it with the usual Mesh tools.
  • TxEmulator.fromChain(tx, …) builds a ledger holding the chain state a transaction reads, from providers, to check the transaction against the chain as it is now.

Contents

packages/mesh-tx-emulator (new):

  • TxEmulator.create(options) sets the starting state:
    • the validator (required) and the network (mainnet by default);
    • protocol parameters replacing the network's, all or some;
    • UTxOs and the starting slot;
    • stake addresses (rewards, deposit, pool and DRep delegation), pools and DReps;
    • governance state: actions, the last enacted action of each purpose, the committee, the treasury and the constitution;
    • known datums.
  • simulate(tx, options?) / submit(tx, options?) take CBOR hex or a TransactionPrototype and return:
    • accepted: txHash, fee, isValid, redeemers (execution units each script used), consumed and produced UTxOs (the collateral and its return for a transaction marked phase-2 invalid), script logs and warnings;
    • rejected: errors, each with its ledger rule and message; with cquisitor every problem, with a hint, locations and the phase.
    • With cquisitor, { requireSignatures: false } checks a transaction before anyone signs it: missing key witnesses are not reported, native scripts pass when they would once signed, and the fee and the size limit are checked as they will be once it is signed.
    • submitTx submits like any Mesh submitter and throws a TxRejectedError carrying the result.
  • State transactions change: UTxOs; stake address registrations, deposits, withdrawn rewards, and pool and DRep delegations; pool registrations and retirements; DRep registrations; governance actions with their deposits; committee hot credentials and resignations; donations. It is read back through the provider interfaces (fetchAccountInfo, fetchPoolState, fetchDRepState, fetchGovernanceProposal, fetchCommitteeState, …) and utxos(filter).
  • Direct state changes, for what the emulator does not compute (rewards, governance outcomes) or to fund a wallet or change a parameter midway: addUtxos / removeUtxos, setAccount / removeAccount, setPool / removePool, setDRep / removeDRep, setGovernanceAction / removeGovernanceAction, setLastEnacted, setCommitteeMember / removeCommitteeMember, setTreasury, setConstitution, setProtocolParameters. A set merges into an existing item (null clears a field) and pays or refunds no deposit.
  • fork(): an independent copy of the whole ledger, so one setup can branch into several scenarios.
  • Time: advanceTo({ slot } | { time } | { epoch }), forward only. Entering an epoch retires the pools due to retire (their deposits go back to their reward accounts), adds the previous epoch's donations to the treasury, and removes the governance actions past their lifetime, returning their deposits. No rewards are paid, and no governance actions are ratified or enacted.
  • TxEmulator.fromChain(tx, { validator, fetcher, governance?, pools?, network? }) reads the outputs the transaction spends and references, the protocol parameters and cost models, and the stake addresses, pools, DReps, governance actions and committee it touches, plus the treasury and the constitution. State no provider reports, or reports in a form that cannot be read, is taken to be as each transaction needs it and listed as a NotChecked warning, never as an error.
  • README covering usage, the two validators and the rules each leaves unchecked, chains, the starting state, direct changes, fromChain, time and limits.

packages/mesh-scalus-emulator: exports the existing toMeshUtxo / fromMeshUtxo converters, so the new package seeds the ledger with them instead of duplicating them. No behaviour change.

Repo wiring:

  • build.yml runs the new package's tests next to the scalus-emulator ones.
  • publish.yml gets a publish job for it.
  • bump-version.sh now covers the package, and the @meshsdk/scalus-emulator dependency pin, which it did not rewrite before.
  • The lockfile adds the workspace package and its dev dependency @cardananium/cquisitor-lib; npm hoists scalus to the root instead of keeping two nested copies.

Validation

npm run build:mesh
npm test --workspace @meshsdk/tx-emulator
npm test --workspace @meshsdk/scalus-emulator
  • @meshsdk/tx-emulator: 122 tests.
    • The emulator suite and the fromChain suite run on both validators: chains of transactions, dry runs, forks, time and epoch boundaries, direct state changes, the provider interfaces, and checks against a chain read through plain and governance-capable fetchers.
    • A cquisitor suite covers the ledger rules only it checks: governance proposals, votes, deposits and guardrails, committee certificates, deposit refunds and unsigned transactions.

@Temasar1 Temasar1 mentioned this pull request Oct 27, 2026
28 tasks
@lisicky
lisicky changed the base branch from main to feat/governance-fetcher-interfaces October 29, 2026 01:06
@nau nau mentioned this pull request Nov 4, 2026

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