Skip to content

feat: port to libcmt v2 — new @deroll/rollup binding + @deroll/codec packages - #183

Draft
tuler wants to merge 1 commit into
prerelease/v2from
feature/libcmt-v2
Draft

feat: port to libcmt v2 — new @deroll/rollup binding + @deroll/codec packages#183
tuler wants to merge 1 commit into
prerelease/v2from
feature/libcmt-v2

Conversation

@tuler

@tuler tuler commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Ports the stack to the libcmt v2 API overhaul ("output indexing" era), tracking machine-guest-tools 2fefcb1, and redesigns the whole app pillar (@deroll/core, @deroll/app, @deroll/wallet, @deroll/router) on top of it. Breaking change, covered by the cmio-libcmt-v2, codec-initial and app-native-redesign changesets.

Design

libcmt v2 split its rollup layer (raw I/O) from a codec module (EVM-ABI wire formats). This PR mirrors that split as two packages, and rebuilds the app stack on them:

  • @deroll/rollup (renamed from @deroll/cmio, packages/bindings/rollup) — exclusively the native rollup.h binding, hence the name. waitForInput({ accept }) returns { type, payload } undecoded, emitOutput(bytes) returns the output index, plus emitReport/emitException/progress/close/run. It knows nothing about ABI formats, so wire-format changes never require a native rebuild. No longer depends on ox.
  • @deroll/codec (new, packages/app/codec) — pure JavaScript EVM-ABI codecs, dual ESM + CommonJS, browser-compatible (no Node.js APIs; verified zero Buffer/node: references in the bundle). Dependencies: ox + abitype (types only).
  • @deroll/core / @deroll/app / @deroll/wallet / @deroll/router — rebuilt on the two packages above (see next section).

References to the Cartesi Machine's own CMIO interface (--cmio-* flags, @deroll/cm config) are intentionally untouched.

Naming follows upstream throughout: the transfer wire formats are Erc*-cased, and so is deroll's API — codec types/encoders (Erc20Transfer, encodeErc20Transfer, …), App methods (createErc20Transfer, …) and the whole wallet surface (withdrawErc20, transferErc721, isErc20Deposit, parseErc1155SingleDeposit, Erc20Deposit, …). Cartesi contract names in prose (ERC20Portal, ERC1155SinglePortal) and standard references ("ERC-20") keep their canonical spelling.

App stack redesign

@deroll/core is now a single hand-authored index.ts whose request/output vocabulary comes straight from the codec:

  • Flat advance data — advance handlers receive the codec's Advance object (chainId, appContract, msgSender, blockNumber, blockTimestamp, prevRandao, index, payload) with no metadata nesting; all payloads are 0x-hex Hex, not Buffers.
  • Boolean handler results — advance handlers return true/false instead of "accept"/"reject", matching the binding. Inspect handlers receive the raw query payload (Hex) directly, and createReport/registerException take the payload directly. The AdvanceRequestData, InspectRequestData, Report, Exception and RequestHandlerResult wrapper types are gone.
  • Synchronous output methods — the native binding emits outputs synchronously, so create*, registerException and stop() no longer return promises; createNotice etc. return the output index (number) directly. Only start() remains async.
  • createCallVoucher replaces createVoucher (the rollup's CallVoucher format; value and payload required), and createDelegateCallVoucher is removed (format dropped by libcmt v2).
  • Typed asset transfers — new createErc20Transfer / createErc721Transfer / createErc1155Transfer / createErc1155BatchTransfer methods emit the dedicated transfer formats without manual ABI encoding, plus createOutput(payload) as an escape hatch for already-encoded outputs.
  • appContext everywhere — every output takes an optional bytes32 tag (default zero hash); createApp({ appContext }) sets an application-wide default, per-output values win.
  • NativeApp drives the loop via waitForInput, decodes advances with the codec, reports+rejects undecodable inputs, skips unknown request types with a warning (upstream forward-compat extension point), and handles the libcmt mock's errno quirks when testing on the host.
  • Wallet — deposit parsers take Hex payloads, detectors use the flat msgSender, and withdraw* debit the ledger and return the typed output object (CallVoucher from withdrawEther, the corresponding Erc*Transfer from the others) to emit with the matching App method: app.createErc20Transfer(wallet.withdrawErc20(token, user, amount)). This routes withdrawals through the App's encoders so the app-wide appContext default applies; the wallet has no codec dependency. The ERC-721/1155 withdrawals drop the dapp parameter, and the create*TransferVoucher helpers are removed.
  • Routerhandler(payload: Hex), still plugs straight into addInspectHandler.

@deroll/codec highlights

  • Single source of truth: one exported Abi.from([...]) with all seven wire formats; each encode/decode function is a one-line AbiFunction.encodeData/decodeData over it, and the argument types are derived from the ABI via abitype (a 6-line NamedArgs mapped type) — types can never drift from the signatures. The abi itself is exported for direct use with viem/ox.
  • ox-native surface: bytes/addresses are 0x-hex strings, numbers are bigint, no conversions inside, invalid values raise ox's own errors. Callers convert at the boundary (Hex.fromBytes(request.payload)).
  • Verified byte-for-byte against libcmt's own cast-generated golden vectors (tests/data.h), plus encode/decode roundtrips.

Wire formats (machine-guest-tools 2fefcb1)

Every output carries appContext — a free-form bytes32 applications use to tag outputs (recipients can filter by it) — as its first parameter. It's optional in the encoders and defaults to the zero hash (exported as zeroHash), matching upstream's own tooling. Following upstream, the single ERC-1155 transfer dropped "Single" from its name, the data payload was removed from the ERC-721/1155 transfers, and the batch pairs are a (uint256,uint256)[] tuple array named items.

Helper Wire format
decodeAdvance / encodeAdvance EvmAdvance(uint64,address,address,uint64,uint64,uint256,uint64,bytes)
encodeNotice Notice(bytes32,bytes)
encodeCallVoucher CallVoucher(bytes32,address,uint256,bytes)
encodeErc20Transfer Erc20Transfer(bytes32,address,address,uint256)
encodeErc721Transfer Erc721Transfer(bytes32,address,address,uint256)
encodeErc1155Transfer Erc1155Transfer(bytes32,address,address,uint256,uint256)
encodeErc1155BatchTransfer Erc1155BatchTransfer(bytes32,address,address,(uint256,uint256)[])

Removed

  • finish(), emitNotice(), emitVoucher(), emitDelegateCallVoucher() and the decoded advance fields on the request — use @deroll/codec with the raw API instead
  • Delegate-call vouchers (dropped by libcmt v2 along with the Output1..Output4 envelope + type-tag design)
  • gio() and loadMerkle/saveMerkle/resetMerkle (no longer part of the libcmt rollup API)
  • The Rollup HTTP Server transport and its types in @deroll/core (Voucher, DelegateCallVoucher, RequestMetadata, …)
  • The data forwarding payload on ERC-721/1155 transfer outputs (dropped by the upstream wire formats)

Tests & docs

  • @deroll/codec: vitest suite with upstream golden vectors, roundtrips, default-appContext equivalence, and ox error propagation; attw green across all resolution modes
  • @deroll/rollup: unit tests exercise the binding through the codec package and assert the exact byte layouts of every output format; the in-machine test packs and installs both tarballs into the guest rootfs
  • @deroll/app: vitest suite drives the real native binding through the libcmt mock (CMT_INPUTS) — dispatch, output bytes vs codec encoders, short-circuit vs broadcast, handler-throw survival, undecodable-advance report+reject, inspect payload, appContext defaulting, unknown-request-type skip
  • @deroll/wallet (40 tests) and @deroll/router (5) updated to the new API
  • Docs: new /codec section; the binding docs moved to /rollup; the whole /app section reworked (create-vouchercreate-call-voucher, typed transfer + createOutput pages, wallet reference on the new signatures, rewritten v1→v2 migration guide); examples updated; docs build is fully green (twoslash type-checks every snippet against the built packages)
  • turbo test now depends on ^build so cross-package tests get built dists

Known follow-ups (not in this PR)

  • @cartesi/viem still ships pre-output-indexing ABIs/portal addresses; bump when output-indexing contracts publish (deposit input encodings verified unchanged)
  • Optional output decoders (decodeNotice, decodeCallVoucher, …) if the explorer pillar needs them

🤖 Generated with Claude Code

https://claude.ai/code/session_01FLcy7oTUN1sLhJLG4tHMSB

@changeset-bot

changeset-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 18e2854

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@deroll/core Minor
@deroll/app Minor
@deroll/wallet Minor
@deroll/router Minor
@deroll/rollup Minor
@deroll/codec Minor
@deroll/create-app Patch
@deroll/docs Patch
@deroll/examples Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
deroll Ready Ready Preview, Comment Jul 13, 2026 4:58pm
deroll-explorer Ready Ready Preview, Comment Jul 13, 2026 4:58pm

@tuler
tuler force-pushed the feature/libcmt-v2 branch from 62bd7a6 to 85a2e60 Compare July 2, 2026 21:56
@tuler tuler changed the title feat(cmio): port binding to libcmt v2 (rollup/codec split, new wire formats) feat: port to libcmt v2 — @deroll/cmio raw binding + new @deroll/codec package Jul 3, 2026
@tuler tuler changed the title feat: port to libcmt v2 — @deroll/cmio raw binding + new @deroll/codec package feat: port to libcmt v2 — new @deroll/rollup binding + @deroll/codec packages Jul 3, 2026
@vercel
vercel Bot temporarily deployed to Preview – deroll-explorer July 3, 2026 20:08 Inactive
@vercel
vercel Bot temporarily deployed to Preview – deroll-explorer July 3, 2026 20:13 Inactive
… stack redesign

Track machine-guest-tools 2fefcb1 (the libcmt v2 "output indexing" overhaul).
The binding is renamed @deroll/cmio -> @deroll/rollup and reduced to a thin,
raw rollup.h wrapper (waitForInput/emitOutput/emitReport/emitException/
progress/close/run); all EVM-ABI wire formats move to the new pure-JS,
browser-compatible @deroll/codec package (dual ESM+CJS, ox + abitype only,
argument types derived from the ABI, verified byte-for-byte against libcmt's
cast-generated golden vectors). Formats follow upstream: a bytes32 appContext
leads every output (optional, zero-hash default), Erc*-cased names, no data
payload on ERC-721/1155 transfers, (uint256,uint256)[] items batch pairs;
delegate-call vouchers and gio are gone.

The app pillar is redesigned on top: advance handlers receive the codec's
flat Advance object (Hex payloads, bigint numbers) and return booleans,
inspect handlers get the raw Hex query, and the App's output methods are
synchronous and typed (createNotice, createCallVoucher, createErc*Transfer,
createOutput as escape hatch) with an app-wide appContext default. The wallet
parses portal deposits from Hex and its withdrawErc* methods return typed
transfer objects for the matching create method; the router reports plain
strings. Examples, docs (new /codec and /rollup sections, reworked /app
section with a v1->v2 migration guide) and changesets are updated; tests
cover the golden vectors, exact output byte layouts, and the full request
loop against the libcmt mock.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FLcy7oTUN1sLhJLG4tHMSB
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.

2 participants