Skip to content

Add ERC: Custom Encoding Layout for ERC-7730 - #1925

Open
forshtat wants to merge 43 commits into
ethereum:masterfrom
forshtat:ideas-erc7730-non-abi-dispatch
Open

forshtat wants to merge 43 commits into
ethereum:masterfrom
forshtat:ideas-erc7730-non-abi-dispatch

Conversation

@forshtat

@forshtat forshtat commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

When opening a pull request to submit a new EIP, please use the suggested template: https://github.com/ethereum/EIPs/blob/master/eip-template.md

We have a GitHub bot that automatically merges some PRs. It will merge yours immediately if certain criteria are met:

  • The PR edits only existing draft PRs.
  • The build passes.
  • Your GitHub username or email address is listed in the 'author' header of all affected PRs, inside .
  • If matching on email address, the email address is the one publicly listed on your GitHub profile.

forshtat and others added 16 commits July 26, 2026 20:02
Adds `layout` and `dispatch` keys so ERC-7730 descriptors can describe
fields whose bytes are not plain Solidity ABI - packed structs, repeated
records, and tag-selected payloads - as seen in Safe MultiSend, Uniswap's
Universal Router, ERC-7579 execute, Circle CCTP messages, EAS attestations,
and ERC-7683 orders. Includes a `call` construct for nested/wrapped
calldata (both byte-embedded and direct signature+positional-args forms)
and a top-level dispatch form for calls that only exist to be redirected.

Worked examples for all six real cases are full, standalone ERC-7730
descriptor files under assets/erc-non-abi-dispatch/, verified against
real mined transactions rather than inlined as snippets. A seventh,
made-up TieredExecutor example demonstrates the direct/positional call
form, explicitly flagged in Rationale as not grounded in an observed
live transaction.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Adds the deterministic-deployment-proxy worked example (initCode/$fallback,
verified EIP-1167 template plus a placeholder Permit2 template) and the
parked stateRef-dispatch feature note, both referenced by the previous
commit's spec text but left out of it.
…action)

Safe's execTransaction signatures field repurposes a fixed-record field
as a byte offset into a shared tail region for EIP-1271 contract
signatures - the one case in this ERC's survey needing genuine
pointer-style indirection within a custom-packed buffer, which object/
sequence/lengthFrom deliberately can't express. Adds the pointer layout
node (bytes width, required containerPath, destination), extends the
byte-width invariant and Security Considerations to cover it (including
the real recursion case: a Safe signing for another Safe), and adds a
worked example verified against a real nested-Safe transaction for the
EOA/APPROVED_HASH paths, with the CONTRACT_SIGNATURE/pointer branch
marked source-verified-only pending a live example.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
…on schema

initCode, $fallback, and pointer are reverted: real, live code paths
are not the same as evidenced usage, and none of the three cleared
that bar despite genuine search effort (see prior discussion). Their
example files (deterministic-deployment-proxy, safe-exectransaction)
are removed, and the spec text, Rationale, Security Considerations,
and Test Cases are updated accordingly. Balancer's joinPool/exitPool
Test Case entry is added, since those files already existed but had
no prose write-up.

All example descriptors are migrated to the ERC's current vocabulary
(type/object/switch/expression/format:"calldata", $default inside
cases) and validate cleanly against a new, full companion JSON Schema
(erc7730-non-abi-dispatch.schema.json) built as a complete schema
rather than a thin extension - composing new properties onto schemas
closed with additionalProperties:false, or extending a schema reached
only via $ref, does not validate the way a thin allOf+$ref extension
would suggest (verified empirically against real tooling, not just by
reading the spec). Fixed several real bugs surfaced only by validating
against actual tooling: format:"array" was never a real ERC-7730
format (the Universal Router example now uses per-element switch via
$index correlation instead); uint/bitfield byte widths were wrongly
restricted to powers of two in the prose, contradicting the spec's own
bitfield example; the switch case-value "format" branch didn't allow
params. All $comment annotations are stripped from the example files.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@eip-review-bot

eip-review-bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Collaborator

File ERCS/erc-0000.md

Requires 1 more review from Editors: @g11tech, @jochem-brouwer, @samwilsn, @xinbenlv

@eip-review-bot eip-review-bot changed the title Add ERC: Companion for ERC-7730 defining non ABI based 'bytes' data dispatch Add ERC: Custom Encoding Layout for ERC-7730 Aug 3, 2026
@github-actions github-actions Bot added the w-ci label Aug 3, 2026
Comment thread ERCS/erc-0000.md
@@ -0,0 +1,436 @@
---
eip: 0000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eip: 0000
eip: 8362

Assigning next sequential EIP/ERC/RIP number.
Numbers are assigned by editors & associates.

Please also update the filename.

Comment thread ERCS/erc-0000.md
title: Custom Encoding Layout for ERC-7730
description: Format to describes any non-standard byte encodings for ERC-7730
author: Alex Forshtat (@forshtat)
discussions-to: https://github.com/ethereum/ERCs/pull/1925

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a discussions topic on Eth Magicians.

@PrisionMike PrisionMike left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not understand where operation is being used and what interaction is trying to achieve.

I love the overall shape of the work. I think this is what it should look like if we want to support these more packed contracts. I would want to see examples for the LiFi *packed bridge functions too.

I am concerned about the deeply nested jsons that we have, the field name mangling etc.

And the write-up could use some more elaboration and some restructuring so it reads more as an extension of ERC-7730.

I will try to use some of these examples as a bespoke parser in Trezor and see how it goes. Will reach out to @forshtat for a synchronized review/rework so we can get it moving faster.

Comment thread ERCS/erc-0000.md Outdated

The mechanism for declaring an entry in a `sequence` data structure that is not represented by an ABI-encoded parameter.

It can also be used as a stand-in for any other complex data structure in the formating process.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be the default explanation. It looks odd to define something as generic as the object to be what element already does well and more specifically.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Rephrased.

Comment thread ERCS/erc-0000.md
"path": "transactions",
"customEncoding": {
"type": "sequence",
"element": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

element should be an attribute of customEncoding ? Any rationale?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just tells us that the transactions parameter IS a sequence of elements.
The customEncoding must have a type. It accepts one of: [uint, bytes, address, bool, bitfield, object, sequence, switch].
As we specified it is a sequence, it can use element.
The alternative would be a wrapper such as { "type": "sequence", "sequence": { "element": ... } }. It adds a nesting level for little gain.
Does this make sense?

Comment thread ERCS/erc-0000.md Outdated
"schema": { "type": "bytes" },
"label": "Execution",
"format": "calldata",
"params": { "calleePath": "target", "amountPath": "value" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visible ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not plan to drop parameters in the first place - added visible back. In general we should double-check custom layouts did not drop features when merging them back to v2.2

Comment thread ERCS/erc-0000.md Outdated
{
"type": "bitfield",
"bytes": 20,
"fields": [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these fields the same as in the parent spec? A full spec using these special encoding members would mangle with the fields in the parent specifcation whose objects don't have a name field.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will these name attributes be used as label or would they be used in the path attribute to assign labels? The examples here seem to suggest the latter, as beforeSwap doesn't read as a label much

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, renamed fields (from the bitfied) to bitranges to avoid any trace of confusion.

So yeah, I think named fields should be used in the path attributes, but it looks like it was not fully specced out. Added text.
But, if the field is a simple value it can be displayed directly with a "Label" I think, what do you say?
Please see the updated example.

Comment thread ERCS/erc-0000.md Outdated
```json
{
"type": "bitfield",
"bytes": 20,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better use something like raw bitwidth for easier indexing of the variables.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is common to have byte-based encodings but with bitwidth we could define a node that starts or ends mid-byte, for example 12 bits, then a uint of 8 bits, then 12 bits.
So I added bitwidth as an optional alternative to bytes. Hope that works.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might even allow for an enum type encoding for packed parameters. So you have these bytes representing an address, and these 2 bits, signalling some flags, etc. I think that would be helpful for Uniswap type descriptors. Will come back to it once I see how that has been encoded here.

forshtat and others added 8 commits October 7, 2026 23:36
…iptor

- Object field entries may carry `offset`, a relative byte skip before decoding.
- Wrap the `countFrom` snippet in a complete `completeTransfer` descriptor so
  the sibling relationship between `numSignatures` and `signatures` is visible.
- Add `offset` to the JSON schema and use it in the Wormhole example.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Avoids clashing with ERC-7730's `fields`, whose items are identified by `path`.
Updates spec, schema and examples.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…n bitranges

- Add "Addressing entries" section: entry `name` is a path segment usable by
  ERC-7730 display fields, with rules for naming, switch, display and conflicts.
- Allow `label`, `format`, `params`, `visible` on bitranges entries and
  constrain entry `name` to an identifier pattern in the schema.
- Replace the tiny bitfield example with a full Uniswap v4 descriptor showing
  both inline and path-based display.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ges example

- `bytes` nodes must set exactly one of `length`, `lengthFrom` or `toEnd`.
- Add example-lifi-packed-bridges.json covering the cBridge, Hop and Across V4
  Min/Packed functions, checked against the facet sources and the schema.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@forshtat

forshtat commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

I did not understand where operation is being used and what interaction is trying to achieve.

I love the overall shape of the work. I think this is what it should look like if we want to support these more packed contracts. I would want to see examples for the LiFi *packed bridge functions too.

I am concerned about the deeply nested jsons that we have, the field name mangling etc.

And the write-up could use some more elaboration and some restructuring so it reads more as an extension of ERC-7730.

I will try to use some of these examples as a bespoke parser in Trezor and see how it goes. Will reach out to @forshtat for a synchronized review/rework so we can get it moving faster.

operation says how the embedded call is executed: CALL, DELEGATECALL, CREATE, CREATE2 or CALLCODE. ERC-7730's calldata format has no such parameter. It assumes a plain call. Safe MultiSend gets it from the data: an operation byte inside each packed transaction (0x01 is DELEGATECALL, anything else is CALL).

interaction describes a call that the contract builds by itself from decoded values. The contract takes account and amount, and internally calls grantReward(account, amount) on a target. There is no calldata bytes array to hand to a format: "calldata" entry in this scenario.

I added an example file to elaborate the distinction.

Thank you for the review, it was extremely helpful!

Regarding the LiFi support, I will continue the investigation but seems like nothing should cause a major issue in these functions, but they did trigger a minor change in the ERC:

Support: yes, for all 16 functions, with no new keyword needed beyond what's already in the PR.

- 8 *Packed functions: no ABI params. Covered by `@.data` + customEncoding object, `offset: 4` to skip the selector, and `uint` / `bytes` / `address`. The `toEnd` flag I just added is needed for Across's trailing message.

Checked:
- Descriptor validates against our schema (0 errors).
- Every packed layout's order and width matches the contract's own encoder or byte map. Total lengths match.
- All 8 *Min signatures match the source.

Gaps and caveats:
- Misleading display: Hop shows `destinationAmountOutMin` and the deadline even when the `destination` is `chain 1`, where the contract ignores them. `visible` can't depend on another field.
- Across `bytes32 addresses`: shown as raw hex. ERC-7730 has interoperableAddressName, but it does not fit this scenario.
- Unverified: `cBridge` slippage unit comes from a comment in LiFi's interface file.

forshtat and others added 2 commits October 8, 2026 13:40
- `string` (like `bytes`) must set exactly one of `length`, `lengthFrom`, `toEnd`.
- Trailing zeros are stripped only for `length`; reject non-NFC text.
- Move the banned-character list into a table; soften the uniqueness claim.
- Mention `string` among entry value types; update the Zodiac example.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Comment thread assets/erc-0000/example-safe-multisend.json Outdated
Comment thread assets/erc-0000/example-safe-multisend.json Outdated
@github-actions github-actions Bot added w-ci and removed w-ci labels Oct 8, 2026
`~.` names the enclosing `object` in a customEncoding tree, or the decoded
payload of a `switch` case. It is optional but recommended, and replaces `.[]`
with `~.[]` for nameless arrays. A string `operation.expression` is a path.
Examples updated to match.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
"label": "Execution",
"switch": {
"expression": {
"path": "mode.callType"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need also an identifier here (could very well be ~) to denote that this is also a pre-decoded path variable.

Also, we may need to assert that a switch can be of certain types only.

forshtat and others added 5 commits October 18, 2026 15:30
…ike attributes

customEncoding moves from a field key to `format: "customEncoding"` with the
root node in `params`, so it looks like every other ERC-7730 format.

Object entries are declared like fields (label, visible, format, params) and may
omit `schema` when their `format` implies a layout. The decoded type must suit
the format. `~.` paths may refer to any sibling entry.

Add a `name` and `path` section explaining declaring vs looking up, with one
example for each way of displaying an entry. Document the switch case-value form
and that wallets MAY show a customEncoding field's label as a section header.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Make customEncoding a format; field-like object entries; explain name vs path
Remove the features that are deferred from the core profile and carry
them in a separate PR instead. Specification text, JSON schema and
examples are all affected.

Features moved:
- Inline `switch` node (a switch that reads its expression from the
  buffer at the cursor), including `payloadFrom`
- Byte-aligned `bitfield` node and `bitranges`
- ASCII/UTF-8 `string` node, including the NFC and bidi display rules
- Literal `count` on `sequence`
- `countFrom` on `sequence`
- Packed Uniswap V3 `path` (the ERC had no text for it, so nothing to
  remove)

Specification (ERCS/erc-0000.md):
- Remove the `bitfield` and `string` sections, the inline `switch`
  section and the `count`/`countFrom` text.
- Remove the Test Cases that depend on them: Uniswap v4 initialize,
  Zodiac Roles v2 and Wormhole Token Bridge.
- Reword references that would otherwise dangle (node type list,
  `bitranges`, `lengthFrom`, `beforeSwap`, and the Balancer example
  link).

Schema (erc7730-non-abi-dispatch.schema.json):
- Remove the `string`, `bitfield` and `switchNode` definitions and the
  `count`/`countFrom` properties of `sequence`.

Examples removed:
- example-uniswap-v4-initialize.json (bitfield)
- example-zodiac-roles.json (string)
- example-wormhole-token-bridge.json (countFrom, inline switch)
- example-balancer-relayer-library.json (inline switch)

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01S9y93SSYgcjrrYsbZycSXr
@github-actions

Copy link
Copy Markdown

The commit e5f8d3d (as a parent of ca42f1e) contains errors.
Please inspect the Run Summary for details.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

6 participants