Skip to content
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: Executable Signed Messages refunded by the contract #1077

Merged
merged 7 commits into from
May 15, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions EIPS/eip-1077.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
eip: 1077
title: Executable Signed Messages refunded by the contract
author: Alex Van de Sande <avsa@ethereum.org>, Ricardo Guilherme Schmidt
discussions-to: https://ethereum-magicians.org/t/erc1077-and-1078-the-magic-of-executable-signed-messages-to-login-and-do-actions/351
status: Draft
type: Standards Track
category: ERC
created: 2018-05-04
requires: 725, 191
---

## Simple Summary

Allowing users to sign messages to show intent of execution, but allowing a third party relayer to execute them is an emerging pattern being used in many projects. 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.

## Abstract

User pain points:

* users don't want to think about ether
* users don't want to think about backing up private keys or seed phrases
* users want to be able to pay for transactions using what they already have on the system, be apple pay, xbox points or even a credit card
* Users don’t want to sign a new transaction at every move
* Users don’t want to download apps/extensions (at least on the desktop) to connect to their apps

App developer pain points:
* Many apps use their own token and would prefer to use those as the main accounting
* Apps want to be able to have apps in multiple platforms without having to share private keys between devices or have to spend transaction costs moving funds between them
* Token developers want to be able for their users to be able to move funds and pay fees in the token
* While the system provides fees and incentives for miners, there are no inherent business model for wallet developers (or other apps that initiate many transactions)

Using signed messages, specially combined with an identity contract that holds funds, and multiple disposable ether-less keys that can sign on its behalf, solves many of these pain points.

### Implementation

The signed messages require the following fields:

* To: the target contract the transaction will be executed upon
* From: the account that will be executed on behalf of
* Value: the amount in ether to be sent
* Data: the bytecode to be executed
* Nonce: a nonce *or* a timestamp
* GasToken: a token in which the gas will be paid (leave 0 for ether)
* Gasprice: the gas price (paid in the selected token)
* GasLimit: the maximum gas to be paid

#### Signing the message

All accounts that will be requesting the transaction must sign a messageHash that is calculated by the following formula:

```
keccak256(
callPrefix,
from,
to,
value,
dataHash,
nonce,
gasPrice,
gasLimit,
gasToken
);
```

The `from` field will always be the contract executing the code (`address(this)`), and the `callPrefix` is the 4 byte standard prefix of the function to be called in the `from` contract. This guarantees that a signed message can be only executed in a single instance.

All signed messageHashes should then be ordered by account and sent to the receiveing contract which then will execute the following actions:

#### keep track of nonces:

If the nonce number is smaller than 10^24 then it should be treated as a Nonce:

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.

If it’s larger than 10^24 *and* larger than the current timestamp multiplied by 10^12 *and* larger than `lastTimestamp` then it should be executed normally but instead of updating the nonce you must update the last Timestamp field. This allows transactions to be scheduled to be executed at a later time. Notice that if multiple timestamped transactions are signed, they **must be executed in the numerical order** (with 10^24 possible transaction slots for every second), otherwise the earlier transaction will not be executable.

### execute transaction

The contract must then verify the signatures of the accounts and check if the public keys match accounts that are authorized to do what they intend to do: depending on the contract implementation, some contracts might require multiple key signatures, other might have specific actions few accounts are authorized to do.

If an identity, then, the contract should implement ERC725 identity management levels: a key must be at least an `action` key to have authorization to demand any call to external contracts, and a `management` key to demand calls to the contract itself.

If the signing accounts are authorized to do so, the contract must execute the requested action. If the current contract is the same as `from` field, then if can simply execute the actions by calling `_to.call.value(_value)(_data)`.

How the contract interprets the intended action depends on its purpose. For instance a token contract can decide to implement it in a way that interprets all actions as token transfers, and uses the `value` to mean token value and `bytecode` as the data to pass to the recipient contract

### 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 of `gasSpent * gasPrice` to the `msg.sender`, that is the account that deployed the message. If there are not enough funds, or if the total surpasses `gasLimit` 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.

**Deployers of transactions have no guarantees that the contract they are interacting with correctly implements the standard and they will be reimbursed for gas, so they should maintain their own white/blacklists of contracts to support, as well as keep track of which tokens and for which gasPrice they’re willing to deploy transactions.**

### Supported functions

`
executeSigned(
address to,
address from,
uint256 value,
bytes data,
uint nonce,
uint gasPrice,
uint gasLimit,
address gasToken,
bytes messageSignatures)
`

Executes the signed message. Execution usually means that a contract will execute a `call` to the `to` address, with `value` amount of ether and `data` as its data. But in some special cases, a token can decide instead to interpret it specifically as executing a `transferAndCall(to,from,data)` or the equivalent.

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.

`
gasEstimate(
address to,
address from,
uint256 value,
bytes data,
uint nonce,
uint gasPrice,
uint gasLimit,
address gasToken,
bytes messageSignatures)
returns (
bool canExecute,
uint gasCost)
`
A read only function that checks if the transaction will be executable and how much gas it’s expected to cost.

`lastNonce() public returns (uint nonce)`
`lastTimestamp() public returns (uint nonce)`

Both are simple read only functions that return the last used Nonce and last used timestamp.

`requiredSignatures(uint type) returns (uint)`

A function which returns the amount of signatures that are required for a given type of action (types being 1 = Management and 2 = Action).

`event ExecutedSigned(bytes32 signHash, uint nonce, bool success);`

Whenever a new transaction is executed it **must** emit an event with the signHash, nonce and either the transaction was sucessfully executed or not. Apps that are waiting for a transaction to be executed should subscribe to the identity and watch this event to see if their transaction was sucessful. If a different signHash is executed with an equal or higher nonce, it means that your transaction has been replaced.

## Implementation
Onde initial implementation of such contract can be found at [the Identity Gas Relay at the Status repository](https://github.com/status-im/contracts/blob/73-economic-abstraction/contracts/identity/IdentityGasRelay.sol)

## 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:

* [EIP 877](https://github.com/ethereum/EIPs/pull/877) An attempt of doing the same but with a change in the protocol
* [Status](https://github.com/status-im/ideas/issues/73)
* [Aragon](https://github.com/aragonlabs/pay-protocol) (this might not be the best link to show their work in this area)
* [Token Standard Functions for Preauthorized Actions](https://github.com/ethereum/EIPs/issues/662)
* [Token Standard Extension 865](https://github.com/ethereum/EIPs/issues/865)
* [Iuri Matias: Transaction Relay](https://github.com/iurimatias/TransactionRelay)
* [uPort: Meta transactions](https://github.com/uport-project/uport-identity#send-a-meta-tx)
* [uPort: safe Identities](https://github.com/uport-project/uport-identity/blob/develop/docs/txRelay.md)
* [Gnosis safe contracts](https://github.com/gnosis/safe-contracts)

Swarm city uses a similar proposition for etherless transactions, called [Gas Station Service](https://github.com/swarmcity/SCLabs-gasstation-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.


## Areas for improvements

This ERC inherits all it's permission systems from ERC720 Identity, which has very basic levels of authorization (can call contract itself, can make external calls, cannot make any calls) and a simple generic multisig. Ideally we need a more comprehensive standard for authorizations, that enables, for instance, that a single key can only make calls to a specific contract, or specific functions. Also, the amount of signatures required to do an action could be variable, maybe a single key could have a limit on how much value it can spend in a single day, requiring more signatures to move higher amounts (how to properly measure the value of a token or NFT transfer is another matter entirely).

## Usage examples

This scheme opens up a great deal of possibilities on interaction as well as different experiments on business models:

* Apps can create individual identities contract for their users which holds the actual funds and then create a different private key for each device they log into. Other apps can use the same identity and just ask to add permissioned public keys to manage the device, so that if one individual key is lost, no ether is lost.
* An app can create its own token and only charge their users in its internal currency for any ethereum transaction. The currency units can be rounded so it looks more similar to to actual amount of transactions: a standard transaction always costs 1 token, a very complex transaction costs exactly 2, etc. Since the app is the issuer of the transactions, they can do their own Sybil verifications and give a free amount of currency units to new users to get them started.
* A game company creates games with a traditional monthly subscription, either by credit card or platform-specific microtransactions. Private keys never leave the device and keep no ether and only the public accounts are sent to the company. The game then signs transactions on the device with gas price 0, sends them to the game company which checks who is an active subscriber and batches all transactions and pays the ether themselves. If the company goes bankrupt, the gamers themselves can set up similar subscription systems or just increase the gas price. End result is a **ethereum based game in which gamers can play by spending apple, google or xbox credits**.
* A standard token is created that doesn’t require its users to have ether, and instead allows tokens to be transferred by paying in tokens. A wallet is created that signs messages and send them via whisper to the network, where other nodes can compete to download the available transactions, check the current gas price, and select those who are paying enough tokens to cover the cost. **The result is a token that the end users never need to keep any ether and can pay fees in the token itself.**
* A DAO is created with a list of accounts of their employees. Employees never need to own ether, instead they sign messages, send them to whisper to a decentralized list of relayers which then deploy the transactions. The DAO contract then checks if the transaction is valid and sends ether to the deployers. Employees have an incentive not to use too many of the companies resources because they’re identifiable. The result is that the users of the DAO don't need to keep ether, and **the contract ends up paying for it's own gas usage**.

### References

* [Universal Logins talk at UX Unconf, Toronto](https://www.youtube.com/watch?v=qF2lhJzngto)

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).