gnd: Support tuple/struct event params in subgraph scaffolding#6661
Open
incrypto32 wants to merge 5 commits into
Open
gnd: Support tuple/struct event params in subgraph scaffolding#6661incrypto32 wants to merge 5 commits into
incrypto32 wants to merge 5 commits into
Conversation
incrypto32
force-pushed
the
incrypto32/gnd-tuples
branch
3 times, most recently
from
July 17, 2026 10:29
390be45 to
9bea540
Compare
Replace the scaffold module's hand-rolled ABI parsing with alloy's typed
Event/EventParam, mirroring the codegen side (which already uses them). Events
are parsed in file order via serde; a malformed event is skipped with a warning
instead of silently dropping individual params.
The shared preprocessing (artifact-format unwrapping, anonymous/param{index}
defaults) moves from commands/codegen.rs to the abi module so scaffold and
codegen share it. An unnamed param is one solc wrote as "name": "" or a
hand-written ABI omitted entirely; both now yield param{index} entity fields
(matching graph-cli) rather than every unnamed param collapsing onto a single
value field, which is not valid GraphQL once an event has two of them.
Scaffolding also accepts Foundry/Hardhat/Truffle artifact ABIs.
Build the manifest event signature with a shared abi::event_signature_with_indexed helper instead of a scaffold-local copy that printed the raw ABI type. The helper uses each param's canonical type (selector_type), so a tuple expands to its components, e.g. Filled((address,uint256),uint256). Previously a tuple param produced Filled(tuple,uint256), whose topic0 hash did not match the on-chain event, so the handler never fired. The helper is lifted from validation, which already built the same string, so the two cannot drift apart on the manifest's signature format.
A tuple parameter now expands to one field per component (data -> data_account, data_amount) in both the schema and the mapping, with nested accessors (event.params.data.account), via a shared flatten_event_inputs helper. Previously a tuple collapsed to a single Bytes field that did not match the generated event params. An address[] is changetype'd to Bytes[] so the assignment type-checks. Only address qualifies: Address extends Bytes, so the cast is an upcast of something that already is a Bytes. ethereum.Tuple extends Array<Value> and is not a Bytes, so casting a tuple[] would be an unchecked reinterpret that compiles and then writes heap pointers into the entity; without a cast it fails to compile, which is the honest outcome until tuple[] is unrolled or encoded properly. An indexed reference type is exempt from unrolling: a topic is 32 bytes, so the EVM stores keccak256(encoding) and the value itself is absent from the log. The binding getter yields Bytes accordingly, so such a param becomes a single bytes32 leaf with no unroll and no cast. indexed_input_type, which already encoded this rule for the codegen, moves to the abi module so both sides share one definition.
Scaffold an ABI whose event carries a non-indexed tuple alongside an indexed tuple and an indexed array, run codegen, and check the generated output: the non-indexed tuple unrolls into per-component fields read through the nested accessor, while the indexed params, which the EVM reduces to a topic hash, each stay a single Bytes field read whole. Asserting against the generated bindings pins the accessors to the getters codegen actually emits.
Regression guard for the LHS/RHS escaping mismatch: codegen a contract with a reserved-word param (`new`) and an unnamed param, then assert the mapping's event.params accessor matches the getter the bindings actually expose (new_, param1).
incrypto32
force-pushed
the
incrypto32/gnd-tuples
branch
from
July 17, 2026 12:43
9bea540 to
f22ffc5
Compare
incrypto32
marked this pull request as ready for review
July 20, 2026 09:43
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.
Base is master now that #6660 is merged.
A tuple/struct event param used to collapse into a single
Bytesfield and got dropped from the event signature, so the scaffolded subgraph didn't build and its topic0 never matched the on-chain event. This adds tuple support to the scaffolder.The first commit swaps the scaffold's hand-rolled ABI parsing for alloy's typed
Event/EventParam, which codegen already used. The fixes build on that.param{index}fields (matching graph-cli) instead of every one collapsing ontovalue, and Foundry/Hardhat/Truffle artifact ABIs work.Filled((address,uint256),uint256), so topic0 matches. Uses a shared signature builder lifted from validation, which had the same code.databecomesdata_account,data_amount) in both schema and mapping, with nested accessors.Bytesfield with no unroll and no cast.indexed_input_typealready encoded this for codegen and moves to the abi module so both sides share one definition.address[]getschangetype<Bytes[]>. Only address:Address extends Bytes, so the cast is an upcast.ethereum.TupleextendsArray<Value>and isn't aBytes, so castingtuple[]would be an unchecked reinterpret that compiles and writes heap pointers into the entity. It gets no cast and fails to build instead. This diverges from graph-cli, whose typecast map caststuple[].Tests: unit coverage for the unroll, signatures, indexed handling and the cast; plus an e2e that scaffolds a tuple ABI (indexed and non-indexed) through real codegen and checks the mapping accessors against the generated getters.
Follow-up, tracked separately: the scaffold still carries Solidity types as strings where codegen uses
DynSolType.