EIP-7702: Set EOA account code for one transaction

Non revocable, replayable signatures delegating an EOA to a contract is functionally equivalent to the security concerns with EIP 3074 invokers. Given how likely it is to work, the only way the scenario you outlined doesn’t happen is if wallets whitelist what code you can sign with extreme prejudice. Back to permissioned innovation.

IMHO, we should not be be passing an address to sign, we should be passing code. Yes it costs 30 extra bytes for some use cases, but as others have pointed it it makes the design more expressive and also more secure in that you don’t need to trust code that you delegate to as much because you can drop permissions first. You can let some provider sponsor your gas without your wallet having to whitelist them because the wallet only calls that code in the context of a spending limit. Giving that up to squeeze a few bytes out is a case of premature optimization.

1 Like

It makes it more expressive in a way which is not compatible with smart accounts. It is also not possible to let users sign arbitrary code. There still needs to be a whitelist in the wallet, otherwise the user may be convinced to sign code that allows an attacker control of the account.

No, if there is still broad disagreement it really isn’t. That was mishandled.

  1. I try explaining why giving special governance powers to client devs is dangerous and ultimately incongruous with our values here:
  1. Saying it’s ok if the client teams agree is self serving given that you can leverage the authority of the majority client in that circle and other participants can’t. Doesn’t mean your arguments have any more merit.

  2. Client devs should not be in the role of Solomonic kings that pass judgement and settle important disagreements. That’s not how the de-facto emergent consensus governance model we had worked before and I hope ACD calling for the inclusion of 3074 was the first and last time it happens.

I still hope to be convinced of that. This ongoing dispute around AA is the dumbest waste of super scarce smarts I’ve ever seen in this space. You and Yoav cancel each other out. I suspect your beef was misdirected up until recently because you thought he wrote my protecting governance from special interests post, and maybe by now it’s just a habit. They die hard

That is one thing we can agree on. Let’s move forward and focus on the technical merit of 7702 and the open questions. I have written a very detailed rationale for why the decisions made in the EIP are safe. I would like to see more discourse on why those points are wrong or why there is a better approach.

And fwiw I thought it was a troll post from someone else in ACD long ago.

The code for dropping privileges can be embedded in the wallet itself just like the code for encoding any transaction. Then you don’t have to trust the code you’re signing as much. Ideally if you authorize a contract to pay for your gas it can steal at most your max gas budget. Since the privilege drop would be encoded as part of the transaction, hardware wallets or other WalletConnect like solutions that separate transaction encoding from signing could parse the transaction and show you what you’re signing.

Before replying any message, I’d like to clarify an important point. My personal objection to the current form of 7702 due to bad security trade offs for EOA users, seem to have raised concerns that ERC-4337 will not support it. I want to reassure everyone - that is not the case.

My current objections are not related to AA. As long as 7702 is compatible with the AA roadmap and doesn’t degrade 4337, we intend to support it. The current version is not great for EOA users but will work fine with 4337, so the protocol will support it.

Not trying to publicly vilify you, just to show that what you wrote is inconsistent with something you wrote in the past. Something you have done yourself in past debates we had - taking an older message out of its original context and trying to show that it’s inconsistent with what’s being said in a different context. It’s strange that you take personal offense, given that trying to prove inconsistency is a method you often apply yourself.

Anyway, my intent is not to vilify anyone so let’s move on.

Neither are other ways we tried. Any argument not consistent with 3074 gets brushed off.

As I wrote many messages ago in this thread, I already made my case and debated the trade offs of every point in 7702, so I meant to stop debating and let the community decide based on that. I had to jump back in because you didn’t wait for the community to reach consensus and just edited the EIP to match your preference. Had you not done that, you wouldn’t have heard another word from me about revocation, etc.

We both made our case and it seems that further debate won’t get us anywhere. We should both take a step back, let the community debate it, and only jump in when something is misunderstood and requires clarification. But you editing the EIP before consensus is reached, is incompatible with that.

I noted the same thing myself on a 3074 breakout call, long before it was included. Using the nonce directly is too restrictive and kills many good use cases. Therefore I proposed maxNonce+bumpNonce during that call. It never made it to the proposal so no one had a chance to evaluate it as an alternative to nonce or to no revocation. Those external people you’re talking about didn’t object to revocation in general, they objected to the restrictive revocation method you chose to include in 3074 despite having a less restrictive option.

That’s why I suspected from the start that there will be an attempt to remove it later. If the intention was to really have revocation in the final version, you would have chosen one that doesn’t restrict use cases. The “chip away” message just confirmed what I assumed earlier.

In the current thread you stated that your preference for 7702 is no revocation > max nonce > current nonce. Since max nonce was also proposed for 3074, choosing current nonce seems strange if you intended for it to get included that way.

In the scenario I described, the wallet is the attacker preparing a rugpull. Therefore it will prompt the user to sign whatever contract it wants. This hypothetical wallet lets you connect your Ledger, sponsors your transactions for the first month to incentivize you to use it, and keeps the authorization you signed - for a perfectly legit contract that just happens to be deployed via a CREATE3 factory. After a month it stops sponsoring gas and you move to a different wallet, not realizing that your account is no longer safe. A year letter, your assets on other chains start getting drained on other chains. The first wallet effectively stole the private key from your Ledger, despite having never seen the actual key.

An address is fine as an optimization, if the signature is over keccak(address+code). The code doesn’t need to be in the transaction if it can be read on-chain. The problem in the new version is that code was replaced with address rather than a signature over both.

I believe I have done that. And the rationale is still incorrect IMHO. To take just one example, I have yet to see a flow in which clients will need to perform a database lookup on the account’s code but not to also load the code for execution. Transaction propagation doesn’t require that, and block validation requires the entire code anyway. Another thing is that the rationale makes a false assumption that the EIP will only be used in one way - each EOA only ever signs one contract. That is inconsistent with how people have been building invokers so we have no reason to believe this time will be different. If the only safe way to use the EIP is “as intended” then the EIP should not make other, unsafe ways possible.

The rationale you added is just… rationalizing why the pre-inclusion 3074 is the right model. Everything is written in support of that, and any contradicting evidence is ignored. This may work on those who aren’t that deep in the weeds and may not understand the nuances above. But you are smart and very familiar with the topic so I assume you do understand. You just choose to ignore it.

Rationale:

I believe that in the long term (3-4 years) - most EOAs will be using a single smart contract implementation (per account).

In the short and even medium term (1-2 years) there will be multiple smart contract accounts being used, and so we will be living in a diverse special-purpose contract world for some time.

One of the great powers of 7702 (inherited from 3074) is the ability to adopt smart contract paradigms gradually.

If every decision we make on 7702 adoption becomes a life long decision - that prohibits us from making these kinds of decisions at all - waiting endlessly for the optimal option. I don’t trust EIP-6900 and EIP-7579 yet, they haven’t been battle tested, but I will want to trust one of them someday. Deciding now if I adopt some intermediate smart contract without modularity or wait until these protocols mature and one of them prevails does not make sense to me. I want to be able to add some gasless/batching contract now and over time graduate myself to possibly also supporting some session keys, and some new capabilities people will invent next year but eventually to adopt a full blow AA smart account towards a transition to full AA.

So I believe there is meaningful value in things like flexible in-protocol revocation and replay protection. It will actually make innovation faster for wallets - because it allows us to do better risk management. Risk management and mitigation is a way to enable faster progress with putting less implications on each decision.

The risk we perceive is that once you sign a 7702 message - you create smart contract risk on your EOA. Having that risk limited/bounded, allows you to innovate more freely. There is a big difference between temporary and indefinite risk.

With all of this in mind, the current opinion on the 6 features is:

  1. Should the contract_code signature also sign over the account’s nonce? Yes, flexible nonce with ability to do both “single transaction authorization” and “indefinite revocable authorization” (e.g. maxNonce+bumpNonce or similar solution)
  2. Should the contract_code signature also sign over the CHAINID? Yes, we (as everyone) currently only sign transactions that can run on a single chain with some protection of cross-chain replay attacks. We like that situation. It is a valuable security measure. Given the use case for cross-chain is limited to very very specific use cases and risk to all actions is not great - we like this protection.
  3. Should the contract_code instead be an address? Unopinionated. But as mentioned in #2, should have some cross chain protection if possible.
  4. Should SSTORE inside the “temporarily contract-ified EOAs’’ be prohibited, like it is in EIP-5806? No. But our opinion on this is not strongly held. It seems like a meaningful limitation for a small edge case already solved by 4337 SCWs.
  5. Should we have initcode and not just contract_code? Unopinionated.
  6. Should we just add a flag to NOT set the code back to zero at the end? Thereby making this EIP also supersede EIP-5003? No. Not in Pectra, due to risk of delays and the learning we will get from seeing this EIP in the real world before fully understanding this transition. It’s good preparation for the future.

This feels as striking the right balance between capabilities and protection and hope it will bring back the conversation to the protocol and get it ready for approval for inclusion.

I just want to reiterate that:

  • I think @matt shouldn’t have opened the PR for EIP-7702 that mostly only reflects his personal takes, when most of the takes are still highly controversial as can be seen in this thread. The PR is misleading for the community at large, most of whom haven’t had the time to review this whole thread and might think that “we” (whatever that means) have already made certain decisions when we really haven’t.

  • I don’t believe it’s productive for @yoavw or anyone else to post screenshots or otherwise take this thread to a non-technical direction. There’s a time and place for governance convos but it’s not in this thread.

Again, I feel both @yoavw and @matt have made their points and preferences clear, and at this point it’s most helpful for others to chime in. The next step should be to collect these arguments and discuss them in person in the next ACDE call. How we break the ties after the call if there are further disagreements (which there will likely be), I don’t know, but personally I’d be fine with @vbuterin making the final decisions as the primary author of this EIP.

2 Likes

I’m not sure this is easy to check for? Even if the contract was deployed with CREATE2, it can be made to have arbitrary code. To rule that out you need static analysis of the deployment code, and it seems more difficult than checking for DELEGATECALL opcodes (another way of having arbitrary behavior).

Signing over contract address without also the chain id seems risky.

2 Likes

Which is why you should sign over the code hash instead.

2 Likes

Yet another way to do this could be to sign the code_hash, but specify the code_address in the transaction, so that nodes don’t have to index code by hash.

As I warm up my debate muscles, I was wondering if it be too naive to propose a block and/or time-based revocation mechanism?

Essentially users would specify the max time that the signed contract_code would be valid. This can be in addition to another set of revocation steps but time is always a constant increasing regardless of your account’s executions on-chain. I’m definitely aware that this doesn’t fully de-risk malicious contract code from being used before the time-based revocation triggers, but this at least provides a solution for not fully “bricking” your account after (unintentionally) max-nonce signing malicious contract code.

Also, would like to say that I support the maximal flexible implementation of what gives EOAs super powers, which is the aura of what we’re trying to accomplish here whether it’s 3074 or 7702.

1 Like

I’m all for additional safeguards, but note that a sane implementation of Yoav’s max nonce proposal would guarantee at the protocol level that it is possible for users to revoke the permission they gave for a contract to act on their behalf.

Unfortunately, this is not currently what is being proposed in the open PR. If this reckless, insecure version of 7702 is accepted it means we max out the risk of signing a buggy or malicious contract. That in turn strengthens the argument for whitelisting which contracts users can authorize. This won’t be great for new wallets and innovation.

The non-revocable version of 7702 also maxes out the risk from malware that can mess with the operation of even the most trusted wallets . Those who have opposed revocation at the protocol level argue that wallet interfaces have to be trusted anyway, but currently this is not true. The best practice for EOA security is to use secure signing solutions, so you don’t have to trust the integrity of the entire stack from the operating system to the extension running in your browser. Enabling protocol level revocation preserves some of the security guarantees you can get from secure signing solutions (e.g hardware wallets / wallet connect). Signing over a contract becomes less like giving up your private key forever and more like using another interface.

This reduces the risk from your computer getting hacked and also the risk of experimenting with new wallet interfaces. For extra security, we should consider standardizing how privileges are dropped so that secure signing solutions give user visibility into what risks they’re taking by authorizing a transaction. There’s a difference between signing over unlimited power to drain your wallet and signing over the power to pay your gas, or interact on your behalf with a specific DEX contract.

Sometimes users may still want to give wallet interfaces the power to drain their assets, but even then there seems to be a meaningful difference in the kind of attacks irrevocable, unlimited authorizations enable. With revocable authorizations, rugpulls will only effect current users. With irrevocable authorizations, rugpulls effect current and past users.

Note that an insecure version of 7702 will open up a kind of rugpull that wasn’t possible before: the risk of malware injecting code into a wallet interface to backdoor the EOA, even when the user is using a hardware wallet. This should be taken seriously. With a more secure version of 7702 , security conscious users would be to use their hardware wallets to enforce the principle of least privilege and bump up the nonce every time they start using a new computer or a new wallet interface, so that the previous wallet interface doesn’t stay “logged in” to your EOA. Unfortunately, signing 7702 transactions will almost always expose users to considerable risk. Unless you’re using a hardware wallet and manually verifying the authenticity of the addresses of the contracts you’re signing, the main reason for believing your account is still safe is that is hasn’t been drained - yet.

1 Like
  • How does this interact with EIP-7706: Separate gas type for calldata?
    • Would we need three new tx types, one for basic (with calldata fee), one for blob (with calldata fee), and one for EOA code (with calldata fee)?
    • Given that we historically never managed to get rid of a tx type, should we move to EIP-6493: SSZ Transaction Signature Scheme together with this EIP and simply make EIP-7702 an SSZ profile?
  • For EIP-4844: Shard Blob Transactions there was a restriction that to is required to be set. Do we have a similar restriction for EIP-7702 transactions, or can a transaction deploy a new contract and also replace its own account code during execution?

On the “sign code” vs “sign address” debate:

First of all, just wanted to be clear about my current stance that signing an address is fine. IMO It is slightly less expressive and removes some interesting use cases that were possible with EIP-3074, but my personal opinion is not that it is fundamentally problematic. Just wanted to share some my point of view so it is considered for the EIP from a “feature” point of view.

AFAIU, the current rationale around signing an implementation address instead of some template code is that it is shorter (less than half the amount of data - 20 bytes vs 45 bytes for the min-proxy code) and that it does not add any additional value. However, I believe it does add additional value, as it enables keys to sign additional context that is accessible on-chain that can be used in a number of (IMO) interesting cases over the current suggested proxy approach. In particular, it can serve a similar purpose to the commit argument from EIP-3074: some additional piece of data that is included in what ultimately gets signed by the account.

In order to not be super generic, I am going to focus on a particular use cases that I believe directly benefit from signing template code instead of a delegation address: account initialization. Currently, the suggested approach to using 7702 to upgrade an EOA to a SCA is to replace code with the suggested proxy implementation linked above. The proxy then takes care of initialization in the cases where the proxy is being used for the first time. This require the initialization to be authenticated using a signature from the EOA, meaning that all 7702 upgrades require at least 2 signatures (one for the 7702 signature, and one for the initialization). It is possible to reduce this to 1 signature in the case where tx.origin is permitted to set code with some minor modifications to the suggested proxy (it can skip the ecrecover if address = tx.origin), but only without gas sponsorship (since the EOA is the tx.origin in this case).

If we were to sign template code instead of an address then this minor downside can be worked around. Specifically, minProxy(implementation) || commit can be used as the code, i.e. you append some commit parameter to the min-proxy code as some additional metadata that doesn’t actually execute, but can be used on-chain as a source of some additional signed data. This allows flows where SCA implementations can accept initialization without additional signatures and instead compute some hash(initializationParameters) and compare it to the last 32 bytes of the account code with extcodecopy, therefore, enabling EOAs to be upgraded to SCAs with a single signature (even with gas sponsoring). This also unlocks some fringe use cases where, for example, you use Nick’s method to generate random 7702 signatures for single use accounts with a pre-determined initial configuration for which you are guaranteed to not have a private key (although, the actual usefulness of this is questionable at best, however a use case that was possible with 3074).

Again, this isn’t a particularly strong argument to justify “WE MUST SIGN CODE!”, but I wanted to share my slight preference for signing code instead of a delegation address.

2 Likes

I’m still not comfortable about optional revocability.

  1. The argument that has been pushed so far is that the signature has same kind of security concerns/handling as a private key or seed phrase. I disagree. Once a tx is made with non-revocable signature, it’s public.
    This is different from private key because if I delete a metamask wallet from my browser, the private key is wiped out. It never leaves my computer (I trust metamask to follow this). This doesn’t happen with signature because this is public, and deleting metamask doesn’t quite provide the same effects.
  2. There is a significant change in the level of trust placed on wallet providers here. Of course, smart wallets are more complex than EOAs (and so there’s already an increase in trust), but I still want to able to delete/leave a smart wallet without having lingering doubts about future signature exploitation. e.g. I realize that a wallet provider I was using is not really safe anymore, and I want to do a “invoke all previous authorizations”-style operation – something analogous to what happens when (in web2) I change password or “sign out from all devices.”
  3. There is a shift of trust (regarding security) from Ethereum protocol to wallet providers, which is uncomfortable. Making revocability optional complicates the UX, and some wallet providers might just decide to make all signatures non-revocable in order to “simplify the UX” and not expose the revocable signature option at all.

As discussed in the last breakout room for this EIP, revocation by account nonce is incompatible with valuable use cases. Optional revocability is proposed as a compromise, but there doesn’t seem to be a consensus in favor of it.

My feeling is that the current design tackles the problem of revocation from a weird angle. The incompability between nonce-based revocation and 7702’s AA goals seems fundamental.

Instead of relying on nonces, an ideal revocation mechanism for 7702 would allow users to express that they no longer authorize a particular address as code for their account.

Could we add a new system contract to track revocation of authorized addresses? Accounts would be able to call into this contract to revoke an address. Clients would have to look up revocation status in the system contract to check the validity of a 7702 authorization. This extra lookup would incur a cost, and 7702 transactions should be priced accordingly. I don’t think it’s unreasonable or too discouraging to charge a little extra for this.

Update: See proposal

2 Likes

Another potential use case for EIP-7702 would be permissionless deterministic CREATE2 deployments. The current status quo is to either use something like Arachnid’s or MicahZoltu’s CREATE2 deployer, or something like the Safe singleton factory.

  • The Arachnid/MicahZoltu CREATE2 deployer is deployed using a transaction generated with Nick’s method, making it completely permissionless at the cost of making the transaction completely sealed. This means that it cannot contain any chain information, or change things in the future such as gasLimit or gasPrice. This is a pretty big downside for new chains since not all chains support non-EIP-155 transactions (so transactions without chain ID replay protection which is required by this method), and some chains may have current gas prices that make the transaction unable to be included at a specific time, or even worse, have a different gas schedule which could potentially make the transaction revert and make the CREATE2 deployer lost forever.
  • The Safe singleton factory works around this by having the team hold a private key and deploy the CREATE2 deployer on request to new networks. While this allows for flexibility in the transaction parameters to ensure that it is valid at the time of deployment, it is far from perfect: it is permissioned (the Safe team holds the private key), and user errors could cause the private key to sign and execute a transaction that would revert and, again, cause the deployer to be lost for ever for a specific chain.

With EIP-7702, a truly permissionless CREATE2 deployment system can be done where you use Nick’s method to sign an EIP-7702 autorization over the bytecode of the CREATE2 deployer with chain ID of 0 and nonce of 0 (so valid for any chain and nonce 0). Then, CREATE2 contract deployments would be done by executing EIP-7702 transactions using the aforementioned authorization. This would be without any of the downsides of the other CREATE2 deployment methods:

  • It is fully permissionless like the Arachnid/MicahZoltu CREATE2 deployer
  • It is not lost forever if a transaction would revert (because of different gas schedule in new chains or protocol updates in the case of the Arachnid/MicahZoltu CREATE2 deployer or user error in the case of the Safe singleton factory)
  • It is not subject to gas fluctuations at the time that a CREATE2 deployer is needed but not available

Note that this requires signing code instead of an address, and allowing non-chain-replay-protected EIP-7702 authorizations. I think there is consensus that allowing non-chain-replay-protected EIP-7702 authorizations is a good idea, but AFAICT the current consensus is that EIP-7702 authorizations should sign addresses and not code.

My stance remains that I don’t think EIP-7702 should be blocked on the “sign code” vs “sign address” debate, but I do find signing code to be more expressive and enable novel use cases that signing address does not, and if the only argument for signing an address is that “signing address leads to slightly smaller transactions and slightly lower gas costs”, I don’t think it is a strong enough argument for choosing to sign addresses instead of code, and hope that the decision is reconsidered.

image

4 Likes

Thanks for this write up @nlordell, I’d like advocate for supporting this exact use case.

There is still no tradeoff-free solution to deterministic deployments across chains. It’s a pain point that many teams have, and solving this problem is a very valuable use case that I’d love to see supported by this EIP. A create2 precompile was discussed but never implemented, and if EIP-7702 supports signing over code, we effectively get that for free.

I see four paths to supporting this:

  1. Change back to signing code instead of an address.
  2. Implicitly support signing both addresses and code: The smallest possible delegatecall minimal proxy is larger than 20 bytes, so if the data is 20 bytes treat it as an address, and if it’s any other length treat it as code.
  3. Explicitly support signing both addresses and code, by adding an additional parameter to the tuple that specifies how to treat the data.
  4. Sign a code hash as suggested above by @wjmelements
2 Likes

Guys, I have already suggested a better solution than both signing code and signing code address: sign the code hash but specify the code location in the transaction. This addresses everyone’s concerns, and has all the benefits of signing over code without putting the code in the transaction.

I don’t want to see the main adoption using minimal DELEGATECALL proxies just because you guys didn’t read my posts in this thread.

3 Likes