EIP-3074: AUTH and AUTHCALL opcodes

A possibility to ease security concerns is to add an AUTHRELEASE opcode. This opcode could be called by any contract that had previously received an AUTH from an EOA and would relinquish the authorization (for a particular commit of course).

This allows for contracts to implement two new kinds of functionalities. First, they could create a batchExecuteThenRelease function, which would allow users to send a signed AUTH message for batch execution, but could call AUTHRELEASE at the end of the execution to ensure that the contract does not retain indefinite control of the EOA. The second feature that can be built with this is a revoke function for an invoker, which would simply call AUTHRELEASE for a particular commit, allowing a user to safely remove authorization from a contract that they no longer trust.

Of course, this system has many downsides. To start, these abilities can be created entirely at the smart contract level, so it does not necessarily require a new opcode, but form often fits function, and it is likely if we added this opcode that many invokers would chose to take advantage of it. An additional downside is it would require the precompile to access storage to store who has released an authorization, which I understand has never been done before.

This sort of opcode would significantly increase the cost of AUTH, since there would need to be a state read to check if the EOA has been released or not.

Your proposal also doesn’t outline how the “release” function works. If you release the EOA itself, how do you “un-release”? If you release the signature, you’ll have to store the hash of the signature in the trie which costs ~22k gas.

I was thinking that release would work exactly as you described, where you release the signature, which should be unique. This would mean that calling auth would only have the increased gas cost of reading state. I do agree though that the increased gas costs may make my proposed solution not worth it, especially since these features can be implemented at the smart contract level.

an address is EOA,and someone uses EIP-3074 for this address

can others create this address as a contract?

for example,hash collisions occur on binance chain and ethereum

https://etherscan.io/address/0x491604c0fdf08347dd1fa4ee062a822a5dd06b5d

This likely isn’t hash collision. Just someone sent the address some BNB and CTSI on BSC.

Hey, I thought I’d post this Twitter conversation that’s being had about ERC-3074 and composability, starting from this tweet:

The concern is that if application developers begin using 3074 in their contracts, then those contracts basically become EOA-only.

You can take a look at this example from @fubuloubu:

transferFrom(msg.sender, this, amt) Can just be transfer(this, amt, auth=sig)

If applications use transfer{ auth: ... }() instead of transferFrom(), then contracts can no longer use this function, since contracts can’t sign messages (@gakonst pointed out that ERP-1271 could be used, but I think it would be difficult to get much adoption of that).

It seems that these issues can be avoided if application-level use of EIP-3074 is discouraged, and AUTH is only invoked from a handful of whitelisted “invoker” contracts.

So my takeaways from this conversation is that usage of EIP-3074 should be limited at these levels:

  1. Wallet layer: wallets should only allow signing 3074 messages for whitelisted invokers (and there should be a very scary warning before adding new invokers to the whitelist)
  2. Language layer: Solidity & Vyper should not add support for 3074 opcodes, invokers should be built in Yul (or using inline Yul)
  3. Social layer: discourage developers from using 3074 opcodes directly, instead encourage them to build for existing invokers.
1 Like

I’ve been under a rock for the past few months, but I was thrilled to learn about 3074 while watching the presentation at EthCC 4. It’s refreshing to finally see more discussion with a focus on improving transaction efficiency and delegation, so that mainstream adoption (especially by enterprise) can happen in the near future. Bravo!

I’m the author of EIP-2746, and so I definitely have an interest in this subject, especially in regard to batching and rules. (I’ll admit that my EIP is definitely not as thought out as this one. I’m absolutely an amateur in this space.) I apologize for not yet being familiar with the implementation, but I was especially curious about the rules being encoded in these AUTHCALL requests (particularly in terms of their flexibility) and about the assumption that these new opcodes could be invoked within a L2.

Basically, I’m wondering what considerations have been made in terms of rule complexity and whether these rules can be other contract methods. I could see an interest in the Invoker executing a standard, lengthy set of rules that are too complicated and too costly to be encoded as part of a transaction’s calldata. In one example, a company might have a complicated validation algorithm that exists as a contract method and simply returns a boolean. In another example, a government or accounting firm could post a contract method (or a EIP-2746 ruletree - shameless plug) that is the standard for calculating a VAT tax, one that could be used by any enterprise without fear, and returns a numeric value. So, my question is this: could an AUTHCALL request result in the Invoker getting input from a contract method (using only a CALL, to be safe), to either evaluate in a rule or to pass along to the sponsored transaction? If not, can the Invoker receive and then store these encoded rules (which are part of the calldata?) on behalf of the caller, so that they can be invoked again at a later time and without the cost of sending them once again? That storage might be costly, but in the case of 3074 being deployed to a L2 (like Optimism), these storage costs could possibly be mitigated. Assuming, of course, that storage might be cheaper on a L2 in the long run.

Very curious about what’s possible. And great work so far!

Whoops…I forgot one question: do you think that EIP-3074 could possibly be compatible with a batching implementation found in a L2, like the Optimism sequencer?

L2s have more flexibility, so they can define their accounts to have arbitrary functionality. But yes, it would be compatible.

I still think there is different way of thinking about contract wallet accounts that benefits significantly from EIP-3074 and L2s could support something similar, even if EIP-3074 doesn’t reach mainnet.

I agree wholeheartedly. In addition to a new paradigm for account management, an Invoker instance from EIP-3074 could be a foundation for organizations and enterprises to build upon. If the Invoker proved to be a stable, safe platform by itself that could control an account through a set of dispatched rules (which are predefined simple actions), then their investment into contract creation (of developing, of auditing, of deploying, etc.) becomes unnecessary as they would just deploy their strategies to the Invoker, accelerating the buy-in of the mainstream.

Unfortunately, mainnet acceptance will probably be difficult to achieve. Do you think that Optimism would be curious to consider 3074 and test the new opcodes in the OVM?

Created a small website that I’ll slowly add educational materials about how EIP-3074 can improve day-to-day lives. Currently just showcasing how much has been spent on approvals.

https://www.approvals.wtf/

1 Like

I have a question about this EIP, especially this line in the Behavior section:

  • If the gas available for the subcall would be less than gas, execution is invalid.

In Frontier, we had this rule also for CALL and CALLDATA but in EIP150 this was removed: the reason was that contracts (in most cases) used something like PUSH 23 GAS SUB CALL, where 23 here is the gas cost of CALL plus the cost of SUB (this thus relies all GAS to the call frame). The problem is, that if you increase the base cost of CALL, this mechanism breaks. This EIP is thus also subject to this case, if we ever change AUTHCALL costs then some contracts will break.

I don’t understand the rationale for it. The contract itself can check if there is enough gas left (can also be put in the commit, for instance). If there is not enough gas left, then revert the frame. I would suggest to use the rule from EIP150: if the GAS wants to send more GAS than currently is left, then send 63/64 of the available gas. Any safety checks can be handled by the contract implementation itself.

An idea was posted to replace WETH with a mapping using EIP-3074:

This would require the “Ether transfer” functionality to be enabled in EIP-3074.

We are working on a draft implementation at EthereumJS/VM: Implement EIP3074: AUTHCALL by jochem-brouwer · Pull Request #1788 · ethereumjs/ethereumjs-monorepo · GitHub

The EIP is really well written, I have not yet encountered any edge cases which are not covered by the spec (but might do so when writing tests).

1 Like

Yes, a small thing:

Note that yParity is expected to be 0 or 1.

I think we should enforce this, so if yParity is not 0 and also not 1 then the operation is invalid, throw and consume all gas. (Maybe this is meant by this sentence already)

2 Likes

We have implemented the EIP in EthereumJS/VM: Implement EIP3074: Authcall by jochem-brouwer · Pull Request #1789 · ethereumjs/ethereumjs-monorepo · GitHub (it is targeted towards a develop branch so it will take some time to start using it directly from npm).

There is one more question I have which is not stated in the EIP. In case we are in a STATICCALL frame, are we allowed to AUTHCALL? Following EIP-214 logic it should throw if either value or valueExt is nonzero, but if both are zero, it should be allowed. Can this be specified in the EIP?

I believe it must throw the same way as CALL, since it’s defined as a list of changes:

AUTHCALL is interpreted the same as CALL, except […]

1 Like

Put up a change to auth to make it more future compatible. Comments welcome!

Had an interesting discussion today where it might be theoretically possible to create a single “meta-invoker” that enforces replay protection/revocable signatures/etc, which then delegate calls into app-specific “sub-invokers”. If that ends up being possible, the whitelisting process would only need to approve the one “meta-invoker” to cover 90% of use cases.

This is now merged. Instead of pushing everything on the stack, auth now takes an address and a memory range.

Currently the memory range contains the commit and signature, and the address from the stack must match the address recovered from the signature.

In the future, should EIP-3074 get merged, auth can be modified to work with EOAs and smart contract wallets!