Hi Magicians,
I’m sharing a design + reference implementation of a modular ERC-4337-style SmartAccount concept.
The goal is to make long-term account operations (many modules, many contexts, frequent upgrades) less fragile by introducing three primitives:
- lane-keyed context domains
- fixed execution phases (stable core + pluggable surface)
- versioned operational primitives for upgrades / rollbacks
I also organized this article
Intent Account Abstraction: Proprietary Development, Management, and Operation Methods and Implementation Code for the L3 Era
about this proposal.
(Current AA whole overview based smart account)(※self analysis)

(Proposal visual summary) DDD-style AA / laneKey / fixed phases / versioned ops:
(※The pink areas are my proposal, and the points I want to discuss here.)

(Quick note on scope) This repository also contains a separate R&D/experiment section, but I’d like to explicitly keep this thread focused on the AA / SmartAccount architecture only. Please feel free to ignore the R&D part for now—I’ll share it in a separate thread later if it’s useful.
1. Lane-keyed context domains
A practical implementation result from this direction is that the laneKey can also serve as a bootstrap target.
In my current implementation direction, AccountFactory maintains laneKey-specific shared aggregator modules and related execution/validation surfaces.
This means a lane is not only a conceptual namespace for routing; it also becomes a concrete unit for account bootstrap.
So the laneKey now plays at least three roles:
- a module slot name
- a factory bootstrap target
- an operational unit for rollout and maintenance
This matters because, without a bootstrap layer, creating a new SmartAccount would require manual per-lane wiring after deployment.
By treating laneKey as a bootstrap target, the developer can pre-register shared lane-specific modules, and new accounts can be created with those lane configs attached from the beginning.
Each “lane” represents a domain / execution context (e.g., app-specific scope). A lane has its own module slots (validator / executor / hooks). This is meant to be a namespacing primitive inside a single account.
2. Fixed execution phases (stable core) + pluggable modules (variable surface)
Another implementation clarification is the separation between account-local identity material and shared operational modules.
In my current direction, user-specific passkey material is stored in the SmartAccount, while the PasskeyValidator itself is stateless and reads credential data from the account.
This keeps identity material local to the account, while validation/execution surfaces remain shared and replaceable.
That separation seems important for keeping the core stable while allowing operational flexibility:
- the stable core keeps user-specific state
- the variable surface remains in shared validators / executors / aggregators
So fixed execution phases are not only about deterministic flow control.
They also help define which parts should remain account-local and which parts should remain swappable shared infrastructure.
The account defines a deterministic phase boundary (validation + optional prehook, then execution: prehook → executor → posthook). The goal is to keep auditing/mental models stable even as modules change.
3. Versioned operational primitives for upgrades/rollbacks
A further implementation insight is that versioning becomes more meaningful when applied to shared composition surfaces rather than only to individual child modules.
In practice, I ended up treating validator/executor aggregators as developer-managed shared operational surfaces.
The child modules under them can remain lane-specific and composable, but the versioned object that operations actually manage is the shared aggregator layer.
This is useful because the real operational question is usually not just “which child module exists,” but rather:
- which combination of authentication and lane policy is active
- which execution surface is active for a given lane
- how those combinations are rolled out or replaced over time
For example, a validator composition can be understood as:
- authentication validator (
PasskeyValidator) - lane policy validator
So versioning the shared composition surface, rather than thinking only in terms of isolated child-module replacement, feels closer to the actual operational unit.
Rather than only tracking modules, I’m experimenting with “version tags” at the aggregator/registry level to support upgrade history, rollback, and consistency checks across lanes.
Repo (design docs + contracts):
Questions (feedback I’m specifically looking for)
-
Lane concept: Is lane-keyed namespacing a reasonable abstraction for modular accounts, or does it conflict with the direction of ERC-7579-style modular account thinking? What would you change in the lane semantics?
-
Validation scope / safety: For those familiar with the “validation scope rules” discussions, what are the main pitfalls you’d expect with per-lane nonce/state and lane-specific validators? Are there obvious footguns?
-
Upgrade/rollback primitive: If we wanted “upgrade / rollback” to be a first-class operational primitive, where should it live conceptually (account vs registry vs module manager)? Any known prior art I should align with?
Non-goals (to keep scope tight)
- Not proposing a new EIP at this stage; just trying to validate the abstraction.
- Not optimizing for minimal bytecode; prioritizing clarity and operational safety.
Any critique—especially on security model boundaries, module lifecycle, and compatibility framing—would be really appreciated.
Thanks!
Implementation update:
Since posting this, I pushed the idea further into a working implementation shape, and a few design clarifications became much more concrete.
The most important one is this:
AccountFactory can manage laneKey-specific shared aggregator modules, so creating a new SmartAccount no longer requires manual per-lane wiring by the user.
In other words, the lane is not only a conceptual namespace. It also becomes a practical bootstrap target.
What this means in implementation terms:
- user-specific passkey material is stored in the
SmartAccount PasskeyValidatoris stateless and reads credential data from the account- lane-specific validators/executors are treated as shared child modules
- validator/executor aggregators are developer-managed shared operational surfaces
AccountFactorypre-registers laneKey-based shared modules and attaches them when a new account is created
This changed the account creation flow quite a bit.
Instead of:
- deploy account
- manually wire each lane
- separately configure validation/execution surfaces
it becomes closer to:
- create account
- store passkey credential in the account
- use the app
That separation feels important to me:
- account-local: user-specific identity material
- shared/developer-managed: operational composition and per-lane module surfaces
So the laneKey is doing more than routing execution.
It is also becoming a unit for:
- validation composition
- execution composition
- operational rollout / upgrades
- account bootstrap
This seems relevant if modular AA is going to scale beyond “a few modules per account” and into systems where many domain-specific lanes need to be managed without turning account creation into an operational mess.
I’d be especially interested in feedback on whether this “factory as lane bootstrap registry” feels like a useful primitive in modular AA, or whether people would rather keep that wiring fully external to the account creation path.
Implementation update (frontend-connected AA verified on OP Sepolia)
A further update since the last post:
I was able to verify the frontend-connected AA flow on OP Sepolia from:
eth_estimateUserOperationGas- final passkey signing
eth_sendUserOperation- successful onchain execution
So this direction is no longer only a design/reference implementation discussion on my side — the flow now works end-to-end in a live testnet environment.
What seems most meaningful to me is that the architecture described in this thread remained useful even after pushing it through actual ERC-4337-style execution constraints:
laneKeystill worked as a practical unit for bootstrap / routing- the fixed phase boundary remained useful for keeping the account core stable while changing operational surfaces
- shared validator/executor aggregators still felt like the right unit for versioned operations
- account-local identity material + shared operational composition turned out to be a practical separation, not just a conceptual one
A few implementation lessons became especially concrete during this process:
- separating estimate mode and final send mode helped a lot for paymaster handling
- strict gas-field hashing in the final authorization path was too brittle, so bounded gas caps worked better than exact gas commitments
- keeping the passkey signature ABI shape identical between estimate and final send was critical
- bundler-estimated verification gas alone was not always sufficient for real WebAuthn verification, so a floor/cap style treatment became important
- matching
postOpexactly to the EntryPoint v0.7 shape was also necessary
So my current view is that these primitives are not only helpful for organizing modular account design on paper; they also seem useful when trying to make a frontend-driven AA flow actually survive estimation, signing, sponsorship, and execution.
I’d still especially welcome feedback on two points:
- whether
laneKeyas a bootstrap/operational unit feels aligned or misaligned with ERC-7579-style modular account thinking - whether versioning the shared aggregator surface is a reasonable operational abstraction, or whether this should live somewhere else conceptually