From d0ee05f7d4e62643926521c52e1a18db5a067fe1 Mon Sep 17 00:00:00 2001 From: saitama2009 Date: Fri, 31 Mar 2023 00:15:06 +0800 Subject: [PATCH 01/15] Add:eip-draft_erc721_holding_time.md --- EIPS/eip-draft_erc721_holding_time.md | 96 +++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 EIPS/eip-draft_erc721_holding_time.md diff --git a/EIPS/eip-draft_erc721_holding_time.md b/EIPS/eip-draft_erc721_holding_time.md new file mode 100644 index 0000000000000..334f2ee8eb395 --- /dev/null +++ b/EIPS/eip-draft_erc721_holding_time.md @@ -0,0 +1,96 @@ +--- +eip: 0000 +title: ERC721 Holding Time Tracking +description: Add holding time information to ERC-721 tokens +author: Combo , Luigi , Saitama +discussions-to: https://ethereum-magicians.org/t/draft-eip-erc721-holding-time-tracking/13605 +status: Draft +type: Standards Track +category: ERC +created: 2023-03-30 +requires: 721 +--- + +## Abstract + +This standard is an extension of ERC-721. It adds an interface that tracks and describes the holding time of a Non-Fungible Token (NFT) by an account. + +## Motivation + +In some use cases, it is valuable to know the duration for which a NFT has been held by an account. This information can be useful for rewarding long-term holders, determining access to exclusive content, or even implementing specific business logic based on holding time. However, the current ERC-721 standard does not have a built-in mechanism to track NFT holding time. Furthermore, in certain scenarios, the holding time should not be reset when an NFT is transferred between accounts, such as during NFT-based staking. + +This proposal aims to address these limitations by extending the ERC-721 standard to include holding time tracking functionality and allowing for selective exclusion of transfers from affecting holding time calculations. + +## Specification + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +**Interface** + +The following interface extends the existing ERC-721 standard: + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0 + +interface IERC721HoldingTime { +  function getHoldingInfo(uint256 tokenId) external view returns (address holder, uint256 holdingTime); +  function setWhitelistedContract(address whitelistContract, bool ignoreReset) external; +} +``` + +**Functions** + +#### getHoldingInfo + +``` +function getHoldingInfo(uint256 tokenId) external view returns (address holder, uint256 holdingTime); +``` + +This function returns the current holder of the specified NFT and the length of time (in seconds) the NFT has been held by the current account. + +* `tokenId`: The unique identifier of the NFT. +* Returns: A tuple containing the current holder's address and the holding time (in seconds). +#### setWhitelistedContract + +``` +function setWhitelistedContract(address whitelistContract, bool ignoreReset) external; +``` + +This function allows the contract owner or an authorized account to specify whether a specific transfer should be ignored for the purposes of tracking holding time. + +* `whitelistContract`: The smart contract address that should be whitelisted for holding time reset exceptions. +* `ignoreReset`: A boolean value indicating whether the holding time reset should be ignored (`true`) or not (`false`) when transferring to or from the specified whitelistContract. +## Rationale + +The addition of the `getHoldingInfo` and `setWhitelistedContract` functions to an extension of the ERC-721 standard enables developers to implement NFT-based applications that require holding time information and allow for selective transfer exceptions. This extension maintains compatibility with existing ERC-721 implementations while offering additional functionality for new use cases. + +The `getHoldingInfo` function provides a straightforward method for retrieving the holding time and holder address of an NFT. By using seconds as the unit of time for holding duration, it ensures precision and compatibility with other time-based functions in smart contracts. + +The `setWhitelistedContract` function introduces flexibility in determining which transfers should affect holding time calculations. By allowing the contract owner or authorized accounts to whitelist specific addresses, transfers involving these addresses will not reset the holding time. This is particularly important for NFT-based financial transactions or special cases where holding time should not be affected by transfers. + +Together, these functions enhance the utility of the ERC-721 standard, enabling developers to create more sophisticated applications and experiences based on NFT holding time and offering the ability to selectively ignore holding time resets during transfers to or from whitelisted addresses. + +## Backwards Compatibility + +This proposal is fully backwards compatible with the existing ERC-721 standard, as it extends the standard with new functions that do not affect the core functionality. + +## Reference Implementation + +An implementation of this EIP will be provided upon acceptance of the proposal. + +## Security Considerations + +This EIP introduces additional state management for tracking holding times and whitelisted addresses, which may have security implications. Implementers should be cautious of potential vulnerabilities related to holding time manipulation and whitelisting management, especially during transfers. Access control measures should be in place to ensure that only authorized accounts can call the `setWhitelistedContract` function. + +When implementing this EIP, developers should be mindful of potential attack vectors, such as reentrancy and front-running attacks, as well as general security best practices for smart contracts. Adequate testing and code review should be performed to ensure the safety and correctness of the implementation. + +Furthermore, developers should consider the gas costs associated with maintaining and updating holding time information and managing whitelisted addresses. Optimizations may be necessary to minimize the impact on contract execution costs. + +It is also important to note that the accuracy of holding time information depends on the accuracy of the underlying blockchain's timestamp. While block timestamps are generally reliable, they can be manipulated by miners to some extent. As a result, holding time data should not be relied upon as a sole source of truth in situations where absolute precision is required. + +Lastly, proper management of the whitelisted addresses is crucial to avoid potential abuse. Contract owners should have a clear understanding of the implications of whitelisting addresses and establish a process for adding or removing addresses from the whitelist. Regular monitoring and auditing of the whitelisted addresses can help identify and mitigate potential risks or abuse. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file From 938c06c0a34ad9972f7bc03fe6508504f3ffbfb7 Mon Sep 17 00:00:00 2001 From: saitama2009 Date: Fri, 31 Mar 2023 00:34:06 +0800 Subject: [PATCH 02/15] Rename: eip file --- EIPS/{eip-draft_erc721_holding_time.md => eip-0000.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename EIPS/{eip-draft_erc721_holding_time.md => eip-0000.md} (96%) diff --git a/EIPS/eip-draft_erc721_holding_time.md b/EIPS/eip-0000.md similarity index 96% rename from EIPS/eip-draft_erc721_holding_time.md rename to EIPS/eip-0000.md index 334f2ee8eb395..9f7e70baedf22 100644 --- a/EIPS/eip-draft_erc721_holding_time.md +++ b/EIPS/eip-0000.md @@ -1,6 +1,6 @@ --- eip: 0000 -title: ERC721 Holding Time Tracking +title: ERC-721 Holding Time Tracking description: Add holding time information to ERC-721 tokens author: Combo , Luigi , Saitama discussions-to: https://ethereum-magicians.org/t/draft-eip-erc721-holding-time-tracking/13605 @@ -13,7 +13,7 @@ requires: 721 ## Abstract -This standard is an extension of ERC-721. It adds an interface that tracks and describes the holding time of a Non-Fungible Token (NFT) by an account. +This standard is an extension of [ERC-721](./eip-721.md). It adds an interface that tracks and describes the holding time of a Non-Fungible Token (NFT) by an account. ## Motivation From eaa06d96d1d66b174d506706e9c80255c68fa96b Mon Sep 17 00:00:00 2001 From: saitama2009 Date: Fri, 31 Mar 2023 00:43:32 +0800 Subject: [PATCH 03/15] Update: lint --- EIPS/eip-0000.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-0000.md b/EIPS/eip-0000.md index 9f7e70baedf22..4b9f29d8b3f20 100644 --- a/EIPS/eip-0000.md +++ b/EIPS/eip-0000.md @@ -2,7 +2,7 @@ eip: 0000 title: ERC-721 Holding Time Tracking description: Add holding time information to ERC-721 tokens -author: Combo , Luigi , Saitama +author: Saitama (@saitama2009), Combo , Luigi discussions-to: https://ethereum-magicians.org/t/draft-eip-erc721-holding-time-tracking/13605 status: Draft type: Standards Track @@ -41,7 +41,7 @@ interface IERC721HoldingTime { **Functions** -#### getHoldingInfo +### getHoldingInfo ``` function getHoldingInfo(uint256 tokenId) external view returns (address holder, uint256 holdingTime); @@ -51,7 +51,8 @@ This function returns the current holder of the specified NFT and the length of * `tokenId`: The unique identifier of the NFT. * Returns: A tuple containing the current holder's address and the holding time (in seconds). -#### setWhitelistedContract + +### setWhitelistedContract ``` function setWhitelistedContract(address whitelistContract, bool ignoreReset) external; @@ -61,6 +62,7 @@ This function allows the contract owner or an authorized account to specify whet * `whitelistContract`: The smart contract address that should be whitelisted for holding time reset exceptions. * `ignoreReset`: A boolean value indicating whether the holding time reset should be ignored (`true`) or not (`false`) when transferring to or from the specified whitelistContract. + ## Rationale The addition of the `getHoldingInfo` and `setWhitelistedContract` functions to an extension of the ERC-721 standard enables developers to implement NFT-based applications that require holding time information and allow for selective transfer exceptions. This extension maintains compatibility with existing ERC-721 implementations while offering additional functionality for new use cases. @@ -93,4 +95,4 @@ Lastly, proper management of the whitelisted addresses is crucial to avoid poten ## Copyright -Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). From 19936bd650323908bb1a9ecec6edfd9d2269fe8f Mon Sep 17 00:00:00 2001 From: saitama2009 Date: Fri, 31 Mar 2023 14:50:23 +0800 Subject: [PATCH 04/15] Rename: eip-6806.md --- EIPS/{eip-0000.md => eip-6806.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename EIPS/{eip-0000.md => eip-6806.md} (99%) diff --git a/EIPS/eip-0000.md b/EIPS/eip-6806.md similarity index 99% rename from EIPS/eip-0000.md rename to EIPS/eip-6806.md index 4b9f29d8b3f20..82cf457eba66a 100644 --- a/EIPS/eip-0000.md +++ b/EIPS/eip-6806.md @@ -1,5 +1,5 @@ --- -eip: 0000 +eip: 6806 title: ERC-721 Holding Time Tracking description: Add holding time information to ERC-721 tokens author: Saitama (@saitama2009), Combo , Luigi From 410225db753b5cff73954203d37a33c5761d88fc Mon Sep 17 00:00:00 2001 From: saitama2009 Date: Mon, 17 Apr 2023 12:49:47 +0800 Subject: [PATCH 05/15] Update: interface and reference implementation --- EIPS/eip-6806.md | 78 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/EIPS/eip-6806.md b/EIPS/eip-6806.md index 82cf457eba66a..9d4460dbdd20b 100644 --- a/EIPS/eip-6806.md +++ b/EIPS/eip-6806.md @@ -33,9 +33,17 @@ The following interface extends the existing ERC-721 standard: // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0 -interface IERC721HoldingTime { -  function getHoldingInfo(uint256 tokenId) external view returns (address holder, uint256 holdingTime); -  function setWhitelistedContract(address whitelistContract, bool ignoreReset) external; +interface IERC6806 { + event HoldingTimeWhitelistSet(address indexed account, bool ignoreReset); + + function getHoldingInfo( + uint256 tokenId + ) external view returns (address holder, uint256 holdingTime); + + function setHoldingTimeWhitelistedAddress( + address account, + bool ignoreReset + ) external; } ``` @@ -52,24 +60,24 @@ This function returns the current holder of the specified NFT and the length of * `tokenId`: The unique identifier of the NFT. * Returns: A tuple containing the current holder's address and the holding time (in seconds). -### setWhitelistedContract +### setHoldingTimeWhitelistedAddress ``` -function setWhitelistedContract(address whitelistContract, bool ignoreReset) external; +  function setHoldingTimeWhitelistedAddress(address account, bool ignoreReset) external; ``` This function allows the contract owner or an authorized account to specify whether a specific transfer should be ignored for the purposes of tracking holding time. -* `whitelistContract`: The smart contract address that should be whitelisted for holding time reset exceptions. -* `ignoreReset`: A boolean value indicating whether the holding time reset should be ignored (`true`) or not (`false`) when transferring to or from the specified whitelistContract. +* `account`: The address that should be whitelisted for holding time reset exceptions, it could be a general address of EOA/Contract. +* `ignoreReset`: A boolean value indicating whether the holding time reset should be ignored (`true`) or not (`false`) when transferring to or from the specified `account`. ## Rationale -The addition of the `getHoldingInfo` and `setWhitelistedContract` functions to an extension of the ERC-721 standard enables developers to implement NFT-based applications that require holding time information and allow for selective transfer exceptions. This extension maintains compatibility with existing ERC-721 implementations while offering additional functionality for new use cases. +The addition of the `getHoldingInfo` and `setHoldingTimeWhitelistedAddress` functions to an extension of the ERC-721 standard enables developers to implement NFT-based applications that require holding time information and allow for selective transfer exceptions. This extension maintains compatibility with existing ERC-721 implementations while offering additional functionality for new use cases. The `getHoldingInfo` function provides a straightforward method for retrieving the holding time and holder address of an NFT. By using seconds as the unit of time for holding duration, it ensures precision and compatibility with other time-based functions in smart contracts. -The `setWhitelistedContract` function introduces flexibility in determining which transfers should affect holding time calculations. By allowing the contract owner or authorized accounts to whitelist specific addresses, transfers involving these addresses will not reset the holding time. This is particularly important for NFT-based financial transactions or special cases where holding time should not be affected by transfers. +The `setHoldingTimeWhitelistedAddress` function introduces flexibility in determining which transfers should affect holding time calculations. By allowing the contract owner or authorized accounts to whitelist specific addresses, transfers involving these addresses will not reset the holding time. This is particularly important for NFT-based financial transactions or special cases where holding time should not be affected by transfers. Together, these functions enhance the utility of the ERC-721 standard, enabling developers to create more sophisticated applications and experiences based on NFT holding time and offering the ability to selectively ignore holding time resets during transfers to or from whitelisted addresses. @@ -79,11 +87,59 @@ This proposal is fully backwards compatible with the existing ERC-721 standard, ## Reference Implementation -An implementation of this EIP will be provided upon acceptance of the proposal. +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC6806.sol"; + +contract ERC6806 is ERC721, Ownable, IERC6806 { + mapping(uint256 => address) private _holder; + mapping(uint256 => uint256) private _holdStart; + mapping(address => bool) private _holdingTimeWhitelist; + + constructor( + string memory name_, + string memory symbol_ + ) ERC721(name_, symbol_) {} + + function _afterTokenTransfer( + address from, + address to, + uint256 firstotTokenId, + uint256 + ) internal override { + if (_holdingTimeWhitelist[from] || _holdingTimeWhitelist[to]) { + return; + } + + if (_holder[firstotTokenId] != to) { + _holder[firstotTokenId] = to; + _holdStart[firstotTokenId] = block.timestamp; + } + } + + function getHoldingInfo( + uint256 tokenId + ) public view returns (address holder, uint256 holdingTime) { + return (_holder[tokenId], block.timestamp - _holdStart[tokenId]); + } + + function setHoldingTimeWhitelistedAddress( + address account, + bool ignoreReset + ) public onlyOwner { + _holdingTimeWhitelist[account] = ignoreReset; + emit HoldingTimeWhitelistSet(account, ignoreReset); + } +} +``` ## Security Considerations -This EIP introduces additional state management for tracking holding times and whitelisted addresses, which may have security implications. Implementers should be cautious of potential vulnerabilities related to holding time manipulation and whitelisting management, especially during transfers. Access control measures should be in place to ensure that only authorized accounts can call the `setWhitelistedContract` function. +This EIP introduces additional state management for tracking holding times and whitelisted addresses, which may have security implications. Implementers should be cautious of potential vulnerabilities related to holding time manipulation and whitelisting management, especially during transfers. Access control measures should be in place to ensure that only authorized accounts can call the `setHoldingTimeWhitelistedAddress` function. When implementing this EIP, developers should be mindful of potential attack vectors, such as reentrancy and front-running attacks, as well as general security best practices for smart contracts. Adequate testing and code review should be performed to ensure the safety and correctness of the implementation. From 0ee58a5c1a886604d81086f80c2d7111327c22fe Mon Sep 17 00:00:00 2001 From: saitama2009 Date: Tue, 18 Apr 2023 15:29:39 +0800 Subject: [PATCH 06/15] Add comments --- EIPS/eip-6806.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EIPS/eip-6806.md b/EIPS/eip-6806.md index 9d4460dbdd20b..53bea0fa60a08 100644 --- a/EIPS/eip-6806.md +++ b/EIPS/eip-6806.md @@ -34,6 +34,8 @@ The following interface extends the existing ERC-721 standard: pragma solidity ^0.8.0 interface IERC6806 { + /// @notice MUST be emitted when `account` is successfully + /// added to or removed from the whitelist. event HoldingTimeWhitelistSet(address indexed account, bool ignoreReset); function getHoldingInfo( From aaf900ffbacebb5af03937298806817cae506f7b Mon Sep 17 00:00:00 2001 From: eth-bot <85952233+eth-bot@users.noreply.github.com> Date: Mon, 1 May 2023 17:34:59 -0700 Subject: [PATCH 07/15] Commit from EIP-Bot --- .github/renovate.json | 9 +- .github/workflows/auto-review-bot.yml | 2 +- .github/workflows/auto-review-trigger.yml | 4 +- .github/workflows/auto-stagnate-bot.yml | 6 +- .github/workflows/ci-rerun-trigger.yml | 33 - .github/workflows/ci.yml | 20 +- .github/workflows/jekyll-label-bot.yml | 2 +- .github/workflows/post-ci.yml | 6 +- .github/workflows/stale.yml | 2 +- EIPS/eip-1.md | 20 + EIPS/eip-1185.md | 224 +- EIPS/eip-1444.md | 2 +- EIPS/eip-2677.md | 3 +- EIPS/eip-3455.md | 71 + EIPS/eip-3525.md | 102 +- EIPS/eip-3651.md | 3 +- EIPS/eip-3690.md | 2 +- EIPS/eip-3855.md | 4 +- EIPS/eip-3860.md | 4 +- EIPS/eip-4736.md | 3 +- EIPS/eip-4788.md | 132 +- EIPS/eip-4844.md | 14 +- EIPS/eip-4881.md | 24 +- EIPS/eip-4895.md | 2 +- EIPS/eip-4972.md | 154 +- EIPS/eip-5000.md | 2 +- EIPS/eip-5006.md | 15 +- EIPS/eip-5007.md | 48 +- EIPS/eip-5008.md | 15 +- EIPS/eip-5164.md | 218 +- EIPS/eip-5219.md | 3 +- EIPS/eip-5507.md | 3 +- EIPS/eip-5564.md | 119 +- EIPS/eip-5615.md | 5 +- EIPS/eip-5639.md | 25 +- EIPS/eip-5656.md | 20 +- EIPS/eip-5725.md | 8 +- EIPS/eip-5773.md | 93 +- EIPS/eip-6059.md | 10 +- EIPS/eip-6065.md | 450 +- EIPS/eip-6105.md | 3 +- EIPS/eip-6120.md | 633 +- EIPS/eip-6220.md | 2 +- EIPS/eip-6327.md | 316 + EIPS/eip-6357.md | 2 +- EIPS/eip-6381.md | 71 +- EIPS/eip-6404.md | 8 +- EIPS/eip-6454.md | 54 +- EIPS/eip-6475.md | 17 +- EIPS/eip-6493.md | 4 +- EIPS/eip-6551.md | 167 +- EIPS/eip-6672.md | 189 + EIPS/eip-6789.md | 113 + EIPS/eip-6810.md | 107 + EIPS/eip-6913.md | 96 + EIPS/eip-747.md | 3 +- ...ip-draft_erc_721_holding_time_tracking.md} | 2 +- _includes/head.html | 2 +- assets/css/style.scss | 9 + assets/eip-3267/contracts/BaseLock.sol | 2 +- assets/eip-3267/contracts/BidOnAddresses.sol | 2 +- assets/eip-4881/deposit_tree_evolution.png | Bin 187296 -> 0 bytes assets/eip-4881/deposit_tree_evolution.svg | 5209 +++++++++++++++++ assets/eip-5007/README.md | 1 + .../eip-5007/contracts/ERC5007Composable.sol | 91 +- .../contracts/ERC5007ComposableTest.sol | 8 +- .../eip-5007/contracts/IERC5007Composable.sol | 33 +- assets/eip-5007/test/test.js | 66 +- assets/eip-5219/IDecentralizedApp.sol | 2 +- assets/eip-5725/README.md | 2 +- assets/eip-5725/contracts/IERC5725.sol | 2 +- assets/eip-6059/contracts/IERC6059.sol | 1 - assets/eip-6059/contracts/NestableToken.sol | 22 +- assets/eip-6065/ERC6065.sol | 255 + assets/eip-6065/corporate-structure.png | Bin 371934 -> 846219 bytes assets/eip-6327/zkpass-1.png | Bin 0 -> 65462 bytes assets/eip-6327/zkpass-2.png | Bin 0 -> 38302 bytes assets/eip-6327/zkpass-3.png | Bin 0 -> 38263 bytes assets/eip-6381/contracts/Emotable.sol | 42 - .../eip-6381/contracts/EmotableRepository.sol | 61 + assets/eip-6381/contracts/IERC6381.sol | 33 + assets/eip-6381/contracts/IEmotable.sol | 20 - .../contracts/mocks/ERC721EmotableMock.sol | 36 - .../eip-6381/contracts/mocks/ERC721Mock.sol | 20 + assets/eip-6381/hardhat.config.ts | 8 +- assets/eip-6381/package.json | 2 +- assets/eip-6381/test/emotable.ts | 91 - assets/eip-6381/test/emotableRepository.ts | 136 + .../eip-6404/tests/normalized/ssz_tx_types.py | 5 +- assets/eip-6454/contracts/IERC6454.sol | 26 + .../eip-6454/contracts/INonTransferable.sol | 13 - ...bleMock.sol => ERC721TransferableMock.sol} | 29 +- assets/eip-6454/package.json | 2 +- .../{nonTransferable.ts => transferable.ts} | 19 +- assets/eip-6466/helpers/ssz_receipt_types.py | 5 +- assets/eip-6475/optional.py | 38 +- assets/eip-6475/tests.py | 17 +- assets/eip-6672/contracts/ERC6672.sol | 62 + .../contracts/interfaces/IERC6672.sol | 11 + assets/eip-6789/MetaMask_ManaLimit.png | Bin 0 -> 49370 bytes 100 files changed, 8100 insertions(+), 1952 deletions(-) delete mode 100644 .github/workflows/ci-rerun-trigger.yml create mode 100644 EIPS/eip-3455.md create mode 100644 EIPS/eip-6327.md create mode 100644 EIPS/eip-6672.md create mode 100644 EIPS/eip-6789.md create mode 100644 EIPS/eip-6810.md create mode 100644 EIPS/eip-6913.md rename EIPS/{eip-6806.md => eip-draft_erc_721_holding_time_tracking.md} (99%) delete mode 100644 assets/eip-4881/deposit_tree_evolution.png create mode 100644 assets/eip-4881/deposit_tree_evolution.svg create mode 100644 assets/eip-6065/ERC6065.sol create mode 100644 assets/eip-6327/zkpass-1.png create mode 100644 assets/eip-6327/zkpass-2.png create mode 100644 assets/eip-6327/zkpass-3.png delete mode 100644 assets/eip-6381/contracts/Emotable.sol create mode 100644 assets/eip-6381/contracts/EmotableRepository.sol create mode 100644 assets/eip-6381/contracts/IERC6381.sol delete mode 100644 assets/eip-6381/contracts/IEmotable.sol delete mode 100644 assets/eip-6381/contracts/mocks/ERC721EmotableMock.sol create mode 100644 assets/eip-6381/contracts/mocks/ERC721Mock.sol delete mode 100644 assets/eip-6381/test/emotable.ts create mode 100644 assets/eip-6381/test/emotableRepository.ts create mode 100644 assets/eip-6454/contracts/IERC6454.sol delete mode 100644 assets/eip-6454/contracts/INonTransferable.sol rename assets/eip-6454/contracts/mocks/{ERC721NonTransferableMock.sol => ERC721TransferableMock.sol} (65%) rename assets/eip-6454/test/{nonTransferable.ts => transferable.ts} (70%) create mode 100644 assets/eip-6672/contracts/ERC6672.sol create mode 100644 assets/eip-6672/contracts/interfaces/IERC6672.sol create mode 100644 assets/eip-6789/MetaMask_ManaLimit.png diff --git a/.github/renovate.json b/.github/renovate.json index 0d37d1bd35ebf..e7f2cbe5ff35e 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -4,5 +4,12 @@ "config:base", ":disableDependencyDashboard" ], - "ignorePaths": ["**/assets/**"] + "ignorePaths": [ + "**/assets/**" + ], + "ignoreDeps": [ + "Pandapip1/jekyll-label-action", + "ethereum/eipw-action", + "ethereum/eip-review-bot" + ] } diff --git a/.github/workflows/auto-review-bot.yml b/.github/workflows/auto-review-bot.yml index 060312b4b7430..edf926131ace9 100644 --- a/.github/workflows/auto-review-bot.yml +++ b/.github/workflows/auto-review-bot.yml @@ -12,7 +12,7 @@ jobs: name: Run steps: - name: Fetch PR Number - uses: dawidd6/action-download-artifact@6765a42d86407a3d532749069ac03705ad82ebc6 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 with: name: pr-number workflow: auto-review-trigger.yml diff --git a/.github/workflows/auto-review-trigger.yml b/.github/workflows/auto-review-trigger.yml index 291bff487c28e..45b815e390dae 100644 --- a/.github/workflows/auto-review-trigger.yml +++ b/.github/workflows/auto-review-trigger.yml @@ -46,13 +46,13 @@ jobs: PR_NUMBER: ${{ github.event.issue.number }} - name: Check File Existence - uses: andstor/file-existence-action@f02338908d150e00a4b8bebc2dad18bd9e5229b0 + uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b id: check_pr_number_exists with: files: pr-number.txt - name: Save PR Number - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 + uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05 if: steps.check_pr_number_exists.outputs.files_exists == 'true' with: name: pr-number diff --git a/.github/workflows/auto-stagnate-bot.yml b/.github/workflows/auto-stagnate-bot.yml index cc7550d759f62..f76d7fd1240ee 100644 --- a/.github/workflows/auto-stagnate-bot.yml +++ b/.github/workflows/auto-stagnate-bot.yml @@ -11,13 +11,13 @@ jobs: name: Auto Stagnant Bot steps: - name: Checkout - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f - name: Setup Node.js Environment - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 + uses: actions/setup-node@d98fa1113850e562f83c7fc3a89c0ecd7a87fbed with: node-version: '14' - name: auto-stagnant-bot - uses: ethereum/EIP-Bot@b3ac0ba3600aea27157fc68d1e36c08cc5a6db77 # mark-eips-stale + uses: ethereum/EIP-Bot@fe4c395bd8bd1f006c353f9b04e694da890ad69a # mark-eips-stale id: auto-stagnant-bot with: GITHUB-TOKEN: ${{ secrets.TOKEN }} diff --git a/.github/workflows/ci-rerun-trigger.yml b/.github/workflows/ci-rerun-trigger.yml deleted file mode 100644 index 7321bf4e7db1a..0000000000000 --- a/.github/workflows/ci-rerun-trigger.yml +++ /dev/null @@ -1,33 +0,0 @@ -on: - issue_comment: - types: - - created - -name: Continuous Integration Re-Trigger -jobs: - trigger: - runs-on: ubuntu-latest - name: Trigger - steps: - - name: Trigger - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0 - if: github.event.issue.pull_request && contains(github.event.comment.body, '@eth-bot rerun') - with: - script: | - let pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.issue.number - }); - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.issue.number, - body: '[RETRIGGER]\n' + pr.data.body - }); - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.issue.number, - body: pr.data.body - }); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cafbe28a74e9..152e4860b0b7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: echo $MERGE_SHA > ./pr/merge_sha - name: Upload PR Number - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 + uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05 with: name: pr_number path: pr/ @@ -42,7 +42,7 @@ jobs: steps: - name: Checkout EIP Repository - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f - name: Install Ruby uses: ruby/setup-ruby@08245253a76fa4d1e459b7809579c62bd9eb718a @@ -67,10 +67,10 @@ jobs: steps: - name: Checkout EIP Repository - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f - name: Link Checker - uses: gaurav-nelson/github-action-markdown-link-check@0a51127e9955b855a9bbfa1ff5577f1d1338c9a5 + uses: gaurav-nelson/github-action-markdown-link-check@d53a906aa6b22b8979d33bc86170567e619495ec with: config-file: config/mlc_config.json use-quiet-mode: no @@ -83,7 +83,7 @@ jobs: steps: - name: Checkout EIP Repository - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f - name: Get Changed Files id: changed @@ -96,7 +96,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run CodeSpell - uses: codespell-project/actions-codespell@2391250ab05295bddd51e36a8c6295edb6343b0e + uses: codespell-project/actions-codespell@57beb9f38f49d773d641ac555d1565c3b6a59938 if: steps.changed.outcome == 'success' with: check_filenames: true @@ -110,9 +110,9 @@ jobs: steps: - name: Checkout EIP Repository - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f - - uses: ethereum/eipw-action@9968ac8604824d17c9d4cb99863e11e5e91342c3 + - uses: ethereum/eipw-action@59cdee4fc5d37c9391f3a0b52857fd5b021a39c2 id: eipw with: token: ${{ secrets.GITHUB_TOKEN }} @@ -123,7 +123,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout EIP Repository - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f - name: Get Changed Files id: changed @@ -136,7 +136,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Lint - uses: DavidAnson/markdownlint-cli2-action@16d9da45919c958a8d1ddccb4bd7028e8848e4f1 + uses: DavidAnson/markdownlint-cli2-action@f5cf187ef11bd3a68a127321b794aa252ff23019 if: steps.changed.outcome == 'success' with: command: config diff --git a/.github/workflows/jekyll-label-bot.yml b/.github/workflows/jekyll-label-bot.yml index d942ef947dea3..549c28c9b4d2a 100644 --- a/.github/workflows/jekyll-label-bot.yml +++ b/.github/workflows/jekyll-label-bot.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: Pandapip1/jekyll-label-action@28a89dbbef321fceaf3cff17f4d29c7a033c3d56 + - uses: Pandapip1/jekyll-label-action@d0fd82c3cd118140a50843906845fca8e59a8b9e with: token: ${{ secrets.GITHUB_TOKEN }} config-path: config/.jekyll-labels.yml diff --git a/.github/workflows/post-ci.yml b/.github/workflows/post-ci.yml index e14e3435ba13d..7057a97f3017c 100644 --- a/.github/workflows/post-ci.yml +++ b/.github/workflows/post-ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Fetch PR Data - uses: dawidd6/action-download-artifact@6765a42d86407a3d532749069ac03705ad82ebc6 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 with: name: pr_number workflow: ci.yml @@ -38,7 +38,7 @@ jobs: Please inspect the [Run Summary](https://github.com/ethereum/EIPs/pull/${{ steps.save-pr-data.outputs.pr_number }}/files) for details. - name: Add Waiting Label - uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + uses: actions-ecosystem/action-add-labels@288072f1a3b596f4350fe135bcfe381a23abadef if: ${{ github.event.workflow_run.conclusion == 'failure' }} with: labels: w-ci @@ -47,7 +47,7 @@ jobs: github_token: ${{ github.token }} - name: Remove Waiting Label - uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 + uses: actions-ecosystem/action-remove-labels@d05162525702062b6bdef750ed8594fc024b3ed7 if: ${{ github.event.workflow_run.conclusion != 'failure' }} with: labels: w-ci diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 731f68b531366..03eaf1bb8c6d3 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest name: Mark Stale Issues steps: - - uses: actions/stale@99b6c709598e2b0d0841cd037aaf1ba07a4410bd + - uses: actions/stale@03af7c36d33f4905e618fac0a1bb7e6d05f0d41b with: # General repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md index c7da6d286afe1..2225d4fbd9b4f 100644 --- a/EIPS/eip-1.md +++ b/EIPS/eip-1.md @@ -85,6 +85,8 @@ If this period results in necessary normative changes it will revert the EIP to **Final** - This EIP represents the final standard. A Final EIP exists in a state of finality and should only be updated to correct errata and add non-normative clarifications. +A PR moving an EIP from Last Call to Final SHOULD contain no changes other than the status update. Any content or editorial proposed change SHOULD be separate from this status-updating PR and committed prior to it. + **Stagnant** - Any EIP in `Draft` or `Review` or `Last Call` if inactive for a period of 6 months or greater is moved to `Stagnant`. An EIP may be resurrected from this state by Authors or EIP Editors through moving it back to `Draft` or it's earlier status. If not resurrected, a proposal may stay forever in this status. >*EIP Authors are notified of any algorithmic change to the status of their EIP* @@ -196,6 +198,24 @@ Other than the specific exceptions listed below, links to external resources **S The process governing permitted external resources is described in [EIP-5757](./eip-5757.md). +### Execution Client Specifications + +Links to the Ethereum Execution Client Specifications may be included using normal markdown syntax, such as: + +```markdown +[Ethereum Execution Client Specifications](https://github.com/ethereum/execution-specs/blob/9a1f22311f517401fed6c939a159b55600c454af/README.md) +``` + +Which renders to: + +[Ethereum Execution Client Specifications](https://github.com/ethereum/execution-specs/blob/9a1f22311f517401fed6c939a159b55600c454af/README.md) + +Permitted Execution Client Specifications URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^(https://github.com/ethereum/execution-specs/blob/[0-9a-f]{40}/.*|https://github.com/ethereum/execution-specs/tree/[0-9a-f]{40}/.*)$ +``` + ### Consensus Layer Specifications Links to specific commits of files within the Ethereum Consensus Layer Specifications may be included using normal markdown syntax, such as: diff --git a/EIPS/eip-1185.md b/EIPS/eip-1185.md index eab7b2bd54ff6..f46d8fcec7fed 100644 --- a/EIPS/eip-1185.md +++ b/EIPS/eip-1185.md @@ -1,26 +1,29 @@ --- eip: 1185 title: Storage of DNS Records in ENS +description: A system to store and retrieve DNS records within the ENS contract. author: Jim McDonald (@mcdee) -status: Stagnant +discussions-to: https://ethereum-magicians.org/t/eip1185-dns-resolver-profile-for-ens/1589 +status: Draft type: Standards Track category: ERC created: 2018-06-26 requires: 137 -discussions-to: https://ethereum-magicians.org/t/eip1185-dns-resolver-profile-for-ens/1589 --- ## Abstract + This EIP defines a resolver profile for ENS that provides features for storage and lookup of DNS records. This allows ENS to be used as a store of authoritative DNS information. ## Motivation + ENS is a highly desirable store for DNS information. It provides the distributed authority of DNS without conflating ownership and authoritative serving of information. With ENS, the owner of a domain has full control over their own DNS records. Also, ENS has the ability (through smart contracts) for a domain's subdomains to be irrevocably assigned to another entity. ## Specification -The resolver profile to support DNS on ENS follows the resolver specification as defined in #137. +The resolver profile to support DNS on ENS follows the resolver specification as defined in [ERC-137](./eip-137.md). -Traditionally, DNS is a zone-based system in that all of the records for a zone are kept together in the same file. This has the benefit of simplicity and atomicity of zone updates, but when transposed to ENS can result in significant gas costs for simple changes. As a result, the resolver works on the basis of record sets. A record set is uniquely defined by the tuple (domain, name, resource record type), for example the tuple (example.com, www.example.com, A) defines the record set of A records for the name www.example.com in the domain example.com. A record set can contain 0 or more values, for example if www.example.com has A records 1.2.3.4 and 5.6.7.8 then the aforementioned tuple will have two values. +Traditionally, DNS is a zone-based system in that all of the records for a zone are kept together in the same file. This has the benefit of simplicity and atomicity of zone updates, but when transposed to ENS can result in significant gas costs for simple changes. As a result, the resolver works on the basis of record sets. A record set is uniquely defined by the tuple `(domain, name, resource record type)`, for example the tuple `(example.com, www.example.com, A)` defines the record set of `A` records for the name `www.example.com` in the domain `example.com`. A record set can contain 0 or more values, for example if `www.example.com` has `A` records `1.2.3.4` and `5.6.7.8` then the aforementioned tuple will have two values. The choice to work at the level of record sets rather than zones means that this specification cannot completely support some features of DNS, such as zone transfers and DNSSEC. It would be possible to build a different resolver profile that works at the zone level, however it would be very expensive to carry out updates and so is not considered further for this EIP. @@ -31,9 +34,11 @@ The DNS resolver interface consists of two functions to set DNS information and `setDNSRecords()` sets, updates or clears 1 or more DNS records for a given node. It has function signature `0x0af179d7`. The arguments for the function are as follows: - - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in [ERC-137](./eip-137.md) - data: 1 or more DNS records in DNS wire format. Any record that is supplied without a value will be cleared. Note that all records in the same RRset should be contiguous within the data; if not then the later RRsets will overwrite the earlier one(s) + ### clearDNSZone(bytes32 node) `clearDNSZone()` removes all DNS records for the domain. It has function signature `0xad5780af`. @@ -41,16 +46,18 @@ The arguments for the function are as follows: Although it is possible to clear records individually with `setDNSRecords()` as described above this requires the owner to know all of the records that have been set (as the resolver has no methods to iterate over the records for a given domain), and might require multiple transactions. `clearDNSZone()` removes all zone information in a single operation. The arguments for the function is as follows: - - node: the namehash of the fully-qualified domain in ENS for which to clear the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to clear the records. Namehashes are defined in [ERC-137](./eip-137.md) ### dnsRecords(bytes32 node, bytes32 name, uint16 resource) view returns (bytes) `dnsRecords()` obtains the DNS records for a given node, name and resource. It has function signature `0x2461e851`. The arguments for the function are as follows: - - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in [ERC-137](./eip-137.md) - name: the `keccak256()` hash of the name of the record in DNS wire format. - - resource: the resource record ID. Resource record IDs are defined in https://en.wikipedia.org/wiki/List\_of\_DNS\_record\_types + - resource: the resource record ID. Resource record IDs are defined in RFC1035 and subsequent RFCs. The function returns all matching records in DNS wire format. If there are no records present the function will return nothing. @@ -58,23 +65,208 @@ The function returns all matching records in DNS wire format. If there are no r `hasDNSRecords()` reports if there are any records for the provided name in the domain. It has function signature `0x4cbf6ba4`. -This function is needed by DNS resolvers when working with wildcard resources as defined in https://tools.ietf.org/html/rfc4592 +This function is needed by DNS resolvers when working with wildcard resources as defined in RFC4592. The arguments for the function are as follows: - - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in [ERC-137](./eip-137.md) - name: the `keccak256()` hash of the name of the record in DNS wire format. The function returns `true` if there are any records for the provided node and name, otherwise `false`. -## Backwards compatibility +## Rationale + +DNS is a federated system of naming, and the higher-level entities control availability of everything beneath them (_e.g._ `.org` controls the availability of `ethereum.org`). A decentralized version of DNS would not have this constraint, and allow lookups directly for any domain with relevant records within ENS. + +## Backwards Compatibility + Not applicable. -## Implementation -The reference implementation of the DNS resolver is at https://github.com/wealdtech/wealdtech-solidity/blob/master/contracts/ens/DNSResolver.sol +## Reference Implementation + +The reference implementation of the DNS resolver is as follows: + +```solidity +pragma solidity ^0.7.4; +import "../ResolverBase.sol"; +import "@ensdomains/dnssec-oracle/contracts/RRUtils.sol"; + +abstract contract DNSResolver is ResolverBase { + using RRUtils for *; + using BytesUtils for bytes; + + bytes4 constant private DNS_RECORD_INTERFACE_ID = 0xa8fa5682; + bytes4 constant private DNS_ZONE_INTERFACE_ID = 0x5c47637c; -https://github.com/wealdtech/ethereal.git can be used to test the functionality of the resolver with the "dns set", "dns get" and "dns clear" commands. -## Test Cases -Test cases for the DNS resolver are at https://github.com/wealdtech/wealdtech-solidity/blob/master/test/ens/DNSResolver.js + // DNSRecordChanged is emitted whenever a given node/name/resource's RRSET is updated. + event DNSRecordChanged(bytes32 indexed node, bytes name, uint16 resource, bytes record); + // DNSRecordDeleted is emitted whenever a given node/name/resource's RRSET is deleted. + event DNSRecordDeleted(bytes32 indexed node, bytes name, uint16 resource); + // DNSZoneCleared is emitted whenever a given node's zone information is cleared. + event DNSZoneCleared(bytes32 indexed node); + + // DNSZonehashChanged is emitted whenever a given node's zone hash is updated. + event DNSZonehashChanged(bytes32 indexed node, bytes lastzonehash, bytes zonehash); + + // Zone hashes for the domains. + // A zone hash is an ERC-1577 content hash in binary format that should point to a + // resource containing a single zonefile. + // node => contenthash + mapping(bytes32=>bytes) private zonehashes; + + // Version the mapping for each zone. This allows users who have lost + // track of their entries to effectively delete an entire zone by bumping + // the version number. + // node => version + mapping(bytes32=>uint256) private versions; + + // The records themselves. Stored as binary RRSETs + // node => version => name => resource => data + mapping(bytes32=>mapping(uint256=>mapping(bytes32=>mapping(uint16=>bytes)))) private records; + + // Count of number of entries for a given name. Required for DNS resolvers + // when resolving wildcards. + // node => version => name => number of records + mapping(bytes32=>mapping(uint256=>mapping(bytes32=>uint16))) private nameEntriesCount; + + /** + * Set one or more DNS records. Records are supplied in wire-format. + * Records with the same node/name/resource must be supplied one after the + * other to ensure the data is updated correctly. For example, if the data + * was supplied: + * a.example.com IN A 1.2.3.4 + * a.example.com IN A 5.6.7.8 + * www.example.com IN CNAME a.example.com. + * then this would store the two A records for a.example.com correctly as a + * single RRSET, however if the data was supplied: + * a.example.com IN A 1.2.3.4 + * www.example.com IN CNAME a.example.com. + * a.example.com IN A 5.6.7.8 + * then this would store the first A record, the CNAME, then the second A + * record which would overwrite the first. + * + * @param node the namehash of the node for which to set the records + * @param data the DNS wire format records to set + */ + function setDNSRecords(bytes32 node, bytes calldata data) external authorised(node) { + uint16 resource = 0; + uint256 offset = 0; + bytes memory name; + bytes memory value; + bytes32 nameHash; + // Iterate over the data to add the resource records + for (RRUtils.RRIterator memory iter = data.iterateRRs(0); !iter.done(); iter.next()) { + if (resource == 0) { + resource = iter.dnstype; + name = iter.name(); + nameHash = keccak256(abi.encodePacked(name)); + value = bytes(iter.rdata()); + } else { + bytes memory newName = iter.name(); + if (resource != iter.dnstype || !name.equals(newName)) { + setDNSRRSet(node, name, resource, data, offset, iter.offset - offset, value.length == 0); + resource = iter.dnstype; + offset = iter.offset; + name = newName; + nameHash = keccak256(name); + value = bytes(iter.rdata()); + } + } + } + if (name.length > 0) { + setDNSRRSet(node, name, resource, data, offset, data.length - offset, value.length == 0); + } + } + + /** + * Obtain a DNS record. + * @param node the namehash of the node for which to fetch the record + * @param name the keccak-256 hash of the fully-qualified name for which to fetch the record + * @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types + * @return the DNS record in wire format if present, otherwise empty + */ + function dnsRecord(bytes32 node, bytes32 name, uint16 resource) public view returns (bytes memory) { + return records[node][versions[node]][name][resource]; + } + + /** + * Check if a given node has records. + * @param node the namehash of the node for which to check the records + * @param name the namehash of the node for which to check the records + */ + function hasDNSRecords(bytes32 node, bytes32 name) public view returns (bool) { + return (nameEntriesCount[node][versions[node]][name] != 0); + } + + /** + * Clear all information for a DNS zone. + * @param node the namehash of the node for which to clear the zone + */ + function clearDNSZone(bytes32 node) public authorised(node) { + versions[node]++; + emit DNSZoneCleared(node); + } + + /** + * setZonehash sets the hash for the zone. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param hash The zonehash to set + */ + function setZonehash(bytes32 node, bytes calldata hash) external authorised(node) { + bytes memory oldhash = zonehashes[node]; + zonehashes[node] = hash; + emit DNSZonehashChanged(node, oldhash, hash); + } + + /** + * zonehash obtains the hash for the zone. + * @param node The ENS node to query. + * @return The associated contenthash. + */ + function zonehash(bytes32 node) external view returns (bytes memory) { + return zonehashes[node]; + } + + function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { + return interfaceID == DNS_RECORD_INTERFACE_ID || + interfaceID == DNS_ZONE_INTERFACE_ID || + super.supportsInterface(interfaceID); + } + + function setDNSRRSet( + bytes32 node, + bytes memory name, + uint16 resource, + bytes memory data, + uint256 offset, + uint256 size, + bool deleteRecord) private + { + uint256 version = versions[node]; + bytes32 nameHash = keccak256(name); + bytes memory rrData = data.substring(offset, size); + if (deleteRecord) { + if (records[node][version][nameHash][resource].length != 0) { + nameEntriesCount[node][version][nameHash]--; + } + delete(records[node][version][nameHash][resource]); + emit DNSRecordDeleted(node, name, resource); + } else { + if (records[node][version][nameHash][resource].length == 0) { + nameEntriesCount[node][version][nameHash]++; + } + records[node][version][nameHash][resource] = rrData; + emit DNSRecordChanged(node, name, resource, rrData); + } + } +} +``` + +## Security Considerations + +Security of this solution would be dependent on security of the records within the ENS domain. This degenenrates to the security of the key(s) which have authority over that domain. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1444.md b/EIPS/eip-1444.md index 4ee0142325e87..9c8e0d93e39b5 100644 --- a/EIPS/eip-1444.md +++ b/EIPS/eip-1444.md @@ -19,7 +19,7 @@ An on-chain system for providing user feedback by converting machine-efficient c ## Motivation -There are many cases where an end user needs feedback or instruction from a smart contact. Directly exposing numeric codes does not make for good UX or DX. If Ethereum is to be a truly global system usable by experts and lay persons alike, systems to provide feedback on what happened during a transaction are needed in as many languages as possible. +There are many cases where an end user needs feedback or instruction from a smart contract. Directly exposing numeric codes does not make for good UX or DX. If Ethereum is to be a truly global system usable by experts and lay persons alike, systems to provide feedback on what happened during a transaction are needed in as many languages as possible. Returning a hard-coded string (typically in English) only serves a small segment of the global population. This standard proposes a method to allow users to create, register, share, and use a decentralized collection of translations, enabling richer messaging that is more culturally and linguistically diverse. diff --git a/EIPS/eip-2677.md b/EIPS/eip-2677.md index e0d136ca58290..7bf2473d7766a 100644 --- a/EIPS/eip-2677.md +++ b/EIPS/eip-2677.md @@ -3,10 +3,11 @@ eip: 2677 title: Limit size of `initcode` author: Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2677-limit-size-of-initcode/4550 -status: Stagnant +status: Withdrawn type: Standards Track category: Core created: 2020-05-18 +withdrawal-reason: Replaced by EIP-3860. --- ## Simple Summary diff --git a/EIPS/eip-3455.md b/EIPS/eip-3455.md new file mode 100644 index 0000000000000..076766dde3a72 --- /dev/null +++ b/EIPS/eip-3455.md @@ -0,0 +1,71 @@ +--- +eip: 3455 +title: SUDO Opcode +description: A new opcode is introduced to allow calling from an arbitrary sender address. +author: William Morriss (@wjmelements), Baptiste Vauthey (@thabaptiser) +discussions-to: https://ethereum-magicians.org/t/eip-3455-sudo-opcode/5860 +status: Draft +type: Standards Track +category: Core +created: 2021-04-01 +--- + +## Abstract +A new opcode, `SUDO`, is introduced with the same parameters as `CALL`, plus another parameter to specify the sender address. + +## Motivation +There are many use cases for being able to set the sender. + +Many tokens are stuck irretrievably because nobody has the key for the owner address. +In particular, at address zero there is approximately 17 billion USD in tokens and ether, according to etherscan. +With `SUDO`, anyone could free that value, leading to an economic boom that would end poverty and world hunger. +Instead it is sitting there idle like the gold in Fort Knox. +`SUDO` fixes this. + +It is a common mistake to send [ERC-20](./eip-20.md) tokens to the token address instead of the intended recipient. +This happens because users paste the token address into the recipient fields. +Currently there is no way to recover these tokens. +`SUDO` fixes this. + +Many scammers have fraudulently received tokens and ETH via trust-trading. +Their victims currently have no way to recover their funds. +`SUDO` fixes this. + +Large amounts of users have accidentally locked up tokens and ether by losing their private keys. +This is inefficient and provides a bad user experience. +To accommodate new and inexperienced users, there needs to be a way to recover funds after the private key has been lost. +`SUDO` fixes this. + +Finally, there are many tokens and ether sitting in smart contracts locked due to a bug. +We could finally close EIP issue #156. +We cannot currently reclaim ether from stuck accounts. +`SUDO` fixes this. + +## Specification +Adds a new opcode (`SUDO`) at `0xf8`. +`SUDO` pops 8 parameters from the stack. +Besides the sender parameter, the parameters shall match `CALL`. + +1. Gas: Integer; Maximum gas allowance for message call, safely using current gas counter if the counter is lower +2. Sender: Address, truncated to lower 40 bytes; Sets `CALLER` inside the call frame +3. To: Address, truncated to lower 40 bytes; sets `ADDRESS` +4. Value: Integer, raises exception amount specified is less than the value in Sender account; transferred with call to recipient balance, sets `CALLVALUE` +5. InStart: Integer; beginning of memory to use for `CALLDATA` +6. InSize: Integer; length of memory to use for `CALLDATA` +7. OutStart: Integer; beginning of memory to replace with `RETURNDATA` +8. OutSize: Integer; maximum `RETURNDATA` to place in memory + +Following execution, `SUDO` pushes a result value to the stack, indicating success or failure. +If the call ended with `STOP`, `RETURN`, or `SELFDESTRUCT`, `1` is pushed. +If the call ended with `REVERT`, `INVALID`, or an EVM assertion, `0` is pushed. + +## Rationale +The `GAS` parameter is first so that callers can tediously compute how much of their remaining gas to send at the last possible moment. +The remaining parameters inherited from `CALL` are in the same order, with sender inserted between. + + +## Security Considerations +It will be fine. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3525.md b/EIPS/eip-3525.md index 5f0573edaf9c8..93bb1fc1d4c42 100644 --- a/EIPS/eip-3525.md +++ b/EIPS/eip-3525.md @@ -1,8 +1,8 @@ --- eip: 3525 title: Semi-Fungible Token -description: Defines a specification where EIP-721 compatible tokens with the same SLOT and different IDs are fungible. -author: Will Wang (@will-edge), Mike Meng , Ethan Y. Tsai (@YeeTsai), Ryan Chow , Zhongxin Wu (@Nerverwind), AlvisDu (@AlvisDu) +description: Defines a specification where ERC-721 compatible tokens with the same SLOT and different IDs are fungible. +author: Will Wang (@will42w), Mike Meng , Yi Cai (@YeeTsai) , Ryan Chow , Zhongxin Wu (@Nerverwind), AlvisDu (@AlvisDu) discussions-to: https://ethereum-magicians.org/t/eip-3525-the-semi-fungible-token status: Final type: Standards Track @@ -13,34 +13,34 @@ requires: 20, 165, 721 ## Abstract -This is a standard for semi-fungible tokens. The set of smart contract interfaces described in this document defines an [EIP-721](./eip-721.md) compatible token standard. This standard introduces an `` triple scalar model that represents the semi-fungible structure of a token. It also introduces new transfer models as well as approval models that reflect the semi-fungible nature of the tokens. +This is a standard for semi-fungible tokens. The set of smart contract interfaces described in this document defines an [ERC-721](./eip-721.md) compatible token standard. This standard introduces an `` triple scalar model that represents the semi-fungible structure of a token. It also introduces new transfer models as well as approval models that reflect the semi-fungible nature of the tokens. -Token contains an EIP-721 equivalent ID property to identify itself as a universally unique entity, so that the tokens can be transferred between addresses and approved to be operated in EIP-721 compatible way. +Token contains an ERC-721 equivalent ID property to identify itself as a universally unique entity, so that the tokens can be transferred between addresses and approved to be operated in ERC-721 compatible way. -Token also contains a `value` property, representing the quantitative nature of the token. The meaning of the 'value' property is quite like that of the 'balance' property of an [EIP-20](./eip-20.md) token. Each token has a 'slot' attribute, ensuring that the value of two tokens with the same slot be treated as fungible, adding fungibility to the value property of the tokens. +Token also contains a `value` property, representing the quantitative nature of the token. The meaning of the 'value' property is quite like that of the 'balance' property of an [ERC-20](./eip-20.md) token. Each token has a 'slot' attribute, ensuring that the value of two tokens with the same slot be treated as fungible, adding fungibility to the value property of the tokens. This EIP introduces new token transfer models for semi-fungibility, including value transfer between two tokens of the same slot and value transfer from a token to an address. ## Motivation -Tokenization is one of the most important trends by which to use and control digital assets in crypto. Traditionally, there have been two approaches to do so: fungible and non-fungible tokens. Fungible tokens generally use the EIP-20 standard, where every unit of an asset is identical to each other. EIP-20 is a flexible and efficient way to manipulate fungible tokens. Non-fungible tokens are predominantly EIP-721 tokens, a standard capable of distinguishing digital assets from one another based on identity. +Tokenization is one of the most important trends by which to use and control digital assets in crypto. Traditionally, there have been two approaches to do so: fungible and non-fungible tokens. Fungible tokens generally use the ERC-20 standard, where every unit of an asset is identical to each other. ERC-20 is a flexible and efficient way to manipulate fungible tokens. Non-fungible tokens are predominantly ERC-721 tokens, a standard capable of distinguishing digital assets from one another based on identity. -However, both have significant drawbacks. For example, EIP-20 requires that users create a separate EIP-20 contract for each individual data structure or combination of customizable properties. In practice, this results in an extraordinarily large amount of EIP-20 contracts that need to be created. On the other hand, EIP-721 tokens provide no quantitative feature, significantly undercutting their computability, liquidity, and manageability. For example, if one was to create financial instruments such as bonds, insurance policy, or vesting plans using EIP-721, no standard interfaces are available for us to control the value in them, making it impossible, for example, to transfer a portion of the equity in the contract represented by the token. +However, both have significant drawbacks. For example, ERC-20 requires that users create a separate ERC-20 contract for each individual data structure or combination of customizable properties. In practice, this results in an extraordinarily large amount of ERC-20 contracts that need to be created. On the other hand, ERC-721 tokens provide no quantitative feature, significantly undercutting their computability, liquidity, and manageability. For example, if one was to create financial instruments such as bonds, insurance policy, or vesting plans using ERC-721, no standard interfaces are available for us to control the value in them, making it impossible, for example, to transfer a portion of the equity in the contract represented by the token. -A more intuitive and straightforward way to solve the problem is to create a semi-fungible token that has the quantitative features of EIP-20 and qualitative attributes of EIP-721. The backwards-compatibility with EIP-721 of such semi-fungible tokens would help utilize existing infrastructures already in use and lead to faster adoption. +A more intuitive and straightforward way to solve the problem is to create a semi-fungible token that has the quantitative features of ERC-20 and qualitative attributes of ERC-721. The backwards-compatibility with ERC-721 of such semi-fungible tokens would help utilize existing infrastructures already in use and lead to faster adoption. ## Specification The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. -**Every [EIP-3525](./eip-3525.md) compliant contract must implement the EIP-3525, EIP-721 and [EIP-165](./eip-165.md) interfaces** +**Every [ERC-3525](./eip-3525.md) compliant contract must implement the ERC-3525, ERC-721 and [ERC-165](./eip-165.md) interfaces** ```solidity pragma solidity ^0.8.0; /** - * @title EIP-3525 Semi-Fungible Token Standard - * Note: the EIP-165 identifier for this interface is 0xd5358140. + * @title ERC-3525 Semi-Fungible Token Standard + * Note: the ERC-165 identifier for this interface is 0xd5358140. */ interface IERC3525 /* is IERC165, IERC721 */ { /** @@ -73,7 +73,7 @@ interface IERC3525 /* is IERC165, IERC721 */ { * @notice Get the number of decimals the token uses for value - e.g. 6, means the user * representation of the value of a token can be calculated by dividing it by 1,000,000. * Considering the compatibility with third-party wallets, this function is defined as - * `valueDecimals()` instead of `decimals()` to avoid conflict with EIP-20 tokens. + * `valueDecimals()` instead of `decimals()` to avoid conflict with ERC-20 tokens. * @return The number of decimals for value */ function valueDecimals() external view returns (uint8); @@ -137,8 +137,8 @@ interface IERC3525 /* is IERC165, IERC721 */ { /** * @notice Transfer value from a specified token to an address. The caller should confirm that - * `_to` is capable of receiving EIP-3525 tokens. - * @dev This function MUST create a new EIP-3525 token with the same slot for `_to`, + * `_to` is capable of receiving ERC-3525 tokens. + * @dev This function MUST create a new ERC-3525 token with the same slot for `_to`, * or find an existing token with the same slot owned by `_to`, to receive the transferred value. * MUST revert if `_fromTokenId` is zero token id or does not exist. * MUST revert if `_to` is zero address. @@ -164,10 +164,10 @@ The slot's enumeration extension is OPTIONAL. This allows your contract to publi pragma solidity ^0.8.0; /** - * @title EIP-3525 Semi-Fungible Token Standard, optional extension for slot enumeration + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for slot enumeration * @dev Interfaces for any contract that wants to support enumeration of slots as well as tokens * with the same slot. - * Note: the EIP-165 identifier for this interface is 0x3b741b9e. + * Note: the ERC-165 identifier for this interface is 0x3b741b9e. */ interface IERC3525SlotEnumerable is IERC3525 /* , IERC721Enumerable */ { @@ -207,11 +207,11 @@ The slot level approval is OPTIONAL. This allows any contract that wants to supp pragma solidity ^0.8.0; /** - * @title EIP-3525 Semi-Fungible Token Standard, optional extension for approval of slot level + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for approval of slot level * @dev Interfaces for any contract that wants to support approval of slot level, which allows an * operator to manage one's tokens with the same slot. * See https://eips.ethereum.org/EIPS/eip-3525 - * Note: the EIP-165 identifier for this interface is 0xb688be58. + * Note: the ERC-165 identifier for this interface is 0xb688be58. */ interface IERC3525SlotApprovable is IERC3525 { /** @@ -230,7 +230,7 @@ interface IERC3525SlotApprovable is IERC3525 { * @dev Caller SHOULD be `_owner` or an operator who has been authorized through * `setApprovalForAll`. * MUST emit ApprovalSlot event. - * @param _owner The address that owns the EIP-3525 tokens + * @param _owner The address that owns the ERC-3525 tokens * @param _slot The slot of tokens being queried approval of * @param _operator The address for whom to query approval * @param _approved Identify if `_operator` would be approved or disapproved @@ -245,7 +245,7 @@ interface IERC3525SlotApprovable is IERC3525 { /** * @notice Query if `_operator` is authorized to manage all of `_owner`'s tokens with the * specified slot. - * @param _owner The address that owns the EIP-3525 tokens + * @param _owner The address that owns the ERC-3525 tokens * @param _slot The slot of tokens being queried approval of * @param _operator The address for whom to query approval * @return True if `_operator` is authorized to manage all of `_owner`'s tokens with `_slot`, @@ -260,7 +260,7 @@ interface IERC3525SlotApprovable is IERC3525 { ``` -### EIP-3525 Token Receiver +### ERC-3525 Token Receiver If a smart contract wants to be informed when they receive values from other addresses, it should implement all of the functions in the `IERC3525Receiver` interface, in the implementation it can decide whether to accept or reject the transfer. See "Transfer Rules" for further detail. @@ -268,15 +268,15 @@ If a smart contract wants to be informed when they receive values from other add pragma solidity ^0.8.0; /** - * @title EIP-3525 token receiver interface - * @dev Interface for a smart contract that wants to be informed by EIP-3525 contracts when receiving values from ANY addresses or EIP-3525 tokens. - * Note: the EIP-165 identifier for this interface is 0x009ce20b. + * @title ERC-3525 token receiver interface + * @dev Interface for a smart contract that wants to be informed by ERC-3525 contracts when receiving values from ANY addresses or ERC-3525 tokens. + * Note: the ERC-165 identifier for this interface is 0x009ce20b. */ interface IERC3525Receiver { /** - * @notice Handle the receipt of an EIP-3525 token value. - * @dev An EIP-3525 smart contract MUST check whether this function is implemented by the recipient contract, if the - * recipient contract implements this function, the EIP-3525 contract MUST call this function after a + * @notice Handle the receipt of an ERC-3525 token value. + * @dev An ERC-3525 smart contract MUST check whether this function is implemented by the recipient contract, if the + * recipient contract implements this function, the ERC-3525 contract MUST call this function after a * value transfer (i.e. `transferFrom(uint256,uint256,uint256,bytes)`). * MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256, * uint256,bytes)'))`) if the transfer is accepted. @@ -300,7 +300,7 @@ interface IERC3525Receiver { **_Transfer:_** -Besides EIP-721 compatible token transfer methods, this EIP introduces two new transfer models: value transfer from ID to ID, and value transfer from ID to address. +Besides ERC-721 compatible token transfer methods, this EIP introduces two new transfer models: value transfer from ID to ID, and value transfer from ID to address. ```solidity function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable; @@ -318,9 +318,9 @@ The second one allows value transfers from one token (specified by `_fromTokenId This EIP provides four kinds of approving functions indicating different levels of approvals, which can be described as full level approval, slot level approval, token ID level approval as well as value level approval. -- `setApprovalForAll`, compatible with EIP-721, SHOULD indicate the full level of approval, which means that the authorized operators are capable of managing all the tokens, including their values, owned by the owner. +- `setApprovalForAll`, compatible with ERC-721, SHOULD indicate the full level of approval, which means that the authorized operators are capable of managing all the tokens, including their values, owned by the owner. - `setApprovalForSlot` (optional) SHOULD indicate the slot level of approval, which means that the authorized operators are capable of managing all the tokens with the specified slot, including their values, owned by the owner. -- The token ID level `approve` function, compatible with EIP-721, SHOULD indicate that the authorized operator is capable of managing only the specified token ID, including its value, owned by the owner. +- The token ID level `approve` function, compatible with ERC-721, SHOULD indicate that the authorized operator is capable of managing only the specified token ID, including its value, owned by the owner. - The value level `approve` function, SHOULD indicate that the authorized operator is capable of managing the specified maximum value of the specified token owned by the owner. - For any approving function, the caller MUST be the owner or has been approved with a higher level of authority. @@ -337,7 +337,7 @@ This EIP provides four kinds of approving functions indicating different levels - The `transferFrom(uint256 _fromTokenId, address _to, uint256 _value)` function, which transfers value from one token ID to an address, SHOULD follow the rule below: - - MUST either find a EIP-3525 token owned by the address `_to` or create a new EIP-3525 token, with the same slot of `_fromTokenId`, to receive the transferred value. + - MUST either find a ERC-3525 token owned by the address `_to` or create a new ERC-3525 token, with the same slot of `_fromTokenId`, to receive the transferred value. - MUST revert unless `msg.sender` is the owner of `_fromTokenId`, an authorized operator or an operator who has been approved the whole token or at least `_value` of it. - MUST revert if `_fromTokenId` is zero token id or does not exist. - MUST revert if `_to` is zero address. @@ -350,33 +350,33 @@ This EIP provides four kinds of approving functions indicating different levels #### Metadata Extensions -EIP-3525 metadata extensions are compatible EIP-721 metadata extensions. +ERC-3525 metadata extensions are compatible ERC-721 metadata extensions. -This optional interface can be identified with the EIP-165 Standard Interface Detection. +This optional interface can be identified with the ERC-165 Standard Interface Detection. ```solidity pragma solidity ^0.8.0; /** - * @title EIP-3525 Semi-Fungible Token Standard, optional extension for metadata + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for metadata * @dev Interfaces for any contract that wants to support query of the Uniform Resource Identifier - * (URI) for the EIP-3525 contract as well as a specified slot. + * (URI) for the ERC-3525 contract as well as a specified slot. * Because of the higher reliability of data stored in smart contracts compared to data stored in * centralized systems, it is recommended that metadata, including `contractURI`, `slotURI` and * `tokenURI`, be directly returned in JSON format, instead of being returned with a url pointing * to any resource stored in a centralized system. * See https://eips.ethereum.org/EIPS/eip-3525 - * Note: the EIP-165 identifier for this interface is 0xe1600902. + * Note: the ERC-165 identifier for this interface is 0xe1600902. */ interface IERC3525Metadata is IERC3525 /* , IERC721Metadata */ { /** - * @notice Returns the Uniform Resource Identifier (URI) for the current EIP-3525 contract. + * @notice Returns the Uniform Resource Identifier (URI) for the current ERC-3525 contract. * @dev This function SHOULD return the URI for this contract in JSON format, starting with * header `data:application/json;`. * See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for contract URI. - * @return The JSON formatted URI of the current EIP-3525 contract + * @return The JSON formatted URI of the current ERC-3525 contract */ function contractURI() external view returns (string memory); @@ -391,9 +391,9 @@ interface IERC3525Metadata is } ``` -#### EIP-3525 Metadata URI JSON Schema +#### ERC-3525 Metadata URI JSON Schema -This is the "EIP-3525 Metadata JSON Schema for `contractURI()`" referenced above. +This is the "ERC-3525 Metadata JSON Schema for `contractURI()`" referenced above. ```json { @@ -424,7 +424,7 @@ This is the "EIP-3525 Metadata JSON Schema for `contractURI()`" referenced above } ``` -This is the "EIP-3525 Metadata JSON Schema for `slotURI(uint)`" referenced above. +This is the "ERC-3525 Metadata JSON Schema for `slotURI(uint)`" referenced above. ```json { @@ -480,7 +480,7 @@ This is the "EIP-3525 Metadata JSON Schema for `slotURI(uint)`" referenced above ``` -This is the "EIP-3525 Metadata JSON Schema for `tokenURI(uint)`" referenced above. +This is the "ERC-3525 Metadata JSON Schema for `tokenURI(uint)`" referenced above. ```json { @@ -509,7 +509,7 @@ This is the "EIP-3525 Metadata JSON Schema for `tokenURI(uint)`" referenced abov }, "properties": { "type": "object", - "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays. Optional, you can use the same schema as the properties section of EIP-3525 Metadata JSON Schema for slotURI(uint) if you need a better description attribute." + "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays. Optional, you can use the same schema as the properties section of ERC-3525 Metadata JSON Schema for slotURI(uint) if you need a better description attribute." } } } @@ -536,7 +536,7 @@ The purpose of maintaining id-to-address transfer function is to maximize the co ### Design decision: Notification/acceptance mechanism instead of 'Safe Transfer' -EIP-721 and some later token standards introduced 'Safe Transfer' model, for better control of the 'safety' when transferring tokens, this mechanism leaves the choice of different transfer modes (safe/unsafe) to the sender, and may cause some potential problems: +ERC-721 and some later token standards introduced 'Safe Transfer' model, for better control of the 'safety' when transferring tokens, this mechanism leaves the choice of different transfer modes (safe/unsafe) to the sender, and may cause some potential problems: 1. In most situations the sender does not know how to choose between two kinds of transfer methods (safe/unsafe); 2. If the sender calls the `safeTransferFrom` method, the transfer may fail if the recipient contract did not implement the callback function, even if that contract is capable of receiving and manipulating the token without issue. @@ -544,31 +544,31 @@ EIP-721 and some later token standards introduced 'Safe Transfer' model, for bet This EIP defines a simple 'Check, Notify and Response' model for better flexibility as well as simplicity: 1. No extra `safeTransferFrom` methods are needed, all callers only need to call one kind of transfer; -2. All EIP-3525 contracts MUST check for the existence of `onERC3525Received` on the recipient contract and call the function when it exists; +2. All ERC-3525 contracts MUST check for the existence of `onERC3525Received` on the recipient contract and call the function when it exists; 3. Any smart contract can implement `onERC3525Received` function for the purpose of being notified after receiving values; this function MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))`) if the transfer is accepted, or any other value if the transfer is rejected. -There is a special case for this notification/acceptance mechanism: since EIP-3525 allows value transfer from an address to itself, when a smart contract which implements `onERC3525Received` transfers value to itself, `onERC3525Received` will also be called. This allows for the contract to implement different rules of acceptance between self-value-transfer and receiving value from other addresses. +There is a special case for this notification/acceptance mechanism: since ERC-3525 allows value transfer from an address to itself, when a smart contract which implements `onERC3525Received` transfers value to itself, `onERC3525Received` will also be called. This allows for the contract to implement different rules of acceptance between self-value-transfer and receiving value from other addresses. ### Design decision: Relationship between different approval models -For semantic compatibility with EIP-721 as well as the flexibility of value manipulation of tokens, we decided to define the relationships between some of the levels of approval like that: +For semantic compatibility with ERC-721 as well as the flexibility of value manipulation of tokens, we decided to define the relationships between some of the levels of approval like that: 1. Approval of an id will lead to the ability to partially transfer values from this id by the approved operator; this will simplify the value approval for an id. However, the approval of total values in a token should not lead to the ability to transfer the token entity by the approved operator. 2. `setApprovalForAll` will lead to the ability to partially transfer values from any token, as well as the ability to approve partial transfer of values from any token to a third party; this will simplify the value transfer and approval of all tokens owned by an address. ## Backwards Compatibility -As mentioned in the beginning, this EIP is backward compatible with EIP-721. +As mentioned in the beginning, this EIP is backward compatible with ERC-721. ## Reference Implementation -- [EIP-3525 implementation](../assets/eip-3525/contracts/ERC3525.sol) +- [ERC-3525 implementation](../assets/eip-3525/contracts/ERC3525.sol) ## Security Considerations -The value level approval and slot level approval (optional) is isolated from EIP-721 approval models, so that approving value should not affect EIP-721 level approvals. Implementations of this EIP must obey this principle. +The value level approval and slot level approval (optional) is isolated from ERC-721 approval models, so that approving value should not affect ERC-721 level approvals. Implementations of this EIP must obey this principle. -Since this EIP is EIP-721 compatible, any wallets and smart contracts that can hold and manipulate standard EIP-721 tokens will have no risks of asset loss for EIP-3525 tokens due to incompatible standards implementations. +Since this EIP is ERC-721 compatible, any wallets and smart contracts that can hold and manipulate standard ERC-721 tokens will have no risks of asset loss for ERC-3525 tokens due to incompatible standards implementations. ## Copyright diff --git a/EIPS/eip-3651.md b/EIPS/eip-3651.md index cd7d6a465c03d..d94c694c6f122 100644 --- a/EIPS/eip-3651.md +++ b/EIPS/eip-3651.md @@ -4,8 +4,7 @@ title: Warm COINBASE description: Starts the `COINBASE` address warm author: William Morriss (@wjmelements) discussions-to: https://ethereum-magicians.org/t/eip-3651-warm-coinbase/6640 -status: Last Call -last-call-deadline: 2023-03-28 +status: Final type: Standards Track category: Core created: 2021-07-12 diff --git a/EIPS/eip-3690.md b/EIPS/eip-3690.md index 99aee94a9d4c2..40ebc7b8858fe 100644 --- a/EIPS/eip-3690.md +++ b/EIPS/eip-3690.md @@ -51,7 +51,7 @@ This feature is introduced on the very same block [EIP-3540](./eip-3540.md) is e ### Validation rules -> This section extends contact creation validation rules (as defined in EIP-3540). +> This section extends contract creation validation rules (as defined in EIP-3540). 4. The `jumpdests` section MUST be present if and only if the `code` section contains `JUMP` or `JUMPI` opcodes. 5. If the `jumpdests` section is present it MUST directly precede the `code` section. In this case a valid EOF bytecode will have the form of `format, magic, version, [jumpdests_section_header], code_section_header, [data_section_header], 0, [jumpdests_section_contents], code_section_contents, [data_section_contents]`. diff --git a/EIPS/eip-3855.md b/EIPS/eip-3855.md index 0820a03563f3b..42034250ae8dd 100644 --- a/EIPS/eip-3855.md +++ b/EIPS/eip-3855.md @@ -4,7 +4,7 @@ title: PUSH0 instruction description: Introduce a new instruction which pushes the constant value 0 onto the stack author: Alex Beregszaszi (@axic), Hugo De la cruz (@hugo-dc), Paweł Bylica (@chfast) discussions-to: https://ethereum-magicians.org/t/eip-3855-push0-instruction/7014 -status: Stagnant +status: Final type: Standards Track category: Core created: 2021-02-19 @@ -29,7 +29,7 @@ The main motivations for this change include: To put the "waste" into perspective, across existing accounts 340,557,331 bytes are wasted on `PUSH1 00` instructions, which means 68,111,466,200 gas was spent to deploy them. In practice a lot of these accounts share identical bytecode with others, so their total stored size in clients is lower, however the deploy time cost must have been paid nevertheless. -An example for 2) is changing the behaviour of `RETURNDATASIZE` such that it may not be guaranteed to be zero at the beginning of the call frame. This was proposed as a way to chain transactions (i.e. EIP-2733). +An example for 2) is changing the behaviour of `RETURNDATASIZE` such that it may not be guaranteed to be zero at the beginning of the call frame. ## Specification diff --git a/EIPS/eip-3860.md b/EIPS/eip-3860.md index 51fb6003054b8..5f8151e19ad1c 100644 --- a/EIPS/eip-3860.md +++ b/EIPS/eip-3860.md @@ -4,7 +4,7 @@ title: Limit and meter initcode description: Limit the maximum size of initcode to 49152 and apply extra gas cost of 2 for every 32-byte chunk of initcode author: Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0) discussions-to: https://ethereum-magicians.org/t/eip-3860-limit-and-meter-initcode/7018 -status: Review +status: Final type: Standards Track category: Core created: 2021-07-16 @@ -36,7 +36,7 @@ Furthermore, the lack of a limit has caused lengthy discussions for some EVM pro We are motivated by three reasons: 1. Ensuring `initcode` is fairly charged (most importantly cost is proportional to `initcode`'s length) to minimize the risks for the future. -2. To have a cost system which is extendable in the future (i.e. for proposals like [EIP-3670](./eip-3670.md)). +2. To have a cost system which is extendable in the future. 3. To simplify EVM engines by the explicit limits (code size, code offsets (`PC`), and jump offsets fit 16-bits). ## Specification diff --git a/EIPS/eip-4736.md b/EIPS/eip-4736.md index 243173e245721..3266787337e5d 100644 --- a/EIPS/eip-4736.md +++ b/EIPS/eip-4736.md @@ -4,8 +4,7 @@ title: Consensus Layer Withdrawal Protection description: Additional security for BLSToExecutionChange operation when a consensus layer mnemonic may be compromised, without changing consensus author: Benjamin Chodroff (@benjaminchodroff), Jim McDonald (@mcdee) discussions-to: https://ethereum-magicians.org/t/consensus-layer-withdrawal-protection/8161 -status: Last Call -last-call-deadline: 2023-03-29 +status: Final type: Standards Track category: Interface created: 2022-01-30 diff --git a/EIPS/eip-4788.md b/EIPS/eip-4788.md index 24f65168b0a18..a2553d4b785e7 100644 --- a/EIPS/eip-4788.md +++ b/EIPS/eip-4788.md @@ -1,10 +1,10 @@ --- eip: 4788 -title: Beacon state root in the EVM -description: Expose beacon chain state roots in the EVM -author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo) -discussions-to: https://ethereum-magicians.org/t/eip-4788-beacon-state-root-in-evm/8281 -status: Stagnant +title: Beacon block root in the EVM +description: Expose beacon chain roots in the EVM +author: Alex Stokes (@ralexstokes), Ansgar Dietrichs (@adietrichs), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4788-beacon-root-in-evm/8281 +status: Draft type: Standards Track category: Core created: 2022-02-10 @@ -12,129 +12,97 @@ created: 2022-02-10 ## Abstract -Commit to the state root of the beacon chain in the `ommers` field in the post-merge execution block. Reflect the changes in the `ommersHash` field of the execution block header. +Commit to the hash tree root of each beacon chain block in the corresponding execution payload header. -Store each beacon chain state root into a contract and add a new opcode that reads this contract. +Store each of these roots in a contract that lives in the execution state and add a new opcode that reads this contract. ## Motivation -Exposing the beacon chain state root allows for proofs about the beacon state to be verified inside the EVM. This functionality supports a wide variety of use cases in smart contracts involving validator status and finality produced by the consensus layer. - -In particular, this functionality is required for beacon chain validator withdrawals to the EVM. +Roots of the beacon chain blocks are crytographic accumulators that allow proofs of arbitrary consensus state. Exposing these roots inside the EVM allows for trust-minimized access to the consensus layer. This functionality supports a wide variety of use cases that improve trust assumptions of staking pools, restaking constructions, smart contract bridges, MEV mitigations and more. ## Specification -| constants | value | units -|--- |--- |--- -| `FORK_TIMESTAMP` | TBD | -| `FORK_EPOCH` | TBD | -| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffd` | -| `OPCODE_VALUE` | `0x48` | -| `G_beacon_state_root` | 20 | gas +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | TBD | +| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffd` | +| `OPCODE_VALUE` | `0x48` | +| `G_beacon_root` | 20 | gas +| `SLOTS_PER_HISTORICAL_ROOT` | 8192 | slot(s) ### Background -The method of injecting the beacon state root in this EIP follows the general strategy of [EIP-4399](./eip-4399.md) to make a post-merge change to the EVM integrating information from the beacon chain. This EIP along with [EIP-3675](./eip-3675.md) should be taken as relevant background to understand the particular approach of this EIP. - -The method for exposing the state root data via opcode is inspired by [EIP-2935](./eip-2935.md). +The high-level idea is that each execution block contains the parent beacon block root. Even in the event of missed slots since the previous block root does not change, +we only need a constant amount of space to represent this "oracle" in each execution block. To improve the usability of this oracle, block roots are stored +in a canonical place in the execution state analogous to a `SSTORE` in given contract's storage for each update. Roots are stored keyed by the slot(s) they pertain to. +To bound the amount of storage this construction consumes, a ring buffer is used that mirrors a block root accumulator on the consensus layer. +The method for exposing the root data via opcode is inspired by [EIP-2935](./eip-2935.md). ### Block structure and validity Beginning at the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST**: -1. set the value of the `ommers` field in the block to an RLP list with one element: the 32 byte [hash tree root](https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization) of the [beacon state](https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beaconstate) from the previous slot to this block. - -2. set the value of the `ommersHash` field in the block header to the Keccak256 hash of the `ommers` field. - -```python -beaconStateRoot = <32 byte value> # provided by consensus client -ommers = RLP([beaconStateRoot]) # in the block body -ommersHash = Keccak256(ommers) # in the block header -``` +1. set 32 bytes of the execution block header after the `withdrawals_root` to the 32 byte [hash tree root](https://github.com/ethereum/consensus-specs/blob/fa09d896484bbe240334fa21ffaa454bafe5842e/ssz/simple-serialize.md#merkleization) of the parent beacon block. -3. Add the block validation that the `ommersHash` does indeed match the expected commitment given the `ommers` value. +*NOTE*: this field is appended to the current block header structure with this EIP so that the size of the header grows after (and including) the `FORK_TIMESTAMP`. ### EVM changes #### Block processing -At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. before processing any transactions), write the beacon state root provided in the block into the storage of the smart contract at `HISTORY_STORAGE_ADDRESS`. This data is keyed by the block number. +At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. before processing any transactions), write the parent beacon root provided in the block header into the storage of the contract at `HISTORY_STORAGE_ADDRESS`. This data is keyed by the slot number. In pseudocode: ```python -beacon_state_root = block.ommers[0] -sstore(HISTORY_STORAGE_ADDRESS, block.number, beacon_state_root) +start_timestamp = get_block(block_header.parent_hash).header.timestamp +start_slot = convert_to_slot(start_timestamp) + +end_timestamp = block_header.timestamp +end_slot = convert_to_slot(end_timestamp) + +parent_beacon_block_root = block_header.parent_beacon_block_root + +for slot in range(start_slot, end_slot): + sstore(HISTORY_STORAGE_ADDRESS, slot % SLOTS_PER_HISTORICAL_ROOT, parent_beacon_block_root) ``` +When using any slot value as a key to the storage, the value under consideration must be converted to 32 bytes with big-endian encoding. + #### New opcode -Beginning at the execution timestamp `FORK_TIMESTAMP`, introduce a new opcode `BEACON_STATE_ROOT` at `OPCODE_VALUE`. This opcode consumes one word from the stack encoding the block number for the root. The opcode has a gas cost of `G_beacon_state_root`. +Beginning at the execution timestamp `FORK_TIMESTAMP`, introduce a new opcode `BEACON_ROOT` at `OPCODE_VALUE`. +This opcode consumes one word from the stack encoding the slot number for the desired root under big-endian discipline. +The opcode has a gas cost of `G_beacon_state_root`. The result of executing this opcode leaves one word on the stack corresponding to a read of the history contract's storage; in pseudocode: ```python -block_number = evm.stack.pop() -sload(HISTORY_STORAGE_ADDRESS, block_number) +slot = evm.stack.pop() +sload(HISTORY_STORAGE_ADDRESS, slot % SLOTS_PER_HISTORICAL_ROOT) ``` -If there is no root stored at the requested block number, the opcode follows the existing EVM semantics of `sload` returning `0`. +If there is no root stored at the requested slot number, the opcode follows the existing EVM semantics of `sload` returning `0`. ## Rationale -### General strategy - -See the rationale for EIP-4399 for discussion about this general strategy of reusing execution block elements for beacon chain data. - -### Fork mechanics - -This EIP requires the consensus layer and execution layer to execute a network upgrade in lockstep. -To carry out this task, a `FORK_EPOCH` (of the beacon chain) will be chosen and then used to compute a timestamp `FORK_TIMESTAMP`. -This `FORK_TIMESTAMP` can be used in the execution layer to identify when the protocol change should be deployed. - -This technique works because the timestamps in post-merge execution blocks are aligned to beacon chain slots and thus serve as a proxy for the slot number. - -Another option for the fork definition would be to pick a beacon chain epoch and an execution payload block number. -This design however is not reliable due to the presence of skipped slots on the beacon chain. - -### Execution layer validations - -By including the beacon state root in the execution block in the deprecated `ommers` field, execution clients can still verify the chain in a self-contained way without relying on an available consensus client. -This property is important during syncing (and likely other phases of execution node operation). - -### Minimizing client code change - -By including the `ommersHash` validation, clients can use existing code with only minimal changes (supplying the actual state root) during block production and verification. -Having the beacon state root value in the `ommers` field means that it is fairly straightforward to provide the value from the block data to the EVM execution context for client implementations as they stand today. - ### Gas cost of opcode -The suggested gas cost is just using the value for the `BLOCKHASH` opcode as `BEACON_STATE_ROOT` is an analogous operation. +The suggested gas cost is just using the value for the `BLOCKHASH` opcode as `BEACON_ROOT` is an analogous operation. ### Why not repurpose `BLOCKHASH`? -The `BLOCKHASH` opcode could be repurposed to provide a beacon state root instead of the current execution block hash. -To minimize code change and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding a new opcode with the desired semantics. - -### Why not bound history of state roots? - -Marginal state growth; adding every single root results in an additional ~84MB of state growth per year compared to ~30 GB of state overall. - -TODO: say something about statelessness -TODO: get latest numbers on state size, and compare against predicted growth - -### Beacon state root instead of block root +The `BLOCKHASH` opcode could be repurposed to provide the beacon root instead of some execution block hash. +To minimize code change, avoid breaking changes to smart contracts, and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding a new opcode with the desired semantics. -Each slot on the beacon chain containing a block has both a block root and a state root (reflecting the state after applying said block). -The beacon block includes the state root so a proof about the state could also be authored against a block root at the cost of a few additional hashes. -Given that most use cases want to prove data encapsulated in a given state, rather than a given block, this EIP suggests exposing state roots over block roots. +### Beacon block root instead of state root -### Block number in lieu of slot +Block roots are preferred over state roots so there is a constant amount of work to do with each new execution block. Otherwise, skipped slots would require +a linear amount of work with each new payload. While skipped slots are quite rare on mainnet, it is best to not add additional load under what would already +be nonfavorable conditions. -The state roots are keyed by the `number` of the execution block. -Another option is to key roots by the beacon chain slot they belong to. -While at first pass this may seem more direct, the beacon chain can have "skipped" slots where a beacon proposer failed to produce a block that was included at a given slot. -Handling roots of skipped slots would complicate the EVM mechanism so this EIP suggests to use the execution block number where each distinct block number is guaranteed to have a distinct root. +Use of block root over state root does mean proofs will require a few additional nodes but this cost is negligible (and could be amortized across all consumers, +e.g. with a singleton state root contract that caches the proof per slot). ## Backwards Compatibility diff --git a/EIPS/eip-4844.md b/EIPS/eip-4844.md index b33e063530f0a..ac003985b9be7 100644 --- a/EIPS/eip-4844.md +++ b/EIPS/eip-4844.md @@ -39,7 +39,7 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes | Constant | Value | | - | - | -| `BLOB_TX_TYPE` | `Bytes1(0x05)` | +| `BLOB_TX_TYPE` | `Bytes1(0x03)` | | `FIELD_ELEMENTS_PER_BLOB` | `4096` | | `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | | `BLOB_COMMITMENT_VERSION_KZG` | `Bytes1(0x01)` | @@ -153,6 +153,7 @@ When a blob transaction is passed through the network (see the [Networking](#net the `TransactionNetworkPayload` version of the transaction also includes `blobs` and `kzgs` (commitments list). The execution layer verifies the wrapper validity against the inner `TransactionPayload` after signature verification as: +- `blob_versioned_hashes` must not be empty - All hashes in `blob_versioned_hashes` must start with the byte `BLOB_COMMITMENT_VERSION_KZG` - There may be at most `MAX_DATA_GAS_PER_BLOCK // DATA_GAS_PER_BLOB` total blob commitments in a valid block. - There is an equal amount of versioned hashes, kzg commitments and blobs. @@ -181,6 +182,8 @@ def signed_tx_hash(tx: SignedBlobTransaction) -> Bytes32: return keccak256(BLOB_TX_TYPE + ssz.serialize(tx)) ``` +Blob transactions with empty `blob_versioned_hashes` are also considered invalid during execution block verification, and must not be included in a valid block. + ### Header extension The current header encoding is extended with a new 256-bit unsigned integer field `excess_data_gas`. This is the running @@ -262,6 +265,7 @@ def point_evaluation_precompile(input: Bytes) -> Bytes: Also verify that the provided commitment matches the provided versioned_hash. """ # The data is encoded as follows: versioned_hash | z | y | commitment | proof | + assert len(input) == 192 versioned_hash = input[:32] z = input[32:64] y = input[64:96] @@ -306,6 +310,7 @@ The block validity conditions are modified to include data gas checks: def validate_block(block: Block) -> None: ... + num_blobs = 0 for tx in block.transactions: ... @@ -314,6 +319,12 @@ def validate_block(block: Block) -> None: # ensure that the user was willing to at least pay the current data gasprice assert tx.message.max_fee_per_data_gas >= get_data_gasprice(parent(block).header) + + num_blobs += len(tx.message.blob_versioned_hashes) + + # check that the excess data gas is correct + expected_edg = calc_excess_data_gas(parent(block).header, num_blobs) + assert expected_edg == block.excess_data_gas ``` The actual `data_fee` as calculated via `calc_data_fee` is deducted from the sender balance before transaction execution and burned, and is not refunded in case of transaction failure. @@ -346,6 +357,7 @@ def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper): blobs = wrapper.blobs # note: assert blobs are not malformatted assert len(versioned_hashes) == len(commitments) == len(blobs) + assert len(versioned_hashes) > 0 # Verify that commitments match the blobs by checking the KZG proof assert verify_aggregate_kzg_proof(blobs, commitments, wrapper.kzg_aggregated_proof) diff --git a/EIPS/eip-4881.md b/EIPS/eip-4881.md index dd269c7161f4c..7c124da6354de 100644 --- a/EIPS/eip-4881.md +++ b/EIPS/eip-4881.md @@ -4,19 +4,22 @@ title: Deposit Contract Snapshot Interface description: Establishing the format and endpoint for transmitting a snapshot of the deposit Merkle tree author: Mark Mackey (@ethDreamer) discussions-to: https://ethereum-magicians.org/t/eip-4881-deposit-contract-snapshot-interface/ -status: Stagnant +status: Review type: Standards Track category: Interface created: 2021-01-29 --- ## Abstract + This EIP defines a standard format for transmitting the deposit contract Merkle tree in a compressed form during weak subjectivity sync. This allows newly syncing consensus clients to reconstruct the deposit tree much faster than downloading all historical deposits. The format proposed also allows clients to prune deposits that are no longer needed to participate fully in consensus (see [Deposit Finalization Flow](#deposit-finalization-flow)). ## Motivation -Most client implementations require beacon nodes to download and store every deposit log since the launch of the deposit contract in order to reconstruct the deposit Merkle tree. This approach requires nodes to store far more deposits than necessary to fully participate in consensus. It also needlessly increases the time it takes for new nodes to fully sync, which is especially noticeable during weak subjectivity sync. Furthermore, if [EIP-4444](./eip-4444.md) is adopted, it will not always be possible to download all historical deposit logs from full nodes. + +To reconstruct the deposit Merkle tree, most client implementations require beacon nodes to download and store every deposit log since the launch of the deposit contract. However, this approach requires beacon nodes to store far more deposits than necessary to participate in consensus. Additionally, this leads to increased sync times for new nodes, which is particularly evident during weak subjectivity sync. This simplistic approach also prevents historical contract logs from being pruned from full nodes, a prospect frequently discussed in the context of limiting state growth. ## Specification + Consensus clients MAY continue to implement the deposit Merkle tree however they choose. However, when transmitting the tree to newly syncing nodes, clients MUST use the following format: ```python @@ -29,6 +32,7 @@ class DepositTreeSnapshot: ``` Where `finalized` is a variable-length list (of maximum size `DEPOSIT_CONTRACT_DEPTH`) containing the hashes defined in the [Deposit Finalization Flow](#deposit-finalization-flow) section below. The fields `deposit_root`, `deposit_count`, and `execution_block_hash` store the same information as the [`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#eth1data) object that corresponds to the snapshot, and `execution_block_height` is the height of the execution block with hash `execution_block_hash`. Consensus clients MUST make this structure available via the Beacon Node API endpoint: + ``` /eth/v1/beacon/deposit_snapshot ``` @@ -37,13 +41,13 @@ Where `finalized` is a variable-length list (of maximum size `DEPOSIT_CONTRACT_D During deposit processing, the beacon chain requires deposits to be submitted along with a Merkle path to the deposit root. This is required exactly once for each deposit. When a deposit has been processed by the beacon chain and the [deposit finalization conditions](#deposit-finalization-conditions) have been met, many of the hashes along the path to the deposit root will never be required again to construct Merkle proofs on chain. These unnecessary hashes MAY be pruned to save space. The image below illustrates the evolution of the deposit Merkle tree under this process alongside the corresponding `DepositTreeSnapshot` as new deposits are added and older deposits become finalized: - +![deposit tree evolution](../assets/eip-4881/deposit_tree_evolution.svg) ## Rationale The format in this specification was chosen to achieve several goals simultaneously: -1. Enable reconstruction of the deposit contract Merkle tree under the adoption of [EIP-4444](./eip-4444.md) +1. Enable reconstruction of the deposit contract Merkle tree without requiring full nodes to store all historical contract logs 2. Avoid requiring consensus nodes to retain more deposits than necessary to fully participate in consensus 3. Simplicity of implementation (see [Reference Implementation](#reference-implementation) section) 4. Increase speed of weak subjectivity sync @@ -81,6 +85,7 @@ class DepositTestCase: ``` This EIP also includes other files for testing: + * [deposit_snapshot.py](../assets/eip-4881/deposit_snapshot.py) contains the same code as the [Reference Implementation](#reference-implementation) * [eip_4881.py](../assets/eip-4881/eip_4881.py) contains boilerplate declarations * [test_deposit_snapshot.py](../assets/eip-4881/test_deposit_snapshot.py) includes code for running test cases against the reference implementation @@ -88,9 +93,11 @@ This EIP also includes other files for testing: If these files are downloaded to the same directory, the test cases can be run by executing `pytest` in that directory. ## Reference Implementation + This implementation lacks full error checking and is optimized for readability over efficiency. If `tree` is a `DepositTree`, then the `DepositTreeSnapshot` can be obtained by calling `tree.get_snapshot()` and a new instance of the tree can be recovered from the snapshot by calling `DepositTree.from_snapshot()`. See the [Deposit Finalization Conditions](#deposit-finalization-conditions) section for discussion on when the tree can be pruned by calling `tree.finalize()`. Generating proofs for deposits against an earlier version of the tree is relatively fast in this implementation; just create a copy of the finalized tree with `copy = DepositTree.from_snapshot(tree.get_snapshot())` and then append the remaining deposits to the desired count with `copy.push_leaf(deposit)`. Proofs can then be obtained with `copy.get_proof(index)`. + ```python from __future__ import annotations from typing import List, Optional, Tuple @@ -123,6 +130,9 @@ class DepositTreeSnapshot: execution_block: Tuple[Hash32, uint64]) -> DepositTreeSnapshot: snapshot = DepositTreeSnapshot( finalized, zerohashes[0], deposit_count, execution_block[0], execution_block[1]) + # A real implementation should store the deposit_root from the eth1_data passed to + # DepositTree.finalize() instead of relying on calculate_root() here. This allows + # the snapshot to be validated using calculate_root(). snapshot.deposit_root = snapshot.calculate_root() return snapshot @@ -305,10 +315,6 @@ Care must be taken not to send a snapshot which includes deposits that haven't b When these conditions are met, the tree can be pruned in the [reference implementation](#reference-implementation) by calling `tree.finalize(eth1data, execution_block_height)` -### Deposit Queue Exceeds EIP-4444 Pruning Period - -The proposed design could fail if the deposit queue becomes so large that deposits cannot be processed within the [EIP-4444 Pruning Period](./eip-4444.md) (currently set to 1 year). The beacon chain can process `MAX_DEPOSITS/SECONDS_PER_SLOT` deposits/second without skipped slots. Even under extreme conditions where 25% of slots are skipped, the deposit queue would need to be >31.5 million to hit this limit. This is more than 8x the total supply of ether assuming each deposit is a full validator. The minimum deposit is 1 ETH so an attacker would need to burn >30 Million ETH to create these conditions. - - ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4895.md b/EIPS/eip-4895.md index 6dab0cdfc72ac..81af3b60b632b 100644 --- a/EIPS/eip-4895.md +++ b/EIPS/eip-4895.md @@ -4,7 +4,7 @@ title: Beacon chain push withdrawals as operations description: Support validator withdrawals from the beacon chain to the EVM via a new "system-level" operation type. author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo) discussions-to: https://ethereum-magicians.org/t/eip-4895-beacon-chain-withdrawals-as-system-level-operations/8568 -status: Review +status: Final type: Standards Track category: Core created: 2022-03-10 diff --git a/EIPS/eip-4972.md b/EIPS/eip-4972.md index 4ca1ed819ad17..a0a57dba7a3a7 100644 --- a/EIPS/eip-4972.md +++ b/EIPS/eip-4972.md @@ -2,31 +2,22 @@ eip: 4972 title: Name-Owned Account description: Name-Owned Account for Social Identity -author: Shu Dong (@dongshu2013), Qi Zhou (@qizhou) +author: Shu Dong (@dongshu2013), Qi Zhou (@qizhou), Zihao Chen (@zihaoccc) discussions-to: https://ethereum-magicians.org/t/eip-4972-name-owned-account/8822 -status: Stagnant +status: Draft type: Standards Track category: ERC created: 2022-04-04 -requires: 20, 721 +requires: 137 --- ## Abstract -This ERC proposes a new type of account - name-owned account (NOA) that is controlled by the owner of the name besides existing externally-owned account (EOA) and contract account (CA). With the new account type, users will be able to transfer/receive tokens using the name-derived address directly instead of the address of the name owner. A NOA can be as a social identity with all states on-chain even under 3rd-party or self custody. It also simplifies porting the social identity from one custody to another. +The ERC suggests expanding the capabilities of the name service, such as ENS, by enabling each human-readable identity to be linked to a single smart contract account that can be controlled by the owner of the name identity. ## Motivation -A popular way to onboard Web2 users to the Web3 world is custody. However, current custody models have severe drawbacks. Considering the following widely adopted custody models: -1. The custodian uses one EOA/CA to hold the assets of all users. This is not compatible with on-chain social protocols since all user activities are off-chain. -2. One EOA per user. The social identity is not portable, which means there is no way for users to migrate their social identity across different custody platforms. -3. One CA (e.g. Gnosis Safe) per user. The one time deployment cost is super high and the user experience is not good. - -To solve all these problems, this ERC proposes a new type of account - name-owned account (NOA). Using NOA as social identity instead of EOA/CA brings huge benefits for users: -- **Easy Web2 user onboarding**. We are providing standard Web2 user experiences with human readable names and 3rd-party custody. Every user of a centralized platform can immediately have a NOA by using the username as the name of NOA custodied by the platform. -- **On-chain states**. All user states are on-chain even under custody so it’s 100% compatible with social protocols. -- **Portable Account**. A NOA can be easily ported to different custody platforms by changing the owner. -- **Flexible Account Management**. We can use one EOA/CA to control any number of NOAs. +Name itself cannot hold any context. We want to build an extension of name service to give name rich context by offering each name owner an extra ready to use smart contract account, which may help the general smart contract account adoption. With NOA, it is possible to hold assets and information for its name node, opening up new use cases such as name node transfers, which involve transferring ownership of the name node as well as the NOA, including any assets and information it holds. ## Specification @@ -35,128 +26,61 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ### Name-Owned Account An NOA has -1. a name for social identity; and -2. an address derived from the name to receive tokens; and -3. owner(s) of the name that can transfer the token. - -The name should be human-readable and can be easily recognized socially. An example is the username of a centralized platform such as FB, Twitter. The name-derived address (NDA) is a normal Ethereum address that should not collide with the existing addresses of EOA/CA. Since we cannot use NDA as msg.sender, the right to transfer the tokens of the NDA is controlled by the owner/owners of the name. The name to owner/owners mapping is managed by an on-chain name service, and the owner/owners are EOA/CA, which can be the addresses of 3-rd custodians (e.g. FB) or self-custodian. By changing the owner of the name to the EOA of the user (can be done by requesting the custodian), the NDA becomes self-custodian, and no one should be able to transfer the assets unless the approved by the self-custodian user. +- a human readable name defined by [ERC-137](./eip-137.md); and +- an owned account(NOA), which is an smart contract account whose address is derived from the name; and +- owner(s) of the name that can deploy and manipulate the owned account. -### Name Representation +The following diagram illustrates the relationship between NOA, name node, and name owner, with the ownership being guaranteed by the name service. -A name is represented by a bytes array which is ABI encoded. It **MAY** contain metadata of the name such as the name service the name belongs to. Examples of the name are "vitalik.eth", "vitalik@0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", or "qizhou.fb". + ┌───────────────┐ ┌───────────┐ ┌───────────────┐ + │ Owned Account ◄──own───┤ Name Node ◄───own───┤ Name Owner │ + └───────────────┘ └───────────┘ └───────────────┘ ### Interface -#### INameOwnedAccount -```solidity -interface INameOwnedAccount { - /// @notice This function resolves the _name to its derived address - /// @dev The implementation SHOULD avoid collision between name - /// derived address and EOA/CA - function addressOfName(bytes memory _name) public view returns(address); - - /// @notice This function returns true if and only if the operator is the owner of the _name - /// @dev The ownership MAY be defined by a name service such as ENS - function isNameOwner(bytes memory _from, address operator) public view returns(bool); -} -``` - -#### `IERC721NOA` - -```solidity -interface IERC721NOA is IERC721, INameOwnedAccount { - /// @notice Transfers the ownership of an NFT from a name to an address - /// @dev Throws unless `msg.sender` is the owner of _from. Throw if _from is - /// not the current owner. Throws if `_to` is the zero address. Throws if - /// `_tokenId` is not a valid NFT. When transfer is complete, this function - /// checks if `_to` is a smart contract (code size > 0). If so, it calls - /// `onERC721Received` on `_to` and throws if the return value is not - /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. - function safeTransferFromName(bytes memory _from, address _to, uint256 _tokenId, bytes _data) public returns(bool); - - /// @notice Transfers the ownership of an NFT from a name to another address - /// @dev This works identically to the other function with an extra data parameter, - /// except this function just sets data to "". - function safeTransferFromName(bytes memory _from, address _to, uint256 _tokenId) public returns(bool); - - /// @notice Change or reaffirm the approved address for an NFT - /// @dev The zero address indicates there is no approved address. - /// Throws unless `msg.sender` is the owner of _owner. Throw if _owner is not - /// the current owner. - function approveFromName(bytes memory _owner, address _operator, uint256 _tokenId) public returns(bool); - - /// @notice Enable or disable approval for a third party ("operator") to manage - /// all of _owner’s assets - /// @dev Throws unless `msg.sender` is the owner of _owner. Throw if _owner is not - /// the current owner. Emits the ApprovalForAll event. The contract MUST allow - /// multiple operators per owner. - function setApprovalForAllFromName(bytes memory _owner, address _operator, bool _approved) public returns(bool); - - /// @notice This function returns true if interfaceId is the id of IERC721NOA - /// @dev see {IERC165-supportsInterface} - function supportsInterface(bytes4 interfaceId) external view returns(bool); -} -``` - -#### `IERC20NOA` - -```solidity -interface IERC20NOA is IERC20, INameOwnedAccount { - /// @notice Transfers _value amount of tokens from name _from to address _to, - /// @dev Throws unless `msg.sender` is the owner of _owner. Throw if _owner is not - /// the current owner. Throw if the balance of _from does not have enough tokens to - /// spend. Emits the Transfer event. - function transferFromName(bytes memory _from, address _to, uint256 _value) public returns(bool); - - /// @notice Allows _spender to withdraw from _owner multiple times, up to - /// the _value amount. - /// @dev Throws unless `msg.sender` is the owner of _owner. Throw if _owner is - /// not the current owner. If this function is called again it overwrites the current - /// allowance with _value. - function approveFromName(bytes memory _owner, address _spender, uint256 _value) public returns(bool); - - /// @notice This function returns true if interfaceId is the id of IERC721NOA - /// @dev see {IERC165-supportsInterface} - function supportsInterface(bytes4 interfaceId) external view returns(bool); -} -``` - -### Authentication - -The transfer and approve function is authenticated if and only if the message sender is the owner of the name. -## Rationale +The core interface required for a name service to have is: -We use bytes array to represent a name to ensure it’s flexible enough to deal with different use cases. E.g. one can encode the name service contract address the name belongs to into the bytes array. One can also encode extra authentication data, such as zero knowledge proofs, into the bytes array. In the future, we may propose a standard to formalize the name for wider adoption. + interface INameServiceRegistry { + /// @notice get account address owned by the name node + /// @params node represents a name node + /// @return the address of an account + function ownedAccount( + bytes32 node + ) external view returns(address); + } -The isNameOwner function is sufficient for authenticating the message sender. One can verify the owner by looking up the name owner from a name service, or check zero knowledge proofs encoded in name to prove the ownership directly without looking up anything. +The core interface required for the name owned account is: -The addressOfName interface decouples the implementation from specific hashing algorithms, as long as the generated address doesn’t collide with EOA/CA address space. + interface INameOwnedAccount { + /// @notice get the name node is mapped to this account address + /// @return return a name node + function name() external view returns(bytes32); -## Backwards Compatibility + /// @notice get the name service contract address where + /// the name is registered + /// @return return the name service the name registered at + function nameService() external view returns(address); + } -The new account type is compatible with existing ERC token standards. +## Rationale -## Reference Implementation -### Name Format +To achieve a one-to-one mapping from the name to the NOA, where each NOA's address is derived from the name node, we must include the name node information in each NOA to reflect its name node ownership. The "name()" function can be used to retrieve this property of each NOA and enable reverse tracking to its name node. The "nameService()" function can get the name service contract address where the name is registered, to perform behaviors such as validation checks. Through these two methods, the NOA has the ability to track back to its actual owner who owns the name node. -The decoded format of bytes name is not defined at this standard. One straightforward implementation would be: -```solidity -bytes memory name = abi.encode((string, ‘address’), (username, nameService)) -``` -where the username is the string representation of the username and nameService is the name service contract address. This will decouple the implementation from specific name services such as ENS. +## Backwards Compatibility -### Name Derived Address (INameOwnedAccount.addressOfName()) +The name registry interface is compatible with ERC-137. -With the bytes format mentioned above, we can follow the similar rule of CREATE2 opcode to compute the NOA address from nameService and hash of the username as `address(keccak256(0xff, keccak256(“eip-4972.addressOfName”), nameService, keccak256(username)))`. This can ensure it won’t collide with existing smart contract account addresses. +## Reference Implementation -### Ownership of a Name (INameOwnedAccount.isNameOwner()) +### Name Owned Account Creation -Normally we can get the owner from the name service and compare it with the message sender. We recommend the name service to define an owner function in the same format as ENS. +The NOA creation is done by a “factory” contract. The factory could be the name service itself and is expected to use CREATE2 (not CREATE) to create the NOA. NOAs should have identical initcode and factory contract in order to achieve deterministic preservation of address. The name node can be used as the salt to guarantee the bijection from name to its owned account. ## Security Considerations No security considerations were found. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5000.md b/EIPS/eip-5000.md index 26804d486a18b..22e278c1ae2be 100644 --- a/EIPS/eip-5000.md +++ b/EIPS/eip-5000.md @@ -76,7 +76,7 @@ The order of arguments matches `addmod` and `mulmod`. ## Backwards Compatibility -This is a new instruction not present pior. +This is a new instruction not present prior. ## Test Cases diff --git a/EIPS/eip-5006.md b/EIPS/eip-5006.md index 996afb18a7215..f735e39f9c3ac 100755 --- a/EIPS/eip-5006.md +++ b/EIPS/eip-5006.md @@ -1,11 +1,10 @@ --- eip: 5006 title: Rental NFT, NFT User Extension -description: Add a user role with restricted permissions to EIP-1155 tokens +description: Add a user role with restricted permissions to ERC-1155 tokens author: Lance (@LanceSnow), Anders (@0xanders), Shrug discussions-to: https://ethereum-magicians.org/t/eip5006-erc-1155-usage-rights-extension/8941 -status: Last Call -last-call-deadline: 2022-08-01 +status: Final type: Standards Track category: ERC created: 2022-04-12 @@ -14,11 +13,11 @@ requires: 165, 1155 ## Abstract -This standard is an extension of [EIP-1155](./eip-1155.md). It proposes an additional role (`user`) which can be granted to addresses that represent a `user` of the assets rather than an `owner`. +This standard is an extension of [ERC-1155](./eip-1155.md). It proposes an additional role (`user`) which can be granted to addresses that represent a `user` of the assets rather than an `owner`. ## Motivation -Like [EIP-721](./eip-721.md), [EIP-1155](./eip-1155.md) tokens may have utility of some kind. The people who “use” the token may be different than the people who own it (such as in a rental). Thus, it would be useful to have separate roles for the “owner” and the “user” so that the “user” would not be able to take actions that the owner could (for example, transferring ownership). +Like [ERC-721](./eip-721.md), [ERC-1155](./eip-1155.md) tokens may have utility of some kind. The people who “use” the token may be different than the people who own it (such as in a rental). Thus, it would be useful to have separate roles for the “owner” and the “user” so that the “user” would not be able to take actions that the owner could (for example, transferring ownership). ## Specification @@ -132,15 +131,16 @@ In the spirit of permissionless interoperability, this standard makes it easier ## Backwards Compatibility -As mentioned in the specifications section, this standard can be fully ERC compatible by adding an extension function set, and there are no conflicts between [EIP-5006](./eip-5006.md) and EIP-1155. +As mentioned in the specifications section, this standard can be fully ERC compatible by adding an extension function set, and there are no conflicts between [ERC-5006](./eip-5006.md) and ERC-1155. -In addition, new functions introduced in this standard have many similarities with the existing functions in EIP-1155. This allows developers to easily adopt the standard quickly. +In addition, new functions introduced in this standard have many similarities with the existing functions in ERC-1155. This allows developers to easily adopt the standard quickly. ## Test Cases Test cases are included in [test.js](../assets/eip-5006/test/test.ts). Run in terminal: + 1. ```cd ../assets/eip-5006``` 1. ```npm install``` 1. ```npx hardhat test``` @@ -154,4 +154,5 @@ See [`ERC5006.sol`](../assets/eip-5006/contracts/ERC5006.sol). This EIP standard can completely protect the rights of the owner, the owner can change the NFT user, the user can not transfer the NFT. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5007.md b/EIPS/eip-5007.md index 007d2a65e756c..10de156ccbfd2 100755 --- a/EIPS/eip-5007.md +++ b/EIPS/eip-5007.md @@ -1,11 +1,11 @@ --- eip: 5007 -title: Time NFT, EIP-721 Time Extension -description: Add start time and end time to EIP-721 tokens. +title: Time NFT, ERC-721 Time Extension +description: Add start time and end time to ERC-721 tokens. author: Anders (@0xanders), Lance (@LanceSnow), Shrug discussions-to: https://ethereum-magicians.org/t/eip-5007-eip-721-time-extension/8924 status: Last Call -last-call-deadline: 2022-09-25 +last-call-deadline: 2023-05-15 type: Standards Track category: ERC created: 2022-04-13 @@ -14,7 +14,7 @@ requires: 165, 721 ## Abstract -This standard is an extension of [EIP-721](./eip-721.md). It proposes some additional functions (`startTime`, `endTime`) to help with on-chain time management. +This standard is an extension of [ERC-721](./eip-721.md). It proposes some additional functions (`startTime`, `endTime`) to help with on-chain time management. ## Motivation @@ -30,7 +30,7 @@ The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SH ```solidity /** - * @dev the EIP-165 identifier for this interface is 0x7a0cdf92. + * @dev the ERC-165 identifier for this interface is 0xf140be0d. */ interface IERC5007 /* is IERC721 */ { /** @@ -58,43 +58,50 @@ The **composable extension** is OPTIONAL for this standard. This allows your NFT ```solidity /** - * @dev the EIP-165 identifier for this interface is 0x620063db. + * @dev the ERC-165 identifier for this interface is 0x32060e69. */ interface IERC5007Composable /* is IERC5007 */ { /** - * @dev Returns the ancestor token id of the NFT. - * + * @dev Returns the asset id of the time NFT. + * Only NFTs with same asset id can be merged. + * * Requirements: * * - `tokenId` must exist. */ - function rootTokenId(uint256 tokenId) external view returns (uint256); + function assetId(uint256 tokenId) external view returns (uint256); /** - * @dev Mint a new token from an old token. - * The rootTokenId of the new token is the same as the rootTokenId of the old token + * @dev Split an old token to two new tokens. + * The assetId of the new token is the same as the assetId of the old token * * Requirements: * * - `oldTokenId` must exist. - * - `newTokenId` must not exist. - * - `newTokenOwner` cannot be the zero address. - * - `newTokenStartTime` require(oldTokenStartTime < newTokenStartTime && newTokenStartTime <= oldTokenEndTime) + * - `newToken1Id` must not exist. + * - `newToken1Owner` cannot be the zero address. + * - `newToken2Id` must not exist. + * - `newToken2Owner` cannot be the zero address. + * - `splitTime` require(oldToken.startTime <= splitTime && splitTime < oldToken.EndTime) */ function split( uint256 oldTokenId, - uint256 newTokenId, - address newTokenOwner, - int64 newTokenStartTime + uint256 newToken1Id, + address newToken1Owner, + uint256 newToken2Id, + address newToken2Owner, + int64 splitTime ) external; /** - * @dev Merge the first token and second token into the new token. + * @dev Merge the first token and second token into the new token. * * Requirements: * * - `firstTokenId` must exist. - * - `secondTokenId` must exist. require((firstToken.endTime + 1) == secondToken.startTime) + * - `secondTokenId` must exist. + * - require((firstToken.endTime + 1) == secondToken.startTime) + * - require((firstToken.assetId()) == secondToken.assetId()) * - `newTokenOwner` cannot be the zero address. * - `newTokenId` must not exist. */ @@ -115,7 +122,7 @@ The max value of `int64` is 9,223,372,036,854,775,807. As a timestamp, 9,223,372 ## Backwards Compatibility -This standard is fully EIP-721 compatible. +This standard is fully ERC-721 compatible. ## Test Cases @@ -125,6 +132,7 @@ Run in terminal: ```shell cd ../assets/eip-5007 +npm install truffle -g npm install truffle test ``` diff --git a/EIPS/eip-5008.md b/EIPS/eip-5008.md index f70f552f5a3a4..d6a65bf4aa452 100755 --- a/EIPS/eip-5008.md +++ b/EIPS/eip-5008.md @@ -1,10 +1,10 @@ --- eip: 5008 -title: EIP-721 Nonce Extension -description: Add a `nonce` function to EIP-721. +title: ERC-721 Nonce Extension +description: Add a `nonce` function to ERC-721. author: Anders (@0xanders), Lance (@LanceSnow), Shrug discussions-to: https://ethereum-magicians.org/t/eip5008-eip-721-nonce-and-metadata-update-extension/8925 -status: Stagnant +status: Review type: Standards Track category: ERC created: 2022-04-10 @@ -13,13 +13,13 @@ requires: 165, 721 ## Abstract -This standard is an extension of [EIP-721](./eip-721.md). It proposes adding a `nonce` function to EIP-721 tokens. +This standard is an extension of [ERC-721](./eip-721.md). It proposes adding a `nonce` function to ERC-721 tokens. ## Motivation Some orders of NFT marketplaces have been attacked and the NFTs sold at a lower price than the current market floor price. This can happen when users transfer an NFT to another wallet and, later, back to the original wallet. This reactivates the order, which may list the token at a much lower price than the owner would have intended. -This EIP proposes adding a `nonce` property to EIP-721 tokens, and the `nonce` will be changed when a token is transferred. If a `nonce` is added to an order, the order can be checked to avoid attacks. +This EIP proposes adding a `nonce` property to ERC-721 tokens, and the `nonce` will be changed when a token is transferred. If a `nonce` is added to an order, the order can be checked to avoid attacks. ## Specification @@ -34,6 +34,7 @@ interface IERC5008 /* is IERC165 */ { function nonce(uint256 tokenId) external view returns(uint256); } ``` + The `nonce(uint256 tokenId)` function MUST be implemented as `view`. The `supportsInterface` method MUST return `true` when called with `0xce03fdab`. @@ -44,13 +45,14 @@ At first `transferCount` was considered as function name, but there may some cas ## Backwards Compatibility -This standard is compatible with EIP-721. +This standard is compatible with ERC-721. ## Test Cases Test cases are included in [test.js](../assets/eip-5008/test/test.ts). Run: + ```sh cd ../assets/eip-5008 npm install @@ -66,4 +68,5 @@ See [`ERC5008.sol`](../assets/eip-5008/contracts/ERC5008.sol). No security issues found. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5164.md b/EIPS/eip-5164.md index 5337a6daa9f26..2180c02f4bf56 100644 --- a/EIPS/eip-5164.md +++ b/EIPS/eip-5164.md @@ -31,80 +31,72 @@ This specification allows contracts on one chain to send messages to contracts o The `MessageDispatcher` lives on the origin chain and dispatches messages to the `MessageExecutor` for execution. The `MessageExecutor` lives on the destination chain and executes dispatched messages. -There are also extensions of `MessageDispatcher`, each defining a method for initiating a message or message batch: +### MessageDispatcher -- `SingleMessageDispatcher` -- `BatchMessageDispatcher` +The `MessageDispatcher` lives on the chain from which messages are sent. The Dispatcher's job is to broadcast messages through a transport layer to one or more `MessageExecutor` contracts. -Alternatively, `MessageDispatcher`s may implement a custom interface for initiating messages. +A unique `messageId` MUST be generated for each message or message batch. The message identifier MUST be unique across chains and dispatchers. This can be achieved by hashing a tuple of `chainId, dispatcherAddress, messageNonce` where messageNonce is a monotonically increasing integer per message. -### MessageDispatcher +#### MessageDispatcher Methods -The `MessageDispatcher` lives on the chain from which messages are sent. The Dispatcher's job is to broadcast messages through a transport layer to one or more `MessageExecutor` contracts. +**dispatchMessage** -A unique `messageId` MUST be generated for each message or message batch. +Will dispatch a message to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`. -To ensure uniqueness, it is RECOMMENDED that a monotonically increasing nonce is used in the calculation of the `messageId`. +`MessageDispatcher`s MUST emit the `MessageDispatched` event when a message is dispatched. -#### MessageDispatcher Events +`MessageDispatcher`s MUST revert if `toChainId` is not supported. -**MessageDispatched** +`MessageDispatcher`s MUST forward the message to a `MessageExecutor` on the `toChainId`. -The `MessageDispatched` event MUST be emitted by the `MessageDispatcher` when an individual message is dispatched. +`MessageDispatcher`s MUST use a unique `messageId` for each message. + +`MessageDispatcher`s MUST return the `messageId` to allow the message sender to track the message. + +`MessageDispatcher`s MAY require payment. ```solidity interface MessageDispatcher { - event MessageDispatched( - bytes32 indexed messageId, - address indexed from, - uint256 indexed toChainId, - address to, - bytes data, - ); + function dispatchMessage(uint256 toChainId, address to, bytes calldata data) external payable returns (bytes32 messageId); } ``` ```yaml -- name: MessageDispatched - type: event +- name: dispatchMessage + type: function + stateMutability: payable inputs: - - name: messageId - indexed: true - type: bytes32 - - name: from - indexed: true - type: address - name: toChainId - indexed: true type: uint256 - name: to type: address - name: data type: bytes + outputs: + - name: messageId + type: bytes32 ``` -**MessageBatchDispatched** +#### MessageDispatcher Events + +**MessageDispatched** -The `MessageBatchDispatched` event MUST be emitted by the `MessageDispatcher` when a batch of messages is dispatched. +The `MessageDispatched` event MUST be emitted by the `MessageDispatcher` when an individual message is dispatched. ```solidity -struct Message { - address to; - bytes data; -} - interface MessageDispatcher { - event MessageBatchDispatched( + event MessageDispatched( bytes32 indexed messageId, address indexed from, uint256 indexed toChainId, - Message[] messages + address to, + bytes data, ); } ``` ```yaml -- name: MessageBatchDispatched +- name: MessageDispatched type: event inputs: - name: messageId @@ -116,94 +108,10 @@ interface MessageDispatcher { - name: toChainId indexed: true type: uint256 - - name: messages - type: Message[] -``` - -### SingleMessageDispatcher - -The `SingleMessageDispatcher` is an extension of `MessageDispatcher` that defines a method, `dispatchMessage`, for dispatching an individual message to be executed on the `toChainId`. - -#### SingleMessageDispatcher Methods - -**dispatchMessage** - -Will dispatch a message to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`. - -`SingleMessageDispatcher`s MUST emit the `MessageDispatched` event when a message is dispatched. - -`SingleMessageDispatcher`s MUST revert if `toChainId` is not supported. - -`SingleMessageDispatcher`s MUST forward the message to a `MessageExecutor` on the `toChainId`. - -`SingleMessageDispatcher`s MUST use a unique `messageId` for each message. - -`SingleMessageDispatcher`s MUST return the `messageId` to allow the message sender to track the message. - -`SingleMessageDispatcher`s MAY require payment. - -```solidity -interface SingleMessageDispatcher is MessageDispatcher { - function dispatchMessage(uint256 toChainId, address to, bytes calldata data) external payable returns (bytes32 messageId); -} -``` - -```yaml -- name: dispatchMessage - type: function - stateMutability: payable - inputs: - - name: toChainId - type: uint256 - name: to type: address - name: data type: bytes - outputs: - - name: messageId - type: bytes32 -``` - -### BatchedMessageDispatcher - -The `BatchedMessageDispatcher` is an extension of `MessageDispatcher` that defines a method, `dispatchMessageBatch`, for dispatching a batch of messages to be executed on the `toChainId`. - -#### BatchedMessageDispatcher Methods - -**dispatchMessageBatch** - -Will dispatch a batch of messages to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`. - -`BatchedMessageDispatcher`s MUST emit the `MessageBatchDispatched` event when a message batch is dispatched. - -`BatchedMessageDispatcher`s MUST revert if `toChainId` is not supported. - -`BatchedMessageDispatcher`s MUST forward the message batch to the `MessageExecutor` on the `toChainId`. - -`BatchedMessageDispatcher`s MUST use a unique `messageId` for each batch of messages. - -`BatchedMessageDispatcher`s MUST return the `messageId` to allow the message sender to track the batch of messages. - -`BatchedMessageDispatcher`s MAY require payment. - -```solidity -interface BatchedMessageDispatcher is MessageDispatcher { - function dispatchMessageBatch(uint256 toChainId, Message[] calldata messages) external payable returns (bytes32 messageId); -} -``` - -```yaml -- name: dispatchMessageBatch - type: function - stateMutability: payable - inputs: - - name: toChainId - type: uint256 - - name: messages - type: Message[] - outputs: - - name: messageId - type: bytes32 ``` ### MessageExecutor @@ -242,47 +150,6 @@ to.call(abi.encodePacked(data, messageId, fromChainId, from)); type: address ``` -#### Error handling - -**MessageAlreadyExecuted** - -`MessageExecutor`s MUST revert if a messageId has already been executed and SHOULD emit a `MessageIdAlreadyExecuted` custom error. - -```solidity -interface MessageExecutor { - error MessageIdAlreadyExecuted( - bytes32 messageId - ); -} -``` - -**MessageFailure** - -`MessageExecutor`s MUST revert if an individual message fails and SHOULD emit a `MessageFailure` custom error. - -```solidity -interface MessageExecutor { - error MessageFailure( - bytes32 messageId, - bytes errorData - ); -} -``` - -**MessageBatchFailure** - -`MessageExecutor`s MUST revert the entire batch if any message in a batch fails and SHOULD emit a `MessageBatchFailure` custom error. - -```solidity -interface MessageExecutor { - error MessageBatchFailure( - bytes32 messageId, - uint256 messageIndex, - bytes errorData - ); -} -``` - #### MessageExecutor Events **MessageIdExecuted** @@ -310,6 +177,33 @@ interface MessageExecutor { type: bytes32 ``` +#### MessageExecutor Errors + +**MessageAlreadyExecuted** + +`MessageExecutor`s MUST revert if a messageId has already been executed and SHOULD emit a `MessageIdAlreadyExecuted` custom error. + +```solidity +interface MessageExecutor { + error MessageIdAlreadyExecuted( + bytes32 messageId + ); +} +``` + +**MessageFailure** + +`MessageExecutor`s MUST revert if an individual message fails and SHOULD emit a `MessageFailure` custom error. + +```solidity +interface MessageExecutor { + error MessageFailure( + bytes32 messageId, + bytes errorData + ); +} +``` + ## Rationale The `MessageDispatcher` can be coupled to one or more `MessageExecutor`. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling `dispatchMessage` without being aware of the `MessageExecutor` address. Messages can also be traced by a client using the data logged by the `MessageIdExecuted` event. diff --git a/EIPS/eip-5219.md b/EIPS/eip-5219.md index 3eb3236e071c8..57567e59d1587 100644 --- a/EIPS/eip-5219.md +++ b/EIPS/eip-5219.md @@ -4,8 +4,7 @@ title: Contract Resource Requests description: Allows the requesting of resources from contracts author: Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/pr-5219-discussion-contract-rest/9907 -status: Last Call -last-call-deadline: 2023-03-22 +status: Final type: Standards Track category: ERC created: 2022-07-10 diff --git a/EIPS/eip-5507.md b/EIPS/eip-5507.md index 444b48682fb22..d1f09b117d4eb 100644 --- a/EIPS/eip-5507.md +++ b/EIPS/eip-5507.md @@ -4,7 +4,8 @@ title: Refundable Tokens description: Adds refund functionality to ERC-20, ERC-721, and ERC-1155 tokens author: elie222 (@elie222), Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/eip-5507-refundable-nfts/10451 -status: Review +status: Last Call +last-call-deadline: 2023-05-02 type: Standards Track category: ERC created: 2022-08-19 diff --git a/EIPS/eip-5564.md b/EIPS/eip-5564.md index 612b3e3e75c28..b3ff72d6f206f 100644 --- a/EIPS/eip-5564.md +++ b/EIPS/eip-5564.md @@ -2,7 +2,7 @@ eip: 5564 title: Stealth Addresses description: Private, non-interactive transfers and interactions -author: Toni Wahrstätter (@nerolation), Matt Solomon (@mds1), Ben DiFrancesco (@apbendi), Vitalik Buterin +author: Toni Wahrstätter (@nerolation), Matt Solomon (@mds1), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin) discussions-to: https://ethereum-magicians.org/t/eip-5566-stealth-addresses-for-smart-contract-wallets/10614 status: Draft type: Standards Track @@ -100,7 +100,7 @@ This specification additionally defines a singleton `ERC5564Messenger` contract ```solidity /// @notice Interface for announcing when something is sent to a stealth address. -contract IERC5564Messenger { +contract IERC5564Messenger is StakeManager{ /// @dev Emitted when sending something to a stealth address. /// @dev See the `announce` method for documentation on the parameters. event Announcement ( @@ -146,13 +146,87 @@ contract IERC5564Messenger { } ``` -An example stealth address scheme for elliptic curves is below. Other stealth schemes may need to modify this approach. +The `ERC5564Messenger` contract inherits the `StakeManager` contract allowing users to stake ETH that is uses as an anti-DoS measure. More details in the ... section. + +```solidity +/// @notice Interface for the Stake Manager contract. +contract StakeManager{ + + /// @dev Emitted when stake is deposited. + /// @param account The address of the staker who deposited the stake. + /// @param totalStaked The new total amount of staked tokens for the account. + event StakeDeposit ( + address indexed account, + uint256 totalStaked + ); + + /// @dev Emitted when stake is withdrawn. + /// @param account The address of the staker who withdrew the stake. + /// @param withdrawAddress The address to which the withdrawn amount was sent. + /// @param amount The amount of tokens withdrawn. + event StakeWithdrawal ( + address indexed account, + address withdrawAddress, + uint256 amount + ); + + /** + * @notice Returns the stake of the account. + * @param account The address of the staker to check the stake for. + * @return uint256 The amount of staked tokens for the account. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @notice Adds the specified amount to the account's stake. + * @dev The function is payable, so the amount is sent as value with the transaction. + * @param staker The address of the staker to add the stake for. + */ + function addStake(address staker) external payable; + + /** + * @notice Withdraws the stake for the caller and sends it to the specified address. + * @param withdrawAddress The address to send the withdrawn amount to. + */ + function withdrawStake(address payable withdrawAddress) external; +} +``` + + +### Stealth meta-address format + +The new address format for the stealth meta-addresses is based on [ERC-3770](./eip-3770.md) and extends it by adding a `st:` (*stealth*) as prefix. +Thus, stealth meta-addresses on Ethereum come with the following format: + +``` +st:eth:0x +``` + +*Notably, the address-format is only used to differentiate stealth addresses from standard addresses, as the prefix is sliced away before doing any computations on the stealth meta-address.* + +--- + +### Initial Implementation of SECP256k1 with View Tags + +This improvement proposal provides a foundation that is not tied to any specific cryptographic system through the `IERC5564Messenger` contract. In addition, it introduces the first implementation of a [stealth address scheme](../assets/eip-5564/scheme_ids.md) that utilizes the SECP256k1 elliptic curve and `view tags`. The SECP256k1 elliptic curve is defined with the equation $y^2 = x^3 + 7 \pmod{p}$, where $p = 2^{256} - 2^{32} - 977$. + +The following reference is divided into three sections: + +1. Stealth address generation + +2. Parsing announcements + +3. Stealth private key derivation + + Definitions: - $G$ represents the generator point of the curve. -- Recipient has published a stealth meta-address that constists of the public keys $P_{spend}$ and $P_{view}$. +#### Generation - Generate stealth address from stealth meta-address: + +- Recipient has access to the private keys $p_{spend}$, $p_{view}$ from which public keys $P_{spend}$ and $P_{view}$ are derived. -- Recipient has has access to the private keys $p_{spend}$, $p_{view}$ from which $P_{spend}$ and $P_{view}$ are derived. +- Recipient has published a stealth meta-address that consists of the public keys $P_{spend}$ and $P_{view}$. - Sender passes the stealth meta-address to the `generateStealthAddress` function. @@ -169,7 +243,7 @@ An example stealth address scheme for elliptic curves is below. Other stealth sc - The function returns the stealth address $a_{stealth}$, the ephemeral public key $P_{ephemeral}$ and the view tag $v$. -An example function to check whether a stealth address belongs to oneself: +#### Parsing - Locate one's own stealth address(es): - User has access to the viewing private key $p_{view}$ and the public spending key $P_{spend}$. @@ -178,26 +252,26 @@ An example function to check whether a stealth address belongs to oneself: - The `checkStealthAddress` function performs the following computations: - Shared secret $s$ is computed by multiplying the viewing private key with the ephemeral public key of the announcement $s = p_{view}$ * $P_{ephemeral}$. - The secret is hashed $s_{h} = h(s)$. + - The view tag $v$ is extracted by taking the most significant byte $s_{h}[0]$ and can be compared to the given view tag. If the view tags do not match, this `Announcement` is not for the user and the remaining steps can be skipped. If the view tags match, continue on. - Multiply the hashed shared secret with the generator point $S_h = s_h \cdot G$. - The stealth public key is computed as $P_{stealth} = P_{spend} + S_h$. - The derived stealth address $a_{stealth}$ is computed as $\textrm{pubkeyToAddress}(P_{stealth})$. - Return `true` if the stealth address of the announcement matches the derived stealth address, else return `false`. -An example function to derive the private key of a stealth address: +#### Private key derivation - Generate the stealth address private key from the hashed shared secret and the spending private key. -- User has access to the viewing private key, spending private key $p_{spend}$. +- User has access to the viewing private key $p_{view}$ and spending private key $p_{spend}$. - User has access to a set of `Announcement` events for which the `checkStealthAddress` function returns `true`. - The `computeStealthKey` function performs the following computations: - Shared secret $s$ is computed by multiplying the viewing private key with the ephemeral public key of the announcement $s = p_{view}$ * $P_{ephemeral}$. - The secret is hashed $s_{h} = h(s)$. - - The stealth private key is computed as $P_{stealth} = p_{spend} + s_h$. + - The stealth private key is computed as $p_{stealth} = p_{spend} + s_h$. ---- -### Parsing ad security considerations +### Parsing considerations Usually, the recipient of a stealth address transaction has to perform the following operations to check weather he was the recipient of a certain transaction: @@ -209,16 +283,7 @@ Usually, the recipient of a stealth address transaction has to perform the follo The view tags approach is introduced to reduce the parsing time by around 6x. Users only need to perform 1x ecMUL and 1x HASH (skipping 1x ecMUL, 1x ecADD and 1x HASH) for every parsed announcement. The 1 bytes length was is based on the maximum required space to reliably filter not matching announcement. With 1 bytes as `viewTag` the probability for users to skip the remaining computations after hashing the shared secret $h(s)$ is $1/256$. This means that users can almost certainly skip the above three operations for any announcements that to do not involve them. Since the view tag reveals one byte of the shared secret, the security margin is reduced from 128 bits to 124 bits. Notably, this only affects the privacy and not the secure generation of a stealth address. -### Stealth meta-address format - -The new address format for the stealth meta-addresses is based on [ERC-3770](./eip-3770.md) and extends it by adding a `st:` (*stealth*) as prefix. -Thus, stealth meta-addresses on Ethereum come with the following format: - -``` -st:eth:0x -``` - -*Notably, the address-format is only used to differentiate stealth addresses from standard addresses, however, the prefix is sliced away before doing any computations on the stealth meta-address.* +--- ## Rationale @@ -240,9 +305,19 @@ You can find an implementation of this standard in TBD. ## Security Considerations +### DoS Measures + +There are potential DoS attack vectors that are not mitigated by network transaction fees. Stealth transfer senders cause an externality for recipients, as parsing announcement events consumes computational resources that are not compensated with gas. Therefore, spamming announcement events *can* be a detriment to the user experience, as it *can* lead to longer parsing times. +We consider the incentives to carry out such an attack as low because **no monetary benefit can be obtained** and, in theory, nothing prevents parsing providers to ignore the spamming when serving announcements to users. +However, sophisticated spamming (*sybil attacks*), which are not considered worth the associated costs, could make it difficult for parsing providers to develop filters for such announcement events. +Therefore, to counter spamming directly, a staking mechanism is introduced in the EIP that allows users to stake an unslashable amount of ETH. Staking allows parsing providers to better tackle potential spam through *sybil attacks*, enabling them to filter spam more effectively. + +Similar to [ERC-4337](./eip-4337), parsing providers agree on a `MINIMUM_STAKE`, such that the minimum required stake is not enforced on-chain. Users *can* withdraw their stake at any time without any delay. Parsing providers can de-prioritize senders who have not staked a certain minimum amount or withdrew their stake immediatly. + +### Recipients' transaction costs + The funding of the stealth address wallet represents a known issue that might breach privacy. The wallet that funds the stealth address MUST NOT have any physical connection to the stealth address owner in order to fully leverage the privacy improvements. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). - diff --git a/EIPS/eip-5615.md b/EIPS/eip-5615.md index c10b454faa870..4dd5d55c6883f 100644 --- a/EIPS/eip-5615.md +++ b/EIPS/eip-5615.md @@ -4,10 +4,11 @@ title: ERC-1155 Supply Extension description: A simple mechanism to fetch token supply data from ERC-1155 tokens author: Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/eip-5615-eip-1155-supply-extension/10732 -status: Review +status: Last Call +last-call-deadline: 2023-03-08 type: Standards Track category: ERC -created: 2022-09-07 +created: 2023-05-25 requires: 1155 --- diff --git a/EIPS/eip-5639.md b/EIPS/eip-5639.md index ff06ae69f6505..71892d0cb2909 100644 --- a/EIPS/eip-5639.md +++ b/EIPS/eip-5639.md @@ -2,21 +2,24 @@ eip: 5639 title: Delegation Registry description: Delegation of permissions for safer and more convenient signing operations. -author: foobar (@0xfoobar), Wilkins Chung (@wwhchung), ryley-o (@ryley-o), Jake Rockland (@jakerockland), andy8052 (@andy8052) +author: foobar (@0xfoobar), Wilkins Chung (@wwhchung) , ryley-o (@ryley-o), Jake Rockland (@jakerockland), andy8052 (@andy8052) discussions-to: https://ethereum-magicians.org/t/eip-5639-delegation-registry/10949 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-09-09 --- ## Abstract + This EIP describes the details of the Delegation Registry, a proposed protocol and ABI definition that provides the ability to link one or more delegate wallets to a vault wallet in a manner which allows the linked delegate wallets to prove control and asset ownership of the vault wallet. ## Motivation + Proving ownership of an asset to a third party application in the Ethereum ecosystem is common. Users frequently sign payloads of data to authenticate themselves before gaining access to perform some operation. However, this method--akin to giving the third party root access to one's main wallet--is both insecure and inconvenient. ***Examples:*** + 1. In order for you to edit your profile on OpenSea, you must sign a message with your wallet. 2. In order to access NFT gated content, you must sign a message with the wallet containing the NFT in order to prove ownership. 3. In order to gain access to an event, you must sign a message with the wallet containing a required NFT in order to prove ownership. @@ -24,27 +27,32 @@ Proving ownership of an asset to a third party application in the Ethereum ecosy 5. In order to prove ownership of an NFT, you must sign a payload with the wallet that owns that NFT. In all the above examples, one interacts with the dApp or smart contract using the wallet itself, which may be + - inconvenient (if it is controlled via a hardware wallet or a multi-sig) - insecure (since the above operations are read-only, but you are signing/interacting via a wallet that has write access) Instead, one should be able to approve multiple wallets to authenticate on behalf of a given wallet. ### Problems with existing methods and solutions + Unfortunately, we've seen many cases where users have accidentally signed a malicious payload. The result is almost always a significant loss of assets associated with the delegate address. In addition to this, many users keep significant portions of their assets in 'cold storage'. With the increased security from 'cold storage' solutions, we usually see decreased accessibility because users naturally increase the barriers required to access these wallets. ### Proposal: Use of a Delegation Registry + This proposal aims to provide a mechanism which allows a vault wallet to grant wallet, contract or token level permissions to a delegate wallet. This would achieve a safer and more convenient way to sign and authenticate, and provide 'read only' access to a vault wallet via one or more secondary wallets. From there, the benefits are twofold. This EIP gives users increased security via outsourcing potentially malicious signing operations to wallets that are more accessible (hot wallets), while being able to maintain the intended security assumptions of wallets that are not frequently used for signing operations. #### Improving dApp Interaction Security + Many dApps requires one to prove control of a wallet to gain access. At the moment, this means that you must interact with the dApp using the wallet itself. This is a security issue, as malicious dApps or phishing sites can lead to the assets of the wallet being compromised by having them sign malicious payloads. However, this risk would be mitigated if one were to use a secondary wallet for these interactions. Malicious interactions would be isolated to the assets held in the secondary wallet, which can be set up to contain little to nothing of value. #### Improving Multiple Device Access Security + In order for a non-hardware wallet to be used on multiple devices, you must import the seed phrase to each device. Each time a seed phrase is entered on a new device, the risk of the wallet being compromised increases as you are increasing the surface area of devices that have knowledge of the seed phrase. Instead, each device can have its own unique wallet that is an authorized secondary wallet of the main wallet. If a device specific wallet was ever compromised or lost, you could simply remove the authorization to authenticate. @@ -52,22 +60,24 @@ Instead, each device can have its own unique wallet that is an authorized second Further, wallet authentication can be chained so that a secondary wallet could itself authorize one or many tertiary wallets, which then have signing rights for both the secondary address as well as the root main address. This, can allow teams to each have their own signer while the main wallet can easily invalidate an entire tree, just by revoking rights from the root stem. #### Improving Convenience + Many invididuals use hardware wallets for maximum security. However, this is often inconvenient, since many do not want to carry their hardware wallet with them at all times. Instead, if you approve a non-hardware wallet for authentication activities (such as a mobile device), you would be able to use most dApps without the need to have your hardware wallet on hand. ## Specification + The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. Let: + - `vault` represent the vault address we are trying to authenticate or prove asset ownership for. - `delegate` represent the address we want to use for signing in lieu of `vault`. - **A Delegation Registry must implement IDelegationRegistry** ```solidity -** +/** * @title An immutable registry contract to be deployed as a standalone primitive * @dev New project launches can read previous cold wallet -> hot wallet delegations * from here and integrate those permissions into their flow @@ -251,6 +261,7 @@ interface IDelegationRegistry { ``` ### Checking Delegation + A dApp or smart contract would check whether or not a delegate is authenticated for a vault by checking the return value of checkDelegateForAll. A dApp or smart contract would check whether or not a delegate can authenticated for a contract associated with a by checking the return value of checkDelegateForContract. @@ -266,18 +277,20 @@ For the purposes of saving gas, it is expected if delegation checks are performe ## Rationale ### Allowing for vault, contract or token level delegation + In order to support a wide range of delegation use cases, the proposed specification allows a vault to delegate all assets it controls, assets of a specific contract, or a specific token. This ensures that a vault has fine grained control over the security of their assets, and allows for emergent behavior around granting third party wallets limited access only to assets relevant to them. ### On-chain enumeration + In order to support ease of integration and adoption, this specification has chosen to include on-chain enumeration of delegations and incur the additional gas cost associated with supporting enumeration. On-chain enumeration allows for dApp frontends to identify the delegations that any connected wallet has access to, and can provide UI selectors. Without on-chain enumeration, a dApp would require the user to manually input the vault, or would need a way to index all delegate events. -## Reference Implementation - ## Security Considerations + The core purpose of this EIP is to enhance security and promote a safer way to authenticate wallet control and asset ownership when the main wallet is not needed and assets held by the main wallet do not need to be moved. Consider it a way to do 'read only' authentication. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5656.md b/EIPS/eip-5656.md index f9abca0a7e1a9..f6b949802e6f5 100644 --- a/EIPS/eip-5656.md +++ b/EIPS/eip-5656.md @@ -4,7 +4,7 @@ title: Memory copying instruction description: An efficient EVM instruction for copying memory areas author: Alex Beregszaszi (@axic), Paul Dworzanski (@poemm), Jared Wasinger (@jwasinger), Casey Detrio (@cdetrio), Pawel Bylica (@chfast), Charles Cooper (@charles-cooper) discussions-to: https://ethereum-magicians.org/t/eip-5656-mcopy-instruction/10890 -status: Draft +status: Review type: Standards Track category: Core created: 2021-02-01 @@ -33,6 +33,7 @@ masked, or'd, and stored again. This overhead is significant. One edge case is i it can be efficiently stored using `MSTORE8`. As example use case, copying 256 bytes costs: + - at least 757 gas pre-EIP-2929 using the identity precompile - at least 157 gas post-EIP-2929 using the identity precompile - at least 96 gas using unrolled `MLOAD`/`MSTORE` instructions @@ -67,14 +68,25 @@ The instruction `MCOPY` is introduced at `0x5c`. This ordering matches the other copying instructions, i.e. `CALLDATACOPY`, `RETURNDATACOPY`. +### Gas costs + +Per yellow paper terminology, it should be considered part of the `W_copy` group of opcodes, and follow the gas calculation for `W_copy` in the yellow paper. While the calculation in the yellow paper should be considered the final word, for reference, as of time of this writing, that currently means its gas cost is: + +``` +words_copied = (length + 31) // 32 +g_verylow = 3 +g_copy = 3 * words_copied + memory_expansion_cost +gas_cost = g_verylow + g_copy +``` + ### Output stack -This instructions returns no stack items. +This instruction returns no stack items. ### Semantics It copies `length` bytes from the offset pointed at `src` to the offset pointed at `dst` in memory. -Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap. +Copying takes place as if an intermediate buffer was used, allowing the destination and source to overlap. If `length > 0` and (`src + length` or `dst + length`) is beyond the current memory length, the memory is extended with respective gas cost applied. @@ -89,7 +101,7 @@ This is still prohibitive for making the precompile a reasonable alternative aga ## Backwards Compatibility -This EIP introduces a new instruction which did not exists previously. Already deployed contracts using this instruction could change their behaviour after this EIP. +This EIP introduces a new instruction which did not exist previously. Already deployed contracts using this instruction could change their behaviour after this EIP. ## Test Cases diff --git a/EIPS/eip-5725.md b/EIPS/eip-5725.md index 75427b33d57f3..7a21e21ca0bd5 100644 --- a/EIPS/eip-5725.md +++ b/EIPS/eip-5725.md @@ -4,7 +4,7 @@ title: Transferable Vesting NFT description: An interface for transferable vesting NFTs which release underlying tokens over time. author: Apeguru (@Apegurus), Marco De Vries , Mario , DeFiFoFum (@DeFiFoFum) discussions-to: https://ethereum-magicians.org/t/eip-5725-transferable-vesting-nft/11099 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-09-08 @@ -13,9 +13,9 @@ requires: 721 ## Abstract -A **Non-Fungible Token** (NFT) standard used to vest tokens ([ERC-20](./eip-20.md) or otherwise) over a vesting release curve. +A **Non-Fungible Token** (NFT) standard used to vest [ERC-20](./eip-20.md) tokens over a vesting release curve. -The following standard allows for the implementation of a standard API for NFT based contracts that hold and represent the vested and locked properties of any underlying token ([ERC-20](./eip-20.md) or otherwise) that is emitted to the NFT holder. This standard is an extension of the [ERC-721](./eip-721.md) token that provides basic functionality for creating vesting NFTs, claiming the tokens and reading vesting curve properties. +The following standard allows for the implementation of a standard API for NFT based contracts which represent the vested and locked properties of underlying [ERC-20](./eip-20.md) tokens that are emitted to respective NFT owners. This standard is an extension of the [ERC-721](./eip-721.md) token which provides basic functionality for creating vesting NFTs, claiming underlying tokens and reading vesting curve properties. ## Motivation @@ -61,7 +61,7 @@ import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; /** * @title Non-Fungible Vesting Token Standard - * @notice A non-fungible token standard used to vest tokens (ERC-20 or otherwise) over a vesting release curve + * @notice A non-fungible token standard used to vest ERC-20 tokens over a vesting release curve * scheduled using timestamps. * @dev Because this standard relies on timestamps for the vesting schedule, it's important to keep track of the * tokens claimed per Vesting NFT so that a user cannot withdraw more tokens than alloted for a specific Vesting NFT. diff --git a/EIPS/eip-5773.md b/EIPS/eip-5773.md index 28985c4358c77..14cc63582f0af 100644 --- a/EIPS/eip-5773.md +++ b/EIPS/eip-5773.md @@ -4,8 +4,7 @@ title: Context-Dependent Multi-Asset Tokens description: An interface for Multi-Asset tokens with context dependent asset type output controlled by owner's preference. author: Bruno Škvorc (@Swader), Cicada (@CicadaNCR), Steven Pineda (@steven2308), Stevan Bogosavljevic (@stevyhacker), Jan Turk (@ThunderDeliverer) discussions-to: https://ethereum-magicians.org/t/multiresource-tokens/11326 -status: Last Call -last-call-deadline: 2023-04-07 +status: Final type: Standards Track category: ERC created: 2022-10-10 @@ -350,78 +349,44 @@ The `getAssetMetadata` function returns the asset's metadata URI. The metadata, ```json { - "title": "Asset Metadata", - "type": "object", - "properties": { - "name": { + "name": "Asset Name", + "description": "The description of the token or asset", + "mediaUri": "ipfs://mediaOfTheAssetOrToken", + "thumbnailUri": "ipfs://thumbnailOfTheAssetOrToken", + "externalUri": "https://uriToTheProjectWebsite", + "license": "License name", + "licenseUri": "https://uriToTheLicense", + "tags": ["tags", "used", "to", "help", "marketplaces", "categorize", "the", "asset", "or", "token"], + "preferThumb": false, // A boolean flag indicating to UIs to prefer thumbnailUri instead of mediaUri wherever applicable + "attributes": [ + { + "label": "rarity", "type": "string", - "description": "Identifies the name of the asset associated with the asset" + "value": "epic", + // For backward compatibility + "trait_type": "rarity" }, - "description": { + { + "label": "color", "type": "string", - "description": "Identifies the general notes, abstracts, or summaries about the contents of the asset" + "value": "red", + // For backward compatibility + "trait_type": "color" }, - "type": { - "type": "string", - "description": "Identifies the definition of the type of content of the asset" - }, - "locale": { - "type": "string", - "description": "Identifies metadata locale in ISO 639-1 format for translations and localisation of the asset" - }, - "license": { - "type": "string", - "description": "Identifies the license attached to the asset" - }, - "licenseUri": { - "type": "string", - "description": "Identifies the URI to the license statement of the license attached to the asset" - }, - "mediaUri": { - "type": "string", - "description": "Identifies the URI of the main media file associated with the asset" - }, - "thumbnailUri": { - "type": "string", - "description": "Identifies the URI of the thumbnail image associated with the asset to be used for preview of the asset in the wallets and client applications (the recommended maximum size is 350x350 px)" - }, - "externalUri": { - "type": "string", - "description": "Identifies the URI to the additional information about the subject or content of the asset" - }, - "properties": { - "type": "object", - "properties": "Identifies the optional custom attributes of the asset" + { + "label": "height", + "type": "float", + "value": 192.4, + // For backward compatibility + "trait_type": "height", + "display_type": "number" } - } + ] } ``` While this is the suggested JSON schema for the asset metadata, it is not enforced and MAY be structured completely differently based on implementer's preference. -The optional properties of the metadata JSON MAY include the following fields, or it MAY incorporate any number of custom fields, but MAY also not be included in the schema at all: - -```json - "properties": { - "rarity": { - "type": "string", - "value": "epic" - }, - "color": { - "type": "string", - "value": "red" - }, - "height": { - "type": "float", - "value": 192.4 - }, - "tags": { - "type": "array", - "value": ["music", "2020", "best"] - } - } -``` - ## Rationale Designing the proposal, we considered the following questions: diff --git a/EIPS/eip-6059.md b/EIPS/eip-6059.md index 39fa926a17540..a07c6c3cd15b8 100644 --- a/EIPS/eip-6059.md +++ b/EIPS/eip-6059.md @@ -4,8 +4,7 @@ title: Parent-Governed Nestable Non-Fungible Tokens description: An interface for Nestable Non-Fungible Tokens with emphasis on parent token's control over the relationship. author: Bruno Škvorc (@Swader), Cicada (@CicadaNCR), Steven Pineda (@steven2308), Stevan Bogosavljevic (@stevyhacker), Jan Turk (@ThunderDeliverer) discussions-to: https://ethereum-magicians.org/t/eip-6059-parent-governed-nestable-non-fungible-tokens/11914 -status: Last Call -last-call-deadline: 2023-04-07 +status: Final type: Standards Track category: ERC created: 2022-11-15 @@ -76,13 +75,10 @@ interface IERC6059 /* is ERC165 */ { * @param ownerAddress Address of the owner of the token. If the owner is another token, then the address MUST be * the one of the parent token's collection smart contract. If the owner is externally owned account, the address * MUST be the address of this account - * @param isNft A boolean value signifying whether the token is owned by another token (`true`) or by an externally - * owned account (`false`) */ struct DirectOwner { uint256 tokenId; address ownerAddress; - bool isNft; } /** @@ -443,7 +439,7 @@ Based on the desired state transitions, the values of these parameters have to b 2. **Abandon child token**\ ![Abandon child token](../assets/eip-6059/img/eip-6059-abandon-child.png) 3. **Unnest child token**\ -![Unnest child token](../assets/eip-6059/img/eip-6059-unnest-child.png)\ +![Unnest child token](../assets/eip-6059/img/eip-6059-unnest-child.png) 4. **Transfer the child token to an EOA or an `ERC721Receiver`**\ ![Transfer child token to EOA](../assets/eip-6059/img/eip-6059-transfer-child-to-eoa.png) 5. **Transfer the child token into a new parent token**\ @@ -475,7 +471,7 @@ See [`NestableToken.sol`](../assets/eip-6059/contracts/NestableToken.sol). The same security considerations as with [ERC-721](./eip-721.md) apply: hidden logic may be present in any of the functions, including burn, add child, accept child, and more. -Since the current owner of the token is allowed to manage the token, there is a possibility that when selling the parent token, the seller might remove a child token post-factum. This is a risk that is inherent to the design of this standard. Marketplaces should take this into account and provide a way to verify the expected child tokens are present when the parent token is being sold or to guard against such a malicious behaviour in another way. +Since the current owner of the token is allowed to manage the token, there is a possibility that after the parent token is listed for sale, the seller might remove a child token just before before the sale and thus the buyer would not receive the expected child token. This is a risk that is inherent to the design of this standard. Marketplaces should take this into account and provide a way to verify the expected child tokens are present when the parent token is being sold or to guard against such a malicious behaviour in another way. Caution is advised when dealing with non-audited contracts. diff --git a/EIPS/eip-6065.md b/EIPS/eip-6065.md index 52373c3f8ad78..fcb237a52c204 100644 --- a/EIPS/eip-6065.md +++ b/EIPS/eip-6065.md @@ -1,9 +1,9 @@ --- eip: 6065 title: Real Estate Token -description: An interface for real estate NFTs that extends EIP-721 +description: An interface for real estate NFTs that extends ERC-721 author: Alex (@Alex-Klasma), Ben Fusek (@bfusek), Daniel Fallon-Cyr (@dfalloncyr) -discussions-to: https://ethereum-magicians.org/t/eip-6065-real-estate-token/11936 +discussions-to: https://ethereum-magicians.org/t/updated-eip-6065-real-estate-token/11936 status: Draft type: Standards Track category: ERC @@ -13,13 +13,13 @@ requires: 721 ## Abstract -This proposal introduces an open structure for physical real estate and property to exist on the blockchain. This standard builds off of [EIP-721](./eip-721.md), adding important functionality necessary for representing real world assets such as real estate. The three objectives this standard aims to meet are: universal transferability of the NFT, private property rights attached to the NFT, and atomic transfer of property rights with the transfer of the NFT. The token contains a hashed operating agreement component, the ability to transfer legal ownership of the property, a payment function, and a repossession function. In addition to the token component, there are legal requirements that have to be met, which are discussed in the specification. +This proposal introduces an open structure for physical real estate and property to exist on the blockchain. This standard builds off of [ERC-721](./eip-721.md), adding important functionality necessary for representing real world assets such as real estate. The three objectives this standard aims to meet are: universal transferability of the NFT, private property rights attached to the NFT, and atomic transfer of property rights with the transfer of the NFT. The token contains a hash of the operating agreement detailing the NFT holder’s legal right to the property, unique identifiers for the property, a debt value and foreclosure status, and a manager address. ## Motivation -Real estate is the largest asset class in the world. By tokenizing real estate, barriers to entry are lowered, transaction costs are minimized, information asymmetry is reduced, ownership structures become more malleable, and a new building block for innovation is formed. However, in order to tokenize this asset class, a common standard is needed that accounts for its real world particularities while remaining flexible enough to adapt to various jurisdictions and regulatory environments. +Real estate is the largest asset class in the world. By tokenizing real estate, barriers to entry are lowered, transaction costs are minimized, information asymmetry is reduced, ownership structures become more malleable, and a new building block for innovation is formed. However, in order to tokenize this asset class, a common standard is needed that accounts for its real world particularities while remaining flexible enough to adapt to various jurisdictions and regulatory environments. -Ethereum tokens involving real world assets are notoriously tricky. This is because Ethereum tokens exist on-chain, while real estate exists off-chain. As such, the two are subject to entirely different consensus environments. For Ethereum tokens, consensus is reached through a formalized process of distributed validators. When a purely-digital NFT is transferred, the new owner has a cryptographic guarantee of ownership. For real estate, consensus is supported by legal contracts, property law, and enforced by the court system. With existing asset-backed EIP-721 tokens, a transfer of the token to another individual does not necessarily have any impact on the legal ownership of the physical asset. +Ethereum tokens involving real world assets are notoriously tricky. This is because Ethereum tokens exist on-chain, while real estate exists off-chain. As such, the two are subject to entirely different consensus environments. For Ethereum tokens, consensus is reached through a formalized process of distributed validators. When a purely-digital NFT is transferred, the new owner has a cryptographic guarantee of ownership. For real estate, consensus is supported by legal contracts, property law, and enforced by the court system. With existing asset-backed ERC-721 tokens, a transfer of the token to another individual does not necessarily have any impact on the legal ownership of the physical asset. This standard attempts to solve the real world reconciliation issue, enabling real estate NFTs to function seamlessly on-chain, just like their purely-digital counterparts. @@ -27,288 +27,77 @@ This standard attempts to solve the real world reconciliation issue, enabling re 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. -In order to meet the above objectives and create an open standard for on-chain property ownership we have created a token structure that builds on the EIP-721 standard and coupled that with a set of legal requirements broad enough to adapt to various jurisdictions and regulatory environments. +In order to meet the above objectives and create an open standard for on-chain property ownership we have created a token structure that builds on the widely-used ERC-721 standard. ### Token Components: -1. Inherits EIP-721 - Allows for backwards compatibility with the most widely accepted NFT token standard. -2. Transferable Administrator of physical asset - Ability for NFT owner to initiate a transfer of the legal owner of the physical asset. -3. Hashed operating agreement - Immutable legal agreement between Administrator and NFT owner that requires both parties to accept any proposed changes before they are made. -4. Payment function - Ability for NFT Administrator to request payment for financing any payments made by the Administrator on behalf of the NFT owner (e.g. property taxes). -5. Repossession function - Ability for Administrator to repossess the asset to ensure legally required payments are made whole (e.g. unpaid property taxes). - -### Token Legal Requirements (Outlined in Hashed Operating Agreement): - -1. Property held by the Administrator is self-contained, transferable, and bankruptcy remote. -2. Transfer of property rights are atomic with the digital transfer of the NFT. -3. The NFT owner Ethereum address has the right to change the Administrator (i.e. legal owner) of the property. -4. The operating agreement for the legal entity which holds the property must be hashed to the NFT and cannot be changed without mutual approval from both the NFT owner and the Administrator. -5. The operating agreement must contain the right for the NFT owner to hold, occupy, rent, alter, resell, or transfer the property. -6. The Administrator has no usage right to the property and may not collateralize, use, or otherwise encumber the property attached to the NFT. -7. The Administrator is the sole legal owner of the property, and responsible for facilitating mandatory filings and payments for the property. -8. The Administrator is entitled to limited liability with regard to the property and has a right to require insurance on the property. -9. Failure of the NFT owner to make required payments for the property (e.g. property taxes) triggers the Administrator’s right to repossess the property in order to make required payments. +1. Inherits ERC-721 - Allows for backwards compatibility with the most widely accepted NFT token standard. +2. operatingAgreementHashOf - immutable hash of the legal agreement detailing the right to ownership and conditions of use with regard to the property +3. Property Unique Identifiers - legal description (from physical deed), street address, GIS coordinates, parcel/tax ID, legal owning entity (on deed) +4. debtOf - readable debt value, currency, and foreclosure status of the NFT +5. managerOf - readable Ethereum address with managing control of property ### Interfaces -We rely on the EIP-721 NFT token standard for all transfer and approval logic. All transfer and approval functions are inherited from this token standard without changes. This allows an NFT under this standard to become interoperable with preexisting NFT exchanges and services, however some care must be taken. Please refer to the `Backwards Compatibility` section. - -#### Administrator - -``` -/// @dev This event emits when a change of NFT Administrator is proposed. -/// Note that contracts can be init'ed with changes proposed without this event emitted. -event AdministratorChangeInit(uint256 indexed _tokenId, address indexed _owner, address indexed _from, address indexed _to, string _extradata); - -/// @dev This event emits when a change of NFT Administrator is canceled. -/// When an EIP-721 transfer event emits, any proposed Administrator changes should be nulled and this event should also emit -event AdministratorChangeCanceled(uint256 indexed _tokenId, address indexed _owner, address indexed _from, address indexed _to, string _extradata); - -/// @dev This event emits when a change of NFT Administrator is accepted. The new Administrator MUST accept this change for this event to emit. -/// This event MUST emit on any change, however, contracts can be init'ed with Administrators set and without emitting events. -event AdministratorChangeAccept(uint256 indexed _tokenId, address indexed _owner, address indexed _from, address indexed _to, string _extradata); - -/// @dev MUST emit if cancelAdministratorAccept is called successfully -event AdministratorChangeAcceptCanceled(uint256 indexed _tokenId, address indexed _owner, address indexed _from, address indexed _to, string _extradata); - -/// @dev MUST emit if finishAdministratorChange is called successfully -event AdministratorChangeFinish(uint256 indexed _tokenId, address indexed _owner, address indexed _from, address indexed _to, string _extradata); - -/// @notice query current Administrator of an NFT -/// @dev NFTs assigned to zero address are considered invalid, and queries about them do throw. -/// @param _tokenId The identifier for an NFT -/// @return The address of the Administrator of the NFT -function administratorOf(uint256 _tokenId) external view returns (address); - -// STEP 1: Owner propose Administrator change, possible to cancel - -/// @notice propose a change of an Administrator for an NFT, called by ownerOf(NFT) -/// @dev Throws unless msg.sender is the current ownerOf this NFT. -/// @param _tokenId The identifier for an NFT -/// @param _to The newly proposed Administrator of an NFT, if _to == address(0), -/// this can be interpreted as _to == msg.sender == ownerOf, and they want to self-custody. -/// @param _extradata An optional field for metadata -function initAdministratorChange(uint256 _tokenId, address _to, string calldata _extradata) external; - -/// @notice query current proposed Administrator of an NFT -/// @dev NFTs assigned to zero addresses are considered invalid, and queries -/// about them do throw. On a EIP-721 transfer event emit, any proposed Administrator should be set to address(0) -/// @param _tokenId The identifier for an NFT -function proposedAdministratorOf(uint256 _tokenId) external view returns (address); - -/// @notice ownerOf(NFT) can cancel Administrator change. After a period of time, you might allow anyone (or old administrator) -/// to cancel the change, as this blocks payment delinquency -> repossess logic. You can call this function as long as -/// Administrator change was not accepted/finalized by new Administrator -/// @dev throw if tokenId doesn't have an Administrator change. -/// also implement logic for who/when can Administrator change be canceled -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function cancelAdministratorChange(uint256 _tokenId, string calldata _extradata) external; - -// STEP 2: New Administrator, accept Administrator change, possible to cancel - -/// @notice new Administrator accept a change of Administrator of an NFT -/// @dev Throws unless msg.sender is proposedAdministratorOf this NFT. you may clear proposedAdministratorOf data -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function acceptAdministratorChange(uint256 _tokenId, string calldata _extradata) external; - -/// @notice once the Administrator change is accepted as a new Administrator, this function needs to return their address -/// @dev on an EIP-721 transfer, this address remains, as the transfer is in progress. -/// @param _tokenId The identifier for an NFT -function pendingAdministratorOf(uint256 _tokenId) external view returns(address); - -/// @notice allow a cancellation of the processing/pending Administrator change -/// @dev determine who is allowed to cancel this change, up to implementor, -/// msg.sender should be pendingAdministratorOf in most cases, but perhaps can be canceled by anyone after a period of time -/// throw if there's no pendingAdministratorOf -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function cancelAdministratorChangeAccept(uint256 _tokenId, string calldata _extradata) external; - -// STEP 3: Finalize Administrator change, cannot cancel. This occurs after the "real world legal steps" to change Administrator have taken place off-chain. - -/// @notice finalize the change in Administrator of the NFT -/// @dev throws if msg.sender is not pendingAdministratorOf. now administratorOf(NFT) will return the new Administrator address, you may clear pendingAdministratorOf() -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function finishAdministratorChange(uint256 _tokenId, string calldata _extradata) external; -``` - -#### Operating Agreement Updates +This EIP inherits the ERC-721 NFT token standard for all transfer and approval logic. All transfer and approval functions are inherited from this token standard without changes. Additionally, this EIP also inherits the ERC-721 Metadata standards for name, symbol, and metadata URI lookup. This allows an NFT under this EIP to become interoperable with preexisting NFT exchanges and services, however, some care must be taken. Please refer to [Backwards Compatibility](#backwards-compatibility) and [Security Considerations](#security-considerations). -``` -/// @dev emit this event if there's a successful call of initOperatingAgreementChange -/// @param _proposer is the msg.sender of the init -event OperatingAgreementChangeInit(uint256 indexed _tokenId, address indexed _proposer, string _updatedAgreementHash, string _extradata); - -/// @dev emit this event if there's a successful call of cancelOperatingAgreementChange -/// also emit this if there is a owner proposed change but the owner transfers to new owner -/// also emit if there is a Administrator proposed change, but the Administrator transfers to a new owner -event OperatingAgreementChangeCancel(uint256 indexed _tokenId, address indexed _proposer, string _extradata); - -// @dev emit if there's a successful call of finishOperatingAgreementChange -// @param _proposer is the msg.sender of the init -// @param _agreer is the msg.sender of the finishOperatingAgreementChange -// @param _updatedAgreementHash must be == the _updatedAgreementHash from the OperatingAgreementChangeInit event -event OperatingAgreementChangeFinish(uint256 indexed _tokenId, address indexed _proposer, address indexed _agreer, string _updatedAgreementHash, string _extradata); - -/// @notice query the current operating agreement, this is recommended to be an IPFS link -/// or some other URL or reference. see best practices for NFT metadata. -/// @dev if tokenId doesn't exist, throw -/// @param _tokenId The identifier for an NFT -/// @returns some string, likely to an external resource as a legal document is very expensive to store on-chain -function operatingAgreementOf(uint256 _tokenId) external view returns(string); - -/// @notice propose a change to the operating agreement -/// @dev throw is msg.sender is not ownerOf(NFT) or is not administratorOf(NFT). update needs to be accepted by the other party -/// (owner if Administrator proposed, Administrator if owner proposed) -/// @param _tokenId The identifier for an NFT -/// @param _updatedAgreementHash Is the proposed new agreement -/// @param _extradata An optional field for metadata -function initOperatingAgreementChange(uint256 _tokenId, string calldata _updatedAgreementHash, string calldata _extradata) external; - -/// @notice view a pending change for _tokenId -/// @dev if _tokenId doesn't exist then throw, if there is not an update proposed then throw -/// also note that if the Administrator has made a proposal, but the Administrator is changed to a new Administrator (finished change) -/// then any update proposals should be nulled -/// also note that is the owner has made a proposal, but the owner changes (EIP-721 transfer), then any update proposals should be nulled as well -/// @param _tokenId The identifier for an NFT -/// @returns _proposer is either the Administrator or owner who proposed the update -/// @returns _updatedAgreementHash is the proposed agreement to be update -function pendingOperatingAgreementOf(uint256 _tokenId) external view returns(address _proposer, string _updatedAgreementHash); - -/// @notice allow proposer to cancel agreement -/// @dev throw if tokenId doesn't exist, or if there is no proposal for this agreement, or if msg.sender was not the proposer of the change -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function cancelOperatingAgreementChange(uint256 _tokenId, string calldata _extradata) external; - -/// @notice accept a change to the operating agreement -/// @dev msg.sender must be ownerOf(NFT) if AdministratorOf(NFT) proposed change, OR must be AdministratorOf(NFT) if ownerOf(NFT) proposed change, ELSE throw -/// also throw if _updatedAgreementHash does not match the originally proposed agreement -/// @param _tokenId The identifier for an NFT -/// @param _updatedAgreementHash hash that MUST match the prior submitted change suggestion hash to confirm the agreement -/// @param _extradata An optional field for metadata -function finishOperatingAgreementChange(uint256 _tokenId, string calldata _updatedAgreementHash, string calldata _extradata) external; -``` - -#### Payments - -``` -/// @dev emit this event when initPayment is called successfully -event PaymentInit(uint256 indexed _tokenId, address indexed _administrator, address indexed _paymentToken, uint256 _amount, bool _decreaseAmtOwed, uint256 _oldestTimestamp, string _extradata); - -/// @dev This event emits when a finishPayment is successful. The payment MUST be completed for this event to emit, and -/// this event MUST emit if the payment is completed and funds are transferred from msg.sender address -event PaymentFinish(uint256 indexed _tokenId, address indexed _administrator, address indexed _paymentToken, uint256 _amount, string _extradata); - -/// @notice Administrator assess payment on the NFT owner -/// @dev throw if msg.sender is not the current administratorOf(_tokenId), you may store payments by token to be paid, and you may sum the values of all other -/// prior unpaid payments. store the timestamp of the oldest unpaid payment for this payment type, if this is a new token with no prior payments -/// then store block.timestamp for this payment, this will be used for delinquent payments -/// if payments are _decreaseAmtOwed, the total amount owed can never be negative, if this will happen null payment storage for this _paymentToken -/// if a new Administrator is adding/decreasing a payment token outstanding by an old Administrator, overwrite the Administrator name in storage -/// you might want to change the timestamp of old Administrator payments to be a new/current timestamp -/// @param _tokenId The identifier for an NFT -/// @param _paymentToken the EIP-20 token address to define the payment -/// @param _amount the amount of EIP-20 token payment due -/// @param _decreaseAmtOwed this decreases any payment by the _amount, this can be used to revise or adjust down any payments, basically adding a negative sign to _amount -/// @param _extradata An optional field for metadata -function initPayment(uint256 _tokenId, address _paymentToken, uint256 _amount, bool _decreaseAmtOwed, string calldata _extradata) external; - -/// @notice query an existing unpaid payment -/// @dev queries about non-existent _tokenId, _token pairings are considered invalid, and queries -/// about them do throw. This can include already completed payments (where the blockchain reference is deleted) -/// @param _tokenId The identifier for an NFT -/// @param _paymentToken the EIP-20 token address to define the payment -/// @returns _amount the amount of _paymentToken that needs to be paid to fulfill payment -/// @returns _receiver is the Administrator of the specific tokenId, who will receive payment -/// @returns _timestamp of the oldest non-completed payment in this _paymentToken -function pendingPaymentOf(uint256 _tokenId, address _paymentToken) external view returns (uint256 _amount, address _receiver, uint256 _timestamp); - -/// @notice NFT owner make payment that was invoiced by Administrator -/// @dev do NOT throw if msg.sender isn't ownerOf(_tokenId), anyone can fulfill a payment if they desire -/// allow msg.sender to make a partial payment for an amount, if _amount > total payments outstanding, then pay their total, do not pay extra -/// also note the Administrator the payment is supposed to go to, we recommend ignoring payments to an old Administrator, or throwing -/// @param _tokenId The identifier for an NFT -/// @param _paymentToken The EIP-20 token address to define the payment -/// @param _amount The amount user desires to pay -/// @param _extradata An optional field for metadata -function finishPayment(uint256 _tokenId, address _paymentToken, uint256 _amount, string calldata _extradata) external; - -/// @notice query if a payment is delinquent, a payment considered to be delinquent is defined by implementor -/// it's recommended that a payment cannot be delinquent if there is a proposedAdministratorOf || pendingAdministratorOf -/// if payments are delinquent, then the underlying physical asset is liable to be repossessed -/// we recommend ignoring payments to an old Administrator in a delinquency determination -/// @dev queries about non-existent payments are considered invalid and queries about them do throw. -/// this can include already completed payments (where the blockchain reference is deleted) -/// @param _tokenId The identifier for an NFT -/// @param _paymentToken The EIP-20 token address to define the payment -/// @returns false if there is no delinquent payment for this payment id, EIP-20 payment token, true if there is -function paymentIsDelinquent(uint256 _tokenId, address _paymentToken) external view returns (bool); -``` -#### Repossess/Foreclosure +#### Solidity Interface ``` -/// @dev this event emits when a initRepossess is successful, only emit if initRepossess is successful -event RepossessInit(uint256 indexed _tokenId, address indexed _administrator, address _token, string _extradata); - -/// @dev this event emits when a cancelRepossess is successful, only emit if there is successful canceled repossess -event RepossessCancel(uint256 indexed _tokenId, address indexed _administrator, string _extradata); - -/// @dev this event emits when finishRepossess is successfully called, only emit if there is a successfully finished repossess -/// @param _amount is the amount of ETH paid back to user after a repossess is complete -event RepossessFinish(uint256 indexed _tokenId, address indexed _administrator, uint256 _amount, string _extradata); - -/// @dev this event emits when claimRepossess is called, only emit if it's successfully called -/// @param _amount is the amount of ETH sent to the user -event RepossessClaim(uint256 indexed _tokenId, address indexed _owner, uint256 _amount) - -/// @notice if this function returns true, then the underlying physical asset has been repossessed -/// due to the user not paying required fees for the asset. if true, then the asset only contains -/// the second return value in wei, and this amount of ETH can be withdrawn at any time by ownerOf NFT. -/// @param _tokenId The identifier for an NFT -/// @returns _repossessed true if the asset has finished repossessing, else false -/// @returns _amount, the amount of ETH that was returned after the repossess took place, zero if claimed (below) -function isRepossessed(uint256 _tokenId) external view returns(bool _repossessed, uint256 _amount); - -/// @notice initialize repossess underlying RWA asset that backs NFT if a payment is delinquent -/// @dev paymentIsDelinquent(_tokenId, _token) must return true, else this function reverts. -/// is msg.sender is not the Administrator, this function reverts. -/// if the Administrator is in transfer state, we recommend not letting a repossess happen as it could be malicious -/// (see paymentIsDelinquent logic) -/// however we also recommend forcing Administrator transfers to happen within a certain period of time to prevent griefing -/// and allowing a repossess after a certain time has elapsed without an Administrator accepting the proposed Administrator change -/// @param _tokenId The identifier for an NFT -/// @param _token The payment token that a payment was delinquent -/// @param _extradata An optional field for metadata -function initRepossess(uint256 _tokenId, address _token, string calldata _extradata) external; - -/// @notice view if a payment has a repossess pending on the asset, this will warn any prospective buyer that the asset is in question -/// @dev true if there is a pending repossess, false otherwise, if tokenId doesn't exist, then throw -/// @param _tokenId The identifier for an NFT -/// @returns true if there is a repossess pending, false if not -function pendingRepossess(uint256 _tokenID) external view returns(bool); - -/// @notice cancel a prior initialized repossess, Administrator can cancel for any reason -/// @dev tokenId must have an initialized repossess, and msg.sender must be administratorOf(tokenId) or function reverts -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function cancelRepossess(uint256 _tokenId, string calldata _extradata) external; - -/// @notice finish repossessing underlying physical asset that backs NFT, underlying asset sold/auctioned at fair value -/// and function is payable so that Administrator can send remaining auction proceeds to contract *in ETH* -/// @dev if msg.sender is not the Administrator, this function reverts -/// @param _tokenId The identifier for an NFT -/// @param _extradata An optional field for metadata -function finishRepossess(uint256 _tokenId, string calldata _extradata) external payable; - -/// @notice after an asset is repossessed, ownerOf(NFT) can claim the proceeds of the repossession -/// @dev throw is ownerOf(_tokenId) != msg.sender, otherwise send amount of ETH from finishRepossess() to caller -/// @param _tokenId The identifier for an NFT -function claimRepossess(uint256 _tokenId) external; +pragma solidity ^0.8.13; + +import "forge-std/interfaces/IERC721.sol"; + +interface IERC6065 is IERC721 { + + // This event MUST emit if the asset is ever foreclosed. + event Foreclosed(uint256 id); + + /* + Next getter functions return immutable data for NFT. You may implement in a struct like: + + struct EIP6065Immutable { + string legal_description_of_property; + string street_address; + string geo_json; + string parcel_id; + string legal_owner; + bytes32 operating_agreement_hash; + } + + and store that in a mapping, however this specific storage method is left to the implementor. + */ + function legalDescriptionOf(uint256 _id) external view returns (string memory); + function addressOf(uint256 _id) external view returns (string memory); + function geoJsonOf(uint256 _id) external view returns (string memory); + function parcelIdOf(uint256 _id) external view returns (string memory); + function legalOwnerOf(uint256 _id) external view returns (string memory); + function operatingAgreementHashOf(uint256 _id) external view returns (bytes32); + + /* + Next getter function returns the debt denomination token of the NFT, the amount of debt (negative debt == credit), and if the underlying + asset backing the NFT has been foreclosed on. This should be utilized specifically for off-chain debt and required payments on the RWA asset. + It's recommended that administrators only use a single token type to denominate the debt. It's unrealistic to require integrating smart + contracts to implement possibly unbounded tokens denominating the off-chain debt of an asset. + + If the foreclosed status == true, then the RWA asset can be seen as severed from the NFT. The NFT is now "unbacked" by the RWA. + + You may implement in a struct like: + + struct EIP6065Mutable { + address debt_token; + int256 debt_amt; + bool foreclosed; + } + + and store that in a mapping, however this specific storage method is left to the implementor. + */ + function debtOf(uint256 _id) external view returns (address debtToken, int256 debtAmt, bool foreclosed); + + // Get the managerOf an NFT. The manager can have additional rights to the NFT or RWA on or off-chain. + function managerOf(uint256 _id) external view returns (address); +} ``` ## Rationale @@ -317,9 +106,7 @@ function claimRepossess(uint256 _tokenId) external; Real world assets operate in messy, non-deterministic environments. Because of this, validating the true state of an asset can be murky, expensive, or time-consuming. For example, in the U.S., change of property ownership is usually recorded at the County Recorder’s office, sometimes using pen and paper. It would be infeasible to continuously update this manual record every time an NFT transaction occurs on the blockchain. Additionally, since real world property rights are enforced by the court of law, it is essential that property ownership be documented in such a way that courts are able to interpret and enforce ownership if necessary. -For these reasons, it is necessary to have a trusted party tasked with the responsibility of ensuring the state of the on-chain property accurately mirrors its physical counterpart. By having an Administrator for the property who issues a legally-binding digital representation of the physical property, we are able to solve for both the atomic transfer of the property rights with the transfer of the NFT, as well as institute a seamless process for making the necessary payments and filings associated with property ownership. - -There are various ways to meet the legal requirements of this standard, especially considering different property ownership laws and regulations between various jurisdictions. Therefore, we do not prescribe a specific legal structure. However, an example structure implemented by Klasma Inc. for property tokenization in the U.S. is provided in the [Reference Implementation](#reference-implementation). +For these reasons, it is necessary to have a trusted party tasked with the responsibility of ensuring the state of the on-chain property NFT accurately mirrors its physical counterpart. By having an Administrator for the property who issues a legally-binding digital representation of the physical property, we are able to solve for both the atomic transfer of the property rights with the transfer of the NFT, as well as institute a seamless process for making the necessary payments and filings associated with property ownership. This is made possible by eliminating the change in legal ownership each time the NFT changes hands. An example Administrator legal structure implemented by Klasma Inc. for property tokenization in the U.S. is provided in the [Reference Implementation](#reference-implementation). While a token that implements this standard must have a legal entity to conduct the off-chain dealings for the property, this implementation is not mandatory. ### Guiding Objectives @@ -329,94 +116,93 @@ We have designed this EIP to achieve three primary objectives necessary for crea A key aspect to private property is the right to transfer ownership to any legal person or entity that has the capacity to own that property. Therefore, an NFT representation of physical property should maintain that universal freedom of transfer. -#### 2. All rights associated with property ownership are maintained - -The rights associated with private property ownership are the right to hold, occupy, rent, alter, resell, or transfer the property. It is essential that these same rights are maintained in an NFT representation of real estate. +#### 2. All rights associated with property ownership are able to be maintained and guaranteed by the NFT -#### 3. Property rights are transferred atomically with the transfer of the NFT. +The rights associated with private property ownership are the right to hold, occupy, rent, alter, resell, or transfer the property. It is essential that these same rights are able to be maintained and enforced with an NFT representation of real estate. -Token ownership on any blockchain is atomic with the transfer of the digital token. To ensure the digital representation of a physical property is able to fully integrate the benefits of blockchain technology, it is essential the rights associated with the property are passed atomically with the transfer of the digital token representation. For this reason, the legal ownership of the property must be packaged in such a way that allows for the atomic transfer of rights with the transfer of the digital token. +#### 3. Property rights are transferred atomically with the transfer of the NFT -This EIP proposes a way to mesh the transfers of off-chain assets (in a legal sense) with on-chain Ethereum blockchain transfers and state-transitions. The following section specifies the technological and legal requirements needed to accomplish this. +Token ownership on any blockchain is atomic with the transfer of the digital token. To ensure the digital representation of a physical property is able to fully integrate the benefits of blockchain technology, it is essential the rights associated with the property are passed atomically with the transfer of the digital token. -### Administrator, Legal Entity, & Administrator Transferability +The following section specifies the technological components required to meet these three objectives. -The Administrator is the legal owner of a singular legal entity special purpose vehicle (SPV) which holds the title to an individual physical property and issues the corresponding NFT. It is the duty of the Administrator to make all necessary filings and payments for the legal entity and corresponding property (e.g. tax filings, property tax payments & required utility payments). In addition to ensuring the property is in good standing with the government, the Administrator is tasked with ensuring the rightful occupancy of the home, signing documents on behalf of the NFT owner when necessary, and posting up-to-date information regarding the condition of the property to the NFT. +### operatingAgreementHashOf -Within the token components exists a function to transfer the Administrator of the asset. Any owner of the physical property NFT can transfer legal ownership of the asset by calling this function. This action kicks off the pen and paper process whereby the Administrator changes ownership of the legal entity. This process also allows the NFT owner to bridge the asset off-chain by transferring ownership of the entity to themselves and taking legal ownership of the title to the property. +An immutable hash of the legal document issued by the legal entity that owns the property. The agreement is unique and contains the rights, terms, and conditions for the specific property represented by the NFT. The hash of the agreement attached to the NFT must be immutable to ensure the legitimacy and enforceability of these rights in the future for integrators or transferees. Upon transfer of the NFT, these legal rights are immediately enforceable by the new owner. For changes to the legal structure or rights and conditions with regard to the property the original token must be burned and a new token with the new hash must be minted. -Trusted roles are antithetical to crypto. Ideally, the Administrator role eventually becomes obsolete. However, currently, this function is essential to providing enforceable property rights to the NFT owner. There are various avenues to explore for making the role of Administrators trust-minimized, including reputation systems and financial/game theory incentives, but they are outside the scope of this standard. +### Property Unique Identifiers -### Hashed Operating Agreement +The following unique identifiers of the property are contained within the NFT and are immutable: -The hashed operating agreement is a legal document issued by the Administrator that contains the rights to the physical property, as well as terms and conditions. This document is hashed to the NFT to ensure the immutability of these rights. In order to make changes to this contract, either the Administrator or NFT owner must submit a change request via the legal entity and it must be approved by the corresponding side. Upon transfer of the NFT, these legal rights are transferred to the new owner. +`legalDescriptionOf`: written description of the property taken from the physical property deed +`addressOf`: street address of the property +`geoJsonOf`: the GeoJSON format of the property’s geospatial coordinates +`parcelIdOf`: ID number used to identify the property by the local authority +`legalOwnerOf`: the legal entity that is named on the verifiable physical deed -As this standard is adopted and developed further, we anticipate a collection of particular operating requirements to become common across different Administrators and asset types. These requirements will be componentized into referenceable hashes that can be easily understood and verified when interacting with a digital representation of a property. +These unique identifiers ensure the physical property in question is clear and identifiable. These strings must be immutable to make certain that the identity of the property can not be changed in the future. This is necessary to provide confidence in the NFT holder in the event a dispute about the property arises. -### Payment Function +These identifiers, especially `legalOwnerOf`, allow for individuals to verify off-chain ownership and legitimacy of the legal agreement. These verification checks could be integrated with something like Chainlink functions in the future to be simplified and automatic. -Payments are a necessary part of owning real estate. Owners must pay for property taxes, basic utilities, and other required costs. Because the Administrator is the legal owner of the entity that holds the title to the property, it is the Administrator’s responsibility to make any and all required payments. Administrators will issue all anticipated fees and payments to the NFT owner using the payment function. Owners are then able to make the necessary payments for the property directly through their NFT. Administrators are strongly encouraged to submit any bills or invoices in “paper form” using the `_extradata` field and attach a link to a PDF or other documentation, as well as group payments by time period to ensure simplicity for the owners. +### debtOf -### Repossession Function +A readable value of debt and denoted currency that is accrued to the property. A positive balance signifies a debt against the property, while a negative balance signifies a credit. -If the payments mentioned in the previous section go unpaid, the property is at risk of having silent liens placed against it or in extreme circumstances, being repossessed by the state. In order to ensure Administrators are able to provide reliable and clean transfers of a property, the Administrator must have the means to make payments without being subject to payment liability risk. If the Administrator makes payments for a property on behalf of a NFT owner and then needs to be reimbursed, the Administrator is exposed to risk of financial loss in the event the NFT owner sells the NFT without reimbursing the Administrator. For this reason all payments need to be funded directly from the NFT owner through the smart contract. +The `debtOf` function also returns the boolean foreclosure status of the asset represented by the NFT. A true result indicates the associated property is no longer backing the NFT, a false result indicates the associated property is still backing the NFT. -If the NFT owner fails to pay the invoice, the Administrator has the right to repossess the property, sell it in order to generate the required funds for payment, and then replace the physical asset backing the original NFT with the remaining funds from the sale in ETH. Any proceeds from the repossession/foreclosure sale must be converted to ETH in order to be returned to the original owner. It is up to the implementer to determine what criterion for payment delinquency triggers a repossession. +There are no standard requirements for how these values are updated as those details will be decided by the implementor. This EIP does however standardize how these values are indicated and read for simplicity of integration. -## Backwards Compatibility +### managerOf -Although this standard is backwards compatible with EIP-721, there are important security and implementation considerations to take into account before any smart contract integration. These considerations primarily surround the built-in payment function of the token. While treating NFTs under this standard as identical to EIP-721 NFTs is technically possible, we recommend considering additional logic to support fee payment and recognize any unpaid obligations. +A readable Ethereum address that can be granted a right to action on the property without being the underlying owner of the NFT. -Specific applications that incorporate these NFTs can suffer losses from incorrect implementation. See `Integration Checks and Considerations` for more details. +This function allows the token to be owned by one Ethereum address while granting particular rights to another. This enables protocols and smart contracts to own the underlying asset, such as a lending protocol, but still allow another Ethereum address, such as a depositor, to action on the NFT via other integrations, for example the Administrator management portal. The standard does not require a specific implementation of the manager role, only the value is required. In many instances the managerOf value will be the same as the owning address of the NFT. -## Reference Implementation +## Backwards Compatibility -This section details an implementation of the legal standard by Klasma Inc. specifically for property tokenization in the U.S. in the 2022 regulatory environment. +This EIP is backwards compatible with ERC-721. However, it is important to note that there are potential implementation considerations to take into account before any smart contract integration. See [Security Considerations](#security-considerations) for more details. -![Sample Corporate Structure Image](../assets/eip-6065/corporate-structure.png) +## Reference Implementation -The Klasma Inc. legal structure for U.S. real estate and property is as follows: +Klasma Labs offers a work in progress reference implementation (*see pull request above*). The technical implementation includes the following additional components for reference, this implementation is not required. -* Klasma Inc., a parent company and property Administrator, owns a bankruptcy remote LLC for each individual property they act as Administrator for. -* This LLC owns a DAO LLC, which issues the NFT for the property and holds the title and deed to the property. -* This structure enables the following three outcomes: - 1. Homeowners are shielded from any financial stress or bankruptcy their physical asset Administrator encounters. In the event of an Administrator bankruptcy or dissolution the owner of the NFT is entitled to transfer of the DAO LLC, or the sale and distribution of proceeds from the property. - 2. Transfer of the rights to the property are atomic with the transfer of the NFT. The rights to the property are issued and controlled by a DAO LLC, a legally recognized entity that can be algorithmically managed, (e.g. managed by smart contract). This enables the enforceable rights to the physical property to be passed digitally with the transfer of the NFT without having to update the legal owner of the property with each transfer. - 3. Each real estate NFT is universally transferable. The DAO LLC will be taxed as a corporation to limit any pass-through tax benefits that could put the token at risk of being deemed a security in the U.S. The DAO LLC will always operate in a tax neutral or negative status thus not requiring any tax payments to be made on behalf of the LLC. Additionally, it is important to note that the NFT associated with a particular property merely provides a means of digital transfer for the private ownership rights to the property. Therefore, there is no action by the Administrator that could increase the value of the asset, ensuring the NFT is deemed a commodity, the same as any other home or property. +Summary of Klasma Inc's implementation: -## Security Considerations +* NFT burn and mint function +* Immutable NFT data (unique identifiers and operating agreement hash) +* Simple debt tracking by Administrator +* Blocklist function to freeze asset held by fraudulent addresses (NOTE: to be implemented in the future) +* Simple foreclosure logic initiated by Administrator +* `managerOf` function implementation to chain this call to other supported smart contracts -This standard attempts to strike a balance between the crypto ethos of “code is law” and the understanding that a stolen home with no possibility of recourse for the owner is a non-starter for almost all users. On a risk-adjusted basis, the benefits of using a decentralized finance protocol are unlikely to offset the possibility of a catastrophic loss of the property via a protocol exploit. Losing your home in a DeFi hack is unacceptable. +### Legal Structure Implementation -On the other hand, granting the Administrator full control of the NFTs through backdoor access to the smart contracts is also unacceptable. Given the complex nature of many exploits, requiring Administrators to act as judge and jury in defining a hack and determining the rightful owner is sub-optimal. The following sections define how private key loss and protocol hacks are addressed, as well as provide important checks and considerations for smart contract integrations, particularly for lending protocols. +This section explains the legal structure and implementation Klasma Inc. employs as an Administrator of this token. The structure detailed below is specific to property tokenization in the U.S. in the 2023 regulatory environment. -### Private Key Loss and Theft +This section details an implementation of the legal standard by Klasma Inc. specifically for property tokenization in the U.S. in the 2022 regulatory environment. -While DeFi protocol hacks leave an immutable trail on-chain, private key hacks do not. A private key transferring an asset legitimately or maliciously looks identical in any blockchain analysis. As such, Administrators should not be tasked with arbitrating or remedying private key hacks or loss. +![corporate-structure|690x388](../assets/eip-6065/corporate-structure.png) -Secure private key storage is a fundamental requirement to be able to interact with the crypto ecosystem. Users unable to do so should either pay for an NFT custody solution, or refrain from owning digital assets altogether. -### Protocol Hacks and Exploits +The Klasma Inc. legal structure for U.S. this token is as follows: -A protocol hack or exploit occurs within the confines of a smart contract integration and thus is reviewable on-chain, via specific transaction hashes and block explorer level evidence. A respectable Administrator should lay out their process for classifying and addressing protocol exploits in the Operating Agreement. +* Klasma Inc., a parent company and property Administrator, owns a bankruptcy remote LLC for each individual property they act as Administrator for. +* The bankruptcy remote LLC is the owner and manager of a DAO LLC. The DAO LLC is on the title and deed and issues the corresponding NFT and operating agreement for the property. +* This structure enables the following three outcomes: -To remedy a hack, the Administrator may issue a charge against the NFT to the new owner of the NFT for the full market value of the underlying asset via the `initPayment()` function. If the new owner does nothing, the Administrator will repossess this asset and return it to the original owner or protocol. To contest the classification of a hack, the new owner may start the `initAdministratorChange()` workflow to change the Administrator or self-custody the asset. Since all Administrators must be legal entities, the original owner may now bring this case to the traditional legal system if they desire. + 1. Homeowners are shielded from any financial stress or bankruptcy their physical asset Administrator encounters. In the event of an Administrator bankruptcy or dissolution the owner of the NFT is entitled to transfer of the DAO LLC, or the sale and distribution of proceeds from the property. + 2. Transfer of the rights to the property are atomic with the transfer of the NFT. The NFT represents a right to claim the asset and have the title transferred to the NFT owner, as well as the right to use the asset. This ensures the rights to the physical property are passed digitally with the transfer of the NFT, without having to update the legal owner of the property after each transfer. -Through leveraging the existing payment and Administrator change flow, a safety mechanism against protocol exploits is provided without inserting a smart contract backdoor. In the event that an exploit is contestable (e.g. a hack, versus an economic exploit, versus a well timed trade), this system provides an avenue for the new asset owner to make her case through the jurisdictional legal system. +Security note: In the event of a private key hack Klasma will not be able to reissue a Home NFT. Klasma home NFT owners who are not confident in their ability to safely store their home NFT will have varying levels of security options (multi-sigs, custodians, etc.). For public, large protocol hacks, Klasma may freeze the assets using the Blocklist function and reissue the home NFTs to the original owners. -### Integration Checks and Considerations +## Security Considerations -The following are checks and recommendations for protocols integrating NFTs under this standard. These are of particular relevance to applications which lend against any asset utilizing this standard. +The following are checks and recommendations for protocols integrating NFTs under this standard. These are of particular relevance to applications which lend against any asset utilizing this standard. -* Lending protocol integrators are recommended to pay any payments on behalf of their NFT depositors by calling `finishPayment()` and adding this balance to their users outstanding debt position. This avoids repossession by the Administrator, which may lead to loans becoming undercollateralized or undefined behavior in the protocol. -* Before accepting NFT deposits, a protocol integrator should check any `pendingPaymentOf()` the asset. A protocol may decide not accept an asset until all payments are cleared, or mark down the fair market value of the asset. -* Protocol integrators should also check if the function `paymentIsDelinquent()` returns `true` for any payments. If so, they should reject the asset as it is at risk of being repossessed. -* Protocol integrators are recommended to implement a time-delay before performing irreversible actions. This is to protect against future to-be-assessed payments that may occur if a hacked NFT is deposited into the protocol. - * For example, a protocol should implement a waiting period before issuing stablecoins as part of a collateralized mortgage on the NFT. If another DeFi protocol can be hacked, and a hacker can immediately run to a different protocol to receive an 80% LTV loan on the asset, it is likely that this second protocol will take a loss when this hack is resolved by the Administrator billing the NFT via `initPayment()` for it’s entire market value. Now this second protocol is stuck with valueless collateral, but already issued a 80% LTV loan. - * Because there is no standardized waiting period, DeFi protocols should specifically whitelist Administrator addresses for deposit into their protocols. Administrators may have specialized descriptor smart contracts to give an upper bound on wait-time recommendations. For example, Administrator A could state that one should wait 7 days for any of their assets, and after 7 days it is guaranteed that there will be no `initPayments()` for any prior malicious activity or hacks of the asset, and the asset is now safe to accept as collateral as its value is simply value(asset) without any possible liabilities. -* It is recommended that protocol integrators expose `initAdministratorChange()` logic in their smart contracts in order to change the Administrator in the future, if necessary. -Protocol integrators may decide to only accept assets with certain operating agreement hashes, viewable by calling `operatingAgreementOf()`. This ensures that all legal clauses and terms in this off-chain contract have been reviewed prior. -* More advanced protocol integrators may decide to expose `initOperatingAgreementChange()` functionality, in case a better legal agreement standard is designed in order to upgrade their assets to the best possible protections. +* Protocol integrators are recommended to check that the unique identifiers for the property and the hash of the operating agreement are immutable for the specific NFTs they wish to integrate. For correct implementation of this standard these values must be immutable to ensure legitimacy for future transferees. +* Protocol integrators are recommended to check the debtOf value for an accurate representation of the value of this token. +* Protocol integrators are recommended to check the foreclose status to ensure this token is still backed by the asset it was originally tied to. +* For extra risk mitigation protocol integrators can implement a time-delay before performing irreversible actions. This is to protect against potential asset freezes if a hacked NFT is deposited into the protocol. Asset freezes are non-mandatory and subject to the implementation of the asset Administrator. ## Copyright diff --git a/EIPS/eip-6105.md b/EIPS/eip-6105.md index 6fe6771ca7031..a30dbff950372 100644 --- a/EIPS/eip-6105.md +++ b/EIPS/eip-6105.md @@ -4,7 +4,8 @@ title: No Intermediary NFT Trading Protocol description: Adds a marketplace functionality with more diverse royalty schemes to ERC-721 author: 5660-eth (@5660-eth), Silvere Heraudeau (@lambdalf-dev), Martin McConnell (@offgridgecko), Abu , Wizard Wang discussions-to: https://ethereum-magicians.org/t/eip6105-no-intermediary-nft-trading-protocol/12171 -status: Review +status: Last Call +last-call-deadline: 2023-04-04 type: Standards Track category: ERC created: 2022-12-02 diff --git a/EIPS/eip-6120.md b/EIPS/eip-6120.md index 913b8c113b870..d5b0d13c32f46 100644 --- a/EIPS/eip-6120.md +++ b/EIPS/eip-6120.md @@ -19,9 +19,9 @@ Application and router contracts have to use the approve-then-call pattern which The Universal Token Router (UTR) separates the token allowance from the application logic, allowing any token to be spent in a contract call the same way with ETH, without approving any other application contracts. -Tokens approved to the Universal Token Router can only be spent in transactions directly signed by their owner, and they have clearly visible token transfer behavior, including token types (ETH, [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md)), `amountInMax`, `amountOutMin`, and `recipient`. +Tokens approved to the Universal Token Router can only be spent in transactions directly signed by their owner, and they have clearly visible token transfer behavior, including token types (ETH, [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md)), `amountIn`, `amountOutMin`, and `recipient`. -The Universal Token Router contract is counter-factually deployed using [EIP-1014](./eip-1014.md) at `0x6120245B546F2F0ce439186cAae8181007366120` across all EVM-compatible networks, so new token contracts can pre-configure it as a trusted spender and no approval transaction is necessary. +The Universal Token Router contract is counter-factually deployed using [EIP-1014](./eip-1014.md) at a single address across all EVM-compatible networks, so new token contracts can pre-configure it as a trusted spender, and no approval transaction is necessary for their interactive usage. ## Motivation @@ -30,7 +30,7 @@ When users approve their tokens to a contract, they trust that: * it only spends the tokens with their permission (from `msg.sender` or `ecrecover`) * it does not use `delegatecall` (e.g. upgradable proxies) -By performing the same security conditions above, the Universal Token Router can be shared by all applications, saving `(n-1)*m*l` approval transactions for old tokens and **ALL** approval transactions for new tokens. +By ensuring the same security conditions above, the Universal Token Router can be shared by all applications, saving `(n-1)*m*l` approval transactions for old tokens and **ALL** approval transactions for new tokens. Before this EIP, when users sign transactions to spend their approved tokens, they trust the front-end code entirely to construct those transactions honestly and correctly. This puts them at great risk of phishing sites. @@ -38,8 +38,8 @@ The Universal Token Router function arguments can act as a manifest for users wh Most of the application contracts are already compatible with the Universal Token Router and can use it to have the following benefits: -* Safely share the user token allowance with all other applications. -* Freely update their helper contract logic. +* Securely share the user token allowance with all other applications. +* Update their peripheral contracts as often as they want. * Save development and security audit costs on router contracts. The Universal Token Router promotes the **security-by-result** model in decentralized applications instead of **security-by-process**. By directly querying token balance change for output verification, user transactions can be secured even when interacting with erroneous or malicious contracts. With non-token results, application helper contracts can provide additional result-checking functions for UTR's output verification. @@ -55,8 +55,16 @@ interface IUniversalTokenRouter { function exec( Output[] memory outputs, Action[] memory actions - ) external payable; - ... + ) payable; + + function pay( + address sender, + address recipient, + uint eip, + address token, + uint id, + uint amount + ); } ``` @@ -76,10 +84,10 @@ struct Output { Token balances of the `recipient` address are recorded at the beginning and the end of the `exec` function for each item in `outputs`. Transaction will revert with `INSUFFICIENT_OUTPUT_AMOUNT` if any of the balance changes are less than its `amountOutMin`. -A special id `ID_721_ALL` is reserved for ERC-721, which can be used in output actions to verify the total amount of all ids owned by the `recipient` address. +A special id `ERC_721_BALANCE` is reserved for ERC-721, which can be used in output actions to verify the total amount of all ids owned by the `recipient` address. ```solidity -ID_721_ALL = keccak256('UniversalTokenRouter.ID_721_ALL') +ERC_721_BALANCE = keccak256('UniversalTokenRouter.ERC_721_BALANCE') ``` ### Action @@ -89,18 +97,11 @@ ID_721_ALL = keccak256('UniversalTokenRouter.ID_721_ALL') ```solidity struct Action { Input[] inputs; - uint flags; address code; // contract code address bytes data; // contract input data } ``` -`flags` can take any number of the following bit flags: - -* `0x1 = ACTION_IGNORE_ERROR`: any contract call failure will be ignored. -* `0x2 = ACTION_RECORD_CALL_RESULT`: the contract call result will be recorded in a `bytes` for subsequent actions. -* `0x4 = ACTION_INJECT_CALL_RESULT`: the last call result `bytes` recorded will be injected to the last empty `bytes` param of the contract function `data`. - ### Input `Input` defines the input token to transfer or prepare before the action contract is executed. @@ -112,52 +113,26 @@ struct Input { uint eip; // token standard: 0 for ETH or EIP number address token; // token contract address uint id; // token id for ERC-721 and ERC-1155 - uint amountInMax; - uint amountSource; // where to get the actual amountIn + uint amountIn; } ``` -`mode` can takes one of the following values: - -* `0 = TRANSFER_FROM_SENDER`: the token will be transferred from `msg.sender` to `recipient`. -* `1 = TRANSFER_FROM_ROUTER`: the token will be transferred from `this` UTR contract to `recipient`. -* `2 = TRANSFER_CALL_VALUE`: the token amount will be passed to the action as the call `value`. -* `4 = IN_TX_PAYMENT`: the token will be allowed to be spent in this transaction by calling `UTR.pay`. -* `8 = ALLOWANCE_BRIDGE`: the token will be transferred from `msg.sender` to `this` UTR contract and is allowed to be spent in this transaction. - -`amountSource` defines how the actual token `amountIn` is acquired from: - -* `0 = AMOUNT_EXACT`: the `amountInMax` value is used. -* `1 = AMOUNT_ALL`: the entire balance of the sender (`msg.sender` or `this`) is used. -* otherwise, extracts the `uint256` value starting from the `amountSource`-th byte of the last recorded call result `bytes`. This value is unpredictable if there's no prior action with the `ACTION_RECORD_CALL_RESULT` flag. +`mode` takes one of the following values: -`amountIn` MUST NOT be greater than `amountInMax`, otherwise, the transaction will be reverted with `EXCESSIVE_INPUT_AMOUNT`. +* `PAYMENT = 0`: the token can be transferred from `msg.sender` to the `recipient` by calling `UTR.pay` from anywhere in the same transaction. +* `TRANSFER = 1`: the token is transferred directly from `msg.sender` to `recipient`. +* `CALL_VALUE = 2`: the `ETH` amount will be passed to the action as the call `value`. -#### Payment In Callback +#### Payment -`IN_TX_PAYMENT` is used for application contracts that use the transfer-in-callback pattern. (E.g. flashloan contracts, Uniswap/v3-core, etc.) - -```solidity -interface IUniversalTokenRouter { - ... - - function pay( - address sender, - address recipient, - uint eip, - address token, - uint id, - uint amount - ) external; -} -``` +`PAYMENT` is the recommended mode for application contracts that use the transfer-in-callback pattern. E.g., flashloan contracts, Uniswap/v3-core, etc. -For each `Input` with `IN_TX_PAYMENT` mode, at most `amountIn` of the token is allowed to be transferred from `msg.sender` to the `recipient` by calling `UTR.pay` from anywhere in the same transaction. +For each `Input` with `PAYMENT` mode, at most `amountIn` of the token can be transferred from `msg.sender` to the `recipient` by calling `UTR.pay` from anywhere in the same transaction. ``` UTR | - | IN_TX_PAYMENT + | PAYMENT | (payments pended for UTR.pay) | | Application Contracts @@ -172,23 +147,32 @@ UTR.pay <----------------------- (call) | END ``` -#### Allowance Bridge +Token's allowance and `PAYMENT` are essentially different as: -`ALLOWANCE_BRIDGE` is the compatibility mode for application contracts that require token approval directly from `msg.sender`. +* allowance: allow a specific `spender` to transfer the token to anyone at any time. +* `PAYMENT`: allow anyone to transfer the token to a specific `recipient` only in that transaction. -For each `Input` with `ALLOWANCE_BRIDGE` mode: +#### Discard Payment -* an `amountIn` of token is transferred from `msg.sender` to `this` UTR contract. -* the `recipient` address is allowed to spend the token from `this` UTR contract. +Sometimes, it's useful to discard the payment instead of performing the transfer, for example, when the application contract wants to burn its own token from `msg.sender`. The following function can be used to verify the payment to the caller's address and discard a portion of it. -Before the end of the `exec` function: +```solidity +interface IUniversalTokenRouter { + ... -* all allowances are revoked. -* all left-over tokens are transferred back to `msg.sender`. + function discard( + address sender, + uint eip, + address token, + uint id, + uint amount + ); +} +``` -### Usage Samples +### Usage Examples -#### `UniswapRouter.swapExactTokensForTokens` +#### Uniswap V2 Router Legacy function: @@ -215,15 +199,13 @@ UniversalTokenRouter.exec([{ amountOutMin, }], [{ inputs: [{ - mode: TRANSFER_FROM_SENDER, + mode: TRANSFER, recipient: UniswapV2Library.pairFor(factory, path[0], path[1]), eip: 20, token: path[0], id: 0, - amountInMax: amountIn, - amountSource: AMOUNT_EXACT, + amountIn: amountIn, }], - flags: 0, code: UniswapV2Helper01.address, data: encodeFunctionData("swapExactTokensForTokens", [ amountIn, @@ -235,130 +217,7 @@ UniversalTokenRouter.exec([{ }]) ``` -#### `UniswapRouter.swapTokensForExactTokens` - -Legacy function: - -```solidity -UniswapV2Router01.swapTokensForExactTokens( - uint amountOut, - uint amountInMax, - address[] calldata path, - address to, - uint deadline -) -``` - -This function accepts the `uint[] amounts` as the last `bytes` param, decode and pass to the internal function `_swap` of `UniswapV2Helper01`. - -```solidity -UniswapV2Helper01.swap( - address[] calldata path, - address to, - bytes calldata amountsBytes -) external { - uint[] memory amounts = abi.decode(amountsBytes, (uint[])); - _swap(amounts, path, to); -} -``` - -This transaction is signed by users to execute the swap instead of the legacy function: - -```javascript -UniversalTokenRouter.exec([{ - eip: 20, - token: path[path.length-1], - id: 0, - amountOutMin: amountOut, - recipient: to, -}], [{ - inputs: [], - flags: ACTION_RECORD_CALL_RESULT, - code: UniswapV2Helper01.address, - data: encodeFunctionData("getAmountIns", [amountOut, path]), -}, { - inputs: [{ - mode: TRANSFER_FROM_SENDER, - eip: 20, - token: path[0], - id: 0, - amountInMax, - amountSource: 32*3, // first item of getAmountIns result array - recipient: UniswapV2Library.pairFor(factory, path[0], path[1]), - }], - flags: ACTION_INJECT_CALL_RESULT, - code: UniswapV2Helper01.address, - data: encodeFunctionData("swap", [path, to, '0x']), -}]) -``` - -The result of `getAmountIns` is recorded and injected into the empty `bytes`, save the transaction from calculating twice with the same data. - -#### `UniswapRouter.addLiquidity` - -Legacy function: - -```solidity -UniswapV2Router01.addLiquidity( - address tokenA, - address tokenB, - uint amountADesired, - uint amountBDesired, - uint amountAMin, - uint amountBMin, - address to, - uint deadline -) -``` - -This transaction is signed by users instead of the legacy function: - -```javascript -UniversalTokenRouter.exec([{ - eip: 20, - token: UniswapV2Library.pairFor(factory, tokenA, tokenB), - id: 0, - amountOutMin: 1, // just enough to verify the correct recipient - recipient: to, -}], [{ - inputs: [], - flags: ACTION_RECORD_CALL_RESULT, - code: UniswapV2Helper01.address, - data: encodeFunctionData("_addLiquidity", [ - tokenA, - tokenB, - amountADesired, - amountBDesired, - amountAMin, - amountBMin, - ]), -}, { - inputs: [{ - mode: TRANSFER_FROM_SENDER, - eip: 20, - token: tokenA, - id: 0, - amountSource: 32, // first item of _addLiquidity results - amountInMax: amountADesired, - recipient: UniswapV2Library.pairFor(factory, tokenA, tokenB), - }, { - mode: TRANSFER_FROM_SENDER, - eip: 20, - token: tokenB, - id: 0, - amountSource: 64, // second item of _addLiquidity results - amountInMax: amountBDesired, - recipient: UniswapV2Library.pairFor(factory, tokenA, tokenB), - }], - flags: 0, - code: UniswapV2Library.pairFor(factory, tokenA, tokenB), - data: encodeFunctionData("mint", [to]), -}]) -``` - -The output token verification is not performed by Uniswap's legacy function and can be skipped. But it SHOULD always be done for the `UniversalTokenRouter` so user can see and review the token behavior instead of blindly trust the front-end code. - -#### Uniswap V3 `SwapRouter` +#### Uniswap V3 Router Legacy router contract: @@ -391,19 +250,12 @@ contract SwapHelper { ) internal { ... // pull payment - UTR.pay( - payer, - recipient, - 20, // EIP - token, - 0, // id - value - ); + UTR.pay(payer, recipient, 20, token, 0, value); } } ``` -This transaction is signed by users to execute the `exactInput` functionality using `IN_TX_PAYMENT` mode: +This transaction is signed by users to execute the `exactInput` functionality using `PAYMENT` mode: ```javascript UniversalTokenRouter.exec([{ @@ -414,56 +266,21 @@ UniversalTokenRouter.exec([{ recipient: to, }], [{ inputs: [{ - mode: IN_TX_PAYMENT, + mode: PAYMENT, eip: 20, token: tokenIn, id: 0, - amountSource: AMOUNT_EXACT, - amountInMax: amountIn, + amountIn: amountIn, recipient: pool.address, }], - flags: 0, code: SwapHelper.address, data: encodeFunctionData("exactInput", [...]), }]) ``` -This transaction is signed by users to execute the `mint` functionality using `ALLOWANCE_BRIDGE` mode: - -```javascript -UniversalTokenRouter.exec([{ - eip: 721, - token: PositionManager.address, - id: ID_721_ALL, - amountOutMin: 1, // expect one more liquidity NFT - recipient: to, -}], [{ - inputs: [{ - mode: ALLOWANCE_BRIDGE, - eip: 20, - token: tokenA, - id: 0, - amountSource: AMOUNT_EXACT, - amountInMax: amountADesired, - recipient: PositionManager.address, - }, { - mode: ALLOWANCE_BRIDGE, - eip: 20, - token: tokenB, - id: 0, - amountSource: AMOUNT_EXACT, - amountInMax: amountBDesired, - recipient: PositionManager.address, - }], - flags: 0, - code: PositionManager.address, - data: encodeFunctionData("mint", [...]), -}]) -``` - ## Rationale -The `Permit` type signature is not supported since the purpose of the Universal Token Router is to eliminate all `approve` signatures for new tokens, and *most* for old tokens. +The `Permit` type signature is not supported since the purpose of the Universal Token Router is to eliminate all interactive `approve` signatures for new tokens, and *most* for old tokens. ## Backwards Compatibility @@ -471,60 +288,19 @@ The `Permit` type signature is not supported since the purpose of the Universal Old token contracts (ERC-20, ERC-721 and ERC-1155) require approval for the Universal Token Router once for each account. -New token contracts can pre-configure the Universal Token Router as a trusted spender, and no approval transaction is required. +New token contracts can pre-configure the Universal Token Router as a trusted spender, and no approval transaction is required for interactive usage. -### Application Contracts +### Applications -Application contracts that use `msg.sender` as the beneficiary address in their internal storage without any function for ownership transfer are the only cases that are **INCOMPATIBLE** with the UTR. +The only application contracts **INCOMPATIBLE** with the UTR are contracts that use `msg.sender` as the beneficiary address in their internal storage without any function for ownership transfer. -All application contracts that accept `recipient` (or `to`) argument instead of using `msg.sender` as the beneficiary address are compatible with the UTR out of the box. +All application contracts that accept `recipient` (or `to`) argument as the beneficiary address are compatible with the UTR out of the box. -Application contracts that transfer tokens (ERC-20, ERC-721, and ERC-1155) to `msg.sender` can use the `TRANSFER_FROM_ROUTER` input mode to re-direct tokens to another `recipient` address. - -```javascript -// sample code to deposit WETH and transfer them out -UniversalTokenRouter.exec([{ - eip: 20, - token: WETH.address, - id: 0, - amountOutMin: 1, - recipient: SomeRecipient, -}], [{ - inputs: [{ - mode: TRANSFER_CALL_VALUE, - eip: 0, // ETH - token: AddressZero, - id: 0, - amountInMax: 123, - amountSource: AMOUNT_EXACT, - recipient: AddressZero, // pass it as the value for the next output action - }], - flags: 0, - code: WETH.address, - data: encodeFunctionData('deposit', []), // WETH.deposit returns WETH token to the UTR contract -}, { - inputs: [{ - mode: TRANSFER_FROM_ROUTER, // transfer token out from this UTR contract - eip: 20, - token: WETH.address, - id: 0, - amountInMax: 123, - amountSource: AMOUNT_ALL, // entire WETH balance of this UTR contract - recipient: SomeRecipient, - }], - // ... continue to use WETH in SomeRecipient - flags: 0, - code: AddressZero, - data: '0x', -}], {value: 123}) -``` - -Applications can also deploy additional adapter contracts to add a `recipient` to their functions. +Application contracts that transfer tokens (ERC-20, ERC-721, and ERC-1155) to `msg.sender` need additional adapters to add a `recipient` to their functions. ```solidity // sample adapter contract for WETH contract WethAdapter { - address immutable WETH = 0x....; function deposit(address recipient) external payable { IWETH(WETH).deposit(){value: msg.value}; TransferHelper.safeTransfer(WETH, recipient, msg.value); @@ -532,30 +308,22 @@ contract WethAdapter { } ``` +Additional helper and adapter contracts might be needed, but they're mostly peripheral and non-intrusive. They don't hold any tokens or allowances, so they can be frequently updated and have little to no security impact on the core application contracts. + ## Reference Implementation ```solidity contract UniversalTokenRouter is IUniversalTokenRouter { - // values with a single 1-bit are preferred - uint constant TRANSFER_FROM_SENDER = 0; - uint constant TRANSFER_FROM_ROUTER = 1; - uint constant TRANSFER_CALL_VALUE = 2; - uint constant IN_TX_PAYMENT = 4; - uint constant ALLOWANCE_BRIDGE = 8; - - uint constant AMOUNT_EXACT = 0; - uint constant AMOUNT_ALL = 1; + uint constant PAYMENT = 0; + uint constant TRANSFER = 1; + uint constant CALL_VALUE = 2; - uint constant EIP_ETH = 0; + uint constant EIP_ETH = 0; - uint constant ID_721_ALL = uint(keccak256('UniversalTokenRouter.ID_721_ALL')); - - uint constant ACTION_IGNORE_ERROR = 1; - uint constant ACTION_RECORD_CALL_RESULT = 2; - uint constant ACTION_INJECT_CALL_RESULT = 4; + uint constant ERC_721_BALANCE = uint(keccak256('UniversalTokenRouter.ERC_721_BALANCE')); // non-persistent in-transaction pending payments - mapping(bytes32 => uint) s_payments; + mapping(bytes32 => uint) t_payments; // accepting ETH for WETH.withdraw receive() external payable {} @@ -568,95 +336,45 @@ contract UniversalTokenRouter is IUniversalTokenRouter { // track the expected balances before any action is executed for (uint i = 0; i < outputs.length; ++i) { Output memory output = outputs[i]; - uint balance = _balanceOf(output.recipient, output.eip, output.token, output.id); + uint balance = _balanceOf(output); uint expected = output.amountOutMin + balance; - require(expected >= balance, 'UniversalTokenRouter: OVERFLOW'); + require(expected >= balance, 'UniversalTokenRouter: OUTPUT_BALANCE_OVERFLOW'); output.amountOutMin = expected; } - bool dirty = false; + address sender = msg.sender; - bytes memory callResult; for (uint i = 0; i < actions.length; ++i) { Action memory action = actions[i]; uint value; for (uint j = 0; j < action.inputs.length; ++j) { Input memory input = action.inputs[j]; uint mode = input.mode; - address sender = mode == TRANSFER_FROM_ROUTER ? address(this) : msg.sender; - uint amount; - if (input.amountSource == AMOUNT_EXACT) { - amount = input.amountInMax; - } else { - if (input.amountSource == AMOUNT_ALL) { - amount = _balanceOf(sender, input.eip, input.token, input.id); - } else { - amount = _sliceUint(callResult, input.amountSource); - } - require(amount <= input.amountInMax, "UniversalTokenRouter: EXCESSIVE_INPUT_AMOUNT"); - } - if (mode == TRANSFER_CALL_VALUE) { - value = amount; - continue; - } - if (mode == TRANSFER_FROM_SENDER || mode == TRANSFER_FROM_ROUTER) { - _transferToken(sender, input.recipient, input.eip, input.token, input.id, amount); - continue; - } - if (mode == IN_TX_PAYMENT) { - bytes32 key = keccak256(abi.encodePacked(msg.sender, input.recipient, input.eip, input.token, input.id)); - s_payments[key] += amount; // overflow: harmless - dirty = true; - continue; - } - if (mode == ALLOWANCE_BRIDGE) { - _approve(input.recipient, input.eip, input.token, type(uint).max); - _transferToken(msg.sender, address(this), input.eip, input.token, input.id, amount); - dirty = true; + if (mode == PAYMENT) { + bytes32 key = keccak256(abi.encodePacked(sender, input.recipient, input.eip, input.token, input.id)); + t_payments[key] = input.amountIn; + } else if (mode == TRANSFER) { + _transferToken(sender, input.recipient, input.eip, input.token, input.id, input.amountIn); + } else if (mode == CALL_VALUE) { + // require(input.eip == EIP_ETH && input.id == 0, "UniversalTokenRouter: ETH_ONLY"); + value = input.amountIn; } } if (action.data.length > 0) { - if (action.flags & ACTION_INJECT_CALL_RESULT != 0) { - action.data = _concat(action.data, action.data.length, callResult); - } (bool success, bytes memory result) = action.code.call{value: value}(action.data); - if (!success && action.flags & ACTION_IGNORE_ERROR == 0) { + if (!success) { assembly { revert(add(result,32),mload(result)) } } - // delete value; // clear the ETH value after call - if (action.flags & ACTION_RECORD_CALL_RESULT != 0) { - callResult = result; - } } - } - - // verify balance changes - for (uint i = 0; i < outputs.length; ++i) { - Output memory output = outputs[i]; - uint balance = _balanceOf(output.recipient, output.eip, output.token, output.id); - require(balance >= output.amountOutMin, 'UniversalTokenRouter: INSUFFICIENT_OUTPUT_AMOUNT'); - } - - // clear all in-transaction storages - if (dirty) { - for (uint i = 0; i < actions.length; ++i) { - Action memory action = actions[i]; - for (uint j = 0; j < action.inputs.length; ++j) { - Input memory input = action.inputs[j]; - if (input.mode == IN_TX_PAYMENT) { - bytes32 key = keccak256(abi.encodePacked(msg.sender, input.recipient, input.eip, input.token, input.id)); - delete s_payments[key]; - continue; - } - if (input.mode == ALLOWANCE_BRIDGE) { - _approve(input.recipient, input.eip, input.token, 0); - uint balance = _balanceOf(address(this), input.eip, input.token, input.id); - if (balance > 0) { - _transferToken(address(this), msg.sender, input.eip, input.token, input.id, balance); - } - } + // clear all in-transaction storages, allowances and left-overs + for (uint j = 0; j < action.inputs.length; ++j) { + Input memory input = action.inputs[j]; + if (input.mode == PAYMENT) { + // in-transaction storages + bytes32 key = keccak256(abi.encodePacked(sender, input.recipient, input.eip, input.token, input.id)); + delete t_payments[key]; } } } @@ -664,25 +382,54 @@ contract UniversalTokenRouter is IUniversalTokenRouter { // refund any left-over ETH uint leftOver = address(this).balance; if (leftOver > 0) { - TransferHelper.safeTransferETH(msg.sender, leftOver); + TransferHelper.safeTransferETH(sender, leftOver); + } + + // verify balance changes + for (uint i = 0; i < outputs.length; ++i) { + Output memory output = outputs[i]; + uint balance = _balanceOf(output); + // NOTE: output.amountOutMin is reused as `expected` + require(balance >= output.amountOutMin, 'UniversalTokenRouter: INSUFFICIENT_OUTPUT_AMOUNT'); } } } - function pay( + function _reducePayment( address sender, address recipient, uint eip, address token, uint id, uint amount - ) public { + ) internal { unchecked { bytes32 key = keccak256(abi.encodePacked(sender, recipient, eip, token, id)); - require(s_payments[key] >= amount, 'UniversalTokenRouter: INSUFFICIENT_ALLOWANCE'); - s_payments[key] -= amount; - _transferToken(sender, recipient, eip, token, id, amount); + require(t_payments[key] >= amount, 'UniversalTokenRouter: INSUFFICIENT_PAYMENT'); + t_payments[key] -= amount; } } + function pay( + address sender, + address recipient, + uint eip, + address token, + uint id, + uint amount + ) override external { + _reducePayment(sender, recipient, eip, token, id, amount); + _transferToken(sender, recipient, eip, token, id, amount); + } + + function discard( + address sender, + uint eip, + address token, + uint id, + uint amount + ) public override { + _reducePayment(sender, msg.sender, eip, token, id, amount); + } + function _transferToken( address sender, address recipient, @@ -709,135 +456,59 @@ contract UniversalTokenRouter is IUniversalTokenRouter { } } - function _approve( - address recipient, - uint eip, - address token, - uint amount - ) internal { - if (eip == 20) { - TransferHelper.safeApprove(token, recipient, amount); - } else if (eip == 1155) { - IERC1155(token).setApprovalForAll(recipient, amount > 0); - } else if (eip == 721) { - IERC721(token).setApprovalForAll(recipient, amount > 0); - } else { - revert("UniversalTokenRouter: INVALID_EIP"); - } - } - function _balanceOf( - address owner, - uint eip, - address token, - uint id + Output memory output ) internal view returns (uint balance) { + uint eip = output.eip; if (eip == 20) { - return IERC20(token).balanceOf(owner); + return IERC20(output.token).balanceOf(output.recipient); } if (eip == 1155) { - return IERC1155(token).balanceOf(owner, id); + return IERC1155(output.token).balanceOf(output.recipient, output.id); } if (eip == 721) { - if (id == ID_721_ALL) { - return IERC721(token).balanceOf(owner); + if (output.id == ERC_721_BALANCE) { + return IERC721(output.token).balanceOf(output.recipient); } - try IERC721(token).ownerOf(id) returns (address currentOwner) { - return currentOwner == owner ? 1 : 0; + try IERC721(output.token).ownerOf(output.id) returns (address currentOwner) { + return currentOwner == output.recipient ? 1 : 0; } catch { return 0; } } if (eip == EIP_ETH) { - return owner.balance; + return output.recipient.balance; } revert("UniversalTokenRouter: INVALID_EIP"); } - - function _sliceUint(bytes memory bs, uint start) internal pure returns (uint x) { - // require(bs.length >= start + 32, "slicing out of range"); - assembly { - x := mload(add(bs, start)) - } - } - - /// https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol - /// @param length length of the first preBytes - function _concat( - bytes memory preBytes, - uint length, - bytes memory postBytes - ) internal pure returns (bytes memory bothBytes) { - assembly { - // Get a location of some free memory and store it in bothBytes as - // Solidity does for memory variables. - bothBytes := mload(0x40) - - // Store the length of the first bytes array at the beginning of - // the memory for bothBytes. - mstore(bothBytes, length) - - // Maintain a memory counter for the current write location in the - // temp bytes array by adding the 32 bytes for the array length to - // the starting location. - let mc := add(bothBytes, 0x20) - // Stop copying when the memory counter reaches the length of the - // first bytes array. - let end := add(mc, length) - - for { - // Initialize a copy counter to the start of the preBytes data, - // 32 bytes into its memory. - let cc := add(preBytes, 0x20) - } lt(mc, end) { - // Increase both counters by 32 bytes each iteration. - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - // Write the preBytes data into the bothBytes memory 32 bytes - // at a time. - mstore(mc, mload(cc)) - } - - // Add the length of postBytes to the current length of bothBytes - // and store it as the new length in the first 32 bytes of the - // bothBytes memory. - length := mload(postBytes) - mstore(bothBytes, add(length, mload(bothBytes))) - - // Move the memory counter back from a multiple of 0x20 to the - // actual end of the preBytes data. - mc := sub(end, 0x20) - // Stop copying when the memory counter reaches the new combined - // length of the arrays. - end := add(end, length) - - for { - let cc := postBytes - } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - mstore(mc, mload(cc)) - } - - // Update the free-memory pointer by padding our last write location - // to 32 bytes: add 31 bytes to the end of bothBytes to move to the - // next 32 byte block, then round down to the nearest multiple of - // 32. If the sum of the length of the two arrays is zero then add - // one before rounding down to leave a blank 32 bytes (the length block with 0). - // mstore(0x40, and( - // add(add(end, iszero(add(length, mload(preBytes)))), 31), - // not(31) // Round down to the nearest 32 bytes. - // )) - } - } } ``` ## Security Considerations -`ACTION_INJECT_CALL_RESULT` SHOULD only be used for gas optimization, not as trusted conditions. Application contract code MUST always expect arbitruary, malformed or mallicious data can be passed in where the call result `bytes` is injected. +Tokens transferred to the UTR contract will be lost forever, as there is no way to transfer them out. + +ETH must be transferred to the UTR contracts before the value is spent in an action call (using `CALL_VALUE`). This ETH value can be siphoned out of the UTR using a re-entrant call inside an action code or rogue token functions. This exploit will not be possible if users don't transfer more ETH than they will spend in that transaction. + +```solidity +// transfer 100 in, but spend only 60, +// so at most 40 wei can be exploited in this transaction +UniversalTokenRouter.exec([ + ... +], [{ + inputs: [{ + mode: CALL_VALUE, + eip: 20, + token: 0, + id: 0, + amountIn: 60, // spend 60 + recipient: AddressZero, + }], + ... +}], { + value: 100, // transfer 100 in +}) +``` ## Copyright diff --git a/EIPS/eip-6220.md b/EIPS/eip-6220.md index 46d6680415744..b744da0d7c49e 100644 --- a/EIPS/eip-6220.md +++ b/EIPS/eip-6220.md @@ -4,7 +4,7 @@ title: Composable NFTs utilizing Equippable Parts description: An interface for Composable non-fungible tokens through fixed and slot parts equipping. author: Bruno Škvorc (@Swader), Cicada (@CicadaNCR), Steven Pineda (@steven2308), Stevan Bogosavljevic (@stevyhacker), Jan Turk (@ThunderDeliverer) discussions-to: https://ethereum-magicians.org/t/eip-6220-composable-nfts-utilizing-equippable-parts/12289 -status: Review +status: Final type: Standards Track category: ERC created: 2022-12-20 diff --git a/EIPS/eip-6327.md b/EIPS/eip-6327.md new file mode 100644 index 0000000000000..d7e8703f605ae --- /dev/null +++ b/EIPS/eip-6327.md @@ -0,0 +1,316 @@ +--- +eip: 6327 +title: Elastic Signature +description: Use password to sign data as private key +author: George (@JXRow) +discussions-to: https://ethereum-magicians.org/t/eip-6327-elastic-signature-es/12554 +status: Draft +type: Standards Track +category: ERC +created: 2023-01-13 +--- + + +## Abstract + +Elastic signature (ES) aims to sign data with a human friendly secret. The secret will be verified fully on-chain and is not stored anywhere. A user can change the secret as often as they need to. The secret does not have a fixed length. The secret will be like a password, which is a better understood concept than private key. This is specifically true for non-technical users. This EIP defines a smart contract interface to verify and authorize operations with ES. + + +## Motivation + +What would a changeable "private key" enable us? For years, we have been looking for ways to lower on-boarding barrier for users, especially those with less technical experiences. Private key custody solutions seem to provide an user friendly on-boarding experience, but it is vendor dependent and is not decentralized. ES makes a breakthrough with Zero-knowledge technology. Users generate proof of knowing the secret and a smart contract will verify the proof. + +### Use case + +ES is an alternative signing algorithm. It is not an either-or solution to the private key. It is designed to serve as an additional signing mechanism on top of the private key signature. + +- A DeFi app can utilize ES into their transfer fund process. Users will be required to provide their passwords to complete the transaction. This gives an extra protection even if the private key is compromised. +- ES can also be used as a plugin to a smart contract wallet, like Account Abstraction [ERC-4337](./eip-4337.md). A decentralized password is picked instead of the private key. This could lead to a smooth onboarding experiences for new Ethereum Dapp users. + + +## Specification + +Let: + +- `pwdhash` represents the hash of the private secret (password). +- `datahash` represents the hash of an intended transaction data. +- `fullhash` represents the hash of `datahash` and all the well-known variables. +- `expiration` is the timestamp after which the intended transaction expires. +- `allhash` represents the hash of `fullhash` and `pwdhash`. + + +There are three parties involved, Verifier, Requester and Prover. + +- A verifier, + - SHOULD compute `fullhash` from a `datahash`, which is provided by the requester. + - SHOULD derive `pwdhash` for a given address. The address can be an EOA or a smart contract wallet. + - SHOULD verify the proof with the derived `pwdhash`, the computed `fullhash` and a `allhash`, which is submitted by the requester. +- A requester + - SHOULD generate `datahash` and decide an `expiration`. + - SHALL request a verification from the verifier with, + - `proof` and `allhash` which are provided by the prover; + - `datahash`; + - `expiration`. +- A prover + - SHOULD generate the `proof` and `allhash` from, + - `datahash` and `expiration` which are agreed with the requester; + - `nonce` and other well-known variables. + +There are also some requirements. + +- well-known variable SHOULD be available to all parties. + - SHOULD include a `nonce`. + - SHOULD include a `chainid`. + - MAY include any variable that is specific to the verifier. +- public statements SHOULD include, + - one reflecting the `pwdhash`; + - one reflecting the `fullhash`; + - one reflecting the `allhash`. +- The computation of `fullhash` SHOULD be agreed by both the verifier and the prover. +- The computation of `datahash` + +### `IElasticSignature` Interface + +This is the verifier interface. + +```solidity +pragma solidity ^0.8.0; + +interface IElasticSignature { + /** + * Event emitted after user set/reset their password + * @param user - an user's address, for whom the password hash is set. It could be a smart contract wallet address + * or an EOA wallet address. + * @param pwdhash - a password hash + */ + event SetPassword(address indexed user, uint indexed pwdhash); + + /** + * Event emitted after a successful verification performed for an user + * @param user - an user's address, for whom the submitted `proof` is verified. It could be a smart contract wallet + * address or an EOA wallet address. + * @param nonce - a new nonce, which is newly generated to replace the last used nonce. + */ + event Verified(address indexed user, uint indexed nonce); + + /** + * Get `pwdhash` for a user + * @param user - a user's address + * @return - the `pwdhash` for the given address + */ + function pwdhashOf(address user) external view returns (uint); + + /** + * Update an user's `pwdhash` + * @param proof1 - proof generated by the old password + * @param expiration1 - old password signing expiry seconds + * @param allhash1 - allhash generated with the old password + * @param proof2 - proof generated by the new password + * @param pwdhash2 - hash of the new password + * @param expiration2 - new password signing expiry seconds + * @param allhash2 - allhash generated with the new password + */ + function resetPassword( + uint[8] memory proof1, + uint expiration1, + uint allhash1, + uint[8] memory proof2, + uint pwdhash2, + uint expiration2, + uint allhash2 + ) external; + + /** + * Verify a proof for a given user + * It should be invoked by other contracts. The other contracts provide the `datahash`. The `proof` is generated by + * the user. + * @param user - a user's address, for whom the verification will be carried out. + * @param proof - a proof generated by the password + * @param datahash - the data what user signing, this is the hash of the data + * @param expiration - number of seconds from now, after which the proof is expired + * @param allhash - public statement, generated along with the `proof` + */ + function verify( + address user, + uint[8] memory proof, + uint datahash, + uint expiration, + uint allhash + ) external; +} +``` + +`verify` function SHOULD be called by another contract. The other contract SHOULD generate the `datahash` to call this. The function SHOULD verify if the `allhash` is computed correctly and honestly with the password. + + +## Rationale + +The contract will store everyone's `pwdhash`. + +![verifier-contract](../assets/eip-6327/zkpass-1.png) + +The chart below shows ZK circuit logic. + +![circuit-logic](../assets/eip-6327/zkpass-2.png) + +To verify the signature, it needs `proof`, `allhash`, `pwdhash` and `fullhash`. + +![workflow](../assets/eip-6327/zkpass-3.png) + +The prover generates `proof` along with the public outputs. They will send all of them to a third-party requester contract. The requester will generate the `datahash`. It sends `datahash`, `proof`, `allhash`, `expiration` and prover's address to the verifier contract. The contract verifies that the `datahash` is from the prover, which means the withdrawal operation is signed by the prover's password. + + +## Backwards Compatibility + +This EIP is backward compatible with previous work on signature validation since this method is specific to password based signatures and not EOA signatures. + + +## Reference Implementation + +Example implementation of a signing contract: + +```solidity +pragma solidity ^0.8.0; + +import "../interfaces/IElasticSignature.sol"; +import "./verifier.sol"; + +contract ZKPass is IElasticSignature { + Verifier verifier = new Verifier(); + + mapping(address => uint) public pwdhashOf; + + mapping(address => uint) public nonceOf; + + constructor() { + } + + function resetPassword( + uint[8] memory proof1, + uint expiration1, + uint allhash1, + uint[8] memory proof2, + uint pwdhash2, + uint expiration2, + uint allhash2 + ) public override { + uint nonce = nonceOf[msg.sender]; + + if (nonce == 0) { + //init password + + pwdhashOf[msg.sender] = pwdhash2; + nonceOf[msg.sender] = 1; + verify(msg.sender, proof2, 0, expiration2, allhash2); + } else { + //reset password + + // check old pwdhash + verify(msg.sender, proof1, 0, expiration1, allhash1); + + // check new pwdhash + pwdhashOf[msg.sender] = pwdhash2; + verify(msg.sender, proof2, 0, expiration2, allhash2); + } + + emit SetPassword(msg.sender, pwdhash2); + } + + function verify( + address user, + uint[8] memory proof, + uint datahash, + uint expiration, + uint allhash + ) public override { + require( + block.timestamp < expiration, + "ZKPass::verify: expired" + ); + + uint pwdhash = pwdhashOf[user]; + require( + pwdhash != 0, + "ZKPass::verify: user not exist" + ); + + uint nonce = nonceOf[user]; + uint fullhash = uint(keccak256(abi.encodePacked(expiration, block.chainid, nonce, datahash))) / 8; // 256b->254b + require( + verifyProof(proof, pwdhash, fullhash, allhash), + "ZKPass::verify: verify proof fail" + ); + + nonceOf[user] = nonce + 1; + + emit Verified(user, nonce); + } + + /////////// util //////////// + + function verifyProof( + uint[8] memory proof, + uint pwdhash, + uint fullhash, //254b + uint allhash + ) internal view returns (bool) { + return + verifier.verifyProof( + [proof[0], proof[1]], + [[proof[2], proof[3]], [proof[4], proof[5]]], + [proof[6], proof[7]], + [pwdhash, fullhash, allhash] + ); + } +} +``` + +verifier.sol is auto generated by snarkjs, the source code circuit.circom is below + +```javascript +pragma circom 2.0.0; + +include "../../node_modules/circomlib/circuits/poseidon.circom"; + +template Main() { + signal input in[3]; + signal output out[3]; + + component poseidon1 = Poseidon(2); + component poseidon2 = Poseidon(2); + + poseidon1.inputs[0] <== in[0]; //pwd + poseidon1.inputs[1] <== in[1]; //address + out[0] <== poseidon1.out; //pwdhash + + poseidon2.inputs[0] <== poseidon1.out; + poseidon2.inputs[1] <== in[2]; //fullhash + out[1] <== in[2]; //fullhash + out[2] <== poseidon2.out; //allhash +} + +component main = Main(); +``` + + +## Security Considerations + +Since the pwdhash is public, it is possible to be crack the password. We estimate the Poseidon hash rate of RTX3090 would be 100Mhash/s, this is the estimate of crack time: + +8 chars (number) : 1 secs + +8 chars (number + english) : 25 days + +8 chars (number + english + symbol) : 594 days + +12 chars (number) : 10000 secs + +12 chars (number + english) : 1023042 years + +12 chars (number + english + symbol) : 116586246 years + +The crack difficulty of private key is 2^256, the crack difficulty of 40 chars (number + english + symbol) is 92^40, 92^40 > 2^256, so when password is 40 chars , it is more difficult to be crack than private key. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-6357.md b/EIPS/eip-6357.md index 8effb32038064..da8d7614aef36 100644 --- a/EIPS/eip-6357.md +++ b/EIPS/eip-6357.md @@ -4,7 +4,7 @@ title: Single-contract Multi-delegatecall description: Allows an EOA to call multiple functions of a smart contract in a single transaction author: Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/eip-6357-single-contract-multicall/12621 -status: Draft +status: Review type: Standards Track category: ERC created: 2023-01-18 diff --git a/EIPS/eip-6381.md b/EIPS/eip-6381.md index 51d60b8600f91..1fa3003cfd206 100644 --- a/EIPS/eip-6381.md +++ b/EIPS/eip-6381.md @@ -1,30 +1,29 @@ --- eip: 6381 -title: Emotable Extension for Non-Fungible Tokens -description: React to Non-Fungible Tokens using Unicode emojis. +title: Public Non-Fungible Token Emote Repository +description: React to any Non-Fungible Tokens using Unicode emojis. author: Bruno Škvorc (@Swader), Steven Pineda (@steven2308), Stevan Bogosavljevic (@stevyhacker), Jan Turk (@ThunderDeliverer) discussions-to: https://ethereum-magicians.org/t/eip-6381-emotable-extension-for-non-fungible-tokens/12710 -status: Review +status: Last Call +last-call-deadline: 2023-05-02 type: Standards Track category: ERC created: 2023-01-22 -requires: 165, 721 +requires: 165 --- ## Abstract -The Emotable Extension for Non-Fungible Tokens standard extends [ERC-721](./eip-721.md) by allowing NFTs to be emoted at. +The Public Non-Fungible Token Emote Repository standard provides an enhanced interactive utility for [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) by allowing NFTs to be emoted at. -This proposal introduces the ability to react to NFTs using Unicode standardized emoji. +This proposal introduces the ability to react to NFTs using Unicode standardized emoji in a public non-gated repository smart contract that is accessible at the same address in all of the networks. ## Motivation With NFTs being a widespread form of tokens in the Ethereum ecosystem and being used for a variety of use cases, it is time to standardize additional utility for them. Having the ability for anyone to interact with an NFT introduces an interactive aspect to owning an NFT and unlocks feedback-based NFT mechanics. - This ERC introduces new utilities for [ERC-721](./eip-721.md) based tokens in the following areas: - - [Interactivity](#interactivity) - [Feedback based evolution](#feedback-based-evolution) - [Valuation](#valuation) @@ -48,23 +47,25 @@ Current NFT market heavily relies on previous values the token has been sold for The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. ```solidity -/// @title EIP-6381 Emotable Extension for Non-Fungible Tokens +/// @title ERC-6381 Emotable Extension for Non-Fungible Tokens /// @dev See https://eips.ethereum.org/EIPS/eip-6381 -/// @dev Note: the ERC-165 identifier for this interface is 0x580d1840. +/// @dev Note: the ERC-165 identifier for this interface is 0x08eb97a6. pragma solidity ^0.8.16; -interface IEmotable is IERC165 { +interface IERC6381 /*is IERC165*/ { /** * @notice Used to notify listeners that the token with the specified ID has been emoted to or that the reaction has been revoked. - * @dev The event SHOULD only be emitted if the state of the emote is changed. + * @dev The event MUST only be emitted if the state of the emote is changed. * @param emoter Address of the account that emoted or revoked the reaction to the token + * @param collection Address of the collection smart contract containing the token being emoted to or having the reaction revoked * @param tokenId ID of the token * @param emoji Unicode identifier of the emoji * @param on Boolean value signifying whether the token was emoted to (`true`) or if the reaction has been revoked (`false`) */ event Emoted( address indexed emoter, + address indexed collection, uint256 indexed tokenId, bytes4 emoji, bool on @@ -72,27 +73,60 @@ interface IEmotable is IERC165 { /** * @notice Used to get the number of emotes for a specific emoji on a token. + * @param collection Address of the collection containing the token being checked for emoji count * @param tokenId ID of the token to check for emoji count * @param emoji Unicode identifier of the emoji * @return Number of emotes with the emoji on the token */ function emoteCountOf( + address collection, uint256 tokenId, bytes4 emoji ) external view returns (uint256); + /** + * @notice Used to get the information on whether the specified address has used a specific emoji on a specific + * token. + * @param emoter Address of the account we are checking for a reaction to a token + * @param collection Address of the collection smart contract containing the token being checked for emoji reaction + * @param tokenId ID of the token being checked for emoji reaction + * @param emoji The ASCII emoji code being checked for reaction + * @return A boolean value indicating whether the `emoter` has used the `emoji` on the token (`true`) or not + * (`false`) + */ + function hasEmoterUsedEmote( + address emoter, + address collection, + uint256 tokenId, + bytes4 emoji + ) external view returns (bool); + /** * @notice Used to emote or undo an emote on a token. * @dev Does nothing if attempting to set a pre-existent state. - * @dev When the state is being changed, the Emoted event MUST be emitted. + * @dev MUST emit the `Emoted` event is the state of the emote is changed. + * @param collection Address of the collection containing the token being checked for emoji count * @param tokenId ID of the token being emoted * @param emoji Unicode identifier of the emoji * @param state Boolean value signifying whether to emote (`true`) or undo (`false`) emote */ - function emote(uint256 tokenId, bytes4 emoji, bool state) external; + function emote( + address collection, + uint256 tokenId, + bytes4 emoji, + bool state + ) external; } ``` +### Pre-determined address of the Emotable repository + +The address of the Emotable repository smart contract is designed to resemble the function it serves. It starts with `0x311073` which is the abstract representation of `EMOTE`. The address is: + +``` +0x311073569e12f7770719497cd3b3aa2db0a0c3d9 +``` + ## Rationale Designing the proposal, we considered the following questions: @@ -101,14 +135,17 @@ Designing the proposal, we considered the following questions: The proposal only accepts the Unicode identifier which is a `bytes4` value. This means that while we encourage implementers to add the reactions using standardized emojis, the values not covered by the Unicode standard can be used for custom emotes. The only drawback being that the interface displaying the reactions will have to know what kind of image to render and such additions will probably be limited to the interface or marketplace in which they were made. 2. **Should the proposal use emojis to relay the impressions of NFTs or some other method?**\ The impressions could have been done using user-supplied strings or numeric values, yet we decided to use emojis since they are a well established mean of relaying impressions and emotions. +3. **Should the proposal establish an emotable extension or a common-good repository?**\ +Initially we set out to create an emotable extension to be used with any ERC-721 compilant tokens. However, we realized that the proposal would be more useful if it was a common-good repository of emotable tokens. This way, the tokens that can be reacted to are not only the new ones but also the old ones that have been around since before the proposal.\ +In line with this decision, we decided to calculate a deterministic address for the repository smart contract. This way, the repository can be used by any NFT collection without the need to search for the address on the given chain. ## Backwards Compatibility -The Emotable token standard is fully compatible with [ERC-721](./eip-721.md) and with the robust tooling available for implementations of ERC-721 as well as with the existing ERC-721 infrastructure. +The Emote repository standard is fully compatible with [ERC-721](./eip-721.md) and with the robust tooling available for implementations of ERC-721 as well as with the existing ERC-721 infrastructure. ## Test Cases -Tests are included in [`emotable.ts`](../assets/eip-6381/test/emotable.ts). +Tests are included in [`emotableRepository.ts`](../assets/eip-6381/test/emotableRepository.ts). To run them in terminal, you can use the following commands: @@ -120,7 +157,7 @@ npx hardhat test ## Reference Implementation -See [`Emotable.sol`](../assets/eip-6381/contracts/Emotable.sol). +See [`EmotableRepository.sol`](../assets/eip-6381/contracts/EmotableRepository.sol). ## Security Considerations diff --git a/EIPS/eip-6404.md b/EIPS/eip-6404.md index 152376cc9cb08..c989a7129753a 100644 --- a/EIPS/eip-6404.md +++ b/EIPS/eip-6404.md @@ -847,11 +847,11 @@ The following representations of the consensus `ExecutionPayload`'s `transaction | Transaction | Native | Baseline | SSZ Union | Normalized | Base + Snappy | Union + Snappy | Norm + Snappy | | - | :-: | :-: | :-: | :-: | :-: | :-: | :-: | -| Legacy | RLP | 106 bytes | 210 bytes | 272 bytes | 109 bytes | 138 bytes | 196 bytes | -| [EIP-155](./eip-155.md) | RLP | 108 bytes | 210 bytes | 272 bytes | 111 bytes | 139 bytes | 195 bytes | -| [EIP-2930](./eip-2930.md) | RLP | 111 bytes | 215 bytes | 272 bytes | 114 bytes | 145 bytes | 195 bytes | +| Legacy | RLP | 106 bytes | 210 bytes | 272 bytes | 109 bytes | 140 bytes | 196 bytes | +| [EIP-155](./eip-155.md) | RLP | 108 bytes | 210 bytes | 272 bytes | 111 bytes | 138 bytes | 195 bytes | +| [EIP-2930](./eip-2930.md) | RLP | 111 bytes | 215 bytes | 272 bytes | 114 bytes | 144 bytes | 195 bytes | | [EIP-1559](./eip-1559.md) | RLP | 117 bytes | 247 bytes | 272 bytes | 117 bytes | 148 bytes | 195 bytes | -| [EIP-4844](./eip-4844.md) | SSZ | 315 bytes | 315 bytes (\*) | 340 bytes | 186 bytes | 186 bytes | 235 bytes | +| [EIP-4844](./eip-4844.md) | SSZ | 315 bytes | 315 bytes (\*) | 341 bytes | 186 bytes | 186 bytes | 238 bytes | - [Baseline](../assets/eip-6404/tests/create_transactions.py) - [SSZ Union](../assets/eip-6404/tests/union/convert_transactions.py) diff --git a/EIPS/eip-6454.md b/EIPS/eip-6454.md index 24df3a0fc8b51..5819f176b3d06 100644 --- a/EIPS/eip-6454.md +++ b/EIPS/eip-6454.md @@ -1,7 +1,7 @@ --- eip: 6454 -title: Minimalistic Non-Transferable NFTs -description: An interface for Non-Transferable Non-Fungible Tokens extension allowing for tokens to be non-transferable. +title: Minimal Transferable NFT detection interface +description: A minimal extension to identify the transferability of Non-Fungible Tokens. author: Bruno Škvorc (@Swader), Francesco Sullo (@sullof), Steven Pineda (@steven2308), Stevan Bogosavljevic (@stevyhacker), Jan Turk (@ThunderDeliverer) discussions-to: https://ethereum-magicians.org/t/minimalistic-transferable-interface/12517 status: Last Call @@ -14,9 +14,9 @@ requires: 165, 721 ## Abstract -The Minimalistic Non-Transferable interface for Non-Fungible Tokens standard extends [ERC-721](./eip-721.md) by preventing NFTs from being transferred. +The Minimalistic Transferable interface for Non-Fungible Tokens standard extends [ERC-721](./eip-721.md) by introducing the ability to identify whether an NFT can be transferred or not. -This proposal introduces the ability to prevent a token from being transferred from their owner, making them bound to the externally owned account, smart contract or token that owns it. +This proposal introduces the ability to prevent a token from being transferred from their owner, making them bound to the externally owned account, abstracted account, smart contract or token that owns it. ## Motivation @@ -54,13 +54,24 @@ pragma solidity ^0.8.16; interface IERC6454 /* is IERC165 */ { /** - * @notice Used to check whether the given token is non-transferable or not. - * @dev If this function returns `true`, the transfer of the token MUST revert execution + * @notice Used to check whether the given token is transferable or not. + * @dev If this function returns `false`, the transfer of the token MUST revert execution * @dev If the tokenId does not exist, this method MUST revert execution * @param tokenId ID of the token being checked - * @return Boolean value indicating whether the given token is non-transferable + * @return Boolean value indicating whether the given token is transferable */ - function isNonTransferable(uint256 tokenId) external view returns (bool); + function isTransferable(uint256 tokenId) external view returns (bool); + + /** + * @notice Used to check whether the given token is transferable or not based on source and destination address. + * @dev If this function returns `false`, the transfer of the token MUST revert execution + * @dev If the tokenId does not exist, this method MUST revert execution + * @param tokenId ID of the token being checked + * @param from Address from which the token is being transferred + * @param to Address to which the token is being transferred + * @return Boolean value indicating whether the given token is transferable + */ + function isTransferable(uint256 tokenId, address from, address to) external view returns (bool); } ``` @@ -68,17 +79,20 @@ interface IERC6454 /* is IERC165 */ { Designing the proposal, we considered the following questions: -1. **Should we propose another Non-Transferable NFT proposal given the existence of existing ones, some even final, and how does this proposal compare to them?**\ +1. **Should we propose another (Non-)Transferable NFT proposal given the existence of existing ones, some even final, and how does this proposal compare to them?**\ This proposal aims to provide the minimum necessary specification for the implementation of non-transferable NFTs, we feel none of the existing proposals have presented the minimal required interface. Unlike other proposals that address the same issue, this proposal requires fewer methods in its specification, providing a more streamlined solution. 2. **Why is there no event marking the token as Non-Transferable in this interface?**\ The token can become non-transferable either at its creation, after being marked as non-transferable, or after a certain condition is met. This means that some cases of tokens becoming non-transferable cannot emit an event, such as if the token becoming non-transferable is determined by a block number. Requiring an event to be emitted upon the token becoming non-transferable is not feasible in such cases. -3. **Should the non-transferable state management function be included in this proposal?**\ - A function that marks a token as non-transferable or releases the binding is referred to as the non-transferable management function. To maintain the objective of designing an agnostic non-transferable proposal, we have decided not to specify the non-transferable management function. This allows for a variety of custom implementations that require the tokens to be non-transferable. -4. **Why should this be an EIP if it only contains one method?**\ - One could argue that since the core of this proposal is to only prevent ERC-721 tokens to be transferred, this could be done by overriding the transfer function. While this is true, the only way to assure that the token is non-transferable before the smart contract execution, is for it to have the non-transferable interface.\ - This also allows for smart contract to validate that the token is non-transferable and not attempt transferring it as this would result in failed transactions and wasted gas. -5. **Why does this proposal use `isNotTransferable` instead of `isTransferable`?**\ - ERC-721 tokens are usually transferable, but this interface focuses on the use case where NFTs may not be transferable. The method name was chosen to reflect this. +3. **Should the transferability state management function be included in this proposal?**\ + A function that marks a token as non-transferable or releases the binding is referred to as the transferability management function. To maintain the objective of designing an agnostic minimal transferable proposal, we have decided not to specify the transferability management function. This allows for a variety of custom implementations that require the tokens to be non-transferable. +4. **Why should this be an EIP if it only contains two methods?**\ + One could argue that since the core of this proposal is to only prevent ERC-721 tokens to be transferred, this could be done by overriding the transfer function. While this is true, the only way to assure that the token is non-transferable before the smart contract execution, is for it to have the transferable interface.\ + This also allows for smart contract to validate whether the token is not transferable and not attempt transferring it as this would result in failed transactions and wasted gas. +5. **Why does this proposal contain two methods with the same name?**\ + Both methods defined in this proposal address the same issue, but in different ways. The first method is used to check whether the token is transferable or not in general, while the second method is used to check whether the token is conditionally transferable or not based on the source and destination addresses. The second method is useful in cases where the transferability of the token is dependent on the source and destination addresses. +6. **What is the best user experience for frontend?**\ + The best user experience for the front end is having a single method that checks whether the token is transferable. This method should handle both cases of transferability, general and conditional. This is why the second method is defined as an overload of the first method.\ + The front end should also be able to handle the case where the token is not transferable and the transfer is attempted. This can be done by checking the return value of the transfer function, which will be false if the token is not transferable. If the token would just be set as non-transferable, without a standardized interface to check whether the token is transferable, the only way to validate transferability would be to attempt a gas calculation and check whether the transaction would revert. This is a bad user experience and should be avoided. ## Backwards Compatibility @@ -86,7 +100,7 @@ The Minimalistic Non-Transferable token standard is fully compatible with [ERC-7 ## Test Cases -Tests are included in [`nonTransferable.ts`](../assets/eip-6454/test/nonTransferable.ts). +Tests are included in [`transferable.ts`](../assets/eip-6454/test/transferable.ts). To run them in terminal, you can use the following commands: @@ -98,12 +112,16 @@ npx hardhat test ## Reference Implementation -See [`ERC721NonTransferableMock.sol`](../assets/eip-6454/contracts/mocks/ERC721NonTransferableMock.sol). +See [`ERC721TransferableMock.sol`](../assets/eip-6454/contracts/mocks/ERC721TransferableMock.sol). ## Security Considerations The same security considerations as with [ERC-721](./eip-721.md) apply: hidden logic may be present in any of the functions, including burn, add asset, accept asset, and more. +A smart contract can implement the proposal interface but returns fraudulent values, i.e., returning `false` for `isTransferable` when the token is transferable. Such a contract would trick other contracts into thinking that the token is non-transferable when it is transferable. If such a contract exists, we suggest not interacting with it. Much like fraudulent [ERC-20](./eip-20.md) or [ERC-721](./eip-721.md) smart contracts, it is not possible to prevent such contracts from existing. We suggest that you verify all of the external smart contracts you interact with and not interact with contracts you do not trust. + +Since the transferability state can change over time, verifying that the state of the token is transferable before interacting with it is essential. Therefore, a dApp, marketplace, or wallet implementing this interface should verify the state of the token every time the token is displayed. + Caution is advised when dealing with non-audited contracts. ## Copyright diff --git a/EIPS/eip-6475.md b/EIPS/eip-6475.md index adcbcbbca4cde..3871a4b29301a 100644 --- a/EIPS/eip-6475.md +++ b/EIPS/eip-6475.md @@ -4,7 +4,7 @@ title: SSZ Optional description: New SSZ type to represent optional values author: Etan Kissling (@etan-status), Zahary Karadjov (@zah) discussions-to: https://ethereum-magicians.org/t/eip-6475-ssz-optional/12891 -status: Draft +status: Review type: Standards Track category: Core created: 2023-02-09 @@ -37,20 +37,13 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S The default value of `Optional[T]` is `None`. -### Illegal types - -`Optional[T]` with `T` that might serialize to empty data `[]` are illegal: - -- `T` MUST NOT be `List[type, N]` -- `T` MUST NOT be a nested `Optional[type]` - ### Serialization ```python if value is None: return b"" else: - return serialize(value) + return b"\x01" + serialize(value) ``` ### Deserialization @@ -58,7 +51,7 @@ else: The deserialization of an `Optional[T]` depends on the input length: - If the input length is 0, the value is `None`. -- Otherwise, deserialize the input as if it represents a value of type `T`. +- Otherwise, the first byte of the deserialization scope must be checked to be `0x01`, the remainder of the scope is deserialized same as `T`. ### Merkleization @@ -71,11 +64,9 @@ An `Optional[T]` is merkleized as a `List[T, 1]`. ### Why not `Union[None, T]`? -SSZ Union types are currently not used in any final Ethereum specification and do not have a finalized design themselves. If the only use case is a workaround for `Optional[T]`, the simpler `Optional[T]` type is sufficient, and support for general unions could be delayed until really needed. - `Union[None, T]` leaves ambiguity about the intention whether the type may be extended in the future, i.e., `Union[None, T, U]`. -The serialization is less compact, due to the extra selector byte. +Furthermore, SSZ Union types are currently not used in any final Ethereum specification and do not have a finalized design themselves. If the only use case is a workaround for lack of `Optional[T]`, the simpler `Optional[T]` type is sufficient, and support for general unions could be delayed until really needed. Note that the design of `Optional[T]` could be used as basis for a more general `Union`. ### Why not `List[T, 1]`? diff --git a/EIPS/eip-6493.md b/EIPS/eip-6493.md index 0234cd35217e6..fe136db55e56f 100644 --- a/EIPS/eip-6493.md +++ b/EIPS/eip-6493.md @@ -4,7 +4,7 @@ title: SSZ Transaction Signature Scheme description: Signature scheme for SSZ transactions author: Etan Kissling (@etan-status), Matt Garnett (@lightclient), Vitalik Buterin (@vbuterin) discussions-to: https://ethereum-magicians.org/t/eip-6493-ssz-transaction-signature-scheme/13050 -status: Draft +status: Review type: Standards Track category: Core created: 2023-02-24 @@ -18,7 +18,7 @@ This EIP defines a signature scheme for [Simple Serialize (SSZ)](https://github. ## Motivation Existing [EIP-2718](./eip-2718.md) transaction types first encoded in the RLP format, and then hashed using keccak256 for signing and finally (post signing) to generate a unique transaction identifier as well. - + However for new transaction types that are encoded in the SSZ format (for e.g. [EIP-4844](./eip-4844.md) blob transactions), it is idiomatic to base their signature hash and their unique identifier on `hash_tree_root` instead. ## Specification diff --git a/EIPS/eip-6551.md b/EIPS/eip-6551.md index b3bea192f57cc..dce1295b2c8ce 100644 --- a/EIPS/eip-6551.md +++ b/EIPS/eip-6551.md @@ -2,7 +2,7 @@ eip: 6551 title: Non-fungible Token Bound Accounts description: An interface and registry for smart contract accounts owned by ERC-721 tokens -author: Jayden Windle (@jaydenwindle), Benny Giang , Steve Jang, Druzy Downs (@druzydowns), Raymond Huynh (@huynhr), Alanah Lam +author: Jayden Windle (@jaydenwindle), Benny Giang , Steve Jang, Druzy Downs (@druzydowns), Raymond Huynh (@huynhr), Alanah Lam , Wilkins Chung (@wwhchung) , Paul Sullivan (@sullivph) discussions-to: https://ethereum-magicians.org/t/non-fungible-token-bound-accounts/13030 status: Draft type: Standards Track @@ -57,7 +57,7 @@ The registry serves as a single entry point for projects wishing to utilize toke - `createAccount` - deploys a token bound account for an ERC-721 token given an `implementation` address - `account` - a read-only function that computes the token bound account address for an ERC-721 token given an `implementation` address -The registry SHALL deploy each token bound account as an [ERC-1167](./eip-1167.md) minimal proxy with immutable arguments. +The registry SHALL deploy each token bound account as an [ERC-1167](./eip-1167.md) minimal proxy with immutable constant data appended to the bytecode. The the deployed bytecode of each token bound account SHALL have the following structure: @@ -65,62 +65,55 @@ The the deployed bytecode of each token bound account SHALL have the following s ERC-1167 Header (10 bytes) (20 bytes) ERC-1167 Footer (15 bytes) -STOP code (1 byte) + (32 bytes) (32 bytes) (32 bytes) (32 bytes) ``` -For example, the token bound account with implementation address `0xbebebebebebebebebebebebebebebebebebebebe`, chain ID `1`, token contract `0xcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf` and token ID `123` would have the following deployed bytecode: +For example, the token bound account with implementation address `0xbebebebebebebebebebebebebebebebebebebebe`, salt `0`, chain ID `1`, token contract `0xcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf` and token ID `123` would have the following deployed bytecode: ``` -363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf000000000000000000000000000000000000000000000000000000000000007b +363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf000000000000000000000000000000000000000000000000000000000000007b ``` -Each token bound account contract SHALL delegate execution to a static account implementation address that implements the token bound account interface. +Each token bound account proxy SHALL delegate execution to a contract that implements the `IERC6551Account` interface. -The registry contract is permissionless, immutable, and has no owner. The complete source code for the registry can be found in the [Registry Implementation](#registry-implementation) section below. The registry can be deployed on any Ethereum chain using the following transaction: +The registry contract is permissionless, immutable, and has no owner. The complete source code for the registry can be found in the [Registry Implementation](#registry-implementation) section below. The registry SHALL be deployed at address `TBD` using Nick's Factory (`0x4e59b44847b379578588920cA78FbF26c0B4956C`) with salt `0x6551655165516551655165516551655165516551655165516551655165516551`. -```json -{ - "nonce": "0x00", - "gasPrice": "0x09184e72a000", - "gasLimit": "0x27100", - "value": "0x00", - "data": "0x608060405234801561001057600080fd5b5061057b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063728a845514610046578063774023de1461006f57806388cdcaeb1461009a575b600080fd5b61005961005436600461043b565b6100ad565b60405161006691906104a3565b60405180910390f35b61008261007d36600461043b565b610107565b6040516001600160a01b039091168152602001610066565b6100826100a836600461043b565b61016e565b60408051602081018590526001600160a01b03841681830152606080820184905282518083038201815260808301909352916100ee9187919060a0016104d6565b6040516020818303038152906040529050949350505050565b6040805160208082018690526001600160a01b038516828401526060808301859052835180840390910181526080909201909252805191012060009081610150878787876100ad565b8051906020012090506101638282610203565b979650505050505050565b60008061018286631dfe9a6f60e31b610217565b9050806101a25760405163340aafcd60e11b815260040160405180910390fd5b6040805160208082018890526001600160a01b038716828401526060808301879052835180840390910181526080909201909252805191012060006101e9888888886100ad565b90506101f760008383610233565b98975050505050505050565b600061021083833061033c565b9392505050565b600061022283610366565b80156102105750610210838361039a565b60008347101561028a5760405162461bcd60e51b815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b81516000036102db5760405162461bcd60e51b815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f6044820152606401610281565b8282516020840186f590506001600160a01b0381166102105760405162461bcd60e51b815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f79000000000000006044820152606401610281565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b6000610379826301ffc9a760e01b61039a565b80156103945750610392826001600160e01b031961039a565b155b92915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03166301ffc9a760e01b178152825160009392849283928392918391908a617530fa92503d9150600051905082801561040c575060208210155b8015610163575015159695505050505050565b80356001600160a01b038116811461043657600080fd5b919050565b6000806000806080858703121561045157600080fd5b61045a8561041f565b93506020850135925061046f6040860161041f565b9396929550929360600135925050565b60005b8381101561049a578181015183820152602001610482565b50506000910152565b60208152600082518060208401526104c281604085016020870161047f565b601f01601f19169190910160400192915050565b733d608e80600a3d3981f3363d3d373d3d3d363d7360601b8152606083901b6bffffffffffffffffffffffff191660148201526e5af43d82803e903d91602b57fd5bf360881b6028820152815160009061053781603885016020870161047f565b91909101603801939250505056fea2646970667358221220a31484d11d6fa32ee5638da0a14955007d7787538f0b73c00ab6413b2f3dfc9364736f6c63430008110033", - "v": "0x1b", - "r": "0x7340000000000000000000000000000000000000000000000000000000000734", - "s": "0x7347347347347347347347347347347347347347347347347347347347347340" -} -``` - -The registry contract SHALL be deployed to the following address: `0xdea5D1eDB1a9035CACB8253109908d0F4A810522` - -The registry SHALL deploy all token bound account contracts using the `create2` opcode with a salt value derived from the ERC-721 token contract address, token ID, and [EIP-155](./eip-155.md) chain ID. +The registry SHALL deploy all token bound account contracts using the `create2` opcode so that the account address for every ERC-721 token is deterministic. The account address for each ERC-721 token SHALL be derived from the unique combination of implementation address, token contract address, token ID, [EIP-155](./eip-155.md) chain ID, and an optional salt. The registry SHALL implement the following interface: ```solidity interface IERC6551Registry { - /// @dev Each registry MUST emit the AccountCreated event upon account creation + /// @dev The registry SHALL emit the AccountCreated event upon successful account creation event AccountCreated( address account, address implementation, uint256 chainId, address tokenContract, - uint256 tokenId + uint256 tokenId, + uint256 salt ); /// @dev Creates a token bound account for an ERC-721 token. /// + /// If account has already been created, returns the account address without calling create2. + /// + /// If initData is not empty and account has not yet been created, calls account with + /// provided initData after creation. + /// /// Emits AccountCreated event. /// - /// @return the address of the created account + /// @return the address of the account function createAccount( address implementation, uint256 chainId, address tokenContract, - uint256 tokenId + uint256 tokenId, + uint256 salt, + bytes calldata initData ) external returns (address); /// @dev Returns the computed address of a token bound account @@ -130,7 +123,8 @@ interface IERC6551Registry { address implementation, uint256 chainId, address tokenContract, - uint256 tokenId + uint256 tokenId, + uint256 salt ) external view returns (address); } ``` @@ -146,7 +140,7 @@ All token bound account implementations MUST implement [ERC-1271](./eip-1271.md) All token bound account implementations MUST implement the following interface: ```solidity -/// @dev the ERC-165 identifier for this interface is `0xeff4d378` +/// @dev the ERC-165 identifier for this interface is `0x400a0398` interface IERC6551Account { /// @dev Token bound accounts MUST implement a `receive` function. /// @@ -200,11 +194,20 @@ interface IERC6551Account { /// /// @return Address of the owner of the ERC-721 token which owns the account function owner() external view returns (address); + + /// @dev Returns a nonce value that is updated on every successful transaction + /// + /// @return The current account nonce + function nonce() external view returns (uint256); } ``` ## Rationale +### Counterfactual Account Addresses + +By specifying a canonical account registry, applications wishing to support this proposal can compute the address for a given token bound account using a certain implementation prior to the deployment of the contract for that account. This allows assets to be sent securely to the owner of a token without needing to know the owner's address. A canonical registry also allows client side applications to query for assets owned by a token from a single entry point. + ### Account Ambiguity The specification proposed above allows ERC-721 tokens to have multiple token bound accounts, one per implementation address. During the development of this proposal, alternative architectures were considered which would have assigned a single token bound account to each ERC-721 token, making each token bound account address an unambiguous identifier. @@ -219,7 +222,7 @@ Finally, this proposal seeks to grant ERC-721 tokens the ability to act as agent ### Proxy Implementation -ERC-1167 minimal proxies are well supported by existing infrastructure and are a common smart contract pattern. However, ERC-1167 proxies do not support storage of constant data. This proposal deploys each token bound account as a lightly modified ERC-1167 proxy with static data appended to the contract bytecode. The appended data is abi-encoded to prevent hash collisions and is preceded by a stop code to prevent accidental execution of the data as code. This approach was taken to maximize compatibility with existing infrastructure while also giving smart contract developers full flexibility when creating custom token bound account implementations. +ERC-1167 minimal proxies are well supported by existing infrastructure and are a common smart contract pattern. This proposal deploys each token bound account using a custom ERC-1167 proxy implementation that stores the salt, implementation address, chain id, token contract address, and token ID as ABI-encoded constant data appended to the contract bytecode. This allows token bound account implementations to easily query this data while ensuring it remains constant. This approach was taken to maximize compatibility with existing infrastructure while also giving smart contract developers full flexibility when creating custom token bound account implementations. ### EIP-155 Support @@ -227,9 +230,9 @@ This proposal uses EIP-155 chain IDs to identify ERC-721 tokens along with their ## Backwards Compatibility -This proposal seeks to me maximally backwards compatible with existing non-fungible token contracts. As such, it does not extend the ERC-721 standard. +This proposal seeks to be maximally backwards compatible with existing non-fungible token contracts. As such, it does not extend the ERC-721 standard. -Additionally, this proposal does not require registries to perform an ERC-165 interface check for ERC-721 compatibility prior to account creation. This is by design in order to maximize backwards compatibility with non-fungible token contracts that pre-date the ERC-721 standard, such as Cryptokitties. Smart contract authors implementing this proposal may optionally choose to enforce interface detection for ERC-721. +Additionally, this proposal does not require the registry to perform an ERC-165 interface check for ERC-721 compatibility prior to account creation. This is by design in order to maximize backwards compatibility with non-fungible token contracts that pre-date the ERC-721 standard, such as Cryptokitties. Smart contract authors implementing this proposal may optionally choose to enforce interface detection for ERC-721. Non-fungible token contracts that do not implement an `ownerOf` method, such as Cryptopunks, are not compatible with this proposal. The system outlined in this proposal could be used to support such collections with minor modifications, but that is outside the scope of this proposal. @@ -243,9 +246,11 @@ pragma solidity ^0.8.13; import "openzeppelin-contracts/utils/introspection/IERC165.sol"; import "openzeppelin-contracts/token/ERC721/IERC721.sol"; +import "openzeppelin-contracts/interfaces/IERC1271.sol"; +import "openzeppelin-contracts/utils/cryptography/SignatureChecker.sol"; import "sstore2/utils/Bytecode.sol"; -contract ExampleERC6551Account is IERC165, IERC6551Account { +contract ExampleERC6551Account is IERC165, IERC1271, IERC6551Account { receive() external payable {} function executeCall( @@ -274,9 +279,10 @@ contract ExampleERC6551Account is IERC165, IERC6551Account { uint256 tokenId ) { + uint256 length = address(this).code.length return abi.decode( - Bytecode.codeAt(address(this), 46, 142), + Bytecode.codeAt(address(this), length - 0x60, length), (uint256, address, uint256) ); } @@ -293,6 +299,24 @@ contract ExampleERC6551Account is IERC165, IERC6551Account { return (interfaceId == type(IERC165).interfaceId || interfaceId == type(IERC6551Account).interfaceId); } + + function isValidSignature(bytes32 hash, bytes memory signature) + external + view + returns (bytes4 magicValue) + { + bool isValid = SignatureChecker.isValidSignatureNow( + owner(), + hash, + signature + ); + + if (isValid) { + return IERC1271.isValidSignature.selector; + } + + return ""; + } } ``` @@ -302,41 +326,42 @@ contract ExampleERC6551Account is IERC165, IERC6551Account { // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "openzeppelin-contracts/utils/introspection/ERC165Checker.sol"; import "openzeppelin-contracts/utils/Create2.sol"; contract ERC6551Registry is IERC6551Registry { - error InvalidImplementation(); + error InitializationFailed(); function createAccount( address implementation, uint256 chainId, address tokenContract, - uint256 tokenId + uint256 tokenId, + uint256 salt, + bytes calldata initData ) external returns (address) { - bool isValidImplementation = ERC165Checker.supportsInterface( - implementation, - type(IERC6551Account).interfaceId + bytes memory code = _creationCode(implementation, chainId, tokenContract, tokenId, salt) + + address _account = Create2.computeAddress( + bytes32(salt), + keccak256(code) ); - if (!isValidImplementation) revert InvalidImplementation(); + if (_account.code.length != 0) return _account; - bytes32 salt = keccak256(abi.encode(chainId, tokenContract, tokenId)); - bytes memory code = _creationCode( - implementation, - chainId, - tokenContract, - tokenId - ); + _account = Create2.deploy(0, bytes32(salt), code); - address _account = Create2.deploy(0, salt, code); + if (initData.length != 0) { + (bool success, ) = _account.call(initData); + if (!success) revert InitializationFailed(); + } emit AccountCreated( _account, implementation, chainId, tokenContract, - tokenId + tokenId, + salt ); return _account; @@ -346,28 +371,29 @@ contract ERC6551Registry is IERC6551Registry { address implementation, uint256 chainId, address tokenContract, - uint256 tokenId + uint256 tokenId, + uint256 salt ) external view returns (address) { - bytes32 salt = keccak256(abi.encode(chainId, tokenContract, tokenId)); bytes32 bytecodeHash = keccak256( - _creationCode(implementation, chainId, tokenContract, tokenId) + _creationCode(implementation, chainId, tokenContract, tokenId, salt) ); - return Create2.computeAddress(salt, bytecodeHash); + return Create2.computeAddress(bytes32(salt), bytecodeHash); } function _creationCode( - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId - ) public pure returns (bytes memory) { + address implementation_, + uint256 chainId_, + address tokenContract_, + uint256 tokenId_, + uint256 salt_ + ) internal pure returns (bytes memory) { return abi.encodePacked( - hex"3d608e80600a3d3981f3363d3d373d3d3d363d73", - implementation, - hex"5af43d82803e903d91602b57fd5bf300", - abi.encode(chainId, tokenContract, tokenId) + hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73", + implementation_, + hex"5af43d82803e903d91602b57fd5bf3", + abi.encode(salt_, chainId_, tokenContract_, tokenId_) ); } } @@ -375,6 +401,8 @@ contract ERC6551Registry is IERC6551Registry { ## Security Considerations +### Fraud Prevention + In order to enable trustless sales of token bound accounts, decentralized marketplaces will need to implement safeguards against fraudulent behavior by malicious account owners. Consider the following potential scam: @@ -389,11 +417,18 @@ To mitigate fraudulent behavior by malicious account owners, decentralized marke Here are a few mitigations strategies to be considered: -- Attach a hash of the token bound accounts contents to the decentralized market order. If the contents of the account have changed base on the hash since the order was placed, consider the offer void. This functionality would need to be supported by the decentralized marketplaces. +- Attach the current token bound account nonce to the marketplace order. If the nonce of the account has changed since the order was placed, consider the offer void. This functionality would need to be supported at the marketplace level. +- Attach a list of asset commitments to the marketplace order that are expected to remain in the token bound account when the order is fulfilled. If any of the committed assets have been removed from the account since the order was placed, consider the offer void. This would also need to be implemented by the marketplace. - Submit the order to the decentralized market via an external smart contract which performs the above logic before validating the order signature. This allows for safe transfers to be implemented without marketplace support. -- Implement a locking mechanism on the token bound account implementation that prevents malicious owners from carrying out this type of scam. +- Implement a locking mechanism on the token bound account implementation that prevents malicious owners from extracting assets from the account while locked + +Preventing fraud is outside the scope of this proposal. + +### Ownership Cycles + +All assets held in an token bound account may be rendered inaccessible if an ownership cycle is created. The simplest example is the case of an ERC-721 token being transferred to it's own token bound account. If this occurs, both the ERC-721 token and all of the assets stored in the token bound account would be permanently inaccessible, since the token bound account is incapable of executing a transaction which transfers the ERC-721 token. -Preventing fraud is outside the scope of this EIP. +Ownership cycles can be introduced in any graph of n>0 token bound accounts. On-chain prevention of these cycles is difficult to enforce given the infinite search space required, and as such is outside the scope of this proposal. Application clients and account implementations wishing to adopt this proposal are encouraged to implement measures that limit the possibility of ownership cycles. ## Copyright diff --git a/EIPS/eip-6672.md b/EIPS/eip-6672.md new file mode 100644 index 0000000000000..e29f63e92b4d5 --- /dev/null +++ b/EIPS/eip-6672.md @@ -0,0 +1,189 @@ +--- +eip: 6672 +title: Multi-redeemable NFTs +description: An extension of ERC-721 which enables an NFT to be redeemed in multiple scenarios for either a physical or digital object +author: RE:DREAMER Lab , Archie Chang (@ArchieR7) , Kai Yu (@chihkaiyu) , Yonathan Randyanto (@Randyanto) +discussions-to: https://ethereum-magicians.org/t/eip-6672-multi-redeemable-nfts/13276 +status: Review +type: Standards Track +category: ERC +created: 2023-02-21 +requires: 165, 721 +--- + +## Abstract + +This EIP proposes an extension to the [ERC-721](./eip-721.md) standard for Non-Fungible Tokens (NFTs) to enable multi-redeemable NFTs. Redemption provides a means for NFT holders to demonstrate ownership and eligibility of their NFT, which in turn enables them to receive a physical or digital item. This extension would allow an NFT to be redeemed in multiple scenarios and maintain a record of its redemption status on the blockchain. + +## Motivation + +The motivation behind our proposed NFT standard is to provide a more versatile and flexible solution compared to existing standards, allowing for multi-redeemable NFTs. Our proposed NFT standard enables multi-redeemable NFTs, allowing them to be redeemed in multiple scenarios for different campaigns or events, thus unlocking new possibilities for commerce use cases and breaking the limitation of one-time redemption per NFT. + +One use case for an NFT that can be redeemed multiple times in various scenarios is a digital concert ticket. The NFT could be redeemed for access to the online concert and then again for exclusive merchandise, a meet and greet with the artist, or any exclusive commerce status that is bound to the NFT. Each redemption could represent a unique experience or benefit for the NFT holder. + +## 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. + +### Redeem and Cancel Functions + +An operator SHALL only make an update to the redemption created by itself. Therefore, the `redeem()` and `cancel()` functions do not have an `_operator` parameter, and the `msg.sender` address MUST be used as the `_operator`. + +### Redemption Flag Key-Value Pairs + +The combination of `_operator`, `_tokenId`, and `_redemptionId` MUST be used as the key in the redemption flag key-value pairs, whose value can be accessed from the `isRedeemed()` function. + +**Every contract compliant with this EIP MUST implement `ERC6672` and `ERC721` interfaces.** + +```solidity +pragma solidity ^0.8.16; + +/// @title ERC-6672 Multi-Redeemable NFT Standard +/// @dev See https://eips.ethereum.org/EIPS/eip-6672 +/// Note: the ERC-165 identifier for this interface is 0x4dddf83f. +interface IERC6672 /* is IERC721 */ { + /// @dev This event emits when an NFT is redeemed. + event Redeem( + address indexed _operator, + uint256 indexed _tokenId, + address redeemer, + bytes32 _redemptionId, + string _memo + ); + + /// @dev This event emits when a redemption is canceled. + event Cancel( + address indexed _operator, + uint256 indexed _tokenId, + bytes32 _redemptionId, + string _memo + ); + + /// @notice Check whether an NFT is already used for redemption or not. + /// @dev + /// @param _operator The address of the operator of the redemption platform. + /// @param _redemptionId The identifier for a redemption. + /// @param _tokenId The identifier for an NFT. + /// @return Whether an NFT is already redeemed or not. + function isRedeemed(address _operator, bytes32 _redemptionId, uint256 _tokenId) external view returns (bool); + + /// @notice List the redemptions created by the given operator for the given NFT. + /// @dev + /// @param _operator The address of the operator of the redemption platform. + /// @param _tokenId The identifier for an NFT. + /// @return List of redemptions of speficic `_operator` and `_tokenId`. + function getRedemptionIds(address _operator, uint256 _tokenId) external view returns (bytes32[]); + + /// @notice Redeem an NFT + /// @dev + /// @param _redemptionId The identifier created by the operator for a redemption. + /// @param _tokenId The NFT to redeem. + /// @param _memo + function redeem(bytes32 _redemptionId, uint256 _tokenId, string _memo) external; + + /// @notice Cancel a redemption + /// @dev + /// @param _redemptionId The redemption to cancel. + /// @param _tokenId The NFT to cancel the redemption. + /// @param _memo + function cancel(bytes32 _redemptionId, uint256 _tokenId, string _memo) external; +} +``` + +### Metadata Extension + +The key format for the `redemptions` key-value pairs MUST be standardized as `operator-tokenId-redemptionId`, where `operator` is the operator wallet address, `tokenId` is the identifier of the token that has been redeemed, and `redemptionId` is the redemption identifier. The value of the key `operator-tokenId-redemptionId` is an object that contains the `status` and `description` of the redemption. + +- Redemption status, i.e. `status` + + The redemption status can have a more granular level, rather than just being a flag with a `true` or `false` value. For instance, in cases of physical goods redemption, we may require the redemption status to be either `redeemed`, `paid`, or `shipping`. It is RECOMMENDED to use a string enum that is comprehensible by both the operator and the marketplace or any other parties that want to exhibit the status of the redemption. + +- Description of the redemption, i.e. `description` + + The `description` SHOULD be used to provide more details about the redemption, such as information about the concert ticket, a detailed description of the action figures, and more. + +The **metadata extension** is OPTIONAL for [ERC-6672](./eip-6672.md) smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. + +```solidity +/// @title ERC-6672 Multi-Redeemable Token Standard, optional metadata extension +/// @dev See https://eips.ethereum.org/EIPS/eip-6672 +interface IERC6672Metadata /* is IERC721Metadata */ { + /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. + /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC + /// 3986. The URI may point to a JSON file that conforms to the "ERC-6672 + /// Metadata JSON Schema". + function tokenURI(uint256 _tokenId) external view returns (string); +} +``` + +This is the "[ERC-6672](./eip-6672.md) Metadata JSON Schema" referenced above. + +```json +{ + "title": "Asset Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + } + }, + "redemptions": { + "operator-tokenId-redemptionId": { + "status": { + "type": "string", + "description": "The status of a redemption. Enum type can be used to represent the redemption status, such as redeemed, shipping, paid." + }, + "description": { + "type": "string", + "description": "Describes the object that has been redeemed for an NFT, such as the name of an action figure series name or the color of the product." + } + } + } +} +``` + +## Rationale + +### Key Choices for Redemption Flag and Status + +The combination of `_operator`, `_tokenId`, and `_redemptionId` is chosen as the key because it provides a clear and unique identifier for each redemption transaction. + +- Operator wallet address, i.e. `_operator` + + It's possible that there are more than one party who would like to use the same NFT for redemption. For example, MisterPunks NFTs are eligible to be redemeed for both Event-X and Event-Y tickets, and each event's ticket redemption is handled by a different operator. + +- Token identifier, i.e. `_tokenId` + + Each NFT holder will have different redemption records created by the same operator. Therefore, it's important to use token identifier as one of the keys. + +- Redemption identifier, i.e. `_redemptionId` + + Using `_redemptionId` as one of the keys enables NFT holders to redeem the same NFT to the same operator in multiple campaigns. For example, Operator-X has 2 campaigns, i.e. campaign A and campaign B, and both campaigns allow for MisterPunks NFTs to be redemeed for physical action figures. Holder of MisterPunk #7 is eligible for redemption in both campaigns and each redemption is recorded with the same `_operator` and `_tokenId`, but with different `_redemptionId`. + +## Backwards Compatibility + +This standard is compatible with [ERC-721](./eip-721.md). + +## Reference Implementation + +The reference implementation of Multi-Redeemable NFT can be found [here](../assets/eip-6672/contracts/ERC6672.sol). + + +## Security Considerations + +An incorrect implementation of [ERC-6672](./eip-6672.md) could potentially allow an unauthorized operator to access redemption flags owned by other operators, creating a security risk. As a result, an unauthorized operator could cancel the redemption process managed by other operators. Therefore, it is crucial for [ERC-6672](./eip-6672.md) implementations to ensure that only the operator who created the redemption, identified using `msg.sender`, can update the redemption flag using the `redeem()` and `cancel()` functions. It is also recommended to isolate the `redeem()` and `cancel()` functions from [ERC-721](./eip-721.md) approval models. + +This [ERC-6672](./eip-6672.md) token is compatible with [ERC-721](./eip-721.md), so wallets and smart contracts capable of storing and handling standard [ERC-721](./eip-721.md) tokens will not face the risk of asset loss caused by incompatible standard implementations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-6789.md b/EIPS/eip-6789.md new file mode 100644 index 0000000000000..ff603a10a49b0 --- /dev/null +++ b/EIPS/eip-6789.md @@ -0,0 +1,113 @@ +--- +eip: 6789 +title: Rename gas to mana +description: This EIP suggests renaming gas to mana, as proposed by Vitalik Buterin in 2015 +author: Pascal Caversaccio (@pcaversaccio) +discussions-to: https://ethereum-magicians.org/t/eip-6789-rename-gas-to-mana/13570 +status: Draft +type: Standards Track +category: Interface +created: 2023-03-27 +--- + +## Abstract + +This EIP suggests renaming `gas` to `mana`, as proposed by Vitalik Buterin in 2015. + +## Motivation + +The underlying motivation for reviving Vitalik's original proposal from 2015 is that we have finally arrived at the age of Proof-of-Stake, and given the roadmap ahead (i.e. "The Surge", "The Scourge", "The Verge", "The Purge", and "The Splurge"), I consider this moment as the last opportunity to make such a far-reaching semantic change. + +## 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. + +The core term `gas` MUST be renamed to `mana`. + +The following opcodes MUST be renamed: + +- `GASPRICE` to `MANAPRICE`; +- `GASLIMIT` to `MANALIMIT`; and +- `GAS` to `MANA`. + +Additionally, the input parameters or outputs of the following opcodes MUST be renamed: + +- `CALL`'s `gas` input parameter to `mana`; +- `CALLCODE`'s `gas` input parameter to `mana`; +- `DELEGATECALL`'s `gas` input parameter to `mana`; +- `STATICCALL`'s `gas` input parameter to `mana`; +- `GASLIMIT`'s `gasLimit` output to `manaLimit`; and +- `GAS`'s `gas` output to `mana`. + +Finally, the following RPC endpoints MUST be renamed: + +- `eth_estimateGas` to `eth_estimateMana`; +- `eth_gasPrice` to `eth_manaPrice`; and +- `eth_maxPriorityFeePerGas` to `eth_maxPriorityFeePerMana`. + +The description of the RPC endpoints MUST be renamed accordingly: + +- `eth_estimateMana`: Generates and returns an estimate of how much `mana` is necessary to allow the transaction to complete; +- `eth_manaPrice`: Returns the current price per `mana` in wei; and +- `eth_maxPriorityFeePerMana`: Returns the current `maxPriorityFeePerMana` per `mana` in wei. + +## Rationale + +- `mana` reflects the increased environmental friendliness of Proof-of-Stake; +- `mana` is generally understood to be ephemeral and non-transferable, which better represents the concept of `gas`; and +- `mana` is generally portrayed as renewable, while (natural) `gas` is non-renewable. + +## Backwards Compatibility + +This proposal is not backward compatible as it renames the core term `gas`. + +## Test Cases + +### Example 1 + +If a transaction requires more `mana` than allowed by the `manaLimit`, it is reverted as an _out-of-mana_ transaction. + +### Example 2 + +A Solidity contract to estimate the used `mana` via the new `manaleft()` syntax (replacing `gasleft()`) for dedicated function calls. + +```solidity +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.8.19; + +contract ManaMetering { + function oldWay() external view returns (string memory, uint256 manaUsed) { + string memory hiMom = "Hi Mom, "; + string memory missYou = "miss you."; + uint256 startMana = manaleft(); + string memory concat = string(abi.encodePacked(hiMom, missYou)); + manaUsed = startMana - manaleft(); + return (concat, manaUsed); + } + + function newWay() external view returns (string memory, uint256 manaUsed) { + string memory hiMom = "Hi Mom, "; + string memory missYou = "miss you."; + uint256 startMana = manaleft(); + string memory concat = string.concat(hiMom, missYou); + manaUsed = startMana - manaleft(); + return (concat, manaUsed); + } +} +``` + +In Vyper, the same behaviour can be replicated with the new transaction property `msg.mana`, which replaces `msg.gas`. + +### Example 3 + +An example of how to set the `manaLimit` in MetaMask: + +![MetaMask manaLimit](../assets/eip-6789/MetaMask_ManaLimit.png) + +## Security Considerations + +There are no security considerations directly related to the renaming of `gas` to `mana`. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-6810.md b/EIPS/eip-6810.md new file mode 100644 index 0000000000000..38a0a8c136203 --- /dev/null +++ b/EIPS/eip-6810.md @@ -0,0 +1,107 @@ +--- +eip: 6810 +title: Ex Post Facto Cascading Revert +description: Allow transactions to be reversed after confirmation +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-6810-ex-post-facto-cascading-revert/13630 +status: Draft +type: Standards Track +category: Core +created: 2023-04-01 +requires: 2718, 2929 +--- + +## Abstract + +A new transaction type reverts one of a sender's prior transactions, and other transactions dependent on that state, recursively. + +## Motivation + +While Ethereum has the capability of reversible transactions through smart contracts, instant settlement is the default. +But sometimes users make mistakes. +Most mistakes are discovered quickly. +However, once the transaction is confirmed, it is settled. +There are many use cases for reverting settled transactions. +Some of the most-common mistakes are listed below. + +- Wrong recipient +- Unintended consequences +- Got scammed + +This feature addresses these issues and more, ending all regret. + +## Specification + +### Parameters + +A new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` `0x5a`. +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chainId, nonce, revertNonce, budget, signatureYParity, signatureR, signatureS])`. +The `signatureYParity, signatureR, signatureS` elements of this transaction represent a secp256k1 signature over `keccak256(0x5a || rlp([chainId, nonce, revertNonce, budget]))`. +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, budgetUsed, removedLogsBloom, [newReceiptPayloads]])`, where `newReceiptPayloads` is a sequential array of the updated receipts of all reverted transactions. + +### Block gas limit + +A transaction of type `0x5a` shall be the only transaction in its block. + +### Cascading revert operation + +A transaction fee budget is initialized to the value specified by `budget`, denominated in ether. +This budget is the transaction fee for this type of transaction. +Reverted transaction fees are refunded from this budget. +Should the budget be insufficient, the Ex Post Facto Cascading Revert transaction fails and the entire budget is paid to the `COINBASE` specified in the block header. +Otherwise, the remainder of the budget after all transactions are reverted is paid to the `COINBASE` account. + +The state is rolled back to the start of the transaction specified by `revertNonce`. +An access list is initialized empty. +Any state previously modified by a reverted transaction is added to the access list. +Any subsequent transaction reading or using state included in the access list must also be reverted. +This operation cascades forward until the current block. + +State includes: + +- ether balance +- contract code +- account nonce +- storage keys + +### Snap sync + +Due to the large amount of state that may be modified by such a transaction, slower clients should use snap sync to load the new state. + +## Rationale + +The transaction must fill the entire block to prevent MEV attacks. + +While some cascading reverts are highly consequential, others are considerably simpler. +The budget ensures the full network cost of the operation is paid. +For example, reversing a token transfer to the wrong recipient would be relatively cheap. +On the other hand, it would be prohibitively expensive to revert all deposits to a custodial exchange. + +Transaction fees must be refunded from this budget rather than the prior block reward in order to protect the security of the consensus protocol. + +Snap sync should be safe because if the state root is invalid then the block producer could get slashed. + +## Backwards Compatibility + +If we find any backwards compatibility issue we can maybe reverse those transactions. +If that doesn't work idk maybe need another hard fork. + +## Test Cases + +- Reverting a transaction that ever funded an account reverts all of that account's subsequent transactions. +- Reverting the transaction that deploys a contract reverts all transactions interacting with that contract. +- Reverting a transfer to a new account does not revert other transactions. + +## Reference Implementation + +Seems simple enough. +TODO this later; should only take a few hours, tops. + +## Security Considerations + +This specification has been audited by Illinois Senator Robert Peters. +No exploits were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-6913.md b/EIPS/eip-6913.md new file mode 100644 index 0000000000000..e2309078e22e3 --- /dev/null +++ b/EIPS/eip-6913.md @@ -0,0 +1,96 @@ +--- +eip: 6913 +title: SETCODE instruction +description: replace code in-place +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-6913-setcode-instruction/13898 +status: Draft +type: Standards Track +category: Core +created: 2023-04-20 +--- + +## Abstract + +Introduce the `SETCODE` (`0x49`) instruction, which replaces the code of the current executing address from memory. +Future calls to the modified contract use the new code. + +## Motivation + +Many contracts are upgradeable in order to facilitate improvement or defer decisions without migrating to a new address. +Contracts presently do this in several ways: + +The oldest method uses `CALL`. +The limitation of this method is that owned state must be modifiable by all future implementations. + +Second, `DELEGATECALL` can proxy the implementation. +Some proxies are minimal while others branch to many separate implementation accounts. +This method can also bypass account code size limits. + +A third method uses `SELFDESTRUCT` and `CREATE2` to replace code in-place. +This method improves upon the prior methods by removing the need to call into external contracts. +One limitation of this method is that any internal state is removed by `SELFDESTRUCT`. +Another limitation is that `SELFDESTRUCT` does not remove code until the end of the transaction, sacrificing availability until `CREATE2` can complete the upgrade. + +Given the upcoming deprecation of `SELFDESTRUCT`, `SETCODE` introduces a better method for replacing code in-place. + +## Specification + +When inside of a `CREATE`-like execution scope that returns new code for the executing address (the account returned by `ADDRESS`), `SETCODE` causes an exceptional abort. +Otherwise, `SETCODE` consumes two words from the stack: offset and length. +These specify a range of memory containing the new code. +Any validations that would be performed on the result of `CREATE` or `CREATE2` occur immediately, potentially causing failure with exceptional abort. +Code replacement is deferred; the current execution scope and its children proceed before code replacement. +After the current execution scope exits successfully (neither reverting nor aborting), the code in the executing account is replaced. +Like `SSTORE`, this account modification will be reverted if a parent scope reverts or aborts. +Unlike `SELFDESTRUCT`, `SETCODE` does not clear account balance or storage. + +Multiple `SETCODE` operations inside the same execution scope are allowed and replace the pending replacement. + +A `SELFDESTRUCT` operation discards the pending code. + +### Gas + +The gas cost of this operation is the sum of Gselfdestruct and the product of Gcodedeposit and the number of bytes in the new code. + +## Rationale + +The gas cost of `SETCODE` is comparable to `CREATE` but excludes Gcreate because no execution context is created, nor any new account. +Other account modification costs are accounted for outside of execution gas. + +Unlike `SELFDESTRUCT`, execution proceeds normally after `SETCODE` in order to allow return data. + +## Backwards Compatibility + +The only prior operation changing code is `SELFDESTRUCT`. +`SELFDESTRUCT` remains compatible by discarding any pending replacement code. + +## Test Cases + +| CodeStart | CallData | CodeResult | Gas | +|----------------------|------------------|----------------------|------| +| 365f5f37365f4900 | 365f5f37365f4900 | 365f5f37365f4900 | 6613 | +| 365f5f37365f4900 | 00 | 00 | 5213 | +| 365f5f37365f4900 | | | 5013 | +| 365f5f37365f49595ffd | 365f5f37365f4900 | 365f5f37365f49595ffd | 6617 | +| 365f5f37365f49fe | 365f5f37365f4900 | 365f5f37365f49fe | all | + +## Security Considerations + +Risks related to SETCODE similarly apply to other upgrade patterns. + +Most contracts should never be replaced and should not be upgradeable. +Any upgrade mechanism can risk permanent failure. +The possibility of upgrade perpetuates such risk. + +Access to upgrade operations should be restricted. +Upgrades should never be performed in a hurry or when tired. +Upgrades should be tested under as similar conditions to production as possible; discrepancies are sources of unexpected results. +When possible, multiple engineers should preview and independently verify pending upgrade procedures. + +Block explorers, wallets, and other interfaces should flag upgradeable code. +Client software should warn against approving [ERC-20](./eip-20.md) or [ERC-721](./eip-721.md) tokens for upgradeable accounts. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-747.md b/EIPS/eip-747.md index d998781a559fe..e60995651dfcd 100644 --- a/EIPS/eip-747.md +++ b/EIPS/eip-747.md @@ -4,8 +4,7 @@ title: wallet_watchAsset RPC Method description: Adds a new RPC method that allows websites to prompt users to watch an asset author: Dan Finlay (@danfinlay), Esteban Mino (@estebanmino), Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/eip-747-eth-watchtoken/1048 -status: Last Call -last-call-deadline: 2023-03-13 +status: Final type: Standards Track category: Interface created: 2018-08-13 diff --git a/EIPS/eip-6806.md b/EIPS/eip-draft_erc_721_holding_time_tracking.md similarity index 99% rename from EIPS/eip-6806.md rename to EIPS/eip-draft_erc_721_holding_time_tracking.md index 53bea0fa60a08..96aa440c57f7d 100644 --- a/EIPS/eip-6806.md +++ b/EIPS/eip-draft_erc_721_holding_time_tracking.md @@ -1,5 +1,5 @@ --- -eip: 6806 +eip: draft_erc_721_holding_time_tracking title: ERC-721 Holding Time Tracking description: Add holding time information to ERC-721 tokens author: Saitama (@saitama2009), Combo , Luigi diff --git a/_includes/head.html b/_includes/head.html index b7bb45f784c22..0d90658589fea 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -66,7 +66,7 @@ }; - +