ERC-1077 (gas relay interface) update #2454
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Updates interface to allow any type of "execution", abstracting only gas relay mechanism here (not execution or account contracts stuff).
Simple Summary
A standard interface for gas abstraction in top of smart contracts.
Allows users to offer EIP-20 token for paying the gas used in a call.
Abstract
A main barrier for adoption of DApps is the requirement of multiple tokens for executing in chain actions. Allowing users to sign messages to show intent of execution, but allowing a third party relayer to execute them can circumvent this problem, while ETH will always be required for ethereum transactions, it's possible for smart contract to take EIP-191 signatures and forward a payment incentive to an untrusted party with ETH for executing the transaction.
Motivation
Standardizing a common format for them, as well as a way in which the user allows the transaction to be paid in tokens, gives app developers a lot of flexibility and can become the main way in which app users interact with the Blockchain.
Specification
Methods
executeGasRelay
Executes
_execData
with currentlastNonce()
and paysmsg.sender
the gas used in specified_gasToken
.executeGasRelayMsg
Returns the
executeGasRelay
message used for signing messages..executeGasRelayERC191Msg
Returns the EIP-191 of
executeGasRelayMsg
used for signing messages and for verifying the execution.lastNonce
Returns the current nonce for the gas relayed messages.
Signed Message
The signed message require the following fields:
Signing the message
The message MUST be signed as EIP-191 standard, and the called contract mMUSTust also implement EIP-1271 which must validate the signed messages.
Messages MUST be signed by the owner of the account contract executing. If the owner is a contract, it must implement EIP-1271 interface and forward validation to it.
In order to be compliant, the transaction MUST request to sign a "messageHash" that is a concatenation of multiple fields.
The fields MUST be constructed as this method:
The first and second fields are to make it EIP-191 compliant. Starting a transaction with
byte(0x19)
ensure the signed data from being a valid ethereum transaction. The second argument is a version control byte. The third being the validator address (the account contract address) according to version 0 of EIP-191. The remaining arguments being the application specific data for the gas relay: chainID as per EIP-1344, execution nonce, execution data, agreed gas Price, gas limit of gas relayed call, gas token to pay back and gas relayer authorized to receive reward.The EIP-191 message must be constructed as following:
Rationale
User pain points:
App developer pain points:
Using signed messages, specially combined with an account contract that holds funds, and multiple disposable ether-less keys that can sign on its behalf, solves many of these pain points.
Multiple signatures
More than one signed transaction with the same parameter can be executed by this function at the same time, by passing all signatures in the
messageSignatures
field. That field will split the signature in multiple 72 character individual signatures and evaluate each one. This is used for cases in which one action might require the approval of multiple parties, in a single transaction.If multiple signatures are required, then all signatures should then be ordered by account and the account contract should implement signatures checks locally (
JUMP
) on EIP-1271 interface which might forward (STATIC_CALL
) the EIP-1271 signature check to owner contract.Keep track of nonces:
Note that
executeGasRelay
function does not take a_nonce
as parameter. The contract knows what is the current nonce, and can only execute the transactions in order, therefore there is no reasonNonces 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.Contracts may accept transactions without nonce (nonce = 0). The contract then must keep the full hash of the transaction to prevent it from being replayed. This would allows contracts to have more flexibilities as you can sign a transaction that can be executed out of order or not at all, but it uses more memory for each transaction. It can be used, for instance, for transactions that the user wants to schedule in the future but cannot know its future nonce, or transactions that are made for state channel contracts that are not guaranteed to be executed or are only executed when there's some dispute.
Execute transaction
After signature validation, the evaluation of
_execBytes
is up to the account contract implementation, it's role of the wallet to properly use the account contract and it's gas relay method.A common pattern is to expose an interface which can be only called by the contract itself. The
_execBytes
could entirely forward the call in this way, as example:address(this).call.gas(_gasLimit)(_execData);
Where
_execData
could call any method of the contract itself, for example:call(address to, uint256 value, bytes data)
: allow any type of ethereum call be performed;create(uint256 value, bytes deployData)
: allows create contractcreate2(uint256 value, bytes32 salt, bytes deployData)
: allows create contract with deterministic addressapproveAndCall(address token, address to, uint256 value, bytes data)
: allows safe approve and call of an ERC20 token.delegatecall(address codeBase, bytes data)
: allows executing code stored on other contractchangeOwner(address newOwner)
: Some account contracts might allow change of ownerfoo(bytes bar)
: Some account contracts might have custom methods of any format.The standardization of account contracts is not scope of this ERC, and is presented here only for illustration on possible implementations.
Using a self call to evaluate
_execBytes
is not mandatory, depending on the account contract logic, the evaluation could be done locally.Gas accounting and refund
The implementing contract must keep track of the gas spent. One way to do it is to first call
gasLeft()
at the beginning of the function and then after executing the desired action and compare the difference.The contract then will make a token transfer (or ether, if
tokenAddress
is nil) in the value ofgasSpent * gasPrice
to themsg.sender
, that is the account that deployed the message. If there are not enough funds, or if the total surpassesgasLimit
then the transaction MUST revert.If the executed transaction fails internally, nonces should still be updated and gas needs to be paid.
Contracts are not obligated to support ether or any other token they don’t want and can be implemented to only accept refunds in a few tokens of their choice.
Usage examples
This scheme opens up a great deal of possibilities on interaction as well as different experiments on business models:
Backwards Compatibility
There is no issues with backwards compatibility, however for future upgrades, as
_execData
contains arbitrary data evaluated by the account contract, it's up to the contract to handle properly this data and therefore contracts can gas relay any behavior with the current interface.Test Cases
TBD
Implementation
One initial implementation of such a contract can be found at Status.im account-contracts repository
Similar implementations
The idea of using signed messages as executable intent has been around for a while and many other projects are taking similar approaches, which makes it a great candidate for a standard that guarantees interoperability:
Swarm city uses a similar proposition for etherless transactions, called Gas Station Service, but it's a different approach. Instead of using signed messages, a traditional ethereum transaction is signed on an etherless account, the transaction is then sent to a service that immediately sends the exact amount of ether required and then publishes the transaction.
Security Considerations
Deployers of transactions (relayers) should be able to call untrusted contracts, which provides no guarantees that the contract they are interacting with correctly implements the standard and they will be reimbursed for gas. To prevent being fooled by bad implementations, relayers must estimate the outcome of a transaction, and only include/sign transactions which have a desired outcome.
Is also interest of relayers to maintaining a private reputation of contracts they interact with, as well as keep track of which tokens and for which
gasPrice
they’re willing to deploy transactions.Copyright
Copyright and related rights waived via CC0.
References