Skip to content

Fix tests account#30

Closed
Dargon789 wants to merge 9 commits into
mainfrom
fix-tests-account
Closed

Fix tests account#30
Dargon789 wants to merge 9 commits into
mainfrom
fix-tests-account

Conversation

@Dargon789

@Dargon789 Dargon789 commented Feb 10, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Add Merkle tree–based wrapped signature verification to IthacaAccount and update existing tests and benchmarks to support the extended signature encoding.

New Features:

  • Support Merkle tree–based signature verification for wrapped signatures in IthacaAccount, allowing digests to be authorized via a signed Merkle root.

Bug Fixes:

  • Correct EIP712 domain usage in account signature approval tests to only rely on the verifyingContract field.

Enhancements:

  • Extend the wrapped signature format to carry an additional merkle flag, and route Merkle-wrapped signatures through dedicated Merkle proof verification logic.
  • Add fuzz and benchmark tests covering Merkle signature validation and ERC20 transfers via the orchestrator using passkeys.

Documentation:

  • Bump the IthacaAccount EIP712 version string to reflect the updated signature scheme.

Tests:

  • Add fuzz tests for Merkle proof–backed signatures and their failure modes, and new orchestrator benchmarks for ERC20 transfers signed with passkeys.
  • Update existing tests, simulations, and gas benchmarks to use the new wrapped signature layout including the Merkle flag.

github-actions Bot and others added 7 commits August 5, 2025 22:58
Add testERC20TransferViaPortoOrchestratorWithPasskey() benchmark to isolate
passkey authentication costs from spend limit enforcement costs.

- Uses secp256k1 passkey for transaction signing
- Sets execution permissions for ERC20 transfers
- Requires spend limits (set to max) for passkey operations
- Gas cost: 116,094 (vs 97,030 without passkey, 117,083 with restrictive limits)
- Provides clean measurement of passkey overhead (~19k gas)

Resolves ithacaxyz#272

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Tanishk Goyal <legion2002@users.noreply.github.com>
@vercel

vercel Bot commented Feb 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
legion Canceled Canceled Feb 10, 2026 1:23pm

@sourcery-ai

sourcery-ai Bot commented Feb 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds Merkle tree–backed wrapped signature support to IthacaAccount, extends unwrapAndValidateSignature with a Merkle flag, fixes EIP712 domain handling in tests, and updates tests/benchmarks (including a new orchestrator benchmark) to use the extended wrapped signature format and cover Merkle verification paths.

Sequence diagram for Merkle-backed wrapped signature verification in IthacaAccount

sequenceDiagram
    actor User
    participant Orchestrator
    participant IthacaAccount
    participant MerkleProofLib

    User->>Orchestrator: submit_authorized_action(wrappedSignature)
    Orchestrator->>IthacaAccount: unwrapAndValidateSignature(digest, wrappedSignature)
    IthacaAccount->>IthacaAccount: detect_wrapped_signature()
    IthacaAccount->>IthacaAccount: parse_keyHash_prehash_flag_merkle_flag()
    alt merkle_flag_is_set
        IthacaAccount->>IthacaAccount: _verifyMerkleSig(digest, innerMerkleSignature)
        IthacaAccount->>MerkleProofLib: verifyCalldata(proof, root, digest)
        alt merkle_proof_valid
            MerkleProofLib-->>IthacaAccount: true
            IthacaAccount->>IthacaAccount: unwrapAndValidateSignature(root, rootSig)
            IthacaAccount-->>Orchestrator: isValid,keyHash
        else merkle_proof_invalid
            MerkleProofLib-->>IthacaAccount: false
            IthacaAccount-->>Orchestrator: false,0x0
        end
    else merkle_flag_not_set
        IthacaAccount->>IthacaAccount: optional_prehash(digest)
        IthacaAccount->>IthacaAccount: load_key_by_keyHash()
        IthacaAccount-->>Orchestrator: isValid,keyHash
    end
    Orchestrator-->>User: action_result
Loading

Updated class diagram for IthacaAccount Merkle signature support

classDiagram
    class IthacaAccount {
        +unwrapAndValidateSignature(digest_bytes32, signature_bytes_calldata) bool,bytes32
        -_verifyMerkleSig(digest_bytes32, signature_bytes_calldata) bool,bytes32
        +_eip712Version() string,string
    }

    class MerkleProofLib {
        +verifyCalldata(proof_bytes32_array_calldata, root_bytes32, leaf_bytes32) bool
    }

    class EfficientHashLib {
        +sha2(input_bytes32) bytes32
    }

    class LibBytes {
        +loadCalldata(data_bytes_calldata, offset_uint256) bytes32
        +truncatedCalldata(data_bytes_calldata, newLength_uint256) bytes_calldata
    }

    class IIthacaAccount
    class EIP712
    class GuardedExecutor

    IthacaAccount --|> IIthacaAccount
    IthacaAccount --|> EIP712
    IthacaAccount --|> GuardedExecutor

    IthacaAccount ..> MerkleProofLib : uses
    IthacaAccount ..> EfficientHashLib : uses
    IthacaAccount ..> LibBytes : uses
Loading

File-Level Changes

Change Details Files
Add Merkle-backed signature verification path to IthacaAccount via a new internal verifier and an extended wrapped signature format.
  • Import MerkleProofLib and introduce _verifyMerkleSig to decode an encoded Merkle proof/root/rootSig from calldata, verify the leaf against the Merkle root, then recursively call unwrapAndValidateSignature on the root digest
  • Extend unwrapAndValidateSignature encoding to abi.encode(innerSignature, bytes32 keyHash, bool prehash, bool merkle), adjust parsing logic for the extra flag byte, and branch to _verifyMerkleSig when the Merkle flag is set
  • Preserve existing EOA/self-signature handling and key lookup/usage flow while routing Merkle signatures through the new verification path
src/IthacaAccount.sol
Update version metadata and EIP712 domain usage to align with simplified domain separator expectations.
  • Bump IthacaAccount version string returned by getNameAndVersion from 0.5.10 to 0.5.11
  • Change Account.t.sol to ignore EIP712 name and version when reading the domain, only using verifyingContract to compute the domain separator
src/IthacaAccount.sol
test/Account.t.sol
Extend test helpers and existing test/benchmark flows to the new wrapped signature format that includes a Merkle flag.
  • Update Base.t.sol signature helpers (_p256Sig, _secp256k1Sig, _multiSig, and gas-estimation helpers) to append an extra zero-valued Merkle flag byte to all constructed signatures
  • Adjust Orchestrator, Benchmark, and SimulateExecute tests to construct wrapped signatures with the new trailing Merkle flag byte in all places that previously encoded only keyHash and prehash
  • Add a new benchmark test for ERC20 transfers via the orchestrator using passkey-based authorization to exercise the updated flow
test/Base.t.sol
test/Benchmark.t.sol
test/Orchestrator.t.sol
test/SimulateExecute.t.sol
snapshots/BenchmarkTest.json
Add fuzz coverage for Merkle-based signatures on accounts using the new wrapped signature format.
  • Introduce testMerkleSignature in Account.t.sol that generates a random Merkle tree of digests, derives the root and proof, wraps a root signature into the new Merkle signature format, and checks valid verification for an in-tree digest
  • Exercise negative cases including invalid digests not in the Merkle tree, tampered proofs, and signatures against an incorrect Merkle root to ensure unwrapAndValidateSignature rejects them
test/Account.t.sol

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@snyk-io

snyk-io Bot commented Feb 10, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@Dargon789 Dargon789 marked this pull request as draft February 10, 2026 12:58
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Dargon789, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces Merkle signature verification capabilities to the IthacaAccount smart contract, enhancing its security and flexibility for signature validation. It includes the core logic for verifying Merkle proofs against a root signature and updates the signature encoding scheme to support this new feature. Accompanying these changes are new unit tests to ensure the correctness of the Merkle signature verification and adjustments to existing test utilities to align with the updated signature format. Additionally, some benchmark values for IthacaAccount operations have been refreshed.

Highlights

  • Merkle Signature Verification: Implemented a new internal function _verifyMerkleSig in IthacaAccount.sol to handle Merkle tree signature verification, leveraging solady/utils/MerkleProofLib.sol.
  • Signature Encoding Update: Modified the unwrapAndValidateSignature function in IthacaAccount.sol to support a new merkle flag in the signature encoding, allowing for conditional Merkle verification.
  • Comprehensive Merkle Signature Tests: Added a new testMerkleSignature function in Account.t.sol to thoroughly test the Merkle signature verification logic, including valid proofs, invalid digests, and tampered proofs.
  • Benchmark Updates: Updated various benchmark values in snapshots/BenchmarkTest.json and introduced a new benchmark test for ERC20 transfers via the Orchestrator with a passkey.
Changelog
  • snapshots/BenchmarkTest.json
    • Updated benchmark values for testERC20Transfer_IthacaAccount and testNativeTransfer_IthacaAccount related tests.
  • src/IthacaAccount.sol
    • Added MerkleProofLib import from Solady.
    • Introduced _verifyMerkleSig function for Merkle tree signature validation.
    • Modified unwrapAndValidateSignature to parse a new merkle flag and conditionally call _verifyMerkleSig.
    • Updated the contract version from 0.5.10 to 0.5.11.
  • test/Account.t.sol
    • Imported the Merkle library from murky.
    • Added testMerkleSignature to verify Merkle proof functionality with various scenarios.
    • Adjusted eip712Domain() destructuring to remove unused name and version variables.
  • test/Base.t.sol
    • Modified _p256Sig, _secp256k1Sig, _multiSig, _estimateGasForEOAKey, and _estimateGasForSecp256r1Key functions to append an additional uint8(0) to the signature, indicating a non-Merkle signature by default.
  • test/Benchmark.t.sol
    • Added testERC20TransferViaPortoOrchestratorWithPasskey to benchmark ERC20 transfers through the Orchestrator using a passkey.
  • test/Orchestrator.t.sol
    • Adjusted the signature construction in a test case to include the new uint8(0) Merkle flag.
  • test/SimulateExecute.t.sol
    • Adjusted the signature construction in a test case to include the new uint8(0) Merkle flag.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Dargon789 Dargon789 marked this pull request as ready for review February 10, 2026 12:58

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In unwrapAndValidateSignature, the wrapped signature layout now relies on hard‑coded offsets (e.g. 0x22, n + 1, n + 2); consider introducing named constants or a small helper to document and centralize this layout to reduce the risk of off‑by‑one bugs in future changes.
  • The inline assembly in _verifyMerkleSig assumes the exact ABI layout of abi.encode(bytes32[] proof, bytes32 root, bytes rootSig); adding brief comments explaining the head/tail offsets (e.g. what lives at signature.offset, +0x20, +0x40) would make this easier to maintain and safer to modify.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `unwrapAndValidateSignature`, the wrapped signature layout now relies on hard‑coded offsets (e.g. `0x22`, `n + 1`, `n + 2`); consider introducing named constants or a small helper to document and centralize this layout to reduce the risk of off‑by‑one bugs in future changes.
- The inline assembly in `_verifyMerkleSig` assumes the exact ABI layout of `abi.encode(bytes32[] proof, bytes32 root, bytes rootSig)`; adding brief comments explaining the head/tail offsets (e.g. what lives at `signature.offset`, `+0x20`, `+0x40`) would make this easier to maintain and safer to modify.

## Individual Comments

### Comment 1
<location> `src/IthacaAccount.sol:498-507` </location>
<code_context>
+    /// - Note: Each leaf of the merkle tree should be a standard digest.
+    /// - The signature for using merkle verification is encoded as:
+    /// - bytes signature = abi.encode(bytes32[] proof, bytes32 root, bytes rootSig)
+    function _verifyMerkleSig(bytes32 digest, bytes calldata signature)
+        internal
+        view
+        returns (bool isValid, bytes32 keyHash)
+    {
+        bytes32[] calldata proof;
+        bytes32 root;
+        bytes calldata rootSig;
+
+        assembly ("memory-safe") {
+            let proofOffset := add(signature.offset, calldataload(signature.offset))
+            proof.length := calldataload(proofOffset)
+            proof.offset := add(proofOffset, 0x20)
+
+            root := calldataload(add(signature.offset, 0x20))
+
+            let rootSigOffset := add(signature.offset, calldataload(add(signature.offset, 0x40)))
+            rootSig.length := calldataload(rootSigOffset)
+            rootSig.offset := add(rootSigOffset, 0x20)
</code_context>

<issue_to_address>
**issue (bug_risk):** Calldata decoding for `signature` in `_verifyMerkleSig` is misaligned due to the `bytes` length word.

Because `signature` is `bytes calldata`, `signature.offset` points to the bytes length word, and the ABI-encoded `(bytes32[] proof, bytes32 root, bytes rootSig)` head actually starts at `signature.offset + 0x20`.

The current assembly treats `signature.offset` as the start of the ABI head, so:
- `calldataload(signature.offset)` reads the length of `signature`, not the `proof` offset.
- `root := calldataload(add(signature.offset, 0x20))` reads the word after the length, which is not guaranteed to be `root`.

This misalignment will produce wrong offsets and can cause garbage or out-of-bounds reads. You need to first skip the bytes length and then treat the remaining region as the ABI head, e.g.:
```solidity
assembly ("memory-safe") {
    let head := add(signature.offset, 0x20) // skip bytes length

    let proofRel  := calldataload(head)
    let rootWord  := calldataload(add(head, 0x20))
    let rootSigRel:= calldataload(add(head, 0x40))

    let proofOffset := add(head, proofRel)
    proof.length := calldataload(proofOffset)
    proof.offset := add(proofOffset, 0x20)

    root := rootWord

    let rootSigOffset := add(head, rootSigRel)
    rootSig.length := calldataload(rootSigOffset)
    rootSig.offset := add(rootSigOffset, 0x20)
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/IthacaAccount.sol

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The wrapped signature layout is now fairly intricate (inner vs root vs outer, plus the keyHash/prehash/merkle trailer) and hard-coded offsets like 0x22, n + 1, and n + 2 make unwrapAndValidateSignature brittle; consider centralizing the encoding/decoding logic (or at least the trailer size and flag positions) behind named constants/helpers so future changes can’t silently desynchronize tests and production code.
  • In _verifyMerkleSig, the inline assembly assumes a well‑formed abi.encode(bytes32[] proof, bytes32 root, bytes rootSig) blob; it would be safer to add minimal length/offset sanity checks (or a require on expected signature.length) to avoid undefined behavior when given malformed or adversarial data.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The wrapped signature layout is now fairly intricate (inner vs root vs outer, plus the keyHash/prehash/merkle trailer) and hard-coded offsets like `0x22`, `n + 1`, and `n + 2` make `unwrapAndValidateSignature` brittle; consider centralizing the encoding/decoding logic (or at least the trailer size and flag positions) behind named constants/helpers so future changes can’t silently desynchronize tests and production code.
- In `_verifyMerkleSig`, the inline assembly assumes a well‑formed `abi.encode(bytes32[] proof, bytes32 root, bytes rootSig)` blob; it would be safer to add minimal length/offset sanity checks (or a `require` on expected `signature.length`) to avoid undefined behavior when given malformed or adversarial data.

## Individual Comments

### Comment 1
<location> `src/IthacaAccount.sol:528-533` </location>
<code_context>
+        return (false, bytes32(0));
+    }
+
     /// @dev Returns if the signature is valid, along with its `keyHash`.
     /// The `signature` is a wrapped signature, given by
-    /// `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`.
</code_context>

<issue_to_address>
**issue (bug_risk):** Signature wrapper encoding comment now describes `abi.encode`, but the decoding logic still assumes a custom packed layout.

The docstring now describes `abi.encode(bytes(innerSignature), bytes32(keyHash), bool(prehash), bool(merkle))`, but the implementation still assumes a custom packed layout: `bytes(innerSignature) || bytes32(keyHash) || byte(prehash) || byte(merkle)`.

- `n = signature.length - 0x22` assumes a 34‑byte trailer `[keyHash (32)] [prehash (1)] [merkle (1)]`.
- `LibBytes.truncatedCalldata(signature, n)` just strips those 34 bytes, not an ABI head.
- `prehash`/`merkle` are read from `n + 1` / `n + 2`, i.e., tail-relative offsets, not ABI slots.

With canonical `abi.encode(bytes, bytes32, bool, bool)`, these fields sit in 32‑byte head slots near the start, so any caller following the docstring will get incorrect decoding. Either:

- keep the packed layout and update the docstring/externally documented encoding, or
- switch to real `abi.encode` and update decoding to read the ABI head/tail instead of using `length - 0x22` and tail-relative offsets.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/IthacaAccount.sol

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces Merkle tree-backed signature support to IthacaAccount, a valuable enhancement. The implementation is accompanied by thorough fuzz tests that cover various scenarios, ensuring the robustness of the new feature. The updates across the test suite to align with the new wrapped signature format are also well-executed. My review includes a couple of suggestions for IthacaAccount.sol to enhance documentation accuracy and code clarity, which should improve maintainability.

Comment thread src/IthacaAccount.sol
Comment thread src/IthacaAccount.sol

@Dargon789 Dargon789 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

#30

@Dargon789 Dargon789 disabled auto-merge February 10, 2026 14:06
@Dargon789 Dargon789 enabled auto-merge (squash) February 10, 2026 17:46
@Dargon789 Dargon789 linked an issue Feb 10, 2026 that may be closed by this pull request
@Dargon789 Dargon789 disabled auto-merge February 11, 2026 02:55
@Dargon789 Dargon789 enabled auto-merge (rebase) February 11, 2026 02:55
@Dargon789 Dargon789 self-assigned this Feb 11, 2026
@Dargon789 Dargon789 added bug Something isn't working documentation Improvements or additions to documentation duplicate This issue or pull request already exists enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers invalid This doesn't seem right labels Feb 11, 2026

@Dargon789 Dargon789 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

#30

@Dargon789 Dargon789 closed this Feb 17, 2026
auto-merge was automatically disabled February 17, 2026 03:46

Pull request was closed

@github-project-automation github-project-automation Bot moved this from Todo to Done in web3-Defi-Gamefi Feb 17, 2026
@sourcery-ai sourcery-ai Bot mentioned this pull request Mar 14, 2026
This was referenced Apr 11, 2026
@sourcery-ai sourcery-ai Bot mentioned this pull request Apr 20, 2026
@sourcery-ai sourcery-ai Bot mentioned this pull request Apr 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation duplicate This issue or pull request already exists enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed invalid This doesn't seem right

Projects

Status: Done

4 participants