It now has been expanded after further discussion with some of the other participants. In particular it expands on the idea proposed by Patrick McCorry to implement a minimal meta transaction forwarder with flexible replay protection (see tweet).
The demo itself show that such system is possible and compatible with more complex solution built on top:
That demo implements EIP-1776 (slightly modified, going to update the EIP soon) on top of it
This minimal forwarder is described in EIP-2585
It implement a barebone though powerful base layer to implement more complex relaying mechanism on top. Contract that want to receive meta transaction just need to check that
msg.sender == address(forwarder) and extract the address from the 20 bytes appended to call data. More complex relaying mechanism can be implemented without requiring change in meta transaction receiver. The demo showcase it with EIP-1776 but system like GSN can be implemented too.
In particular the minimal forward does not implement relayer repayment mechanism but can support it at a higher level. It simply ensure valid signer and replay protection.
Here are the current features :
|EIP-1271 / EIP-1654||✔||- allow account contract to use same mechanism and benefit for anything built on top||- add from in signed message and data|
|- add signatureType|
|msg.value||✔||- allow meta tx to send ETH||- add value in signed message (not in data)||Meta tx processor built on top can ensure relayer is rewarded for it (via token exchange for example)|
|flexible replay protection||✔||- can have more flexible or cheaper (in gas) replay protection||- add replayProtection contract address in signed message and data|
|- make nonce a |
|support building on top with one sig||✔||- only one sig reduce overhead||- add innerMessageHash|
|- easier to support for walle|
|fork protection (chainId)||✔||- add chainId in signed message (not in data, unless it want to support past chainId, see below)||- When a fork happen, user can decide to not send their meta tx to fork with different chainId|
|fork transition protection||- add chainId in signed message and data||- when a fork happen, any meta tx submitted before the fork remains valid in both||Need to add chainId cache or better use EIP-1965 (not yet in)|
|EIP-712||✔||- show a default message display for wallet that do not support the forwarder standard but support EIP-712||- add overhead (compleixity and operations)||Meta tx processor built on top will not be able to shows their parameter via default EIP-712 support (uses innerMessageHash)|
|Basic Signature||✔||- simpler||- does not have nice default display||Meta tx processor built on top will not be able to display their info neither (uses innerMessageHash)|
|batch capability||✔||- allow to support batch transaction like ||- add a function batch|
EIP-1776 is a full solution for meta-tx including relayer repayment that provides safety guarantees for both relayers and signers
It now use EIP-2585 Forwarder for signature verification by using the
innerMessageHash parameter allowing the EIP-712 message format to be embedded in the EIP-2585 message.
Every recipient contract supporting EIP-2585 (which only use the basic
_getTxSigner() mechanism though 20bytes appended to the call) can receive EIP-1776 Meta Transaction
Contrary to earlier version, it removes support for ERC20 transfer as the same can be achieved through batch call that EIP-2585 support.
Use can now transact with ERC-20 by doing an approve and call this way
EIP-1776 distinctive features are as follow :
We can categorize meta-transaction support in at least, 6 different dimensions and here we will examine for each, the reasoning behind the choice
There are roughly 4 type of implementation
Since EIP-1776 use EIP-2585, EIP-1776 follows the SIngleton proxy. This is good for the following reason :
Another differentiation is the ability of relayer to get paid.
In our opinion, It is such an important feature for relayers that we should ensure it is at least possible to implement it on top, if not already present.
In that regard one thing that becomes important as soon as a relayer get paid, is that there is a mechanism to ensure the relayer cannot make the meta-tx fails. hence the need for txGas in EIP-1776.
Another important EIP that would help here is EIP-1930 as the EVM call have poor support for passing an exact amount of gas to a call.
It is also worth noting the importance of the
baseGas parameter here too, as this make our implementation independent of opcode pricing while ensuring the relayer can account for the extra gas required to execute the meta transaction processing cost.
While an earlier version of EIP-1776 supported token transfer as primitive, the latest does not as the same can be achieved with batch meta transaction that EIP-2585 supports. See demo.
Another differentiation possible for non-account based meta transaction is how the signer is being picked up by the recipient.
An earlier version of EIP-1776 was using the first parameter as it was easy to support, but the appending of signer data at the end of the call data is more generic and is what EIP-2585 uses.
Earlier meta tx implementation assumed that the relayer was responsible of ensuring that the meta transaction call would not fails. So that when it fails, the whole tx would revert and the relayer would not get its refund if any.
This is in our opinion, not a great idea as this complexify the role of the relayer, since it has to guess whether the meta-tx call will not fails for reason out of its knowledge.
As such, we decided to add the
txGas parameter that dictate how much gas need to be passed into the meta-tx call. If not enough is passed in the relayer's tx revert, causing relayer to lose its gas.
While this seems trivial to implement, this is not the case and almost every meta transaction implementation out there fails to implement it properly
They all seems to believe that the gas parameter passed to the various CALL opcode are a specific gas amount. This is not the case and the gas value only act as a maximum value unfortunately. To my knowledge, we are the first to implement correctly. Unfortunately in order to remain opcode pricing independent we relies on the specific behavior of EIP-150 63/64 rules to do so. The proper way to fix it for all meta transaction implementation would be to get EIP-1930 accepted in the next hard fork.
As show here, there are different replay protection mechanism with each their pros and cons. EIP-2586 is very flexible for that. User can choose different mechanism by simply specifying the contract in charge of replay protection. EIP-1776 inherit it.
Since EIP-1776 is based on EIP-2585, it automatically support Meta Transaction Batching.
In order to avoid the possibilities of relayers submitting 2 meta-tx with the same nonce, at the expense of the relayer getting its tx included later, the proposal offer a mechanism to avoid it.
Every meta-tx can include a relayer field. This field has 2 purpose, the obvious one is to limit the message to be used by a specific relayer. the second is to ensure the relayer that if the tx get included it get a reward for inclusion
Relayer can thus reject any meta-tx that do not specify their relayer address so that user are incentivized to only submit one tx at a time, or run the risk of paying the cost of the extra relayed tx.
Of course, if the user got rid of its payment token as part of one of this competing tx, the user remains safe and one of the relayer will not get its refund, so this is not full proof.
Also this is not implemented in the demo nor in the code provided.