EIP-7702: Set EOA account code for one transaction

I’d go with a nonce.

That isn’t exactly true, right? If the mempool/bundler knows exactly what conditions can cause the op to become invalid, it can be safely propagated/submitted.

A nonce contract with strict rules about invalidation could serve that purpose.

The obvious implication is that accounts that do not adhere to 7562 do not need to be served.

That is equivalent to defining your whitelist as “all contracts adhering to 7562”.

I can avoid saying “whitelist” if its implication of human interaction is too strong to ignore.

For initcode to be possible, it is necessary (but not sufficient) to write 7562-like rules for bundlers. So my question becomes:

Is it “possible” or “impossible” to write 7562-like rules for initcode?

Not exactly: whitelisting is a manual process to decide that “X” is good or bad, where the code (of block-builder, bundler, or on-chain) can’t verify it, and has to trust that external entity that whitelisted it.

The erc-7562 rules are automatic, and deterministically verify that the validation code is consistent (does the same off-chain and on-chain) and isolated (can’t interfere with validation of another UserOp)

So yes, it is possible, but I’m not sure what you can check: specifically, you’re limited to storage associated with your own address (account storage is allowed but irrelevant for an EOA, so you’re only allowed to access your own token balances)

If that’s already allowed by 7562, represent your nonce as an ERC-20 and burn your nonce by minting yourself a new token :rofl:

Acknowledged! I will refrain from saying whitelist.

Got it. So you’re not talking about checking conditions specific to the way the contract is used, but add replay protection at the beginning of the transaction rather than when the contract is first called during that transaction. That makes sense.

No. It’s not enough to know what conditions are checked. You also need to know that they can’t be used to invalidate multiple UserOps with one change.

Yes.

This is equivalent to saying that Ethereum whitelists only EOAs that use ECDSA signatures and have ETH. That’s not whitelisting - that’s the protocol. 7562 is the protocol.

It is possible to write such initcode, just not easy. And it would require bundlers to work harder during propagation and simulate 7702 transactions rather than just 4337 validations.

But given the use case you highlighted, it might make sense to do that. 4337 accounts would have to use a pure initcode, whereas other contracts can do different things in initcode.

  1. I think @SamWilsn is talking about a contract that is not meant to be used in a UserOp, so it’s ok for its initcode to do anything. Only contracts used in UserOps need to follow these rules. If a node propagates a UserOp with a non-compliant initcode it’ll simply get banned by other nodes for spamming invalid ops.
  2. In UserOps, initcode wouldn’t be allowed to use associated storage, for the same reason a deployment UserOp can’t use associated storage unless the factory is staked. In the 7702 case this means no storage access in initcode whatsoever because there’s no EOA storage and associated storage cannot be allowed.

No. See my reply to Dror above. Tokens require the associated storage rule, which does not apply during deployment unless it is done by a staked factory (which is not the case here).

But I think it doesn’t matter for your use case. You are talking about checking a nonce in initcode of a contract not meant to be used with 4337, so the 4337 rules are irrelevant. The initcode can do whatever it wants, it just won’t be usable in a 4337 bundle.

To summarize, I do see your point for using initcode in non-4337 use cases. If we can’t find another way to solve the same use case, then you convinced me that initcode is needed. Bundlers will have to inspect initcode in UserOps that specify a 7702 contract to ensure that it is pure. 4337 accounts should simply have an empty constructor and do everything in validateUserOp instead.

2 Likes

With the maxNonce + bumpNonce proposal, would there be the concern that a user can be tricked into bumping their nonce to the max value 2^64-1 and therefore effectively bricking their account?

Nowhere else in the protocol do we carve out fallback mechanisms for buggy code. The protocol doesn’t force smart contract wallets to have a revocation. Let’s not make the mistake for transient smart contract wallets.

1 Like

No, since bumpNonce takes a uint32 and the nonce is uint64. The user would have to be tricked to bumping it 4 billion times to cause that. That’s a lot of signing to do.

1 Like

I agree with this, we should specify the code with their 20 byte identifier (address).

They should also take care to only sign implementation that follow the best practices for revocation. :slight_smile:

I think the originating transaction should be able to send value.

1 Like
  1. Not the same. The user has a way to recover from signing a contract that didn’t follow best practices, by revoking it. But no way to recover from signing a contract that doesn’t implement revocation if the protocol doesn’t provide a way. Revocation is the user’s last line of defense.

  2. See later messages - Sam already convinced me that it’s not good enough and we need initcode to enable mitigation. It’ll complicate things a bit for 4337 but I see no way to mitigate it otherwise.

1 Like

Why don’t you have in-4337 revocation for users if you feel so strongly about it?

How would that work? The validation function is a part of the account, not a part of the protocol. The user has no way to prove identity to EntryPoint. But EOAs do have protocol level identity proving because the account is not abstracted. The 7702 contract does not become the account. It is only authorized to act inside the account. Therefore revocation makes sense. If the contract became the account (as in 5003 or 7377) then there couldn’t be a revocation mechanism because the protocol can no longer identify the user.

4337 would certainly need to restrict accounts a bit.

It could enforce all compatible accounts have code that is exactly a proxy contract where only the entry point can set the implementation. Every 4337 account code will be the same ~50-100 byes and they can point to whatever implementation code they like.

In the entry point (the 4337 protocol), there would need to be a new mechanism to authenticate implementation change requests. Since there is “no way to recover from signing a contract that doesn’t implement revocation”, we can’t trust user code. There will need to be a list of allowed authorization modules which the protocol assumes is safe. In the event this is enshrined, these modules will simply become native code. Generally as long we support multi-sigs from a wide range of crypto primitives, that is probably a reasonable place to start. Open to other module types, but it’s possible to add more later as-needed. Once the entry point authenticates an implementation change request, it can dispatch it to the user’s smart account.

Is this as powerful as 4337 is today? No. Is it safer? By your argument “revocation is the user’s last line of defense” and the desire to enshrine a revocation scheme in the core protocol: yes.

Now of course I don’t support this type of mechanism. I think users and wallets have the responsibility to do things that are safe. However, I don’t see why if revocation is that critical that we can’t write it into 4337 or some native account abstraction proposal. It will never be as powerful as just letting the user run whatever code they want, but if we have the opinion that the protocol needs to protect users then let’s protect all the users.

There is no difference between the two. In both cases a user has authorized code to act inside the account. What code is running is simply an implementation detail.

Trolling aside, why do you oppose to a revocation mechanism like maxNonce? Can you point out a use case it blocks? Or maybe a way in which it restricts the user? Or some UX degradation? A cost increase? You seem to feel so strongly about it, so there must be some reason I’m missing.

It’s merely an emergency mechanism that comes at no cost and can save the user from disasters. Set maxNonce to 2^32-1 and it only invalidates the sig if the user makes more than 4 Billion transactions. Do you see that happening?

As long as the user sends less than 4 billion transactions, there is no observable effect to this mechanism, nothing the user needs to do or know, no additional flows to implement in the wallet or in any dapp, no additional state for nodes to store. The only flow associated with it, is bumping the nonce in case of emergency (that for most users would hopefully never happen).

If revocation deteriorated some use case, e.g. if we just used the nonce as-is so that sigs would keep becoming invalid when the user transacts, then we would need to debate whether it is worth it. But if there’s no observable cost to the user and everything works the same way, why are we debating this?

What’s the downside, other than me proposing it?

It’s the difference between authorizing someone to make recurring withdrawals from your bank account, and letting someone be your bank.

It isn’t a troll. You are on one hand advocating for in-protocol revocation with 7702 because it is “the user’s last line of defense”, yet in 4337 provide no such defense for users. That isn’t consistent.

Maybe what I proposed as a revocation system for 4337 isn’t great, but if we worked on it a bit it would probably end up in a decent state. It would restrict 4337 accounts, yes. But so does max nonce.

I will admit that the max nonce does not restrict EOA accounts much, especially if it’s possible to sign arbitrarily large nonces. But it is still an unnecessary wart in the protocol – as seen by 4337s lack of revocation.

It has nothing to do with the proposer :slight_smile: . My order of preference is:

  1. no revocation
  2. max nonce
  3. current nonce

If we must have in-protocol revocation, maxNonce is my preference because I believe it is a better mechanism than the current nonce mechanism.

In the max nonce proposal you suggested I can’t see an observable cost to the user beyond the additional 8 bytes of tx data to include the nonce in the code / sig tuple. At this point, the cost is to the protocol in additional and unnecessary complexity.

I will add that it isn’t perfectly clear how to deal with invalid nonces in 7702 though. We don’t want that to cause the entire bundle to become invalid and dropped, but it also feels a little weird to allow invalid ones in the tx. It seems like the latter is most similar to how it would have behaved in 3074 so I suppose we can go with that until a better solution is discovered.

1 Like

Did some back of the napkin estimation for how much code would be required per signature.

Looks like it’ll be about 78 bytes, and you get:

  • chain id check
  • revocation logic
  • bytecode copied from an on-chain template contract

Here’s the (untested) bytecode I used: contract_code.etk · GitHub

1 Like

First, let’s move 4337 (or any SCA, like Safe) revocation to its own thread. If you can suggest a general to force ability of future “update impl”, while keeping it decentralized ,I’m all for it.

The user doesn’t opt-in to use 7702 accounts .
once the protocol is added, the wallet, not the user, decides when to sign something.
Since there is a “one source of truth” (ecdsa sig), I think it should be used to limit “eternal damage”.

And re: nonce bump

  1. It doesn’t increase the tx size, just like tx nonce doesn’t appear in a normal tx. You only sign it.
  2. There is one observable side effect to the current suggested 2^32 bump: off-chain code treat nonce as total txs of the account (well, that’s the rpc name eth_getTransactionCount)
    The protocol currently limits nonce to 64 bit, but afaik, the code uses a whole storage slot.
    What if the bump is increased to 2^64? that side-effect is removed (but requires a separate rpc to read it)

Adding a side note that this would be easily integratable into EIP-6493 SSZ Transaction as well. RLP transaction types are quite difficult to deprecate over time (it only ever partially happened for the replayable transactions without chain ID, which are refused at RPC level but still accepted in blocks)

1 Like

@yoavw not sure if you already described it somewhere but how exactly would bumpNonce be implemented? A new opcode? Something in this new transaction type?

Also, @SamWilsn seemed to have convinced you that we need initcode but I’m having trouble pinpointing exactly what he said that convinced you. Do either of you mind explaining?