-
Notifications
You must be signed in to change notification settings - Fork 456
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merged by EIP-Bot.
- Loading branch information
Showing
1 changed file
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
--- | ||
eip: 7604 | ||
title: ERC-1155 Permit Approvals | ||
description: Permit approvals for ERC-1155 tokens | ||
author: calvbore (@calvbore), emiliolanzalaco (@emiliolanzalaco) | ||
discussions-to: https://ethereum-magicians.org/t/proposal-for-a-new-eip-erc-2612-style-permits-for-erc1155-nfts/15504 | ||
status: Draft | ||
type: Standards Track | ||
category: ERC | ||
created: 2024-01-27 | ||
requires: 165, 712, 1155, 1271, 2098, 5216 | ||
--- | ||
|
||
## Abstract | ||
|
||
The "permit" approval flow for both [ERC-20](./eip-20.md) and [ERC-721](./eip-721.md) are large improvements for the existing UX of the token underlying each ERC. This ERC extends the "permit" pattern to [ERC-1155](./eip-20.md) tokens, borrowing heavily upon both [ERC-4494](./eip-4494.md) and [ERC-2612](./eip-2612.md). | ||
|
||
The structure of [ERC-1155](./eip-1155.md) tokens requires a new ERC to account for the token standard's use of both token IDs and balances (also why this ERC requires [ERC-5216](./eip-5216.md)). | ||
|
||
## Motivation | ||
|
||
The permit structures outlined in both [ERC-4494](./eip-4494) and [ERC-2612](./eip-2612) allows a signed message to create an approval, but are only applicable to their respective underlying tokens ([ERC-721](./eip-721) and [ERC-20](./eip-20)). | ||
|
||
## Specification | ||
|
||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. | ||
|
||
Three new functions must be added to ERC-1155 and ERC-5216. | ||
|
||
```solidity | ||
interface IERC1155Permit { | ||
function permit(address owner, address operator, uint256 tokenId, uint256 value, uint256 deadline, bytes memory sig) external; | ||
function nonces(address owner, uint256 tokenId) external view returns (uint256); | ||
function DOMAIN_SEPARATOR() external view returns (bytes32); | ||
} | ||
``` | ||
|
||
The semantics of which are as follows: | ||
|
||
For all addresses `owner`, `spender`, uint256's `tokenId`, `value`, `deadline`, and `nonce`, bytes `sig`, a call to `permit(owner, spender, tokenId, value, deadline, sig)` MUST set `allowance(owner, spender, tokenId)` to `value`, increment `nonces(owner, tokenId)` by 1, and emit a corresponding `Approval` event defined by [ERC-5216](./eip-5216.md), 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][tokenId]` (before state update) is equal to `nonce` | ||
- `sig` is a valid `secp256k1`, [ERC-2098](./eip-2098.md), or [ERC-1271](./eip-1271.md) signature from `owner` of the message: | ||
``` | ||
keccak256(abi.encodePacked( | ||
hex"1901", | ||
DOMAIN_SEPARATOR, | ||
keccak256(abi.encode( | ||
keccak256("Permit(address owner,address spender,uint256 tokenId,uint256 value,uint256 nonce,uint256 deadline)"), | ||
owner, | ||
spender, | ||
tokenId, | ||
value, | ||
nonce, | ||
deadline)) | ||
)); | ||
``` | ||
|
||
If any of these conditions are not met the `permit` call MUST revert. | ||
|
||
Where `DOMAIN_SEPARATOR` MUST be defined according to [EIP-712](./eip-712.md). The `DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, and satisfy the requirements of EIP-712, but is otherwise unconstrained. A common choice for `DOMAIN_SEPARATOR` is: | ||
``` | ||
DOMAIN_SEPARATOR = keccak256( | ||
abi.encode( | ||
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), | ||
keccak256(bytes(name)), | ||
keccak256(bytes(version)), | ||
chainid, | ||
address(this) | ||
)); | ||
``` | ||
|
||
In other words, the message is the following EIP-712 typed structure: | ||
``` | ||
{ | ||
"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": "tokenId", | ||
"type": "uint256" | ||
}, | ||
{ | ||
"name": "value", | ||
"type": "uint256" | ||
}, | ||
{ | ||
"name": "nonce", | ||
"type": "uint256" | ||
}, | ||
{ | ||
"name": "deadline", | ||
"type": "uint256" | ||
} | ||
], | ||
"primaryType": "Permit", | ||
"domain": { | ||
"name": erc1155name, | ||
"version": version, | ||
"chainId": chainid, | ||
"verifyingContract": tokenAddress | ||
}, | ||
"message": { | ||
"owner": owner, | ||
"spender": spender, | ||
"tokenId": tokenId, | ||
"value": value, | ||
"nonce": nonce, | ||
"deadline": deadline | ||
} | ||
}} | ||
``` | ||
|
||
The `permit` function MUST check that the signer is not the zero address. | ||
|
||
Note that nowhere in this definition do we refer to `msg.sender`. The caller of the `permit` function can be any address. | ||
|
||
This EIP requires [ERC-165](./eip-165.md). ERC-165 is already required in [ERC-1155](./eip-1155.md), but is further necessary here in order to register the interface of this ERC. Doing so will allow easy verification if an NFT contract has implemented this ERC or not, enabling them to interact accordingly. The ERC-165 interface of this ERC is `0x7409106d`. Contracts implementing this ERC MUST have the `supportsInterface` function return `true` when called with `0x7409106d`. | ||
|
||
## Rationale | ||
|
||
The `permit` function is sufficient for enabling a `safeTransferFrom` transaction to be made without the need for an additional transaction. | ||
|
||
The format 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 Permits that effectively never expire. Likewise, the `value` argument can be set to `uint(-1)` to create Permits with effectively unlimited allowances. | ||
|
||
EIP-712 typed messages are included because of its use in [ERC-4494](./eip-4494.md) and [ERC-2612](./eip-2612.md), which in turn cites widespread adoption in many wallet providers. | ||
|
||
This ERC focuses on both the `value` and `tokenId` being approved, ERC-4494 focuses only on the `tokenId`, while ERC-2612 focuses primarily on the `value`. ERC-1155 does not natively support approvals by amount, thus this ERC requires ERC-5216, otherwise a `permit` would grant approval for an account's entire `tokenId` balance. | ||
|
||
Whereas ERC-2612 splits signatures into their `v,r,s` components, this ERC opts to instead take a `bytes` array of variable length in order to support [ERC-2098](./eip-1271.md) signatures, which may not be easily separated or reconstructed from `r,s,v` components (65 bytes). | ||
|
||
## Backwards Compatibility | ||
|
||
No backward compatibility issues found. | ||
|
||
## Security Considerations | ||
|
||
The below considerations have been copied from ERC-4494. | ||
|
||
Extra care should be taken when creating transfer functions in which `permit` and a transfer function can be used in one function to make sure that invalid permits cannot be used in any way. This is especially relevant for automated NFT platforms, in which a careless implementation can result in the compromise of a number of user assets. | ||
|
||
The remaining considerations have been copied from [ERC-2612](./eip-2612.md) with minor adaptation, and are equally relevant here: | ||
|
||
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 `ownerOf(tokenId) != address(0)` to avoid `permit` from creating an approval to any `tokenId` which does not have an approval set. | ||
|
||
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 applies to `permit` as well. | ||
|
||
If the `DOMAIN_SEPARATOR` contains the `chainId` and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a future chain split.. | ||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via [CC0](../LICENSE.md). |