Repository navigation
Release Notes
Scalus 1.5 turns the Emulator into a small Cardano node. Your tests get a mempool, real blocks, forks, rollbacks and epoch boundaries with rewards and governance, and the Emulator reports a failure by the Haskell ledger's own error name. All of it works from Scala, Java and JavaScript, with no devnet to start.
Highlights
- Test forks and rollbacks in a unit test, with no devnet. The Emulator now works as a small Cardano node: a mempool, real blocks, forks and rollback. dApp, wallet and indexer authors can check what their code does with a pending, dropped or rolled-back transaction, from Scala, Java or JavaScript.
- Test staking, rewards and governance across epochs. The Emulator runs the ledger's epoch boundary: stake snapshots, rewards, pool retirement, and the expiry, ratification and enactment of proposals. It is tested against 533 epoch transitions of the Haskell ledger.
- Assert the node's own error names.
Emulator.ledgerFailures(tx)lists every ledger failure of a transaction by its cardano-ledger name, such asMissingRedeemers. A test can check that a bad transaction fails for the right reason, or port a Haskell ledger test as it is. - Run one compiled validator on many transactions. The JIT compiles a script once, and
applyToruns it on each script context, with the CEK machine's result and budget. This suits indexers, chain replays and fuzzers. - Write a validator with two imports.
scalus.cardano.onchain.plutus.v3.*andprelude.*now also bringOutputDatum, the builtin data types andCompile. - Get the node's verdict on unusual bytes. A deeply nested datum decodes, and a script with bytes after its program, or a string that is not UTF-8, fails as on chain.
- Run on the fastest Plutus VM. On the Plutus VM Benchmark (15 evaluators, 89 scripts, every result checked against the node's plutus-core), the Scalus CEK machine ranks first on decode + eval: 1.42× faster than Plutigo (Go), 1.58× faster than amaru-uplc (Rust) and 2.65× faster than plutus-core (Haskell).
Upgrading
- The Emulator runs the epoch rules at each epoch boundary, with no off switch. A test that crosses two or more boundaries and asserts an absolute treasury, reserves or reward balance changes.
Context.testMainnet(),UtxoEnv.testMainnet()and the JavaScriptEmulator.createwithouttreasuryhold the mainnet treasury, not 0. Useemulator.treasuryascurrentTreasuryValue.- The Emulator rejects a PlutusV3 transaction that spends and references one UTxO, and the proposals and votes that the ledger rejects, such as a vote by an unregistered DRep.
ProtocolParams.monetaryExpansionandtreasuryCutareUnitInterval, andpoolPledgeInfluenceisNonNegativeInterval; read them with.toDouble. Recompile a 1.4 jar that callsProtocolParams.copywith named arguments: it fails withNoSuchMethodError.GovStateis a case class, not anArray[Dom.Element]. UseGovState.emptyand==.- A positional pattern on
PoolsState,VotingState,EmulatorState,EmulatorInitialStateor the testkitImmutableEmulatorno longer compiles. Use the field accessors. Block.hashis the header hash. The old value isblock.header.headerBody.blockBodyHash.checkTransactionreportsNotFoundfor a transaction of an imported applied-tx log.- Flat decoding (
decodeFlat,fromCbor,PlutusScript.program) rejects bytes after the filler. Read a program that other bytes follow withdecodeFlatWithRemainingBytes. SubmitError.ruleisPassedUnexpectedlyorCollectErrorswhere it wasIllegalArgument.UplcEvaluatorhas a new abstract method,applyTo. An implementation outside Scalus must add it.scalus-pluginis no longer published for Scala 3.3.7. Use Scala 3.3.8 or 3.3.9.- The API deprecated in 1.3.0 is removed. See Removed.
Added
- Emulator chain:
ChainConfig.manualkeeps submitted transactions in a mempool untilproduceBlock();ChainConfig.instant, the default, forges one block per submit.txStatus(hash)reportsPending,Confirmed,Dropped,RolledBackorNotFound. - Forks and rollback:
produceBlock(on, txs)forges a block anywhere in the block tree, chain selection takes the longest chain, androllback(n)removes blocks.ChainConfig.orphanTxssends orphaned transactions back to the mempool or drops them. findUtxos(query, view)reads the tip, the mempool or aChainView.Stable(depth)block.EmulatorBase.diff(from, to)gives theChainEvents between two tips (experimental).StreamingEmulatorstreams the Emulator's real blocks, forks and rollbacks included.- Epoch boundary: stake snapshots, the reward update and its payout, pool reaping, proposal expiry, ratification and enactment, and DRep expiry with dormant epochs.
ChainConfig.withPoolPerformance,withActiveSlotsCoeffandwithBlockIssuerset how pools earn. - Governance: the Conway GOV rule, a
GovStatewith proposals, votes, roots, the committee and the constitution, and committee hot keys and resignations.EmulatorInitialStatetakescommittee,committeeHotKeys,constitutionandreserves. Emulator.ledgerStatereturns the whole ledger state in the node'sNewEpochStateshape.snapshots,pendingRewards,pendingRatify,govState,reservesandcurrentEpochread its parts.Emulator.ledgerFailures(tx)andTransactionException.ledgerNamesgive every ledger failure in the ledger's order, with the ledger's names.- Java:
EmulatorJavaApigainstryProduceBlock,tryRollback,getMempoolTxs,getDiff,getLedgerStateandgetLedgerFailureNames. The new parameterless enums are Java enums. - JavaScript:
Emulator.createtakes the chain, governance and reward options. The emulator gainsproduceBlock,rollback,getTxStatus,awaitTx,getLedgerStateandledgerFailures. - AI agents in TypeScript projects: scalus.org serves
llms-ts.txt, the npm README andscalus.d.tsin one file. ThescalusClaude Code plugin (1.9.0) adds thescalus:typescriptskill and loads its routing table in any project whosepackage.jsondepends onscalus. ProtocolParamUpdate.applyTo(params)applies a parameter update as the ledger enacts it.BlockHeader.hashandBlock.bodyHash(...).- JIT:
UplcEvaluator.applyTo(args*)evaluates a compiled script applied to constant arguments. scalus.cardano.onchain.plutus.v3re-exportsOutputDatum, the builtin data types andCompile.scalus-verification(experimental): quantify over enums, lists, maps andDataencodings (forAllData,forAllDataLimited,DataEncoding), and over types that contain themselves.Budget.Within(n)proves a statement on the runs that end withinnsteps.- Examples: the USDCx minting policy, recreated from its Plinth script (446 bytes against 483), and verified guarantees of the one-shot minting policy.
Changed
StreamingEmulatorkeeps up tosecurityParamblocks and delivers rollbacks. Its defaultrollbackHorizonisSome(0), andnewEmptyBlock()forges throughproduceBlock().- A script context error starts with the ledger's
ContextErrorname, for exampleInlineDatumsNotSupported: .... - The
_3library artifacts are built with Scala 3.3.9. scalus.jsgrows by about 83 KB for the chain features.- Dependency updates: jsoniter-scala 2.41.6, jackson-databind 2.22.4, Yaci DevKit 0.4.7.
Deprecated
RewardsCalculationandStakeDistribution: usesnapshotsandpendingRewards.- The
EmulatorInitialStateconstructor,applyandcopy: start fromEmulatorInitialState.emptyand use thewithmethods, or the JavaBuilder. clearAppliedTxs,StreamingEmulator.syntheticBlockHash, and theinitialAppliedTxLogandappliedTxLogparameters (in the scaladoc only). Usesnapshot().SIRHashCodeInRec: it never worked.
Removed
The API deprecated in 1.3.0. The JavaScript new Emulator(...) constructor stays deprecated.
- The
scalus.preludepackage-object forwarders (===,asScalus,pow,log2and the others): use the same names inscalus.cardano.onchain.plutus.prelude. - The
scalus.builtinaliasesG1Element,G2Element,MLResultand the JavaScriptNodeJsPlatformSpecific: usescalus.uplc.builtinandscalus.uplc.builtin.bls12_381. Value.lovelaceAmount: usegetLovelace.CaseDataBranchError: useCaseDataNonConstrErrororCaseIndexOutOfBounds.TallyingBudgetSpenderLogger.getLogsWithBudget: usetraces, orgetLogsfor the text.VarUInt: usePointer.toBytesandPointer.parseFrom.- The
DelegationStatemapsrewards,deposits,stakePoolsanddreps, and its 4-map constructor andapply: useaccounts. - The 4-argument
UtxoEnvand the 6-argumentStakeCertificatesExceptionconstructors andapply. StakeCertificatesMutator,StakePoolCertificatesMutatorandVotingCertificatesMutator: useCertsMutator.EmulatorState.datumsand the testkitImmutableEmulator.applywith decodeddatums: usebinaryDatums.- bloxbean
StakeStateResolver.resolveForTx(tx, epoch, defaultDeposit): useresolveForTx(tx, slot).
Fixed
- The Emulator checks what the ledger checks and it missed: a repeated VRF key at protocol version 11, a pool metadata hash over 32 bytes, the Byron attributes of the collateral return, the output size as the ledger writes it, Byron
attributesSize, and the reference-script limits of a block (1 MiB) and of the mempool (200 KiB). - The Emulator keeps the ledger's bookkeeping:
State.deposited,DRepState.delegates, DRep expiry and the committee state. - Moving the slot back before a boundary that no block crossed restores the donations it moved.
DataCBOR decoding, encoding andserialiseDatahave no nesting limit, as in Plutus. Metadata and native scripts stop at 1000 levels with an error, not a stack overflow.- The flat decoder reads a
dataconstant as its first CBOR item and rejects astringthat is not UTF-8, as Plutus does. Its errors show the bytes around the failure. PlutusScriptEvaluatornames a script it cannot decode, and the Emulator rejects it in phase 1.- The compiler accepts a case class with a one-field wrapper of a
List, such asVarargs, andValidatorobjects under-Yexplicit-nulls.
Get it
// sbt
addSbtPlugin("org.scalus" % "scalus-sbt-plugin" % "1.5.0")
libraryDependencies += "org.scalus" %% "scalus" % "1.5.0"npm install scalus@1.5.0Documentation
- Documentation: https://scalus.org/docs
- Getting started: https://scalus.org/docs/get-started