diff --git a/EIPS/eip-2612.md b/EIPS/eip-2612.md new file mode 100644 index 00000000000000..39608e0d832b0a --- /dev/null +++ b/EIPS/eip-2612.md @@ -0,0 +1,203 @@ +--- +eip: 2612 +title: permit – 712-signed approvals +author: Martin Lundfall (@Mrchico) +discussions-to: https://github.com/ethereum/EIPs/issues/2613 +status: Draft +type: Standards Track +category: ERC +created: 2020-04-13 +requires: 20, 712 +--- + + + +## Simple Summary + +A function `permit` extending [ERC-20](https://eips.ethereum.org/EIPS/eip-20) which allows for approvals to be made via `secp256k1` signatures. This kind of "account abstraction for ERC-20" brings about two main benefits: + +- transactions involving ERC-20 operations can be paid using the token itself rather than ETH, +- approve and pull operations can happen in a single transaction instead of two consecutive transactions, + +while adding as little as possible over the existing ERC-20 standard. + +## Abstract + +Arguably one of the main reasons for the success of ERC-20 tokens lies in the interplay between `approve` and `transferFrom`, +which allows for tokens to not only be transferred between externally owned accounts (EOA), but to be used in other contracts under application specific conditions by abstracting away `msg.sender` as the defining mechanism for token access control. + +However, a limiting factor in this design stems from the fact that the ERC-20 `approve` function itself is defined in terms of `msg.sender`. This means that user's _initial action_ involving ERC-20 tokens must be performed by an EOA [1]. If the user needs to interact with a smart contract, then they need to make 2 transactions (`approve` and the smart contract call which will internally call `transferFrom`). Even in the simple use case of paying another person, they need to hold ETH to pay for transaction gas costs. + +This ERC extends the ERC-20 standard with a new function `permit`, which allows users to modify the `allowance` mapping using a signed message, instead of through `msg.sender`. + +For an improved user experience, the signed data is structured following [ERC-712](https://eips.ethereum.org/EIPS/eip-712), which already has wide spread adoption in major RPC providers. + + +## Motivation + +While ERC-20 tokens have become ubiquotous in the Ethereum ecosystem, their status remains that of second class tokens from the perspective of the protocol. The ability for users to interact with Ethereum without holding any ETH has been a [long outstanding goal](https://github.com/ethereum/EIPs/blob/ed621645c8f3bc5756492f327cda015f35d9f8da/EIPS/eip-101.md) and the [subject](https://eips.ethereum.org/EIPS/eip-1077) [of](https://eips.ethereum.org/EIPS/eip-777) [many](https://github.com/ethereum/EIPs/issues/1776#) [EIPs](https://eips.ethereum.org/EIPS/eip-1271). + +So far, many of these proposals have seen very little adoption, and the ones that have been adopted (such as [ERC-777](https://eips.ethereum.org/EIPS/eip-777)), introduce a lot of additional functionality, causing [unexpected behavior in mainstream contracts](https://medium.com/consensys-diligence/uniswap-audit-b90335ac007). + +This ERC proposes an alternative solution which is designed to be as minimal as possible and to only address _one problem_: the lack of abstraction in the ERC-20 `approve` method. + +While it may be tempting to introduce `*_by_signature` counterparts for every ERC-20 function, they are intentionally left out of this ERC-20 for two reasons: + + - the desired specifics of such functions, such as decision regarding fees for `transfer_by_signature`, possible batching algorithms, varies depending on the use case, and, + - they can be implemented using a combination of `permit` and additional helper contracts without loss of generality. + + +## Specification + +A new method +```solidity +function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) +``` +and a new storage item +```solidity +mapping(address=>uint256) nonces; +``` +with accompanying getter function are introduced, the semantics of which are as follows: + +For all addresses `owner`, `spender`, uint256s `value`, `deadline` and `nonce`, uint8 `v`, bytes32 `r` and `s`, +a call to `permit(owner, spender, value, deadline, v, r, s)` will set +`approval[owner][spender]` to `value`, +increment `nonces[owner]` by 1, +and emit a corresponding `Approval` event, +if and only if the following conditions are met: + + +- The current blocktime is less than or equal to `deadline`. +- `owner` is not the zero address. +- `nonces[owner]` (before the state update) is equal to `nonce`. +- `r`, `s` and `v` is a valid `secp256k1` signature from `owner` of the message: + +in solidity pseudocode: +```solidity +keccak256(hex"1901" + ++ keccak256( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + ++ keccak256(bytes(erc20name)) + ++ keccak256(bytes(version)) + ++ bytes(chainid) + ++ bytes(tokenAddress)) + ++ keccak256( + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") + ++ bytes(owner) + ++ bytes(spender) + ++ bytes(value) + ++ bytes(nonce) + ++ bytes(deadline)) +)) +``` +where `++` denotes bytestring concatenation, `tokenAddress` is the address of the token contract, `chainid` is the chain id of the network it is deployed to and `erc20name` is the name of the token as defined by `ERC-20`. `version` is a `string` defined at contract deployment which remains constant throughout the lifetime of the contract, but is otherwise unconstrained. + +In other words, the message is the ERC-712 typed structure: + +```js +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [{ + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ], + "primaryType": "Permit", + "domain": { + "name": erc20name, + "version": version, + "chainId": chainid, + "verifyingContract": tokenAddress + }, + "message": { + "owner": owner, + "spender": spender, + "value": value, + "nonce": nonce, + "deadline": deadline + } +}} +``` + +Note that nowhere in this definition we refer to `msg.sender`. The caller of the `permit` function can be any address. + + +## Rationale +The `permit` function is sufficient for enabling any operation involving ERC-20 tokens to be paid for using the token itself, rather than using ETH. +An example of a contract which enables gasless token transactions can be found [here](https://github.com/dapphub/ds-dach). + +It avoids any calls to unknown code. + +The `nonces` mapping is given for replay protection. + +A common use case of `permit` has a relayer submit a `Permit` on behalf of the `owner`. In this scenario, the relaying party is essentially given a free option to submit or withhold the `Permit`. If this is a cause of concern, the `owner` can limit the time a `Permit` is valid for by setting `deadline` to a value in the near future. The `deadline` argument can be set to `uint(-1)` to create `Permit`s that effectively never expire. + +ERC-712 typed messages are included because of its wide spread adoption in many wallet providers. + + +## Backwards Compatibility +There are already a couple of `permit` functions in token contracts implemented in contracts in the wild, most notably the one introduced in the `dai.sol`. + +Its implementation differs slightly from the presentation here in that: +- instead of taking a `value` argument, it takes a bool `allowed`, setting approval to 0 or `uint(-1)`. +- the `deadline` argument is instead called `expiry`. This is not just a syntactic change, as it effects the contents of the signed message. + +There is also an implementation in the token [`Stake`](https://etherscan.io/address/0x0Ae055097C6d159879521C384F1D2123D1f195e6#code) with the same ABI as `dai` but with different semantics: it lets users issue "expiring approvals", that only allow `transferFrom` to occur while `expiry >= block.timestamp`. + +The specification presented here is in line with the implementation in [Uniswap-v2](https://github.com/uniswap/uniswap-v2-core). + +## Test Cases + +Some basic tests can be found here https://github.com/Uniswap/uniswap-v2-core/blob/master/test/UniswapV2ERC20.spec.ts. + +## Implementation +[UniswapV2ERC20.sol](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) + +## Security Considerations + +Though the signer of a `Permit` may have a certain party in mind to submit their transaction, another party can always front run this transaction and call `permit` before the intended party. The end result is the same for the `Permit` signer, however. + +Since the ecrecover precompile fails silently and just returns the zero address as `signer` when given malformed messages, it is important to ensure `owner != address(0)` to avoid `permit` from creating an approval to spend "zombie funds" belong to the zero address. + +Signed `Permit` messages are censorable. The relaying party can always choose to not submit the `Permit` after having received it, withholding the option to submit it. The `deadline` parameter is one mitigation to this. If the signing party holds ETH they can also just submit the `Permit` themselves, which can render previously signed `Permit`s invalid. + +The standard [ERC-20 race condition for approvals](https://swcregistry.io/docs/SWC-114) applies to `permit` as well. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +[1] - Unless the address owning the token is actually a contract wallet. Although contract wallets solves many of the same problems that motivates this EIP, they are currently only scarcely adopted in the ecosystem. Contract wallets suffer from a UX problem -- since they separate the EOA `owner` of the contract wallet from the contract wallet itself (which is meant to carry out actions on the `owner`s behalf and holds all of their funds), user interfaces need to be specifically designed to support them. The `permit` pattern reaps many of the same benefits while requiring little to no change in user interfaces.