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.
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:
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)
Language layer: Solidity & Vyper should not add support for 3074 opcodes, invokers should be built in Yul (or using inline Yul)
Social layer: discourage developers from using 3074 opcodes directly, instead encourage them to build for existing invokers.
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.
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.
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)
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?
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.