feat: Shadow Accounts — cross-chain smart account presence via interop#2112
Open
valera-grinenko-ai wants to merge 9 commits intomatter-labs:draft-v32from
Open
feat: Shadow Accounts — cross-chain smart account presence via interop#2112valera-grinenko-ai wants to merge 9 commits intomatter-labs:draft-v32from
valera-grinenko-ai wants to merge 9 commits intomatter-labs:draft-v32from
Conversation
Shadow Accounts give a home-chain user a smart account on remote chains, enabling automated relay during bundle execution (e.g., origin-routed private interop B→A→C) and L2→L1 contract interaction without a separate L1 account. Core design: - ShadowAccount implements IERC7786Recipient with two-layer auth: msg.sender must be InteropHandler, ERC-7786 sender must match owner. - ShadowAccountFactory deploys one account per owner per chain via CREATE2 (salt = keccak256(ERC-7930 encoded owner)). - InteropHandler routes calls through shadow accounts when InteropCall.shadowAccount is true (lazy deploy via factory). - New shadowAccount(bool) ERC-7786 call attribute, mutually exclusive with indirectCall. - Supports arbitrary calls and delegatecalls (for script contracts). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reorder CallAttributes fields for efficient 32-byte slot packing (bools grouped together) to satisfy gas-struct-packing lint rule. - Regenerate selectors and zkstack-out (InteropCenter ABI). - Exclude delegatecall-loop from Slither — ShadowAccount delegatecall targets are chosen by the authenticated owner, not by untrusted input. - Prettier formatting fixes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ran recompute_hashes.sh with the expected compiler version (foundry-zksync-v0.1.5, commit 807f47ace) to regenerate AllContractsHashes.json and zkstack-out artifacts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
358bf97 to
238e6e6
Compare
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…used helper - Update InteropCall.shadowAccount NatSpec to reflect implementation (was still saying "always false, not yet implemented"). - Simplify shadow account routing in _executeCalls to a ternary. - Remove unused _buildSingleDelegatecallPayload test helper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cache keccak256(owner) as _ownerHash in ShadowAccount to avoid re-hashing variable-length bytes on every receiveMessage call. - Change predictAddress from external to public so the factory can call it internally without an external self-call (this.predictAddress). - Remove assert in factory — rely on CREATE2 determinism directly. - Recompute AllContractsHashes.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0xValera review pattern: demands edge-case coverage and "checking of effects of interactions, not just calling functions." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Drop redundant vm.mockCall on call targets — etched STOP opcode already makes the call succeed cleanly. - Replace vm.mockCallRevert with a real AlwaysReverter contract. - Remove vm.mockCall on the predicted shadow account in the InteropHandler integration test — the real CREATE2-deployed ShadowAccount now runs receiveMessage end-to-end. Added assertions verifying the account is deployed and its owner matches the sender. The L2_MESSAGE_VERIFICATION mock is kept (matches every other interop test in the file — cannot remove without a real L2→L1 Merkle proof). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contributor
|
please point this at #2076 it is a modification of that branch. It also has some other work, which ideally does not get lost |
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
Implements Shadow Accounts as a standalone protocol building block on
draft-v32. A Shadow Account is a smart account deployed on a remote chain via interop, giving a home-chain user contract interaction capabilities there without an EOA or separate deployment.Primary use cases (from spec):
What this PR adds
New contracts (
l1-contracts/contracts/interop/):ShadowAccount.sol— ERC-7786 recipient with two-layer authorization (InteropHandler as caller, ERC-7930 owner as sender). ExecutesShadowAccountCall[]payloads supporting bothcallanddelegatecall(for script contracts that read remote state). Cacheskeccak256(owner)at initialization for gas-efficient authorization.ShadowAccountFactory.sol— CREATE2 deployer. One account per owner per chain. Salt =keccak256(ERC-7930 encoded owner)ensures deterministic addresses without querying the target chain.predictAddressispublicfor gas-efficient internal use.IShadowAccount.sol,IShadowAccountFactory.sol— Interfaces and data types.Modified contracts:
InteropHandler._executeCalls— WheninteropCall.shadowAccount == true, routes through the sender's ShadowAccount (lazy-deploying via factory) instead of callinginteropCall.todirectly.InteropCenter— Parses newshadowAccount(bool)ERC-7786 call attribute; enforces mutual exclusion withindirectCall; propagates flag toInteropCall.IERC7786Attributes— AddedshadowAccount(bool)attribute declaration.CallAttributesstruct — Addedbool shadowAccountfield (packed withindirectCallfor storage efficiency).Config.sol—SUPPORTED_INTEROP_ATTRIBUTES5 → 6.L2ContractAddresses.sol—L2_SHADOW_ACCOUNT_FACTORY_ADDRat0x10012.L2ContractInterfaces.sol—L2_SHADOW_ACCOUNT_FACTORYinterface constant.InteropErrors.sol— Six new shadow account error types (with selectors viaerrors-lint).Design decisions
0x12L2_BASE_TOKEN_HOLDER(0x11); follows existing patternkeccak256(ERC-7930 owner)_ownerHash = keccak256(owner)at initreceiveMessagecallshadowAccount+indirectCallinteropCall.towhen shadow=truetopreserved for event loggingWhat this PR does NOT include
Test plan
23 new tests in
L2ShadowAccountTestAbstract.t.sol/L2ShadowAccountL1Test.t.sol:receive()accepts ETH🤖 Generated with Claude Code