-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ERC-1776 Native Meta Transactions #1776
Comments
Nice! Two initial comments:
|
^^ Such good input. @PhABC, is this similar to your "Meta20" work? Do you smell a collaboration or should these two remain separate? |
Yes, it's very closely related to the MetaTX ERC20 wrapper indeed. Ideally the ERC-20 wrapper can comply with this standard and vice versa. For those wondering what Austin is referring to, here's the relevant repository: https://github.com/horizon-games/ERC20-meta-wrapper. The native metaTX token contract is here. |
yes we could easily add support for ERC-1155. it should work as ERC-777. We could simply a third EIP712 message type definition with the same data
Currently the standard as it stands to not preocuppy itself with the function that execute the meta transaction. If it did, I would like to know more about the reasoning behind the optionality of the arguments : which one, what would be the default values / behavior in each case ? Would the signature scheme need to change ?
Good point, I ll add that |
I want to discuss the current data passed related to the "gas payment" first, perhaps how this data is encoded in function calls is a different topic indeed.
I would personally remove
To keep, this is important.
I would rename that to
Should leave in as well.
I would add a field such that the user can specify how they will pay for gas: In WETH? In DAI? In WBTC?
I was once pondering if we should add a In our native meta-tx, we are currently using this as a "gasReceipt" struct: // Gas Receipt
struct GasReceipt {
uint256 gasLimit; // Max amount of gas that can be reimbursed
uint256 baseGas; // Base gas cost (includes things like 21k, CALLDATA size, etc.)
uint256 gasPrice; // Price denominated in token X per gas unit
address feeToken; // Token to pay for gas as `uint256(tokenAddress)`, where 0x0 is MetaETH
address payable feeRecipient; // Address to send payment to
} Will probably remove the |
I'm also thinking of whether we should add a general |
Hi @PhABC
I made this decision to include the actual ethereum gasPrice as a message field for 2 reasons :
Indeed, but unless it was clear enough, such gasLimit represent the amount of gas passed into the call (executed by the meta-transaction as part of the transaction executed by the relayer) so that the call is deterministic based solely on the input of the signer. As such the meta-transaction implementation need to ensure enough gas is passed to the transaction so that when calling
Interesting, I actually did not thought about that because for our use case we did not plan to support such. At the same time, the change is minimal and for contract that do not want to support different token, they would not even need to add that to the calldata. Still, how wallet would know whether other tokens (non-native) are supported ? Maybe the field could still be added but wallet should not think that they can let the user chose any token. So when an EIP712 signature is requested with the field This forbids the ability to suggest a default one though, unless the standard evolve to be a different RPC call (I liked the idea of being solely a EIP712 message definition, for several reason, one being backward compatibility). So unless we can clarify the idea , my current stance is to not include it as part of this standard, that focus on native meta transaction.
baseGas is indeed an important topic and that is what I was alluding to by the "extra gas required by the relayer". One thing is certain is that wallet/browser will need a way to know that value to display it to the user. I was at some point thinking of adding a mandatory function like By the way, as mentioned about
This is not that simple. a user could simply specify a high gasLimit to make believe the relayer that its margin will cover the extra fee, while the actual transaction consume very little.
Instead of including it in the message, I was thinking that the wallet should estimate gas of the relayer transaction to predict the actual amount of token being refunded. This would require us to define the abi signature of such function and is not ideal in my opinion as it gives you the total gas cost and can't predict the actual gas used when executed for real. I currently like to have it included as part of the message to sign to avoid ambiguity and ensure the data is the same for everybody (wallet, user, relayer). This does not allow the wallet/browser to figure it by itself though. But is that even desired ? To summarize our 3 options are :
1 and 2 together would :
Having said that, I am not sure whether wallet need to be able to figure the value by themselves. At the end of the day, a relayer would be needed and such relayer would not accept random tokens. As such the relayer would be able to know what baseGas to use and the wallet would be able to know without having the smart contract to publicly tell. As such the Note though that if
Hmm, if you only need to differentiate between ERC-20 and ERC-1155 you could do as the current draft do in regard to ERC777 and ERC20 : 2 different signature scheme with the same data
I would say, let's focus on something concrete we have now. We could always create a new standard for wallet to support ERC-721... (as an extension) |
This doesn't really matter because the relayer can always wait before submitting the tx. If multiple relayers can execute, then this is no problem and if the relayer is enforced by you, you can change relayer if the service they provide isn't good. Think of exchanges like Binance that will withdraw your funds with a gas price of 50 Gwei to ensure good customer service. It also gives no room to the relayer to adjust the gas price if market changes, which reduces the chances that the tx will be submitted.
Only supporting the current token makes the MetaTX market less efficient however, since now relayers are "forced" to accept the given token if they want to serve their users, instead of only accepting DAI or WBTC for instance. This adds complexity to the relayer and could fragment relayers. It's likely that smaller/new tokens would not be able to find reliable relayers as the risk for the relayers is too large. However, if users could specify
Wallet just need to check token balances and check the approvals. Wallet could by default use the native token of the contract, which offers the same experience as your current proposal, but with the added flexibility that one could use another token to pay for gas.
Here again I would not enforce this per say and leave it to the relayer, where the contract reimburses up to the gas limit. A relayer will therefore have no incentive to exceed and the transaction will still go through if the relayer undervalues the amount of gas required. In your proposal, if
Miners need to make the same type of decisions, where if a transaction gasLimit can be much higher than what is actually consumed. I don't think this will be a problem in practice with good enough tools.
That sounds reasonable. The simplicity of including a baseGas field for most implementations seems indeed worth it.
Without |
I am not convinced: For the case when there is many relayers, indeed, the gasPrice used will tend towards the ethereum gasPrice equivalent to the tokenGasPrice value but then this is mostly equivalent to letting the user dictate the gasPrice. If there is only one relayer (whether it was the only specified or not) on the other hand, the user becomes at the mercy of its decision. That relayer is incentivised to get paid more than what it is providing and so the user will wait longer than it should for its transaction. While we could argue that's the nature of such small market, my point is that the tokenGasPrice can still increase to offer incentive for such relayer to relay, on the other hand the actual ethereum gasPrice should be in the hand of the signer, so the relayer can't malicious grab the price and provide a slow experience. In such situation, the user can't simply increase the tokenGasPrice as he has no guarantee the relayer will increase teh gasPrice in exchange. In other words, the only advantage a gasPrice determined by the relayer bring to the user is when the relayer (by potentially losing money, at least its margin) increase the gasPrice. The other way could only be to the detriment of the user. As such for gas market price changes, which is only a problem for the user when the gas price is not sufficient anymore. We could allow the relayer to increase it but never make it lower than specified by the user. This conflict though with the other issue arising from letting the relayer set the gas price: the user is not in control of the execution data anymore. In principle, I think it is important that the relayer should not have any say to the data being sent as part of the transaction. gasPrice, is such data. I agree though that we could specify the need to not use gasPrice in receiver call as part of the standard. Personally I do not see any use case that would be restricted but is that a safe assumption? if that is safe, I would agree to let the relayer increase the gasPrice but not decrease it. By the way, there is another potential side benefit of a fixed gasPrice: it might help a network of relayers to coordinate since there would be no easy way for a late relayer to grab the price by submiting a transaction later.
I think you convinced me on this one, not from this response specifically but from the general idea of generic meta transaction. This has some implications though and I am still ensure if they all fine. For example the current draft allow the transaction to specify a token amount to send to the destination. If we want to support that, we probably want to add yet another field One thing worth noting here is that as I said we would only need one smart contract implementation for all native meta transaction, then. The current proposal could still focus on the message format, each field's role, receivers expectation and wallet support though.
The question was about how the wallet know that the token supporting meta transaction support a specific token, not that the user approved the token. But now that I think a bit more clearly about such generic meta transaction contract, I can see that there is not reason the implementation limit which tokens are supported, this is up to the receiver (relayer and destination) to accept them or not.
Again by principle, the relayer should not have any say in the data passed to the call and this is even more important for gas than gasPrice. But more importantly, if the relayer decide what amount of gas is passed to the call, when it fails, who is to pay the meta transaction? This should not be the user if the call failed due to the lack of gas.
The only time the gasLimit would be too low in my proposal is when the user miscalculate the cost. It can't become insuficient. As such if ever the user sign a gasLimit too low, the relayer (provided it sent enough gas for the whole tx) will be rewarded for the execution anyway (and the meta nonce increased) since we can calculate that it was the user's mistake not the relayer.
That's not a fair comparison. In the miner case, there is not extra margin decided by the miner The miner simply knows that they will be paid x for y gas.
As said about ERC1155 we can easily add ERC712 specific message preamble in the TYPEHASH. I' ll add that to the proposal. We could also simply add a string field instead that state the ERC to be used like : |
Hello. Can we compare what has been done here with EIP1613/Tabookey ( https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1613.md ) ? Can we combine both ? (Native MetaTX in your token contract + you use Tabookey's relay) |
Updated the draft with latest improvement. Most notably, the addition of |
Just made a new update, that include the following:
I also added a paragraph on the use of |
There seems to be a bit of a misunderstanding here as the Gas Station Network (EIP 1613) has always supported native meta transactions signed by externally owned accounts. From day one. The differences between the GSN and EIP 1776 is that:
An update to the GSN is in progress that adds:
|
EIP712 format is created to make it easy for user to understand and see what he is signing which is way better than signing a hexadecimal string. And as more and more DApps will be including meta transactions and blockchain mainstream adoption increases, with the current fields in the format given in this EIP, I still feel a normal end-user would not know what is happening in the meta transaction. So a description field in the format should be added, that'll include the description of transaction in a human readable form. |
@tomarsachin2271 thanks for your feedback. The goal of this standard is so that when it get accepted, wallets will be able to show meaningful information, instead of the raw EIP-712 message. They would also let the user specify/change some of the parameter, like expiryTime. |
Can someone point me out the main differences between this and ERC865? I looked into the main function that recovers the sender from the signature and then let a third party submit the transaction while paying for the gas fee and receives tokens as reward. Looks the same to me. Thanks:) |
@tina1998612 the main difference is that EIP-1776 support any call, while ERC865 only support erc20 transfer |
There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment. |
Introduction
Native Meta transactions (a term first coined by Austin Thomas Griffith here : https://medium.com/gitcoin/native-meta-transactions-e509d91a8482) allows users that simply own ERC20 or ERC777 (ERC1155 could be added too) tokens to perform operations on the ethereum network without needing to own ether themselves by letting a third party, the relayer, the responsibility to execute a transaction on the ethereum network carrying the desired operations (the so called meta transaction) in exchange of a token reward to cover the relayer's gas cost. They differ from traditional meta transactions (https://medium.com/uport/making-uport-smart-contracts-smarter-part-3-fixing-user-experience-with-meta-transactions-105209ed43e0) in that they only require support from the meta transaction processor contract itself and do not need the user wallet to be contract based.
The proposal here define the message format and meta transaction processor contract interface required for web3 wallets and browsers to provide a meaningful meta-transaction display when users are requested to approve. This could even allow wallets/browsers to not bother users by displaying an ether balance when such users do not have any ether or when the application being used does not require it.
It does not dictate how such signed message get injected on the network except for the meaning of each message parameters and their security requirements. More precisely, it does not dictate the ABI signature for the smart contract function executing the meta-transaction (the one signed and broadcast by the relayer). This is left as work for another standard.
Nevertheless due to nature of ERC20 this proposal also need to define how smart contract recipient of such meta transaction need to behave when being called. In particular it specify how the
from
field is to be verified.Why native meta transactions?
It is common today for users to own ERC20 tokens without owning any ether. More and more applications reward their users without requiring prior on-chain interactions. It is thus possible for users to have been given tokens without them ever owning ether. With native meta transactions and a willing relayer (the company behind the token for example), such users can now interact with ethereum.
Without meta transactions, it would be impossible for them to interact with the ethereum network when required. Indeed, unless they have ether they can't interact with the ethereum network, requiring them to go through a difficult and costly process to acquire it. They can't even exchange their token for ether without having ether to pay the gas associated with such transaction.
While normal meta-transactions are possible using smart contract based wallet (like gnosis safe: https://safe.gnosis.io), there are currently more users with EOA (Externally Owned Account) based wallet and this might be the case for a while as there is an inherent cost to smart contract wallet. Native meta transactions also allow new tokens to be used without requiring generic relayer support on the part of the smart contract wallet. They thus offer native support for meta-transactions without any requirement from the users except to have a private key and enough tokens to pay for the gas.
As mentioned, Native Meta Transactions are great for applications whose users do not necessarily own or even know about ether. This is also useful for applications that want to distribute their tokens to new non-crypto users. Indeed, with such meta-transactions they can then start interacting in ethereum without having to think about ether. Application with the support of web3 wallet and browsers can then provide a less confusing experience where users can simply operate on one currency, at least until their horizon expands.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
For operability and the ability for wallet to display a more meaningful UI the following need to be defined:
Message format
The proposal is using a message format based on ERC712 so that wallet that support ERC712 but do not support the proposal described here can still offer an approval display showing all the information albeit in a less than ideal presentation.
Here is the proposed ERC712 message format :
typeHash = keccak256("ERC20MetaTransaction(address from,address to,address tokenContract,uint256 amount,bytes data,uint256 batchId,uint256 batchNonce,uint256 expiry,uint256 txGas,uint256 baseGas,uint256 tokenGasPrice,address relayer)");
The meaning of each field is as follow:
amount
field if non-zero. This allows the standard to work for both per-token metatx implementation and general implementation that would support multiple token.to
to
(if empty, only a transfer will be executed)batch and nonce
In order for the wallet or application to request a valid meta transaction it needs to be able to know the current nonce
The token contract MUST implement a getter for the current nonce as follow:
function meta_nonce(address from, uint256 batchId) external view returns(uint256);
Nonces work similarly to normal ethereum transactions: a transaction can only be executed if it matches the last nonce + 1, and once a transaction has occurred, the
lastNonce
will be updated to the current one. This prevents transactions to be executed out of order or more than once.But instead of being one-dimensional, each nonce is actually associated to a batchId. This is offer great flexibility to the user and allow them to batch meta-transaction together if so desried while still allowing them to submit other meta-tx in paralel.
expiry and EIP-1681
While a previous version of this standard was using a
minGasPrice
to ensure that the user meta-transaction get included at a minimum price, such field becomes unecessary if we have anexpiry
field that have also a more meaningful purpose.One of the danger,
minGasPrice
were protecting against, was relayers that would include the tx at a very low gas price to get a higher profit. With anexpiry
field, the relayer has to ensure the gasPrice is adequate so that the meta-tx is included in time.On the other hand, the relayer is now risking to submit the transaction just a bit too late. To solve this, we can rely on EIP-1681 that can ensure the relayers that if the cannot get included after the expiry time-limit.
execute transaction and receiver verification
When executing the meta-transaction the contract must then verify the signature and if the nonce matches as specified above.
Then for the case of ERC20 the implementation need to ensure that the first parameter of the call being made is equal to
from
. The receiver will thus be able to accept calls from the token by knowing that the first parameter is indeed thefrom
and not some arbitrary address. This means only such receiver will be able to accept such meta transactions securely.In order to do it, the receiver simply check if msg.sender is the meta transaction processor contract itself and if so can assume that first parameter is equal to the
from
specified as part of the meta transaction message,Remember, such ERC20 meta transaction receiver need to have as first parameters the "sender" whose token will be deduced.
for example:
The meta transaction processor will ensure the ERC20 token as the permitted allowance and
Balance checks
To protect from malicious user the relayers also need to ensure the user (
from
) has enough balance. While it is technically possible for the user to withdraw token just in time (between the meta-tx is send and mined), it is unlikely to happen since it is unlikely to benefit the user unless it wishes to cancel the last minute. They could achieve a similar feat anyway by publishing a different signed message with the same nonce (albeit at a higher gas cost than a simple transfer). This is a risk that need to be taken by the relayer.Gas accounting and refund
The
txGas
set as part of the message represent the gas passed to the contract call made (the meta transaction). This ensure the signer that its call will be executed as intended with as many gas as it asked for. This means though that the total gas cost of the realyer's transaction will be higher than that.As mentioned above, this is solved with
baseGas
parameter that can be updated if opcode pricing changes. While this might feel like yet another extra field, it is important to note that the alternative (not having it) is worse since either the smart contract hard code the extra gas or the relayer have to pay the cost.Gas estimate
In order to estimate the
txGas
to use for the meta transaction, the meta transaction call data can be used. The behaviour will be identical. As such there is no need to expose an estimateGas function for that.As for the extra gas required by the relayer, the whole meta transaction call can be estimated as usual.
Relayer cooperations
One possibility that remains a problem for relayers is that a user could submit the meta-tx message and signature to multiple relayer at once.
This possibility pose a problem to relayers as they run the risk that their tx get included after another.
With the addition of expiry, we could imagine a simple method to avoid such issues:
Every meta-tx that include both an expiry field and relayer field, can always be included in a block if it reaches before
expiry
. If the nonce is already used, the actual meta-tx is not executed, but the relayer is getting rewarded for the gas spent (+baseGas
).This would ensure relayers that they get paid for the work they do, while still allowing users to choose which relayer they want. The expiry field would also allow.
Example Implementation
see https://github.com/wighawag/singleton-1776-meta-transaction/blob/master/contracts/src/GenericMetaTxProcessor.sol
wallet / browser
Web3 wallet and browsers MUST provide a meaningful interface when such signed message is being requested. They could for example show a similar UI to traditional ether transaction except it should clearly state the token being used as well as the other parameters. For the data field, they should also be able to use function signature registry to display the function being called and the arguments.
The text was updated successfully, but these errors were encountered: