Repository navigation
Refactor: Move codec package to Ox - #205
Merged
Merged
Conversation
Pins the derived types, the hand-rolled byte readers and writers, and every expected payload as a frozen literal, so a change of encoding library has to preserve them.
🦋 Changeset detectedLatest commit: 191c166 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tuler
reviewed
Oct 22, 2026
viem stays a devDependency until the source stops importing it. Ox ships no CommonJS build, so the require entry point needs Node 22.12 or newer.
Unknown selectors now raise ox's AbiItem.NotFoundError. Ox has no polymorphic size and does not resolve a function by selector when handed a resolved ABI item, so portal gains a byteSize helper and both hex decoders guard the selector explicitly.
Also notes that the CommonJS entry point needs Node 22.12, since ox ships no CommonJS build.
brunomenezes
force-pushed
the
refactor/move-codec-to-ox
branch
from
October 23, 2026 11:27
4436ad9 to
191c166
Compare
tuler
approved these changes
Oct 23, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Moves
@cartesi/codecfrom viem to ox1.8.9, the Ethereum primitives library viem itself is built on, and drops viem from the package entirely — runtime, peer and dev.The package is pure encode/decode with no client, transport or chain machinery at all, yet it carried
viem: ^2.0.0as a peer dependency, so decoding a blob meantnpm install @cartesi/codec@alpha viemand pulling in chains, clients, transports and accounts. There is now nothing to install alongside it.The "breaking" change: data with an unrecognized selector raises a different error class.
decodeInputanddecodeOutputnow throw ox'sAbiItem.NotFoundErrorwhere they threw viem'sAbiFunctionSignatureNotFoundError. Nothing about the signatures changes, so this is invisible at compile time and only shows up where a consumer matches the old class withinstanceof.Every exported type is unchanged. ox's
Hex.Hexis`0x${string}`,Bytes.BytesisUint8Array, andAddress.Addressis the abitypeAddressviem re-exports, so code typing codec values with viem'sHex,ByteArrayandAddressstill compiles.Only
@cartesi/codecmoves, all the other packages stay as is.Commits order
characterize(tests) → depend on ox → swap the source → docs, so the suite that proves the migration lands first and stays green across it.Refactoring
All five files under
packages/codec/src/move to ox. Most of it is mechanical —bytesToBigInt→Bytes.toBigInt,getAddress→Address.checksum,encodePacked→AbiParameters.encodePacked— but four differences are not, and each would be easy to miss in review.AbiFunction.decodeDatadoes not verify the selector when handed a resolved ABI item; it checksHex.size(data) >= 4and decodes the rest. viem'sdecodeFunctionDataalways resolves the function by the leading four bytes and throws when there is no match. Without a guard,decodeInputwould silently decode unknown-selector data asEvmAdvancearguments. Both hex decoders now compare the selector explicitly before decoding, and a test pins that a well-formed blob with the wrong selector throws rather than decodes.decodeOutputHexrelied on viem's{ functionName, args }union to drive itsswitch. Replaced by a selector table resolved once at module load, dispatching onHex.slice(data, 0, 4), so narrowing comes from the literalswitchrather than library inference — exact tuple types, no casts. One table now feeds the hex dispatch, the byte array dispatch and the three byte array encoders, where there were two parallel sources before, and asatisfies Record<OutputName, …>makes a fourthOutputsfunction a compile error.size.Hex.sizeandBytes.sizeare separate where viem'ssizetook either, soassertMinSize— the one genuinelyHex | Uint8Arraysite — goes through a newbyteSizehelper insrc/bytes.ts.falsein ox 0.9.6,truein 1.x) where viem's is unconditional. It is passed explicitly on the three calls that decode an address so the value is pinned at the call site;Notice(bytes)carries no address, so it is omitted there.abitypestays a direct dependency for exactly one type,AbiParameterToPrimitiveType, which ox has no public equivalent for. Its ABI extraction types do move —ExtractAbiFunction→AbiFunction.FromAbi,ExtractAbiFunctionNames→AbiFunction.ExtractNames— and those are literal aliases of the abitype originals. ox and codec both resolveabitype@1.3.0, so the derived types share one instance.Imports are per-module entrypoints (
import * as Hex from "ox/Hex"), not ox's root module. tsdown keeps ox external either way, so no ox code is bundled into codec at all — what differs is what Node loads at runtime.dist/index.cjsreferences exactly six modules:ox/AbiFunction,ox/AbiItem,ox/AbiParameters,ox/Address,ox/Bytes,ox/Hex. Importing fromoxinstead would evaluate all 83 modules its root re-exports, pulling inzod,@noble/*,@scure/*andens-normalize. A bundler with deep scope analysis (Vite, Rollup, webpack 5, esbuild) shakes the unused ones away and the distinction disappears. However, under plain Node, which is who the CommonJS entry point serves, it does not. Measured here: 57 ms and 16 MB of heap to require the built CJS, against 183 ms and 67 MB to require ox's root.Breaking changes
AbiItem.NotFoundError(ABI function with data "0xdeadbeef" not found.) instead of viem'sAbiFunctionSignatureNotFoundError. Both the hex and the byte array path raise it, as before, and the message still carries the offending selector.AbiParameters.DataSizeTooSmallError, previously viem'sSliceOffsetOutOfBoundsErrororPositionOutOfBoundsErrordepending on how short.dist/index.cjsreaches it through Node'srequire(esm), which is unflagged from that version on. The ESM entry point is unaffected. Verified by packing the tarball and installing it as a real consumer:require()works on 22.12 with no experimental warning, since Node suppresses it for requires originating undernode_modules, and fails withERR_REQUIRE_ESMon 22.11 and below.The root
enginesfloor also rises, to^22.18 || ^24.11 || >=26, which is a stricter and unrelated constraint: it is whattsdown@0.23.0requires. Below 22.18process.features.typescriptis false, tsdown falls back to the optionalunrunpeer this repo does not install, andpnpm buildfails. Root describes what a contributor needs to build and codec's ownenginesdescribes what a consumer needs at runtime.Fixes
include: ["**/src"]in@cartesi/codec,@cartesi/clientand@cartesi/reactmatched nothing, so the reporters had an empty map and no package had usable coverage. Narrowed to./src/**. codec additionally excludessrc/index.ts, which only re-exports, andsrc/types.ts, which declares only types — neither carries a statement to cover; client and react keep their existingexcludelists.Tests
The suite goes from 70 tests in 4 files to 183 in 7, all landing before the source changed.
ParamsToObject— exactly what the migration rewrites.codec.test-d.tsis new, withtypecheckenabled invitest.config.tsas@cartesi/clientalready does. Its assertions name the structural types —`0x${string}`,Uint8Array, abitype'sAddress— rather than any library's aliases, which is what makes the pin hold whichever library sits underneath.src/bytes.tshad no suite. The hand-rolled byte readers and writers were covered only through whatever offsets their three callers happened to use.bytes.test.tsreaches the bounds checks, the padding rules andreadSize's safe-integer guard, which no test could previously hit.representation.test.tsis a nine-case table asserting the hex and byte array paths agree, for input, the three outputs and the five deposits.portal.test.tsrebuilt its expectations at test time with viem'sconcat/numberToHex/encodeAbiParameters. They are now constants generated from the viem implementation while it was still installed, so they cannot drift with whichever library does the encoding and they read in the diff..toThrow()calls became one helper per file pinning the class and the quoted selector in the message. Also added thesliceFromboundary at each deposit type's exact minimum size,assertMinSize's full message, and labelled checksum assertions forVoucher.destinationon both paths — the existing fixture0x…01has no alphabetic digit, so checksumming it was the identity and the assertion could not fail.PS: The evidence that behaviour is preserved: swapping the source to ox failed exactly five tests, all of them the unknown-selector error class, with the other 178 passing untouched, every frozen payload literal included. The
getAddressexpectations were frozen to checksummed literals and still pass, which is also what shows ox'sAddress.checksumagrees with viem'sgetAddress.PS²: I opened an issue#524 on Ox GitHub repo regarding add provenance to releases of 1.x as it affected consumers of cartesi/codec. However, only when these consumers have the
trustPolicy: no-downgradeenabled (e.g. which rollups-explorer has)