. Sorry, didnât mean to start bike-shedding on the opcode, just wanted to share some (what I consider) are some minor design smells with it. You are right that it is only a small part of the EIP, and not the most interesting bit either. I had no notes on the rest though, and generally really like the approach and find it elegantly designed.
This is no longer a priority then? AFAICT, if I have an old-school proxy-contract based smart account, I wonât be able to use EIP-8141 directly (since the top level proxy contract call frame cannot APPROVE). Naively, I think you can work around this by having a general and permissionless âEIP-8141 trampolineâ contract that can be called with a frame transaction to make this EIP compatible with those legacy accounts.
I may have misread the EIP or missed some important detail, but canât you make it so frame transactions are invalid if some SENDER frame were to revert? In particular, consider a frame transaction with 3 frames all targeting the tx.sender:
VERIFY with some signature, which executes APPROVE 0 (so execution only)
SENDER which, for example, does some DeFi interaction with some slippage
VERIFY frame which just checks TXPARAMLOAD 0x15 1 to read the status of our second frame, and reverts if it is 0 and executes APPROVE 1 (so payment only) otherwise
This way, the account could conceivably create a transaction that would only be valid (and pay gas for it) if their DeFi interaction doesnât revert, for example due to price movement. Does this become an issue for building blocks? It feels more complicated than regular transactions: whether or not the transaction is valid potentially depends on its placement in the block, where as currently, the validity of the transaction is a static property, and its placement in the block just has an effect on its outcome.
Edit: This is true of frame transactions in general though - you can (for example) just check the state of the DeFi contract you are interacting with in the VERIFY, so you can block transactions that would revert. If this is allowed, this is a cool use-case for frame transactions: donât pay gas on reverts .
The EIP removes access list, since they do not provide much of a cost benefit to users now.
However, this lack of benefit is actually an artifact of current gas pricing and the relative current slowness of Ethereum compared to the future and to performance blockchains.
Access lists enable a tremendous speed up as you start hitting thousands of transactions per second. It adds a lot of latency to execution to walk through the transaction and whenever you hit a storage read, pause executing the transaction, fetch the slot from disk, and then resume executing the transaction. It is drastically faster to read storage slots from an access list in an optimized fashion at high speed from disk before those transactions are ever worked with.
Monad 4x increased the cold access cost for reading or writing a slot, since disk reads is a performance bottleneck, however if access lists are used, the total charges is back to what would be normal rate for access listed slot. This makes access lists is both heavily incentivized (which matches the actual performance behavior at scale) and not have the same penalty of getting it wrong. If 50% of your access listed slots are not read during the transaction, you are still cutting your cold access cost in half.
I think itâs likely a mistake to remove access lists from frame transactions. Preloaded read slots removes a key performance bottleneck. Not having access lists in a core transaction type like this is almost certain to affect Ethereum in the future.
It can only be called when CALLER == frame.target , otherwise it results in an exceptional halt.
Wait - shouldnât this be when CALLER == ENTRY_POINT? Otherwise you cannot naively execute APPROVE in a VERIFY frame (which has the ENTRY_POINT caller). At least, my interpretation of this would mean that the examples donât work:
Example 1: Simple Transaction
Frame
Caller
Target
Data
Mode
0
ENTRY_POINT
Null (sender)
Signature
VERIFY
1
Sender
Target
Call data
SENDER
Frame 0 verifies the signature and calls APPROVE(0x2) to approve both execution and payment. Frame 1 executes and exits normally via RETURN.
Here, frame 0 has CALLER == ENTRY_POINT != frame.sender which, from how I read the standard should revert. Or is the expectation that the implementation at sender must do a self-CALL that executes APPROVE internally?
Other than that, I like the change that APPROVE is scoped to a transaction and doesnât require propagation. It makes implementing this for old-school proxy-based accounts much easier, and avoids any compatibility woes WRT CALL returning values other than 0 or 1.
Thereâs another issue related to EIP-7825. Does frame transaction allow us to bypass this limit by making the total gas limit of all frames greater than 2^24?
One issue I ran into with the ERC-20 paymaster pattern: the paymaster must charge users maxCost Ă rate tokens upfront (in a SENDER frame), but the protocol refunds unused gas as ETH to the payer. So the paymaster structurally profits from the difference between what it collects in tokens and what it actually spends in gas.
One fix can be adding a frameGasUsed(uint256 frameIndex) TXPARAM opcode so a post-op frame could calculate and refund the overcharge. But this leaks runtime information across frames, which seems to conflict with current frame isolation design.
Is the overcharge just an application-level concern that paymasters should solve offchain? Or would a controlled TXPARAM extension be worth considering?
Has it been analyzed how EIP-8141 would integrate with async execution proposals? Letâs take EIP-7886 as a working example (however, I believe, in general async execution designs will share these issues).
If we were to combine EIP-8141 and EIP-7886, would it mean that Deploy and VERIFY frames need to be run in the transaction validation pipeline? That is, in EIP-7886, where we have sender_address = recover_sender(...) followed by operations on that account, we would instead need to run some frames, so that sender and payer are established, and then proceed with the usual EIP-7886 logic.
Now EVM needs to run in the validation pipeline, but I think this is hard but manageable.
Thereâs an issue that this makes it necessary to enshrine rules for frames which run at validation, so that consensus can be achieved. This is more difficult and restrictive than the current assumption in EIP-8141, that the mempool decides what Deploy/VERIFY logic can run.
A way I see to fix this is to consider EIP-8141 with Deploy/VERIFY frames fixed to a [Deploy] VERIFY [VERIFY] sequence in the head of the frame list, as well as a gas limit imposed. This would make it easier to continue with async execution improvements like EIP-7886, while still retaining the possibility to lift these restrictions in a future upgrade.
These Deploy and VERIFY frames would run against stale state, as they donât have access to any effects of preceding txs in the block. While that is OK-ish for senderâs VERIFY, it seems to preclude paymasters verifying ERC-20 balances to ensure later reimbursement (or at least leave a griefing vector open)
The stale state problem for VERIFY frames is structurally similar to what
EIP-4337 faces at the bundler level â validation reading state that can change
before execution. EIP-4337 mitigates this with storage access restrictions
(validation can only read the accountâs associated storage) plus off-chain
simulation and bundler reputation.
Since EIP-8141 enshrines frame semantics at the protocol level, does the fixed
[Deploy] VERIFY [VERIFY] sequence open room for a stronger version of this â
e.g., VERIFY frames declaring an explicit state access list, so the protocol
can detect conflicts with preceding transactions and reject at the mempool
level? That would turn the paymaster griefing vector from a runtime surprise
into a pre-inclusion rejection, which is something EIP-4337 can only
approximate off-chain.
Iâd say fixed [Deploy] VERIFY [VERIFY] + state access list is not enough to help with stale state. Access lists would still need to compare against non-stale state, which is inaccessible when VERIFY frames run during tx validation.
For now it seems to me that under async execution, gas must be paid for with Eth, as it is the only means the protocol has sufficient control over.
Frame transactions would make checking transaction inclusion now require:
Execution of a Turing complete programming language
Access to all blockchain state
These are not trivial things.
Not only are they complex, but they are incompatible with evolutionary paths that Ethereum is likely to be taking soon. Privacy preserving transactions, censorship resistance, asynchronous block execution, and more distributed state - all of these get harder with frame transactions in the mix.
EIP-7702 sounds simple, and yet itâs impressive just how many areas of code it affects, and how many constraints it adds on new block level features.
Yes, we do strongly need a standardized way to batch transactions and separate payers from authentication at the transaction layer. These are strong improvements that will make a difference to users and developers.
However, itâs worth asking why EIP-4337 and EIP-7702 which advertise both payer/auth separation and batch transaction features have been relatively unsuccessful and painful. We donât want to repeat the same mistakes by making another infinitely flexible EVM based system that doesnât provide a good user experience, doesnât provide standardization, leaves the codebase less flexible for the future, and sees only limited adoption.
The fragmented tooling around EIP-7702 with different wallets using different, incompatible backend contracts is something we need to avoid. We need something simple and standardized, developer and user friendly at the transaction layer, far more than we need yet another way to be infinitely flexible.
The root of all the problems is that wallet providers have been slow to switch from EOAs to smart accounts. Otherwise, we wouldnât have 7702 or any EIP to define new transaction methods.
Yes, the lack of adoption is a problem. But we should understand why and not repeat the same mistake for a third time.
The problem is not that wallets wonât implement things: EIP-1559 transactions became standard quickly and today legacy transactions are uncommon.
Why did EIP-1559 do well? It had a clear, simple standard, a clear benefit, and required minimal work from anyone beyond just the spec being implemented in wallets. This is what we want.
On the other hand, EIP-4337 smart accounts and EIP-7702 delegations do exactly what this Frame Transaction proposal does - create EVM level, infinitely powerful smart wallet contracts that can do anything. As a result, each wallet that tries to use them ends up implementing their own extremely dangerous code, which is quite costly to secure and get right. And there are a LOT of ways that things could go wrong. This drives out all but the largest or most dedicated teams, removes interoperability, and kills network effects.
If we need A, B, and C, just build those things into the protocol so that everyone can use the same standard, the experience is great, the cost to use is low, and the tooling shared. This is how you get adoption.
Per discussion with @matt we are proposing to support EOAs with frame transactions, making it possible for EOAs to be sponsored by dapps, pay gas in ERC-20s, etc.
By supporting EOAs, frame txns can vastly improve UX for all Ethereum users, not just users of new smart accounts.
Is it not possible to achieve the same functionality with EIP-7702? Say I delegate my EOA to a contract that will 1) execute APPROVE if provided with the EOAâs ECDSA signature and 2) has a method to transfer ETH when called in a SENDER frame. Whatâs missing compared to your proposal?
Itâs true that a EOA could work with 8141 by delegating to a 8141-compatible account. However itâs still important that 8141 supports EOAs natively because it flips the default from âyour EOA canât use any AA features unless you delegate to a smart accountâ to âyour EOA can use most AA features by default, and can do more if you delegate to a smart account.â
This is really important because weâve seen in practice that adoption for EIP-7702 has been low/slow due to the huge inertia of existing players:
Hardware wallet companies are very risk-averse (as they should be) and do not want to create new vectors of attacks by letting users delegate EOAs to smart accounts.
A few of the most well-funded consumer wallet companies (e.g. MetaMask/Uniswap) were able to develop & audit their own 7702 account implementations, but most wallet companies do not have such resources and chose to minimize risks by doing nothing.
Another factor hampering 7702 adoption (and smart account adoption in general) is that, for a 7702 account to work on a chain, the account implementation must be deployed on the chain. In practice, since thereâs an ever-growing list of EVM chains, itâs not possible to deploy the account implementation on every chain, so a 7702 account (as a smart account) doesnât work consistently across chains.
Now consider EOAs using 8141:
EOAs can enjoy AA benefits such as gas abstraction â including sponsored transactions and paying gas in ERC20s â without taking on any smart contract risks.
EOA wallets can integrate with 8141 by simply supporting a new transaction type, without having to develop/audit any smart account implementation.
EOAs can enjoy AA benefits consistently across all chains who are EVM-compatible, without depending on any contract deployments.
For these reasons, we think 8141 has the potential to be used much more widely with EOAs than 7702 did.
EIP-8141
âŚonly a single transaction can be pending for an account that uses frame transactions.
I just did a quick look at the last 1,000 blocks (288,508 txs). 17.8% of these transactions involve a single sender sending more than one transaction per block.