Skip to content

Commit 7c9338f

Browse files
authored
Update ERC-3009: Move to Draft
Merged by EIP-Bot.
1 parent 7ff6313 commit 7c9338f

File tree

6 files changed

+163
-83
lines changed

6 files changed

+163
-83
lines changed

ERCS/erc-3009.md

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,55 @@
11
---
22
eip: 3009
33
title: Transfer With Authorization
4+
description: Transfer fungible assets via a signed authorization
45
author: Peter Jihoon Kim (@petejkim), Kevin Britz (@kbrizzle), David Knott (@DavidLKnott)
5-
discussions-to: https://github.com/ethereum/EIPs/issues/3010
6-
status: Stagnant
6+
discussions-to: https://ethereum-magicians.org/t/erc-3009-transfer-with-authorization/25698
7+
status: Draft
78
type: Standards Track
89
category: ERC
910
created: 2020-09-28
1011
requires: 20, 712
1112
---
1213

13-
## Simple Summary
14-
15-
A contract interface that enables transferring of fungible assets via a signed authorization.
16-
1714
## Abstract
1815

1916
A set of functions to enable meta-transactions and atomic interactions with [ERC-20](./eip-20.md) token contracts via signatures conforming to the [EIP-712](./eip-712.md) typed message signing specification.
2017

2118
This enables the user to:
2219

2320
- delegate the gas payment to someone else,
24-
- pay for gas in the token itself rather than in ETH,
21+
- pay gas in the token itself rather than in ETH,
2522
- perform one or more token transfers and other operations in a single atomic transaction,
2623
- transfer ERC-20 tokens to another address, and have the recipient submit the transaction,
2724
- batch multiple transactions with minimal overhead, and
2825
- create and perform multiple transactions without having to worry about them failing due to accidental nonce-reuse or improper ordering by the miner.
2926

27+
Please note that this ERC does not apply to smart contract accounts, as the `transfer` and `approve`/`transferFrom` functions from the ERC-20 standards can directly be used.
28+
29+
<!-- TODO (Editor's note): we don't permit links outside of the EIP/ERC repositories, with some exceptions noted in EIP-1
30+
31+
This ERC has been live in production within the USDC smart contract since 2020, and serves as a critical component of the [x402](https://www.x402.org/) standard.
32+
33+
-->
34+
3035
## Motivation
3136

32-
There is an existing spec, [EIP-2612](./eip-2612), that also allows meta-transactions, and it is encouraged that a contract implements both for maximum compatibility. The two primary differences between this spec and EIP-2612 are that:
37+
There is an existing spec, [ERC-2612](./eip-2612.md), that also allows meta-transactions, and it is encouraged that a contract implements both for maximum compatibility. The two primary differences between this spec and ERC-2612 are that:
3338

34-
- EIP-2612 uses sequential nonces, but this uses random 32-byte nonces, and that
35-
- EIP-2612 relies on the ERC-20 `approve`/`transferFrom` ("ERC-20 allowance") pattern.
39+
- ERC-2612 uses sequential nonces, but this uses random 32-byte nonces, and that
40+
- ERC-2612 relies on the ERC-20 `approve`/`transferFrom` ("ERC-20 allowance") pattern.
3641

37-
The biggest issue with the use of sequential nonces is that it does not allow users to perform more than one transaction at time without risking their transactions failing, because:
42+
The biggest issue with the use of sequential nonces is that it does not allow users to perform more than one transaction at a time without risking their transactions failing, because:
3843

3944
- DApps may unintentionally reuse nonces that have not yet been processed in the blockchain.
4045
- Miners may process the transactions in the incorrect order.
4146

4247
This can be especially problematic if the gas prices are very high and transactions often get queued up and remain unconfirmed for a long time. Non-sequential nonces allow users to create as many transactions as they want at the same time.
4348

44-
The ERC-20 allowance mechanism is susceptible to the [multiple withdrawal attack](https://blockchain-projects.readthedocs.io/multiple_withdrawal.html)/[SWC-114](https://swcregistry.io/docs/SWC-114), and encourages antipatterns such as the use of the "infinite" allowance. The wide-prevalence of upgradeable contracts have made the conditions favorable for these attacks to happen in the wild.
49+
The ERC-20 allowance mechanism is susceptible to the <!-- TODO (Editor's note): same note as above.
50+
[multiple withdrawal attack](https://blockchain-projects.readthedocs.io/multiple_withdrawal.html) -->/<!-- TODO (Editor's note): you can copy the SWC into your assets directory because it's MIT licensed. [SWC-114](https://swcregistry.io/docs/SWC-114) -->, and encourages antipatterns such as the use of the "infinite" allowance. The wide prevalence of upgradeable contracts has made the conditions favorable for these attacks to happen in the wild.
4551

46-
The deficiencies of the ERC-20 allowance pattern brought about the development of alternative token standards such as the [ERC-777](./eip-777) and [ERC-677](https://github.com/ethereum/EIPs/issues/677). However, they haven't been able to gain much adoption due to compatibility and potential security issues.
52+
The deficiencies of the ERC-20 allowance pattern brought about the development of alternative token standards such as the [ERC-777](./eip-777.md) and <!-- TODO (Editor's note): if you'd like to link to ERC-677, you'll need to pull request it into the repository (i.e. upgrade it) [ERC-677](https://github.com/ethereum/EIPs/issues/677) -->. However, they haven't been able to gain much adoption due to compatibility and potential security issues.
4753

4854
## Specification
4955

@@ -197,7 +203,7 @@ Params := { Authorizer, Nonce }
197203

198204
```
199205
// "‖" denotes concatenation.
200-
Digest := Keecak256(
206+
Digest := Keccak256(
201207
0x1901 ‖ DomainSeparator ‖ Keccak256(ABIEncode(TypeHash, Params...))
202208
)
203209
@@ -294,7 +300,7 @@ One might say transaction ordering is one reason why sequential nonces are prefe
294300
- For native Ethereum transactions, when a transaction with a nonce value that is too-high is submitted to the network, it will stay pending until the transactions consuming the lower unused nonces are confirmed.
295301
- However, for meta-transactions, when a transaction containing a sequential nonce value that is too high is submitted, instead of staying pending, it will revert and fail immediately, resulting in wasted gas.
296302
- The fact that miners can also reorder transactions and include them in the block in the order they want (assuming each transaction was submitted to the network by different meta-transaction relayers) also makes it possible for the meta-transactions to fail even if the nonces used were correct. (e.g. User submits nonces 3, 4 and 5, but miner ends up including them in the block as 4,5,3, resulting in only 3 succeeding)
297-
- Lastly, when using different applications simultaneously, in absence of some sort of an off-chain nonce-tracker, it is not possible to determine what the correct next nonce value is if there exists nonces that are used but haven't been submitted and confirmed by the network.
303+
- Lastly, when using different applications simultaneously, in the absence of some sort of an off-chain nonce tracker, it is not possible to determine what the correct next nonce value is if there exists nonces that are used but haven't been submitted and confirmed by the network.
298304
- Under high gas price conditions, transactions can often "get stuck" in the pool for a long time. Under such a situation, it is much more likely for the same nonce to be unintentionally reused twice. For example, if you make a meta-transaction that uses a sequential nonce from one app, and switch to another app to make another meta-transaction before the previous one confirms, the same nonce will be used if the app relies purely on the data available on-chain, resulting in one of the transactions failing.
299305
- In conclusion, the only way to guarantee transaction ordering is for relayers to submit transactions one at a time, waiting for confirmation between each submission (and the order in which they should be submitted can be part of some off-chain metadata), rendering sequential nonce irrelevant.
300306

@@ -310,9 +316,9 @@ One might say transaction ordering is one reason why sequential nonces are prefe
310316

311317
## Backwards Compatibility
312318

313-
New contracts benefit from being able to directly utilize EIP-3009 in order to create atomic transactions, but existing contracts may still rely on the conventional ERC-20 allowance pattern (`approve`/`transferFrom`).
319+
New contracts benefit from being able to directly utilize [ERC-3009](./eip-3009.md) in order to create atomic transactions, but existing contracts may still rely on the conventional ERC-20 allowance pattern (`approve`/`transferFrom`).
314320

315-
In order to add support for EIP-3009 to existing contracts ("parent contract") that use the ERC-20 allowance pattern, a forwarding contract ("forwarder") can be constructed that takes an authorization and does the following:
321+
In order to add support for ERC-3009 to existing contracts ("parent contract") that use the ERC-20 allowance pattern, a forwarding contract ("forwarder") can be constructed that takes an authorization and does the following:
316322

317323
1. Extract the user and deposit amount from the authorization
318324
2. Call `receiveWithAuthorization` to transfer specified funds from the user to the forwarder
@@ -372,7 +378,7 @@ contract DepositForwarder {
372378
"Failed to transfer the minted tokens"
373379
);
374380
375-
uint256 remainder = _token.balanceOf(address(this);
381+
uint256 remainder = _token.balanceOf(address(this));
376382
if (remainder > 0) {
377383
require(
378384
_token.transfer(from, remainder),
@@ -385,13 +391,16 @@ contract DepositForwarder {
385391
}
386392
```
387393

394+
<!-- TODO (Editor's note): please move your test cases into your assets directory (assuming they're under a permissive license)
388395
## Test Cases
389396
390397
See [EIP3009.test.ts](https://github.com/CoinbaseStablecoin/eip-3009/blob/master/test/EIP3009.test.ts).
398+
-->
399+
400+
## Reference Implementation
391401

392-
## Implementation
402+
### `EIP3009.sol`
393403

394-
**EIP3009.sol**
395404
```solidity
396405
abstract contract EIP3009 is IERC20Transfer, EIP712Domain {
397406
// keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
@@ -454,7 +463,8 @@ abstract contract EIP3009 is IERC20Transfer, EIP712Domain {
454463
}
455464
```
456465

457-
**IERC20Transfer.sol**
466+
### `IERC20Transfer.sol`
467+
458468
```solidity
459469
abstract contract IERC20Transfer {
460470
function _transfer(
@@ -465,14 +475,16 @@ abstract contract IERC20Transfer {
465475
}
466476
```
467477

468-
**EIP712Domain.sol**
478+
### `EIP712Domain.sol`
479+
469480
```solidity
470481
abstract contract EIP712Domain {
471482
bytes32 public DOMAIN_SEPARATOR;
472483
}
473484
```
474485

475-
**EIP712.sol**
486+
### `EIP712.sol`
487+
476488
```solidity
477489
library EIP712 {
478490
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
@@ -494,8 +506,8 @@ library EIP712 {
494506
EIP712_DOMAIN_TYPEHASH,
495507
keccak256(bytes(name)),
496508
keccak256(bytes(version)),
497-
address(this),
498-
bytes32(chainId)
509+
chainId,
510+
address(this)
499511
)
500512
);
501513
}
@@ -521,15 +533,15 @@ library EIP712 {
521533
}
522534
```
523535

524-
A fully working implementation of EIP-3009 can be found in [this repository](../assets/eip-3009/EIP3009.sol). The repository also includes [an implementation of EIP-2612](../assets/eip-3009/EIP2612.sol) that uses the EIP-712 library code presented above.
536+
A fully working implementation of ERC-3009 can be found in [this repository](../assets/eip-3009/ERC3009.sol). The repository also includes [an implementation of ERC-2612](../assets/eip-3009/ERC2612.sol) that uses the EIP-712 library code presented above.
525537

526538
## Security Considerations
527539

528-
Use `receiveWithAuthorization` instead of `transferWithAuthorization` when calling from other smart contracts. It is possible for an attacker watching the transaction pool to extract the transfer authorization and front-run the `transferWithAuthorization` call to execute the transfer without invoking the wrapper function. This could potentially result in unprocessed, locked up deposits. `receiveWithAuthorization` prevents this by performing an additional check that ensures that the caller is the payee. Additionally, if there are multiple contract functions accepting receive authorizations, the app developer could dedicate some leading bytes of the nonce could as the identifier to prevent cross-use.
540+
Use `receiveWithAuthorization` instead of `transferWithAuthorization` when calling from other smart contracts. It is possible for an attacker watching the transaction pool to extract the transfer authorization and front-run the `transferWithAuthorization` call to execute the transfer without invoking the wrapper function. This could potentially result in unprocessed, locked up deposits. `receiveWithAuthorization` prevents this by performing an additional check that ensures that the caller is the payee. Additionally, if there are multiple contract functions accepting receive authorizations, the app developer could dedicate some leading bytes of the nonce as an identifier to prevent cross-use.
529541

530542
When submitting multiple transfers simultaneously, be mindful of the fact that relayers and miners will decide the order in which they are processed. This is generally not a problem if the transactions are not dependent on each other, but for transactions that are highly dependent on each other, it is recommended that the signed authorizations are submitted one at a time.
531543

532-
The zero address must be rejected when using `ecrecover` to prevent unauthorized transfers and approvals of funds from the zero address. The built-in `ecrecover` returns the zero address when a malformed signature is provided.
544+
The zero address must be rejected when `ecrecover` is used to prevent unauthorized transfers and approvals of funds from the zero address. The built-in `ecrecover` returns the zero address when a malformed signature is provided.
533545

534546
## Copyright
535547

assets/erc-3009/EIP712.sol

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity 0.6.12;
4+
5+
import { ECRecover } from "./ECRecover.sol";
6+
7+
/**
8+
* @title EIP712
9+
* @notice A library that provides EIP712 helper functions
10+
*/
11+
library EIP712 {
12+
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
13+
bytes32
14+
public constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
15+
16+
/**
17+
* @notice Make EIP712 domain separator
18+
* @param name Contract name
19+
* @param version Contract version
20+
* @return Domain separator
21+
*/
22+
function makeDomainSeparator(string memory name, string memory version)
23+
internal
24+
view
25+
returns (bytes32)
26+
{
27+
uint256 chainId;
28+
assembly {
29+
chainId := chainid()
30+
}
31+
32+
return
33+
keccak256(
34+
abi.encode(
35+
EIP712_DOMAIN_TYPEHASH,
36+
keccak256(bytes(name)),
37+
keccak256(bytes(version)),
38+
chainId,
39+
address(this)
40+
)
41+
);
42+
}
43+
44+
/**
45+
* @notice Recover signer's address from a EIP712 signature
46+
* @param domainSeparator Domain separator
47+
* @param v v of the signature
48+
* @param r r of the signature
49+
* @param s s of the signature
50+
* @param typeHashAndData Type hash concatenated with data
51+
* @return Signer's address
52+
*/
53+
function recover(
54+
bytes32 domainSeparator,
55+
uint8 v,
56+
bytes32 r,
57+
bytes32 s,
58+
bytes memory typeHashAndData
59+
) internal pure returns (address) {
60+
bytes32 digest = keccak256(
61+
abi.encodePacked(
62+
"\x19\x01",
63+
domainSeparator,
64+
keccak256(typeHashAndData)
65+
)
66+
);
67+
return ECRecover.recover(digest, v, r, s);
68+
}
69+
}

assets/erc-3009/EIP712Domain.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity 0.6.12;
4+
5+
abstract contract EIP712Domain {
6+
/**
7+
* @dev EIP712 Domain Separator
8+
*/
9+
bytes32 public DOMAIN_SEPARATOR;
10+
}

assets/erc-3009/EIP2612.sol renamed to assets/erc-3009/ERC2612.sol

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,12 @@
1-
/**
2-
* SPDX-License-Identifier: MIT
3-
*
4-
* Copyright (c) 2020 CENTRE SECZ
5-
*
6-
* Permission is hereby granted, free of charge, to any person obtaining a copy
7-
* of this software and associated documentation files (the "Software"), to deal
8-
* in the Software without restriction, including without limitation the rights
9-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
* copies of the Software, and to permit persons to whom the Software is
11-
* furnished to do so, subject to the following conditions:
12-
*
13-
* The above copyright notice and this permission notice shall be included in
14-
* copies or substantial portions of the Software.
15-
*
16-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
* SOFTWARE.
23-
*/
1+
// SPDX-License-Identifier: MIT
242

253
pragma solidity 0.6.12;
264

275
import { IERC20Internal } from "./IERC20Internal.sol";
286
import { EIP712Domain } from "./EIP712Domain.sol";
297
import { EIP712 } from "./EIP712.sol";
308

31-
abstract contract EIP2612 is IERC20Internal, EIP712Domain {
9+
abstract contract ERC2612 is IERC20Internal, EIP712Domain {
3210
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
3311
bytes32
3412
public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
@@ -63,7 +41,7 @@ abstract contract EIP2612 is IERC20Internal, EIP712Domain {
6341
bytes32 r,
6442
bytes32 s
6543
) external {
66-
require(deadline >= now, "EIP2612: permit is expired");
44+
require(deadline >= now, "ERC2612: permit is expired");
6745

6846
bytes memory data = abi.encode(
6947
PERMIT_TYPEHASH,
@@ -75,7 +53,7 @@ abstract contract EIP2612 is IERC20Internal, EIP712Domain {
7553
);
7654
require(
7755
EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner,
78-
"EIP2612: invalid signature"
56+
"ERC2612: invalid signature"
7957
);
8058

8159
_approve(owner, spender, value);

0 commit comments

Comments
 (0)