github Emurgo/cardano-serialization-lib 16.0.0
16.0.0 Cliffhanger

2 hours ago

CBOR Int finally covers the whole range the spec allows — and everything that was quietly truncating, clamping or panicking at the edges of that range now says so out loud.

Breaking changes

  • CBOR Int boundary extended to the full RFC 8949 range [-2^64, 2^64 - 1]. Int::from_str("-18446744073709551616") (-2^64) now succeeds. As a knock-on:

    • Int::as_negative() returns None for -2^64, whose magnitude 2^64 does not fit a BigNum. In 15.0.3 it returned Some(BigNum(0)) through a silent as u64 cast.
    • BigInt::as_int() returns Some(Int) for -2^64 instead of None, including the two-limb [0, 1] representation.
  • Mint::as_positive_multiasset() and Mint::as_negative_multiasset() return Result<MultiAsset, JsError> instead of MultiAsset. MultiAsset amounts are u64-backed, so -2^64 — reachable only through the deliberately permissive CBOR/JSON deserializers — has no representable projection. In 15.0.3 this path hid the problem behind a truncating cast, so a -2^64 burn projected to an amount of 0; widening Int turned the same spot into a reachable .unwrap(), i.e. a panic (an unrecoverable abort across the wasm boundary, not a catchable JsError) triggerable by wire data. It now returns Err naming the offending policy and amount. The projection is all-or-nothing — no entry is silently dropped, so entry counts between a Mint and its projection still match. Callers must add ? / .unwrap().

    TransactionBuilder::get_total_input() and get_total_output() (already Result) can now surface this error. add_mint_asset_and_output() and add_mint_asset_and_output_min_required_coin() are unaffected in practice — MintAssets::insert rejects out-of-int64 amounts before the projection runs.

  • MintAssets::insert and MintAssets::new_from_entry now enforce Conway mint = {+ policy_id => {+ asset_name => nonzero_int64}} (also matching Mary int64). Any value with |x| > i64::MAX is rejected — that includes -2^64 and the previously-tolerated band (i64::MAX, u64::MAX][-u64::MAX, i64::MIN). In 15.0.3 only zero was rejected, so out-of-int64 amounts flowed through and produced on-chain-invalid mints. Clamp into int64 before insertion. Deserialization paths (CBOR, serde JSON) are left permissive so callers can apply their own policy.

  • CostModel::from(Vec<i128>) replaced with impl TryFrom<Vec<i128>> for CostModel. Values outside the CBOR int range now return Err(JsError) instead of producing a silently-malformed Int. Switch call sites to CostModel::try_from(vec![...])?, and bring std::convert::TryFrom into scope on 2018-edition crates.

  • Value::checked_sub no longer clamps multiassets. The coin path still errors on underflow. The multiasset path now returns Err(JsError("underflow")) when an RHS amount exceeds the matching LHS amount, or when the RHS holds a non-zero asset the LHS lacks. Use Value::clamped_sub (backed by SaturatingSub) for the old behaviour.

  • Value::is_zero() is now semantic — true whenever every asset amount is zero. 15.0.3 additionally required multiasset.len() == 0, so a Value carrying an all-zero MultiAsset was not considered zero.

  • Value and MultiAsset arithmetic (checked_add, checked_sub, clamped_sub, MultiAsset::sub) now routes through num_traits::{CheckedSub, SaturatingSub} and drops zero asset entries plus policies that become empty; a multiasset that ends up semantically zero is replaced with None in Value, including the case where the LHS already carried an all-zero MultiAsset and the RHS has none. 15.0.3 only dropped entries when len() literally hit zero, so any all-zero or empty-but-present MultiAsset carried straight through.

  • encode_json_value_to_metadatum (BasicConversions): JSON object keys whose integer value falls outside [-2^64, 2^64 - 1] now fall back to string encoding instead of constructing a malformed Int.

Other changes

  • Rust ergonomics across numeric wrappers, Value, MultiAsset, Assets, PlutusData and the Vec wrappers: additional From / Default / Hash / Eq impls, iterator traits, builder-style methods, PlutusList::push, impl_btmap_wrapper, and constructor macros — assets!, multi_asset!, plutus_list!, plutus_map!, plutus_bytes!, plutus_constr!. None of this changes the JS/WASM API.
  • Int gains inherent checked_{add,sub,mul} and saturating_{add,sub}, plus From<i32 / u32 / i64 / u64 / BigNum>, From<&Int> for i128, TryFrom<i128>, Display and FromStr. The encoder now picks canonical major-1 headers across the full range, which fixes a debug-build panic on i64::MIN and lets values down to -2^64 serialize.
  • cbor_event bumped 2.1.32.4.0 (additive only — new *_sz writer methods and the Sz enum; nothing removed, no signature changed).
  • Fix: post-alonzo TransactionOutput deserialization accepts indefinite-length maps (bf … ff). The trailing break byte previously failed with UnexpectedKeyType(Special). Unterminated maps, and maps missing the mandatory amount, still error.
  • Fix: from_bech32 on hash types no longer panics on a bech32 string with a valid checksum but an invalid base32 payload length (e.g. addr_vkh1q5x4pp8) — it returns JsError. Previously from_base32().unwrap() aborted, which is not catchable across the wasm boundary.
  • Fix: CSL compiles as a Rust dependency on wasm32-unknown-unknown under dont-expose-wasm (noop_proc_macro is now reachable on every target).
  • Fix: property-test-api and with-bench feature compilation; empty MultiAsset arithmetic case.
  • Tests: RFC 8949 / CDDL byte-exact Int CBOR serialization across every header-size boundary (1 / 2 / 3 / 5 / 9 bytes), positive and negative, including i64::MIN and -2^64; branch coverage for every BigInt::as_int() match arm.
  • Build: wasm-bindgen widened to ">=0.2.93, <0.3". The npm pipeline pins per variant with cargo update --precise before each build (asm.js0.2.93, others → 0.2.103) because binaryen's wasm2js rejects the multi-table modules emitted from 0.2.95+.
  • CI now runs cargo test --all-features --all-targets. rust/Cargo.lock is committed — this does not affect downstream consumers, since cargo ignores a library dependency's lockfile.

Thanks

Special thanks to @AmbientTea (Nikolaos Dymitriadis), who is behind the bulk of this release. Eleven PRs — #744, #745, #746, #747, #748, #749, #750, #751, #752, #753, #754 — brought the numeric and Value types up to idiomatic Rust: real num_traits arithmetic, the std trait impls that were missing, the constructor macros, and the all-features build fix. Several of the behaviour corrections listed above (checked_sub no longer clamping, semantic is_zero, consistent zero-entry handling) came out of that work — it turned a pile of quiet edge-case surprises into explicit, testable semantics. Thank you for the patience on the review round-trips, and for waiting on the release.

Thanks also to @baima365-web for #759, which removed the panicking unwrap from from_bech32.

Published

https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-browser/v/16.0.0
https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-nodejs/v/16.0.0
https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-asmjs/v/16.0.0
https://crates.io/crates/cardano-serialization-lib/16.0.0

Experimental packages with gc support

https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-nodejs-gc/v/16.0.0
https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-browser-gc/v/16.0.0
https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-asmjs-gc/v/16.0.0

Full Changelog: 15.0.3...16.0.0

Don't miss a new cardano-serialization-lib release

NewReleases is sending notifications on new releases.