After an initial read I have two questions for the start:
Why did you chose a self-call for execution rather than a dedicated entrypoint address? This would have allowed to distinguish this from a self call in contract methods.
Why did you chose to have the account authentication logic and gas payment logic in one EIP? From past experience I would say that this lowers the chances of adoption by the core team due to complexity. Separating this into multiple EIPs might make sense.
I can split this out into two EIPs, agree that would keep it much cleaner. Initially wanted to have ~parity for the things we cared about for AA and ERC20 payments was one of them.
As for the self call:
this allows tx.origin to be the wallet address
enforces that the calldata must be interpreted by the wallet code
no entrypoint overhead
can determine if msg.sender == tx.origin && msg.sender == address(this)
Any specific use case for the self call initiated from code vs from the initial transaction frame?
Changes to token balances should be understood as changes in storage (e.g., ERC20) rather than turning them into a transaction format attribute, as this increases protocol complexity. Additionally, a 2D nonce structure can be simplified to a nonceBitmap, as shown here: https://ethereum-magicians.org/c/web/70
regarding the self call I was mostly thinking about the requirements to make an account like Safe compatible to it. For config changes the Safe checks on self calls. Therefore it would be directly compatible to have an “entrypoint”.
Just to note: I don’t mean that there should be an entrypoint contract, rather that tx.origin (or msg.sender) is set to a pre-defined address, to easily check that this flow is taken.
Edit:
can determine if msg.sender == tx.origin && msg.sender == address(this)
This would would be true for the initial invocation and any follow up calls, right? At least for Safe it would be interesting to differentiate these. That being said, this is also true for EIP-7702 and the solution most likely would be transient storage.
Yes that would be true for both initial invoke and follow up calls.
Yes seems like transient storage could be a solution here, I cant right now see how that breaks.
Initial frame: works (sets transient flag)
Initial frame to another address which calls into the wallet later (not this tx type): wouldnt be triggered, caller cant be msg.sender ?
The above is true for any other as well I think but havent thought too deeply on it.
…The main tradeoff here is swapping out arbitrary code execution in validation which allows us to have no wasted/unpaid computation and performant inclusion checks (just nonce, payer balance, sig matches sig slot) and a performant mempool.
The solution is backwards compatible with ERC-4337 and allows existing smart accounts to migrate by registering their keys with the precompile.
Disabling arbitrary validation code enables us to have node optimizations for reasons mentioned above.
We believe this tradeoff is acceptable because the vast majority of wallets today verify a single key: secp256k1, P-256, or WebAuthn. These cover EOAs, hardware wallets, and passkeys. BLS enables signature aggregation where needed. Delegated keys provide subaccount functionality. The key type system is extensible—new signature schemes can be added via protocol upgrades with clearly defined verification logic.
The primary capability lost by removing arbitrary validation is custom recovery logic. However, recovery flows remain fully supported through multiple pathways:
1. ERC-4337 compatibility: Accounts can still use EntryPoint-based recovery modules alongside native AA transactions
2. Relayer-assisted recovery: Guardians sign recovery authorizations off-chain; a relayer submits the transaction and pays gas (standard pattern used by Safe, Argent today)
3. Future protocol level recovery: ie. native multisig of trusted external accounts, potential zk implementation if performant enough