Repository navigation
refactor(typewriter): parse apps from solc output, tested with inline Solidity - #116
Merged
Merged
Conversation
… Solidity Split Solidity parsing into a pure fundamental and a thin build step: - sol-parse.ts: parseSolidity(solcOutput, path) reads the app from solc's standard JSON output, with no IO. It matches dispatch branches and decodes by declaration ID instead of identifier names, so renamed dispatch params, reversed comparisons, `Mutation(mutation) == Mutation.X`, inline decodes, and an inherited `dispatch`/`Mutation` all work. Mutation params can now be nested structs, user-defined value types, and contracts, and `uint`/`int` canonicalize to uint256/int256. Duplicate branches are an error, and an app that links an external library is rejected (the old hex check was non-strict and never caught it). - sol-build.ts: loadSolidityApp runs forge with --build-info and passes the solc output to parseSolidity, replacing the artifact globbing and import-graph walk. - The tests compile inline Solidity with solc-js next to the real Typewriter.sol and check results and errors with inline snapshots. The forge fixture contracts are gone; test/contracts stays for Typewriter.t.sol. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Add the missing cases found by listing what a valid app must satisfy: a scheduler fixed in the inheritance list, a missing state variable, a file-level Mutation enum (now named in the error), a numeric comparison in dispatch, and nested struct params through executeInputSchema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dispatch is the app's to write, so the parser no longer analyzes it. Mutation.X takes its params from the one struct named X in the entrypoint file or its imports, the convention the README already documents. The app contract must inherit only Typewriter and declare Mutation inside itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…execute Each Mutation member X needs exactly one library XMutation, in the entrypoint file or its imports, that declares struct X (the params) and function execute(State storage, X memory, bytes32) internal, where State is the type of the app's state variable. A ...Mutation library without a Mutation member is an error. OrderBook renames its executeX functions to execute and gives AddInstrument's an accountID param. Assessment moves its mutation logic out of dispatch into AMutation.execute and BMutation.execute, unchanged, and its sim storage layout constant takes the new state astId. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app contract can't add public or external functions (including fallback and receive) or public variables, and keeps all its storage in state; constants are allowed. execute must return nothing. A library named ...Mutation without a Mutation member is no longer an error: the only library check is one XMutation per Mutation member. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop internals (revm, storage proxies, slot-write rehydration, preimages, the reorg algorithm, forge/AST parsing, the scheduler-only execute ABI) and list the app contract rules in one place. Fix two stale examples: the Concepts mutation now follows the XMutation library convention, and the FORCE_INCLUSION_DELAY constructor passes scheduler to Typewriter. The root README had drifted to document features that no longer exist (safe status, onFatalError, mutation id); it is now a copy of the package README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 13340c2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kyscott18
added a commit
that referenced
this pull request
Nov 2, 2026
…ct rules Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 tasks done
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
Solidity parsing is now a pure fundamental with a thin IO step in front:
sol-parse.ts:parseSolidity(solcOutput, path)takes solc's standard JSON output and returns{ contractName, abi, bytecode, storageLayout, mutations }, or throws aConfigError. It does no IO and doesn't use Effect. The file went from 829 to 338 lines.sol-build.ts:loadSolidityApp(entrypoint)runsforge build --build-info, hands the solc output toparseSolidity, and refreshes the.sol.d.ts. This replaces the artifact globbing and import-graph walk. A full-project build still reproducesapps/token's committedToken.sol.d.tsbyte for byte.createSolidityTypewriterAppis gone.index.tsand the sim pass the parsed app straight tocreateTypewriterApp.Behavior changes
XMutationlibrary for eachMutationmemberX: it must declarestruct Xandexecute(State storage, X memory, bytes32) internal. The parser no longer readsdispatch.executeXtoexecuteand givesAddInstrumentanaccountIDparam (forge tests 24/24;OrderBook.sol.d.tsregenerated, AST ids only). Assessment moves its mutation logic fromdispatchintoAMutation.execute/BMutation.executeunchanged; the sim'sstateastId constant is updated. Token already conformed.Typewriter, not be abstract, declareMutationinside itself, add no public/external functions or variables, and keep all storage instate.tuple/tuple[]; abipg, ox, andexecuteInputSchemaalready support them), user-defined value types, and contracts/interfaces (address).uint/intcanonicalize touint256/int256. Before, a struct field writtenuintproduced the ABI typeuint.executesignature, public/external entrypoints or extra storage on the app contract, and apps that link an external library. The old bytecode check used non-strictHex.validate, which only checks for0x, so unlinked placeholders passed. Typewriter executes only the app contract, so a library call would also break offchain execution.AddMutation must declare `struct Add` with the mutation's params and `function execute(State storage state, Add memory mutation, bytes32 accountID) internal`.What a valid typewriter contract needs, and the test for each
TypewriterTypewriterOwnabletoo(address scheduler)statethat is a structenum Mutationinside itselfMutationmembers don't use built-in names; at most 253 membersMutation.Xhas exactly one libraryXMutationin the entrypoint file or its importsXMutationdeclaresstruct X(the params) andfunction execute(State storage, X memory, bytes32) internalreturning nothing,Statebeing thestatevariable's typeexecuteAdd; noaccountID; otherState;calldata;public; returns a valuepublic/externalfunctions (incl.fallback/receive) or public variables, and no storage besidesstate(constants allowed)State public state, extra variable, immutable; constants and internal/private helpers allowedexecuteInputSchemamutation-schema.test.tsnested structsdispatchis the app's own code: the parser doesn't read it. Not yet checked: determinism (external calls,COINBASE,PREVRANDAO, …), the next PR.Tests
sol-parse.test.tscompiles inline Solidity with solc-js 0.8.37 next to the realTypewriter.sol(~230ms per compile), runsparseSolidity, and checks the result with inline snapshots. There are 20 tests: every ABI type a param can have, library lookup across imports, every error path, and the declaration formatter. The forge fixture contracts are deleted.test/contracts/foundry.tomlstays, since it runsTypewriter.t.sol(21/21 pass).Test plan
bun testin packages/typewriter: 91 pass, 0 failtsc --noEmit,biome check .forge testintest/contracts(Typewriter.t.sol)🤖 Generated with Claude Code