Skip to content

feat: enhance TypedEncoder with additional encoding types#47

Closed
re1ro wants to merge 11 commits intomainfrom
feat/typed-data-encoder
Closed

feat: enhance TypedEncoder with additional encoding types#47
re1ro wants to merge 11 commits intomainfrom
feat/typed-data-encoder

Conversation

@re1ro
Copy link
Collaborator

@re1ro re1ro commented Oct 28, 2025


TypedEncoder - Complete Library Addition

This PR adds the TypedEncoder library - a comprehensive runtime struct encoding system supporting 10 encoding types for EIP-712 hashing, ABI encoding,
function calls, and contract address computation.

All 10 Encoding Types (All New in This PR)

Standard ABI Encoding

  1. Struct - Standard struct encoding with proper head/tail layout, produces abi.encode() compatible output
  2. Array - Polymorphic array encoding where nested structs become array elements encoded as bytes
  3. ABI - Pure ABI encoding without offset wrapper, used when embedding structs as bytes in parent structures
  4. Packed - Returns abi.encodePacked(all_fields) without hashing for custom byte encoding (returns dynamic bytes)

Function Call Encoding

  1. CallWithSelector - Combines bytes4 selector with ABI-encoded params for contract calls (like abi.encodeWithSelector)
  2. CallWithSignature - Computes selector from signature string and combines with params (like abi.encodeWithSignature)

Hash Encoding

  1. Hash - Computes keccak256(abi.encodePacked(all_fields)) for compact hash commitments with expandable underlying data (returns 32 bytes)

Contract Address Computation

  1. Create - Computes CREATE opcode addresses using RLP encoding: keccak256(rlp([deployer, nonce]))[12:] (returns 20-byte address)
  2. Create2 - Computes CREATE2 deterministic addresses per EIP-1014: keccak256(0xff ++ deployer ++ salt ++ initCodeHash)[12:] (returns 20-byte address)
  3. Create3 - Computes bytecode-independent addresses using two-stage CREATE2 + CREATE pattern (returns 20-byte address)

Quick Reference Table

Encoding Type Output Type Dynamic/Static Primary Use Case
Struct ABI-encoded struct Depends on fields Standard struct encoding
Array ABI-encoded array Always dynamic Polymorphic struct arrays
ABI Raw ABI encoding Always dynamic Nested bytes encoding
Packed bytes Always dynamic Custom byte encoding
CallWithSelector Calldata Always dynamic Contract calls with selector
CallWithSignature Calldata Always dynamic Contract calls with signature
Hash bytes32 Static (32 bytes) Compact hash commitments
Create address Static (20 bytes) CREATE address prediction
Create2 address Static (20 bytes) Deterministic deployments
Create3 address Static (20 bytes) Bytecode-independent addressing

re1ro and others added 6 commits October 28, 2025 06:44
- Moved src/libs/TypedEncoder.sol to src/lib/TypedEncoder.sol
- Updated import paths in test files
- Removed empty src/libs/ directory
- All tests passing (28/28)
* feat: add polymorphic support

* address PR comment
Update import from src/libs/ to src/lib/ to match new TypedEncoder location
- Remove asBytes parameter from _encodeAbi and _encodeChunkFields
- Add _hasDynamicFields helper to distinguish encoding type from field contents
- Simplify _encodeAsArray to enforce single chunk and encode structs normally
- Fix ABI encoding type to properly wrap as bytes fields in parent structs
- Add offset wrapper for dynamic ABI structs, skip for static

Tests:
- Add TypedEncoderErrors.t.sol with 10 error validation tests
- Add TypedEncoderNested.t.sol with 10 complex nesting tests
- Refactor TypedEncoderPolymorphic.t.sol to use CallWithSignature properly
- Update TypedEncoderCalldata.t.sol expectations for ABI encoding as bytes
- Fix EIP-712 typeHash alphabetical ordering
- All 68 tests passing
@re1ro re1ro requested a review from fish-sammy October 28, 2025 22:28
@re1ro re1ro self-assigned this Oct 28, 2025
Copilot AI review requested due to automatic review settings October 28, 2025 22:28
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR moves the TypedEncoder library from src/libs/ to src/lib/ and significantly enhances it with new encoding types and comprehensive test coverage. The main changes include:

  • Added EncodingType enum with 5 encoding modes (Struct, Array, ABI, CallWithSelector, CallWithSignature)
  • Added custom error types for better error handling
  • Added support for polymorphic arrays and calldata encoding
  • Updated all test files to use the new import path and add the required encodingType field
  • Added new comprehensive test files covering nested structs, polymorphic arrays, calldata encoding, and error cases

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/lib/TypedEncoder.sol New location with enhanced functionality including encoding types, polymorphic array support, and calldata encoding capabilities
src/libs/TypedEncoder.sol Old file removed (library relocated)
test/libs/TypedEncoderHash.t.sol Updated import path and added encodingType field to all Struct initializations
test/libs/TypedEncoderEncode.t.sol Updated import path and added encodingType field to all Struct initializations
test/libs/TypedEncoderNested.t.sol New comprehensive test file for nested struct encoding scenarios
test/libs/TypedEncoderPolymorphic.t.sol New test file for polymorphic array encoding
test/libs/TypedEncoderCalldata.t.sol New test file for CallWithSelector and CallWithSignature encoding
test/libs/TypedEncoderErrors.t.sol New test file for error handling validation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

re1ro added 3 commits October 29, 2025 07:29
Add new Hash encoding type that computes keccak256(abi.encodePacked(all_fields))
for compact hash commitments with expandable underlying data.

Core implementation:
- Added EncodingType.Hash enum value
- Implemented _encodeHash() to compute keccak256 of packed struct data
- Implemented _encodePackedChunk() for abi.encodePacked field packing
- Implemented _encodePackedArray() for array packing without length prefix
- Updated encode() to handle Hash type (returns 32 bytes)
- Updated _isDynamic() to treat Hash as static (32-byte output)
- Updated _encodeChunkFields() to handle nested Hash structs

Key features:
- Nested Hash structs create tree of hashes (hash first, then pack bytes32)
- Arrays packed without length prefix for maximum compactness
- No structural restrictions (any chunk/field configuration allowed)
- Static when nested in parent structs (encoded inline, no offset)

Test coverage:
- 13 comprehensive tests covering primitives, nesting, arrays, edge cases
- All 81 tests pass (13 new + 68 existing)
…dEncoder

Add four new encoding types to extend TypedEncoder functionality:

- Packed: Returns abi.encodePacked(all_fields) without hashing for custom
  byte encoding. Supports recursive packing of nested Packed structs.
  Returns dynamic bytes.

- Create: Computes CREATE opcode addresses using RLP encoding. Implements
  comprehensive RLP encoding for nonces 0 to uint64 max (9 ranges).
  Formula: keccak256(rlp([deployer, nonce]))[12:]. Returns 20-byte address.

- Create2: Computes CREATE2 deterministic addresses per EIP-1014.
  Formula: keccak256(0xff ++ deployer ++ salt ++ initCodeHash)[12:].
  Returns 20-byte address.

- Create3: Computes bytecode-independent addresses using two-stage pattern.
  Stage 1: CREATE2 intermediary, Stage 2: CREATE with nonce=1.
  Based on Axelar implementation. Returns 20-byte address.

Core implementation:
- Added 4 EncodingType enum values
- Added 3 custom validation errors
- Implemented 4 core encoding functions with full validation
- Updated encode(), _encodePackedChunk(), _encodeChunkFields(), _isDynamic()
- Packed treated as dynamic, Create/Create2/Create3 as static

Test coverage:
- 39 new tests (15 Packed + 24 Create*)
- All 120 tests passing (81 existing + 39 new)
- Comprehensive coverage of primitives, nesting, arrays, errors, edge cases
@re1ro re1ro force-pushed the feat/typed-data-encoder branch from b1f82be to d422ca8 Compare October 29, 2025 16:28
re1ro added 2 commits October 29, 2025 12:34
- enforce chunk structure for create/call/array encodings before delegating
- document recursion constraints and external struct usage via harnesses
- temporarily skip revert expectation tests until revert assertions can run
@re1ro
Copy link
Collaborator Author

re1ro commented Oct 31, 2025

closed in favor of #52

@re1ro re1ro closed this Oct 31, 2025
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