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

Changes for mintWithPermission #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
123 changes: 118 additions & 5 deletions EIPS/eip-4973.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
eip: 4973
title: Account-bound Tokens
description: A standard interface for non-transferrable NFTs binding to an Ethereum account like a legendary World of Warcraft item binds to a character.
author: Tim Daubenschütz (@TimDaub), Raphael Roullet (@ra-phael)
author: Tim Daubenschütz (@TimDaub), Raphael Roullet (@ra-phael), Rahul Rumalla (@rahulrumalla)
discussions-to: https://ethereum-magicians.org/t/eip-4973-non-transferrable-non-fungible-tokens-soulbound-tokens-or-badges/8825
status: Draft
type: Standards Track
category: ERC
created: 2022-04-01
requires: 165, 721
requires: 165, 721, 712, 1271, 2098
---

## Abstract
Expand Down Expand Up @@ -44,7 +44,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S

- [EIP-721](./eip-721.md)'s `ERC721` (`0x80ac58cd`)

An ABT receiver must be able to always call `function burn(address _tokenId)` to disassociate themselves from an ABT publicly.
An ABT receiver must be able to always call `function burn(address _tokenId)` to dissociate themselves from an ABT publicly.

```solidity
// SPDX-License-Identifier: CC0-1.0
Expand Down Expand Up @@ -77,7 +77,7 @@ interface IERC4973 /* is ERC165, ERC721Metadata */ {
/// @return The address of the owner bound to the ABT
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice Destroys `tokenId`. At any time, an ABT receiver must be able to
/// disassociate themselves from an ABT publicly through calling this
/// dissociate themselves from an ABT publicly through calling this
/// function.
/// @dev Must emit a `event Revoke` with the `address to` field pointing to
/// the zero address.
Expand All @@ -88,6 +88,111 @@ interface IERC4973 /* is ERC165, ERC721Metadata */ {

See [`EIP-721`](./eip-721.md) for a definition of its metadata JSON Schema.

The implementor might also leverage the OPTIONAL extension of `ERC4973Permit`, through which `mintWithPermission` is made available. The method allows for minting ABTs only when consent is established between both the parties. The `msg.sender` is the destination address of the token and it is important to note that both the `from` and `msg.sender` (i.e `to`) addresses are authenticated by Ethereum and hence establishes a mutually agreed consent amongst them.

```
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.6;

/// @title Account-bound tokens, optional permissioned minting extension
/// @dev See https://eips.ethereum.org/EIPS/eip-4973
/// Note: the ERC-165 identifier for this interface is 0x8fac1c1c
interface IERC4973Permit {
function mintWithPermission(
address from,
string calldata uri,
bytes calldata signature
) external returns (uint256);
}
```

The signature here is of a message digest `MintPermit(address from,address to,string tokenURI)` signed using ECDSA, by the `from` address. Once minted, a user will not be able to reuse the signature to mint additional tokens. Note that these signatures can be generated offchain through libraries such as web3.js or ethers.js and be verified on-chain where the signer can be recovered from the signature (as long as the message digest is encoded according to `EIP-712`).

The implementation also supports [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271), where signatures on behalf of a contract can be verified. For example, in the case of signing via a multisig contract.

The semantics of the signature are as follows:

The signature must be a valid `secp256k1` or [EIP-2098](https://eips.ethereum.org/EIPS/eip-2098) from the account mentioned in the `from` address.

```
keccak256(abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
keccak256("MintPermit(address from,address to,string tokenURI)"),
from,
to,
keccak256(bytes(tokenURI))
));
```

where `DOMAIN_SEPARATOR` MUST be defined according to [EIP-712](https://eips.ethereum.org/EIPS/eip-712). 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 ERC-712 typed structure:
```js
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"MintPermit": [
{
"name": "from",
"type": "address"
},
{
"name": "to",
"type": "address"
},
{
"name": "tokenURI",
"type": "string"
}
],
"primaryType": "Permit",
"domain": {
"name": erc721name,
"version": version,
"chainId": chainid,
"verifyingContract": tokenAddress
},
"message": {
"from": fromAddress,
"to": toAddress,
"tokenURI": tokenURI
}
}
}
```

This EIP requires [EIP-165](https://eips.ethereum.org/EIPS/eip-165). It is further necessary here in order to register the interface of this EIP. Doing so will allow easy verification if an ABT contract has implemented this EIP or not, enabling them to interact accordingly. The interface of this EIP (as defined in EIP-165) is 0x8fac1c1c. Contracts implementing this EIP MUST have the supportsInterface function return true when called with 0x8fac1c1c.

## Rationale

### Interface
Expand All @@ -102,7 +207,7 @@ Still, since `EIP-4973` supports [`EIP-721`](./eip-721.md)'s `ERC721Metadata` ex

Although other implementations of account-bound tokens are possible, e.g., by having all transfer functions revert, `EIP-4973` is superior as it supports feature detection through [`EIP-165`](./eip-165.md).

We expose `function burn(address _tokenId)` and require it to be callable at any time by an ABT's owner as it ensures an owner's right to publicly disassociate themselves from what has been issued towards their account.
We expose `function burn(address _tokenId)` and require it to be callable at any time by an ABT's owner as it ensures an owner's right to publicly dissociate themselves from what has been issued towards their account.

### Exception handling

Expand All @@ -116,6 +221,14 @@ In cases where implementers want to make account-bound tokens shareable among di

ABTs can be indexed by tracking the emission of `event Attest` and `event Revoke`. To guarantee reliable and implementation-independent indexable information, neither `event Attest` nor `event Revoke` include a `from` argument to depict the transaction sender. Instead, as a `from` property wouldn't be authenticated and hence opens a security vector, we omit it and advise indexers to substitute it with the transaction-level `from` field which gets authenticated through Ethereum's transaction signature validation prior to inclusion in a block.

### Consent

We have made available an extension `ERC4973Permit`, which enables minting of ABTs only when there's active *consent* from both parties participating in the transaction. The primary purpose of this extension is to avoid non-consented non-transferable tokens ending up in a user's account.

### Content Addressability

Account-bound tokens are meant to be "forever tokens" associated to an account, unless otherwise dissociated by the user themselves. This means that not only can the user not transfer ownership, but the minter also cannot withdraw, transfer or change ownership. This includes mutating or removing any remote content as a means of censoring or manipulating specific users. It is essential that the `tokenURI` is content addressable, in order to make this token truly bound to the account without any risk of mutation.

## Backwards Compatibility

We have adopted the [`EIP-165`](./eip-165.md) and `ERC721Metadata` functions purposefully to create a high degree of backward compatibility with [`EIP-721`](./eip-721.md). We have deliberately used [`EIP-721`](./eip-721.md) terminology such as `function ownerOf(...)` or `function balanceOf(...)` to minimize the effort of familiarization for `EIP-4973` implementers already familiar with, e.g., [`EIP-20`](./eip-20.md) or [`EIP-721`](./eip-721.md).
Expand Down