diff --git a/.github/workflows/auto-label-bot.yml b/.github/workflows/auto-label-bot.yml index 77590a1b89f212..d4edd8fdb50bb5 100644 --- a/.github/workflows/auto-label-bot.yml +++ b/.github/workflows/auto-label-bot.yml @@ -1,6 +1,10 @@ on: pull_request_target: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + name: Auto Label Bot jobs: jekyll-label-action: diff --git a/.github/workflows/auto-review-bot.yml b/.github/workflows/auto-review-bot.yml index 696694c700aa17..e0dd983eea6f07 100644 --- a/.github/workflows/auto-review-bot.yml +++ b/.github/workflows/auto-review-bot.yml @@ -5,6 +5,10 @@ on: types: - completed +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + name: Auto Review Bot jobs: auto-review-bot: diff --git a/.github/workflows/auto-review-trigger.yml b/.github/workflows/auto-review-trigger.yml index 69c081d7bd7737..644e1b01622567 100644 --- a/.github/workflows/auto-review-trigger.yml +++ b/.github/workflows/auto-review-trigger.yml @@ -19,6 +19,10 @@ on: types: - created +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + name: Auto Review Bot Trigger jobs: trigger: diff --git a/.github/workflows/auto-stagnate-bot.yml b/.github/workflows/auto-stagnate-bot.yml index c7e8d73c7d1663..abc25f1acefb0a 100644 --- a/.github/workflows/auto-stagnate-bot.yml +++ b/.github/workflows/auto-stagnate-bot.yml @@ -11,9 +11,9 @@ jobs: name: Auto Stagnant Bot steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 - name: Setup Node.js Environment - uses: actions/setup-node@v2 + uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 with: node-version: '14' - name: auto-stagnant-bot diff --git a/.github/workflows/ci-rerun-trigger.yml b/.github/workflows/ci-rerun-trigger.yml index 31121c2eeccda2..7321bf4e7db1ae 100644 --- a/.github/workflows/ci-rerun-trigger.yml +++ b/.github/workflows/ci-rerun-trigger.yml @@ -10,7 +10,7 @@ jobs: name: Trigger steps: - name: Trigger - uses: actions/github-script@v6 + uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0 if: github.event.issue.pull_request && contains(github.event.comment.body, '@eth-bot rerun') with: script: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 662c7b49bff775..837c270b27cc6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,10 @@ on: - ready_for_review - edited +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: save-pr: name: Save PR Number @@ -27,7 +31,7 @@ jobs: echo $MERGE_SHA > ./pr/merge_sha - name: Upload PR Number - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 with: name: pr_number path: pr/ @@ -39,7 +43,7 @@ jobs: steps: - name: Checkout EIP Repository uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - + - name: Install OpenSSL run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev @@ -67,17 +71,25 @@ jobs: steps: - name: Checkout EIP Repository uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - - - name: Delete Unchanged Files - uses: Pandapip1/delete-unchanged-files@2c27069573bbeb6703790ac5c872e9b1a100d96c + + - name: Get Changed Files + id: changed + continue-on-error: true + run: | + echo "CHANGED_FILES<> $GITHUB_ENV + gh pr diff ${{ github.event.number }} --name-only | sed -e 's|$|,|' | xargs -i echo "{}" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run CodeSpell uses: codespell-project/actions-codespell@2391250ab05295bddd51e36a8c6295edb6343b0e + if: steps.changed.outcome == 'success' with: check_filenames: true ignore_words_file: config/.codespell-whitelist + path: ${{ env.CHANGED_FILES }} skip: .git,Gemfile.lock,**/*.png,**/*.gif,**/*.jpg,**/*.svg,.codespell-whitelist,vendor,_site,_config.yml,style.css - only_warn: 1 eipw-validator: name: EIP Walidator @@ -87,8 +99,34 @@ jobs: - name: Checkout EIP Repository uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - - uses: ethereum/eipw-action@dist + - uses: ethereum/eipw-action@bbf342b62cb8046439c3508f1e98e23a4a9cd27a id: eipw with: token: ${{ secrets.GITHUB_TOKEN }} - unchecked: 1, 5069 + unchecked: 1, 5069, 5757 + + markdownlint: + name: Markdown Linter + runs-on: ubuntu-latest + steps: + - name: Checkout EIP Repository + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + + - name: Get Changed Files + id: changed + continue-on-error: true + run: | + echo "CHANGED_FILES<> $GITHUB_ENV + gh pr diff ${{ github.event.number }} --name-only | grep -E -x '[^/]+\.md|EIPS/eip-[0-9]+\.md' >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Lint + uses: DavidAnson/markdownlint-cli2-action@16d9da45919c958a8d1ddccb4bd7028e8848e4f1 + if: steps.changed.outcome == 'success' + with: + command: config + globs: | + config/.markdownlint.yaml + ${{ env.CHANGED_FILES }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ef732f82be6f67..f0eebe2a6c43e0 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -24,11 +24,13 @@ jobs: close-issue-message: This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback. days-before-issue-stale: 7 days-before-issue-close: 49 # 49 + 7 weeks = 3 months - exempt-issue-labels: discussions-to + exempt-issue-labels: discussions-to, e-consensus stale-issue-label: w-stale # PR config stale-pr-message: There has been no activity on this pull request for 2 weeks. It will be closed after 3 months of inactivity. If you would like to move this PR forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. close-pr-message: This pull request was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment. days-before-pr-stale: 14 days-before-pr-close: 42 # 42 + 14 weeks = 3 months + exempt-pr-labels: e-review, e-consensus + exempt-pr-milestones: "Manual Merge Queue" stale-pr-label: w-stale diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md index 3c7b466d950abc..a580d23501dc42 100644 --- a/EIPS/eip-1.md +++ b/EIPS/eip-1.md @@ -38,6 +38,7 @@ An EIP must meet certain minimum criteria. It must be a clear and complete descr ### Special requirements for Core EIPs If a **Core** EIP mentions or proposes changes to the EVM (Ethereum Virtual Machine), it should refer to the instructions by their mnemonics and define the opcodes of those mnemonics at least once. A preferred way is the following: + ``` REVERT (0xfe) ``` @@ -48,13 +49,13 @@ REVERT (0xfe) Parties involved in the process are you, the champion or *EIP author*, the [*EIP editors*](#eip-editors), and the [*Ethereum Core Developers*](https://github.com/ethereum/pm). -Before you begin writing a formal EIP, you should vet your idea. Ask the Ethereum community first if an idea is original to avoid wasting time on something that will be rejected based on prior research. It is thus recommended to open a discussion thread on [the Ethereum Magicians forum](https://ethereum-magicians.org/) to do this. +Before you begin writing a formal EIP, you should vet your idea. Ask the Ethereum community first if an idea is original to avoid wasting time on something that will be rejected based on prior research. It is thus recommended to open a discussion thread on [the Ethereum Magicians forum](https://ethereum-magicians.org/) to do this. Once the idea has been vetted, your next responsibility will be to present (by means of an EIP) the idea to the reviewers and all interested parties, invite editors, developers, and the community to give feedback on the aforementioned channels. You should try and gauge whether the interest in your EIP is commensurate with both the work involved in implementing it and how many parties will have to conform to it. For example, the work required for implementing a Core EIP will be much greater than for an ERC and the EIP will need sufficient interest from the Ethereum client teams. Negative community feedback will be taken into consideration and may prevent your EIP from moving past the Draft stage. ### Core EIPs -For Core EIPs, given that they require client implementations to be considered **Final** (see "EIPs Process" below), you will need to either provide an implementation for clients or convince clients to implement your EIP. +For Core EIPs, given that they require client implementations to be considered **Final** (see "EIPs Process" below), you will need to either provide an implementation for clients or convince clients to implement your EIP. The best way to get client implementers to review your EIP is to present it on an AllCoreDevs call. You can request to do so by posting a comment linking your EIP on an [AllCoreDevs agenda GitHub Issue](https://github.com/ethereum/pm/issues). @@ -64,9 +65,9 @@ These calls generally result in a "rough consensus" around what EIPs should be i :warning: The EIPs process and AllCoreDevs call were not designed to address contentious non-technical issues, but, due to the lack of other ways to address these, often end up entangled in them. This puts the burden on client implementers to try and gauge community sentiment, which hinders the technical coordination function of EIPs and AllCoreDevs calls. If you are shepherding an EIP, you can make the process of building community consensus easier by making sure that [the Ethereum Magicians forum](https://ethereum-magicians.org/) thread for your EIP includes or links to as much of the community discussion as possible and that various stakeholders are well-represented. -*In short, your role as the champion is to write the EIP using the style and format described below, shepherd the discussions in the appropriate forums, and build community consensus around the idea.* +*In short, your role as the champion is to write the EIP using the style and format described below, shepherd the discussions in the appropriate forums, and build community consensus around the idea.* -### EIP Process +### EIP Process The following is the standardization process for all EIPs in all tracks: @@ -84,7 +85,7 @@ 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. -**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. +**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* @@ -98,12 +99,12 @@ Each EIP should have the following parts: - Preamble - RFC 822 style headers containing metadata about the EIP, including the EIP number, a short descriptive title (limited to a maximum of 44 characters), a description (limited to a maximum of 140 characters), and the author details. Irrespective of the category, the title and description should not include EIP number. See [below](./eip-1.md#eip-header-preamble) for details. - Abstract - Abstract is a multi-sentence (short paragraph) technical summary. This should be a very terse and human-readable version of the specification section. Someone should be able to read only the abstract to get the gist of what this specification does. -- Motivation _(optional)_ - A motivation section is critical for EIPs that want to change the Ethereum protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the EIP solves. This section may be omitted if the motivation is evident. +- Motivation *(optional)* - A motivation section is critical for EIPs that want to change the Ethereum protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the EIP solves. This section may be omitted if the motivation is evident. - Specification - The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Ethereum platforms (cpp-ethereum, go-ethereum, parity, ethereumJ, ethereumjs-lib, [and others](https://ethereum.org/en/developers/docs/nodes-and-clients). - Rationale - The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale should discuss important objections or concerns raised during discussion around the EIP. -- Backwards Compatibility _(optional)_ - All EIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their consequences. The EIP must explain how the author proposes to deal with these incompatibilities. This section may be omitted if the proposal does not introduce any backwards incompatibilities, but this section must be included if backward incompatibilities exist. -- Test Cases _(optional)_ - Test cases for an implementation are mandatory for EIPs that are affecting consensus changes. Tests should either be inlined in the EIP as data (such as input/expected output pairs, or included in `../assets/eip-###/`. This section may be omitted for non-Core proposals. -- Reference Implementation _(optional)_ - An optional section that contains a reference/example implementation that people can use to assist in understanding or implementing this specification. This section may be omitted for all EIPs. +- Backwards Compatibility *(optional)* - All EIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their consequences. The EIP must explain how the author proposes to deal with these incompatibilities. This section may be omitted if the proposal does not introduce any backwards incompatibilities, but this section must be included if backward incompatibilities exist. +- Test Cases *(optional)* - Test cases for an implementation are mandatory for EIPs that are affecting consensus changes. Tests should either be inlined in the EIP as data (such as input/expected output pairs, or included in `../assets/eip-###/`. This section may be omitted for non-Core proposals. +- Reference Implementation *(optional)* - An optional section that contains a reference/example implementation that people can use to assist in understanding or implementing this specification. This section may be omitted for all EIPs. - Security Considerations - All EIPs must contain a section that discusses the security implications/considerations relevant to the proposed change. Include information that might be important for security discussions, surfaces risks and can be used throughout the life-cycle of the proposal. E.g. include security-relevant design decisions, concerns, important discussions, implementation-specific guidance and pitfalls, an outline of threats and risks and how they are being addressed. EIP submissions missing the "Security Considerations" section will be rejected. An EIP cannot proceed to status "Final" without a Security Considerations discussion deemed sufficient by the reviewers. - Copyright Waiver - All EIPs must be in the public domain. The copyright waiver MUST link to the license file and use the following wording: `Copyright and related rights waived via [CC0](../LICENSE.md).` @@ -143,7 +144,7 @@ Headers that permit lists must separate elements with commas. Headers requiring dates will always do so in the format of ISO 8601 (yyyy-mm-dd). -#### `author` header +### `author` header The `author` header lists the names, email addresses or usernames of the authors/owners of the EIP. Those who prefer anonymity may use a username only, or a first name and a username. The format of the `author` header value must be: @@ -163,25 +164,25 @@ It is not possible to use both an email and a GitHub username at the same time. At least one author must use a GitHub username, in order to get notified on change requests and have the capability to approve or reject them. -#### `discussions-to` header +### `discussions-to` header While an EIP is a draft, a `discussions-to` header will indicate the URL where the EIP is being discussed. The preferred discussion URL is a topic on [Ethereum Magicians](https://ethereum-magicians.org/). The URL cannot point to Github pull requests, any URL which is ephemeral, and any URL which can get locked over time (i.e. Reddit topics). -#### `type` header +### `type` header The `type` header specifies the type of EIP: Standards Track, Meta, or Informational. If the track is Standards please include the subcategory (core, networking, interface, or ERC). -#### `category` header +### `category` header The `category` header specifies the EIP's category. This is required for standards-track EIPs only. -#### `created` header +### `created` header The `created` header records the date that the EIP was assigned a number. Both headers should be in yyyy-mm-dd format, e.g. 2001-08-14. -#### `requires` header +### `requires` header EIPs may have a `requires` header, indicating the EIP numbers that this EIP depends on. @@ -213,7 +214,7 @@ The current EIP editors are - Matt Garnett (@lightclient) - Sam Wilson (@SamWilsn) -Emeritus EIP editors are +Emeritus EIP editors are - Casey Detrio (@cdetrio) - Hudson Jameson (@Souptacular) @@ -251,15 +252,15 @@ The editors don't pass judgment on EIPs. We merely do the administrative & edito The `title` field in the preamble: - - Should not include the word "standard" or any variation thereof; and - - Should not include the EIP's number. +- Should not include the word "standard" or any variation thereof; and +- Should not include the EIP's number. ### Descriptions The `description` field in the preamble: - - Should not include the word "standard" or any variation thereof; and - - Should not include the EIP's number. +- Should not include the word "standard" or any variation thereof; and +- Should not include the EIP's number. ### EIP numbers diff --git a/EIPS/eip-1153.md b/EIPS/eip-1153.md index 8635ebea85e0f1..9ce8ac8620e26e 100644 --- a/EIPS/eip-1153.md +++ b/EIPS/eip-1153.md @@ -1,8 +1,8 @@ --- eip: 1153 title: Transient storage opcodes -author: Alexey Akhunov (@AlexeyAkhunov), Moody Salem (@moodysalem) description: Add opcodes for manipulating state that behaves identically to storage but is discarded after every transaction +author: Alexey Akhunov (@AlexeyAkhunov), Moody Salem (@moodysalem) discussions-to: https://ethereum-magicians.org/t/eip-transient-storage-opcodes/553 status: Review type: Standards Track @@ -12,6 +12,7 @@ requires: 2200, 3529 --- ## Abstract + This proposal introduces transient storage opcodes, which manipulate state that behaves identically to storage, except that transient storage is discarded after every transaction. In other words, the values of transient storage are never deserialized from storage or serialized to storage. Thus transient storage is cheaper since it never requires disk access. Transient storage is accessible to smart contracts via 2 new opcodes, `TLOAD` and `TSTORE`, where “T” stands for "transient:" ``` @@ -20,6 +21,7 @@ TSTORE (0xb4) ``` ## Motivation + Running a transaction in Ethereum can generate multiple nested frames of execution, each created by `CALL` (or similar) instructions. Contracts can be re-entered during the same transaction, in which case there are more than one frame belonging to one contract. Currently, these frames can communicate in two ways: via inputs/outputs passed via `CALL` instructions, and via storage updates. If there is an intermediate frame belonging to another untrusted contract, communication via inputs/outputs is not secure. Notable example is a reentrancy lock which cannot rely on the intermediate frame to pass through the state of the lock. Communication via storage (`SSTORE`/`SLOAD`) is costly. Transient storage is a dedicated and gas efficient solution to the problem of inter frame communication. Storage refunds accumulated due to inter frame communication are also limited to 20% of gas spent by a transaction due to [EIP-3529](./eip-3529.md) (introduced in the London hard fork). This greatly reduces the refunds for transiently-set storage slots in otherwise low-cost transactions. For example, in order to receive the full refund of one re-entrancy lock, the transaction must spend ~80k gas on other operations. @@ -27,9 +29,10 @@ Storage refunds accumulated due to inter frame communication are also limited to Language support could be added in relatively easy way. For example, in Solidity, a qualifier `transient` can be introduced (similar to the existing qualifiers `memory` and `storage`, and Java's own `transient` keyword with a similar meaning). Since the addressing scheme of `TSTORE` and `TLOAD` is the same as for `SSTORE` and `SLOAD`, code generation routines that exist for storage variables, can be easily generalised to also support transient storage. Potential use cases enabled or improved by this EIP include: + 1. Reentrancy lock 2. Constructor arguments loaded from the factory contract (for on-chain-computable CREATE2 addresses as in Uniswap V3) -3. Single transaction ERC20 approvals, e.g. `#approveAndCall(address callee, uint256 amount, bytes memory data)` +3. Single transaction [EIP-20](./eip-20.md) approvals, e.g. `#approveAndCall(address callee, uint256 amount, bytes memory data)` 4. More generic libraries that use callbacks, for example generalised sorting with functions `Less` and `Swap` defined. 5. Contracts that require control before and after method execution (e.g. via callbacks) 6. Shared memory (borrowed from early draft of similar EIP by @holiman). When implementing contract-proxies using `DELEGATECALL`, all direct arguments are relayed from the caller to the callee via the `CALLDATA`, leaving no room for meta-data between the proxy and the proxee. Also, the proxy must be careful about `storage` access to avoid collision with `target` `storage`-slots. Since `transient storage` would be shared, it would be possible to use `transient storage` to pass information between the `proxy` and the `target`. @@ -37,6 +40,7 @@ Potential use cases enabled or improved by this EIP include: These opcodes are more efficient to execute than the `SSTORE` and `SLOAD` opcodes because the original value never needs to be loaded from storage (i.e. is always 0). The gas accounting rules are also simpler, since no refunds are required. ## Specification + Two new opcodes are added to EVM, `TLOAD` (`0xb3`) and `TSTORE` (`0xb4`). Note that previous drafts of this EIP specified the values `0x5c` and `0x5d` for `TLOAD` and `TSTORE` respectively, but these have been modified so as not to conflict with other draft EIPs. They use the same arguments on stack as `SLOAD` (`0x54`) and `SSTORE` (`0x55`). @@ -57,19 +61,22 @@ When transient storage is used in the context of `DELEGATECALL` or `CALLCODE`, t If a frame reverts, all writes to transient storage that took place between entry to the frame and the return are reverted, including those that took place in inner calls. This mimics the behavior of persistent storage. -If the `TSTORE` opcode is called within the context of a `STATICCALL`, the call must revert. `TLOAD` is allowed within the context of a `STATICCALL`. +If the `TSTORE` opcode is called within the context of a `STATICCALL`, it will result in an exception instead of performing the modification. `TLOAD` is allowed within the context of a `STATICCALL`. ## Rationale + Another option to solve the problem of inter-frame communication is repricing the `SSTORE` and `SLOAD` opcodes to be cheaper for the transient storage use case. This has already been done as of [EIP-2200](./eip-2200.md). However, [EIP-3529](./eip-3529.md) reduced the maximum refund to only 20% of the transaction gas cost, which means the use of transient storage is severely limited. Another approach is to keep the refund counter for transient storage separate from the refund counter for other storage uses, and remove the refund cap for transient storage. However, that approach is more complex to implement and understand. For example, the 20% refund cap must be applied to the gas used _after_ subtracting the uncapped gas refund. Otherwise, the refund amount available subject to the 20% refund cap could be increased by executing transient storage writes. Thus it is preferable to have a separate mechanism that does not interact with the refund counter. Future hard forks can remove the complex refund behavior meant to support the transient storage use case, encouraging migration to contracts that are more efficient for the Ethereum clients to execute. -Relative cons of this transient storage EIP: +Relative cons of this transient storage EIP: + - Does not address transient usages of storage in existing contracts - New code in the clients - New concept for the yellow paper (more to update) Relative pros of this transient storage EIP: + - Transient storage opcodes are considered separately in protocol upgrades and not inadvertently broken (e.g. [EIP-3529](./eip-3529.md)) - Clients do not need to load the original value - No upfront gas cost to account for non-transient writes @@ -79,14 +86,17 @@ Relative pros of this transient storage EIP: - Future storage designs (e.g. Verkle tree) do not need to account for transient storage refunds ## Backwards Compatibility + This EIP requires a hard fork to implement. Since this EIP does not change behavior of any existing opcodes, it is backwards compatible with all existing smart contracts. ## Reference Implementation + Because the transient storage must behave identically to storage within the context of a single transaction with regards to revert behavior, it is necessary to be able to revert to a previous state of transient storage within a transaction. At the same time reverts are exceptional cases and loads, stores and returns should be cheap. A map of current state plus a journal of all changes and a list of checkpoints is recommended. This has the following time complexities: + - On entry to a call frame, a call marker is added to the list - `O(1)` - New values are written to the current state, and the previous value is written to the journal - `O(1)` - When a call exits successfully, the marker to the journal index of when that call was entered is discarded - `O(1)` @@ -208,6 +218,7 @@ contract TryDOS { ``` ## Security Considerations + `TSTORE` presents a new way to allocate memory on a node with linear cost. In other words, each TSTORE allows the developer to store 32 bytes for 100 gas, excluding any other required operations to prepare the stack. Given 30 million gas, the maximum amount of memory that can be allocated using TSTORE is: ``` @@ -228,7 +239,8 @@ However, if you only spend 1M gas allocating memory in each context, and make ca 30M gas * ~21,872 words / 1M gas * 32 bytes/word * 1MB / 2^20 bytes = ~20MB ``` -For smart contract developers, there are no security considerations for existing contracts because they do not contain the new `TSTORE` or `TLOAD` opcodes. New contracts that do use the `TSTORE` and `TLOAD` opcodes operate under all the same assumptions as with storage. This EIP introduces no additional mental overhead for the developer. In some cases, transient storage simplifies contracts in that contract transient storage does not need to be cleared at the end of every transaction, e.g. in the ERC20 temporary approvals use case. +For smart contract developers, there are no security considerations for existing contracts because they do not contain the new `TSTORE` or `TLOAD` opcodes. New contracts that do use the `TSTORE` and `TLOAD` opcodes operate under all the same assumptions as with storage. This EIP introduces no additional mental overhead for the developer. In some cases, transient storage simplifies contracts in that contract transient storage does not need to be cleared at the end of every transaction, e.g. in the [EIP-20](./eip-20.md) temporary approvals use case. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1202.md b/EIPS/eip-1202.md index e812d80784047a..9ab0f843a315da 100644 --- a/EIPS/eip-1202.md +++ b/EIPS/eip-1202.md @@ -1,159 +1,62 @@ --- eip: 1202 -title: Voting Standard +title: Voting Interface +description: A general interface for voting on-chain author: Zainan Victor Zhou (@xinbenlv), Evan (@evbots), Yin Xu (@yingogobot) +discussions-to: https://ethereum-magicians.org/t/eip-1202-voting-interface/11484 +status: Draft type: Standards Track category: ERC -status: Draft created: 2018-07-08 -discussions-to: https://github.com/ethereum/EIPs/issues/1202 --- -## Simple Summary - -Propose a standard interface for voting. - ## Abstract -This proposal creates a standard API for implementing voting within smart contract. This standard provides functionalities to voting as well as to view the vote result and set voting status. +This EIP is an API for implementing voting with smart contract. This standard provides functionalities to voting as well as to view the vote result and set voting status. ## Motivation Voting is one of the earliest example of EVM programming, and also a key to DAO/organizational governance process. We foresee many DAOs will ultimately need to leverage voting as one of the important part of their governance. By creating a voting standard for smart contract / token, we can have the following benefits -### Benefits +### Benefits of having a standard 1. Allow general UI and applications to be built on top of a standardized voting to allow more general user to participate, and encourage more DApp and DAO to think about their governance 2. Allow delegate voting / smart contract voting, automatic voting 3. Allow voting results to be recorded on-chain, in a standard way, and allow DAOs and DApps to honor the voting result programmatically. -4. Allow the compatibility with token standard such as [ERC-20](./eip-20.md) or other new standards([EIP-777](./eip-777.md)) and item standard such as [EIP-721](./eip-721.md) +4. Allow the compatibility with token standard such as [EIP-20](./eip-20.md) or other new standards([EIP-777](./eip-777.md)) and item standard such as [EIP-721](./eip-721.md) 5. Create massive potential for interoperability within Ethereum echo systems and other system. -6. Allow setting voting deadline, allow determine on single or multiple options. Allow requiring voting orders. (trade-off is interface complexity, we might need [ERC-20](./eip-20.md) approach and later a [EIP-777](./eip-777.md) for advanced voting) +6. Allow setting voting deadline, allow determine on single or multiple options. Allow requiring voting orders. (trade-off is interface complexity, we might need [EIP-20](./eip-20.md) approach and later a [EIP-777](./eip-777.md) for advanced voting) 7. Recording the voting with weights with token amount. 8. Possibly allow trust-worthy privacy-safe voting and anonymous voting (with either voter address being un-associated with the vote they cast, given a list of randomized/obfuscated voting options). - 8 -9. Possibly allow result in reward by voting participation or voting result -### Use-cases: +9. Possibly allow result in reward by voting participation or voting result. + +### Non-Goal / Out of Scope + +1. **Delegation**: We intentionally leave delegation out of scope. A separate EIP could be proposed to address this particular use case. +2. **Eligibility or Weights**: Some of the implementing want to have weights or eligibility to vote to be configurable. Such as OpenZeppelin's implementation of GovernorBravo uses snapshot. Aslo weights calculation such as quadratic voting is not within the scope of this EIP. This EIP is intend to be flexible for +any current and new voting weights calculation. +3. **Proposal**: We intentionally leave Proposal out of scope. Proposals are going to be identified by `proposalId` but what information of the proposal includes, +whether they are on-chain or off-chain and whether they are exeutable, is leaved out from this proposal. A separate EIP could be proposed to address this particular use case. See one of such proposals [EIP-5247](./eip-5247.md) +4. **Signature Aggregations / Endorsement**: When implementing contracts want to allow user to submit their vote or approval of vote offline and have some other +account to generate the transaction, the signature aggregations or endorsements are not in scope of this EIP. A separate EIP could be proposed to address this particular use case. See one of such proposals here [EIP-5453](./eip-5453.md). + +### Use-cases 1. Determine on issuing new token, issuing more token or issuing sub-token 2. Determine on creating new item under [EIP-721](./eip-721.md) 3. Determine on election on certain person or smart contract to be delegated leader for project or subproject 4. Determine on auditing result ownership allowing migration of smart contract proxy address -## Specifications +## Specification -### Draft V2 +1. Compliant contracts MUST implement the `IERC1202Core` below ```solidity -pragma solidity >=0.7.0 <0.9.0; - - -/// @title Core interface of ERC1202: A list of *REQUIRED* methods and events for -/// a contract to be considered conforming to ERC1202. -/// -/// @author Zainan Victor Zhou -/// -/// @dev Each ERC1202 contract is a cluster of issues being voted on, or done voted. -/// Any contract of ERC1202 **MUST** implement ALL the following methods and events. -/// -/// Each *issue* is identified with an `issueId`, -/// For any given `issue`, each available option in that issue is -/// identified with an `optionId`. -interface ERC1202Core { - - /// @dev Cast a vote for an issue with `issueId` for option with `optionId` - /// @param _issueId: the issue this vote is casting on. - /// @param _optionIds: an *ordered* array of the options being casted for the issue. - /// Whenever referring to the options as a whole, the order MUST be maintained. - /// @return a boolean if TRUE means the vote is casted successfully. - function vote(uint _issueId, uint[] memory _optionIds) external returns (bool); - - /// @dev Query the top ranked options of an issue given issueId and - /// a limit of max number of top options. - /// @param _issueId: the issue being queried for the top options. - /// @param _limit: the max number of top options the caller expect to return. - /// @return an ordered list of the top options for given issueId and limit, - /// where the first in array is the most favorite one, and the last in - /// array is the least favorite one among the list. - /// Specifically, WHEN limit = 0, returns the default length of winning - /// options in their ranking in an issue. - function topOptions( - uint _issueId, uint _limit - ) external view returns (uint[] memory); - - /// @dev This event is emitted when a vote has been casted. - /// @param issueId the issue the vote is being cased on. - /// @param optionIds an ordered list of the options the vote is casting for. - event OnVote(uint indexed issueId, uint[] optionIds, address indexed voterAddr); - -} - -/// @title Metadata interface for ERC1202: A list of *RECOMMENDED* methods and events for -/// a contract to be considered conforming to ERC1202. -/// -/// @author Zainan Victor Zhou -interface ERC1202Metadata { - - /// @notice A descriptive text for an issue in this contract. - function issueText() external view returns (string memory _text); - - /// @notice A distinct Uniform Resource Identifier (URI) for a given issue. - /// @dev Throws if `_issueId` is not a valid issue; - /// URIs are defined in RFC 3986. - function issueURI(uint256 _issueId) external view returns (string memory _uri); - - /// @notice A descriptive text for an option in an issue in this contract. - function optionText(uint _issueId, uint _optionId) external view returns (string memory _text); - - /// @notice A distinct Uniform Resource Identifier (URI) for a given option in a given issue. - /// @dev Throws if `_issueId` is not a valid option-issue combination; - /// URIs are defined in RFC 3986. - function optionURI(uint _issueId, uint _optionId) external view returns (string memory _uri); -} - -/// @title Status interface for ERC1202: A list of *RECOMMENDED* methods and events for -/// a contract to be considered conforming to ERC1202. -/// -/// @author Zainan Victor Zhou -interface ERC1202Status { - - /// @dev This event is emitted when an issue has changed status. - /// @param issueId the issue about which a status change has happened. - /// @param isOpen the status - event OnStatusChange(uint indexed issueId, bool indexed isOpen); - - /// @dev Sets the status of a issue, e.g. open for vote or closed for result. - /// @param _issueId the issue of Status being set. - /// @param _isOpen the status to set. - /// @return _success whether the setStatus option succeeded. - function setStatus(uint _issueId, bool _isOpen) external returns (bool _success); - - /// @dev Gets the status of a issue, e.g. open for vote or closed for result. - /// @param _issueId the issue of Status being get. - /// @return _isOpen the status of the issue. - function getStatus(uint _issueId) external view returns (bool _isOpen); - - /// @dev Retrieves the ranked options voted by a given voter for a given issue. - /// @param _issueId the issue - /// @param _voter the aaddres of voter. - /// @return _optionIds the ranked options voted by voter. - function voteOf(uint _issueId, address _voter) external view returns (uint[] memory _optionIds); -} - -``` - -### Draft V3 - -#### Single Option Proposal: Yes/Nay/Abstain - -```solidity -pragma solidity >=0.7.0 <0.9.0; - interface IERC1202Core { event VoteCast( address indexed voter, - uint256 proposalId, + uint256 indexed proposalId, uint8 support, uint256 weight, string reason, @@ -165,173 +68,93 @@ interface IERC1202Core { uint8 support, uint256 weight, string calldata reasonUri, - bytes calldata extraParams // TODO: under what circumstance the params will be used? - ) external returns (uint256 balance); - - function execute(uint256 proposalId, bytes memory extraParams) - external - returns (uint256 balance); - - function votingPeriodFor(uint256 proposalId) external view returns (uint256 startBlock, uint256 endBlock); -} - -interface IERC1202XDelegate { - function delegates(address account) external view returns (address); - function delegate(address delegatee, bytes[] calldata extraParams) external; -} + bytes calldata extraParams + ) external payable returns; -interface IERC1202XProposal { - event ProposalCreated( + function castVoteFrom( + address from, uint256 proposalId, - string proposalUri, - address proposer, - address[] targets, - uint256[] values, - string[] signatures, - bytes[] calldatas, - uint256 startBlock, - uint256 endBlock - ); - event ProposalExecuted(uint256 proposalId); - // TODO: add Proposal Cancel/Edit/Withdraw Event and Functions? + uint8 support, + uint256 weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable returns; - // TODO: decide whether we require generating ProposalId in the method or not? - // TODO: if require generating ProposalId internally, can it be incremental hash-generated? - // TODO: what if proposal need to demonstrate sufficient support? How to input quorum? - function proposeProposal( - uint256 proposalId, - string memory proposalUri, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - uint256 startblock, - uint256 endblock, - bytes memory extraParams - ) external returns (uint256 registeredProposalId); - - // TODO: what's most proper way to update the voting period? - // TODO: do we want to include cancel or withdraw? - // TODO: what's the best way to include weight scheme? + function execute(uint256 proposalId, bytes memory extraParams) payable external; } - ``` -#### Multi-Option Proposal: Board Elections or Ranked Elections +2. Compliant contracts MAY implement the `IERC1202MultiVote` Interface. If the intention is for multi-options to be supported, e.g. for ranked-choices +or variant weights voting, Compliant contracts MUST implement `IERC1202MultiVote` Interface. ```solidity -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; - -interface IERC1202Core { - event VoteCast( +interface IERC1202MultiVote { + event MultiVoteCast( address indexed voter, - uint256 proposalId, - uint8[] optionIds, - uint256[] weights, + uint256 indexed proposalId, + uint8[] support, + uint256[] weight, string reason, bytes extraParams ); - function castVote( + function castMultiVote( uint256 proposalId, - uint8[] calldata optionIds, - uint256[] calldata weights, + uint8[] support, + uint256[] weight, string calldata reasonUri, - bytes calldata extraParams // TODO: under what circumstance the params will be used? - ) external returns (uint256[] memory balance); - - function execute(uint256 proposalId, bytes memory extraParams) - external - returns (uint256 balance); - - function votingPeriodFor(uint256 proposalId) - external - view - returns (uint256 startBlock, uint256 endBlock); -} - -interface IERC1202XDelegate { - function delegates(address account) external view returns (address); - function delegate(address delegatee, bytes[] calldata extraParams) external; + bytes calldata extraParams + ) external payable; } +``` -interface IERC1202XProposal { - event ProposalCreated( - uint256 proposalId, - string proposalUri, - uint8[] optionIds, - address proposer, - address[][] targets, - uint256[][] values, - string[][] signatures, - bytes[][] calldatas, - uint256 startBlock, - uint256 endBlock - ); - event ProposalExecuted(uint256 proposalId); - // TODO: add Proposal Cancel/Edit/Withdraw Event and Functions? +### Getting Info: Voting Period, Eligibility, Weight - // TODO: decide whether we require generating ProposalId in the method or not? - // TODO: if require generating ProposalId internally, can it be incremental hash-generated? - // TODO: what if proposal need to demonstrate sufficient support? How to input quorum? - function proposeProposal( - uint256 proposalId, - string calldata proposalUri, - uint8[] calldata optionIds, - address[] calldata targets, - uint256[] calldata values, - bytes[] calldata calldatas, - uint256 startblock, - uint256 endblock, - bytes calldata extraParams - ) external returns (uint256 registeredProposalId); - - // TODO: what's most proper way to update the voting period? - // TODO: do we want to include cancel or withdraw? - // TODO: what's the best way to include weight scheme? +```solidity +interface IERC1202Info { + function votingPeriodFor(uint256 proposalId) external view returns (uint256 startPointOfTime, uint256 endPointOfTime); + function eligibleVotingWeight(uint256 proposalId, address voter) external view returns (uint256); } - ``` ## Rationale We made the following design decisions and here are the rationales. -- **Granularity and Anonymity:**: We created a `view` function `ballotOf` primarily making it easier for people to check the vote from certain address. This has the following assumptions: +### Granularity and Anonymity + +We created a `view` function `ballotOf` primarily making it easier for people to check the vote from certain address. This has the following assumptions: - - It's possible to check someone's vote directly given an address. If implementor don't want to make it so easily, they can simply reject all calls to this function. We want to make sure that we support both anonymous voting an non-anonymous voting. However since all calls to a smart contract is logged in block history, there is really no secrecy unless done with cryptography tricks. I am not cryptography-savvy enough to comment on the possibility. Please see "Second Feedback Questions 2018" for related topic. +- It's possible to check someone's vote directly given an address. If implementor don't want to make it so easily, they can simply reject all calls to this function. We want to make sure that we support both anonymous voting an non-anonymous voting. However since all calls to a smart contract is logged in block history, there is really no secrecy unless done with cryptography tricks. I am not cryptography-savvy enough to comment on the possibility. Please see "Second Feedback Questions 2018" for related topic. - - It's assumes for each individual address, they can only vote for one decision. They can distribute their available voting power into more granular level. If implementor wants allow this, they ask the user to create another wallet address and grant the new address certain power. For example, a token based voting where voting weight is determined by the amount of token held by a voter, a voter who wants to distribute its voting power in two different option(option set) can transfer some of the tokens to the new account and cast the votes from both accounts. +- It's assumes for each individual address, they can only vote for one decision. They can distribute their available voting power into more granular level. If implementor wants allow this, they ask the user to create another wallet address and grant the new address certain power. For example, a token based voting where voting weight is determined by the amount of token held by a voter, a voter who wants to distribute its voting power in two different option(option set) can transfer some of the tokens to the new account and cast the votes from both accounts. -- **Weight**: We assume there are `weight` of votes and can be checked by calling `weightOf(address addr)`, and the weight distribution is either internally determined or determined by constructor. However we have not been considering updating the weight distribution. Please comment on this design decision as we want to learn how likely an implementor would want to be able to update the voting weight distributions. +### Weights -## Backward Compatibility +We assume there are `weight` of votes and can be checked by calling `eligibleVotingWeight(proposalId, address voter)`, and the weight distribution is either internally determined or determined by constructor. -There is no backward compatibility issue we are aware of. +## Backwards Compatibility + +1. The `support` options are chosen to be `uint8` for the purpose to be backward compatible for GovernorBravo. It can be increased in the future ## Security Considerations -EIP-1202 is a voting standard. We expect the voting standard to be used in connection with other contracts such as token distributions, conducting actions in consensus or on behalf of an entity, multi-signature wallets, etc. +We expect the voting standard to be used in connection with other contracts such as token distributions, conducting actions in consensus or on behalf of an entity, multi-signature wallets, etc. The major security consideration is to ensure only using the standard interface for performing downstream actions or receiving upstream input (vote casting). We expect future audit tool to be based on standard interfaces. -It's also important to note as discussed in this standard that for the sake of simplicity, EIP-1202 is kept in the very basic form. It can be extended to support many different implementation variations. Such variations might contain different assumptions of the behavior and interpretation of actions. One example would be: What does it mean if someone votes multiple times through `vote`? +It's also important to note as discussed in this standard that for the sake of simplicity, this EIP is kept in the very basic form. It can be extended to support many different implementation variations. Such variations might contain different assumptions of the behavior and interpretation of actions. One example would be: What does it mean if someone votes multiple times through `vote`? - Would that mean the voter is increasing their weight, or - vote multiple options in the meanwhile, or - Does the latter vote override the previous vote? -Because of the flexible nature of voting, we expect many subsequent standards need to be created as an extension of EIP-1202. We suggest any extension or implementations of this standard be thoroughly audited before included in large scale or high asset volume applications. +Because of the flexible nature of voting, we expect many subsequent standards need to be created as an extension of this EIP. We suggest any extension or implementations of this standard be thoroughly audited before included in large scale or high asset volume applications. The third consideration is non-triviality. Some voting applications assume **_anonymity_**, **_randomness_**, **_time-based deadline_**, **_ordering_**, etc, these requirements in Ethereum are known to be non-trivial to achieve. We suggest any applications or organizations rely on audited and time-proven shared libraries when these requirements need to be enforced in their applications. The fourth consideration is potential abuse. When voting is standardized and put on contract, it is possible to write another contract that rewards a voter to vote in a certain way. It creates potential issues of bribery and conflict of interest abuse that is previously hard to implement. -## Work Directory - -The drafting and revision of EIP-1202 is conducted at [GitHub/xinbenlv/eip-1202](https://github.com/xinbenlv/eip-1202) - ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1271.md b/EIPS/eip-1271.md index f9bbdd8c74ecae..55da665aef721e 100644 --- a/EIPS/eip-1271.md +++ b/EIPS/eip-1271.md @@ -127,12 +127,7 @@ Example implementation of a signing contract: } // Recover ECDSA signer - signer = ecrecover( - keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)), - v, - r, - s - ); + signer = ecrecover(_hash, v, r, s); // Prevent signer from being 0x0 require( diff --git a/EIPS/eip-2294.md b/EIPS/eip-2294.md index 1fd805d5bb50ab..9837e6ad5dc67c 100644 --- a/EIPS/eip-2294.md +++ b/EIPS/eip-2294.md @@ -4,7 +4,7 @@ title: Explicit bound to Chain ID size description: Adds a maximum value to the Chain ID parameter to avoid potential encoding issues that may occur when using large values of the parameter. author: Zainan Victor Zhou (@xinbenlv), Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2294-explicit-bound-to-chain-id/11090 -status: Draft +status: Review type: Standards Track category: Core created: 2019-09-19 @@ -12,36 +12,48 @@ requires: 155 --- ## Abstract -This EIP restricts the size of the [EIP-155](./eip-155.md) Chain ID parameter to a particular maximum value `floor(MAX_UINT64 / 2) - 36`, in order to ensure that there is a standard around how this parameter is to be used between different projects. -## Motivation -EIP-155 introduces the Chain ID parameter, which is an important parameter used for domain separation (replay protection) of Ethereum protocol signed messages. However, it does not specify any properties about the size that this parameter takes. @axic mentions this concern in the 109th comment of [EIP-1344](./eip-1344.md)'s `discussion-to` +Starting from `blocknum = FORK_BLKNUM`, this EIP restricts the size of the [EIP-155](./eip-155.md) Chain ID parameter to a particular maximum value `floor(MAX_UINT64 / 2) - 36`, in order to ensure that there is a standard around how this parameter is to be used between different projects. -> Allowing it to be 256-bit wide means that the RLP encoding of a transaction must use >256-bit arithmetic to calculate the v field. +## Motivation -<-- -TODO(xinbenlv): clean up motivation by splitting rationale of design decisions from motivation above. ---> +EIP-155 introduces the Chain ID parameter, which is an important parameter used for domain separation (replay protection) of Ethereum protocol signed messages. However, it does not specify any properties about the size that this parameter takes. Allowing it to be 256-bit wide means that the RLP encoding of a transaction must use >256-bit arithmetic to calculate the v field. and suggests a reasonable maximum enforced size in order to ensure that there are no issues when encoding this parameter. This would allow a sufficient amount of different values for this parameter, which is typically chosen by community consensus as a genesis parameter for a given chain and thus does not change often. + +Without a well-chosen value of Chain ID, there could be differences in the implementation of [EIP-155](./eip-155.md) (and [EIP-1344](./eip-1344.md) by derivative) in both client codebase and external tooling that could lead to consensus-critical vulnerabilities being introduced to the network. By making this limit explicit, we avoid this scenario for Ethereum and any project which uses the Ethereum codebase. + There have been suggestions of using a hash-based identifier in place on Chain ID to allow the value to adapt over time to different contentious forks and other scenarios. This proposal does not describe this behavior, but ~63 bits of entropy should be enough to ensure that no collisions are likely for reasonable (e.g. non-malicious) uses of this feature for that purpose. +Also, the field `chainID` have experienced increasing usage and dependencies, due more and more contracts are depending on [EIP-1344](./eip-1344.md) to expose CHAIN ID in the smart contract execution. For example when used with [EIP-712](./eip-712.md), [EIP-1271](./eip-1271.md) for on-contract signature verification, chainId has been increasingly introduced for replay attack prevention. It's security critical to ensure clients depending on the chainId computation in cryptography yields identical result for verification in +all cases. + Originally, this EIP was created by Bryant Eisenbach (@fubuloubu) and Alex Beregszaszi (@axic). ## Specification -The maximum value of Chain ID is `9,223,372,036,854,775,771` (`MAX_CHAIN_ID`). This value is `floor(MAX_UINT64 / 2) - 36`, and is chosen to avoid overflow when performing uint64 math. For reference, a value of 0 or less is also disallowed. -A client should reject a value outside of this range in a provided transaction, and disallow a genesis configuration with a value for Chain ID outside of this limit. Due to how the calculation for chain ID is performed, the maximum value seen during the arithmetic is `CHAIN_ID * 2 + 36`, so clients must test to ensure no overflow conditions are encountered when the highest value is used. No underflow is possible. +Starting from `blocknum = FORK_BLKNUM`, the maximum value of Chain ID is `9,223,372,036,854,775,771` (`MAX_CHAIN_ID`). + +1. Compliant client MUST reject a value outside of the range of `[0, MAX_CHAIN_ID]` in a provided transaction starting from `blocknum = FORK_BLKNUM` +2. Compliant client MUST disallow a genesis configuration with a value for Chain ID outside of this limit. + +Due to how the calculation for chain ID is performed, the maximum value seen during the arithmetic is `CHAIN_ID * 2 + 36`, so clients must test to ensure no overflow conditions are encountered when the highest value is used. No underflow is possible. ## Rationale -Without a well-chosen value of Chain ID, there could be differences in the implementation of EIP-155 (and EIP-1344 by derivative) in both client codebase and external tooling that could lead to consensus-critical vulnerabilities being introduced to the network. By making this limit explicit, we avoid this scenario for Ethereum and any project which uses the Ethereum codebase. + +The `MAX_CHAIN_ID` is calculated to avoid overflow when performing uint64 math. For reference, a value of 0 or less is also disallowed. ## Backwards Compatibility -This EIP introduces a change that affects previous implementations of this feature. However, since no known chain makes use of a value outside of the suggested bounds, there should not be an issue in adopting this limit on the size of this parameter, therefore the impact should be non-existent. + +This EIP introduces a change that affects previous implementations of this feature. However, as of time of writing(2022-10-18) no known chain makes use of a value outside of the suggested bounds, there should not be an issue in adopting this limit on the size of this parameter, therefore the impact should be non-existent. + +If any other chain is operating with a incompatible `chainId`, we advised they make proper arrangement when this EIP becomes adopted. ## Security Considerations + Needs discussion. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index 3abe276052f57e..a74b9260f8dbf7 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -16,10 +16,12 @@ requires: 3540, 3670, 4200 This proposal provides a _complete_, _efficient_, _safe_ and _static_ control-flow facility. It introduces two new opcodes to support calling and returning from subroutines: + * `RJUMPSUB relative_offset` -- relative jump to subroutine * `RETURNSUB` -- return to `PC` after most recent `RJUMPSUB`. It depends on the two new opcodes proposed by [EIP-4200](./eip-4200.md) to support static jumps: + * `RJUMP relative_offset` — relative jump to `PC + relative_offset` * `RJUMPI relative_offset` — conditional relative jump @@ -31,42 +33,43 @@ This is among the simplest possible proposals that meets these requirements. ## Motivation -### We need a complete control-flow facility. +### A complete control-flow facility. Jumps, conditional jumps and subroutines were proposed by Alan Turing in 1945 as a means of organizing the logic of the code and the design of the memory crystals for his Automatic Computing Engine: > We wish to be able to arrange that sequences of orders can divide at various points, continuing in different ways according to the outcome of the calculations to date... We also wish to be able to arrange for the splitting up of operations into subsidiary operations... To start on a subsidiary operation we need only make a note of where we left off the major operation and then apply the first instruction of the subsidiary. When the subsidiary is over we look up the note and continue with the major operation. -> -> — Alan Turing — *in B.E. Carpenter, R.W. Doran, "The other Turing machine." The Computer Journal, Volume 20, Issue 3, January 1977.* +> +> — Alan Turing — in B.E. Carpenter, R.W. Doran, "The other Turing machine." The Computer Journal, Volume 20, Issue 3, January 1977. In more contemporary terms, we have sequences of instructions, jumps and conditional jumps that divide sequences into blocks, subroutine calls, and a stack of addresses to return to. The details vary, but similar facilities have proven their value across a long line of important machines over the last 75 years, including most all of the machines we have programmed or implemented -- physical machines including the Burroughs 5000, CDC 7600, IBM 360, DEC PDP-11 and VAX, Motorola 68000, Sun SPARC, and Intel x86s, as well as virtual machines for Scheme, Forth, Pascal, Java, Wasm, and others. Unlike these machines, the Ethereum Virtual Machine _does not_ provide subroutine operations. Instead, they must be synthesized using the dynamic `JUMP` instruction, which takes its destination on the stack. Further, the EVM provides _only_ dynamic jumps, impeding the static analysis we need. -### We need efficient control-flow. +### Efficient control-flow. Efficient to write by hand, compile from high level labguages, validate at deploy time, interpret by VMs, and compile to machine code. Static jumps, conditional jumps, and subroutines are sufficient and efficient in space and time, as shown by historical experience and as we will show for the EVM below. -### We need safe control-flow. +### Safe control-flow. -The EVM has unusually high requirements for safety. Not only do many smart contracts control inordinately large amounts of valuable Ether, but once placed on the blockchain any defects are visible to attackers and cannot be repaired. We need to statically validate important safety constraints on code at initialization time. +The EVM has unusually high requirements for safety. Not only do many smart contracts control inordinately large amounts of valuable Ether, but once placed on the blockchain any defects are visible to attackers and cannot be repaired. We propose to statically validate important safety constraints on code at initialization time. -### We need static control-flow. +### Static control-flow. The EVM's dynamic jumps cause two major problems. First, the need to synthesize static jumps and subroutines with dynamic jumps wastes space and gas with needlessly complex code, as we will show below. Worse, jumps that can dynamically branch to any destination in the code can cause quadratic "path explosions" when traversing the flow of control. For Ethereum, this is a denial-of-service vulnerability that prevents us, at initialization time, from validating the safe use of EVM code and from compiling EVM code to machine code. -We need static control-flow to support streaming, one-pass, and other near-linear compilers of EVM bytecode to machine code. +We _need_ static control-flow to validate program safety and to compile EVM bytecode to machine code -- in time and space linear in the size of the code. ## Specification ### Opcodes -#### `RJUMPSUB (0x5f) relative_offset +#### `RJUMPSUB (0x5f) relative_offset` Transfers control to a subroutine. + 1. Decode the `relative_offset` from the immediate data at `PC`. 2. Push the current `PC + 3` to the `return stack`. 3. Set `PC` to `PC + relative_offset`. @@ -78,24 +81,28 @@ The gas cost is _low_. #### `RETURNSUB (0x5e)` Returns control to the caller of a subroutine. + 1. Pop the `return stack` to `PC`. The gas cost is _verylow_. _Notes:_ -* _Values popped off the `return stack` do not need to be validated, since they are alterable only by `RJUMPSUB` and `RETURNSUB`._ + +* _Values popped off the `return stack` do not need to be validated, since they are alterable only by `RJUMPSUB` and `RETURNSUB`._ * _The description above lays out the semantics of these instructions in terms of a `return stack`. But the actual state of the `return stack` is not observable by EVM code or consensus-critical to the protocol. (For example, a node implementer may code `RJUMPSUB` to unobservably push `PC` on the `return stack` rather than `PC + 1`, which is allowed so long as `RETURNSUB` observably returns control to the `PC + 3` location.)_ ### Validity -If the execution of an instruction would violate a condition, then the execution is in an exceptional halting state. The Yellow Paper defines five such states. +_Execution_ is defined in the Yellow Paper as a sequence of changes in the EVM state. The conditions on valid code are preserved by state changes. At runtime, if execution of an instruction would violate a condition the execution is in an exceptional halting state. The Yellow Paper defines six such states. + 1. Insufficient gas 2. More than 1024 stack items -3. Insufficient stack items -4. Invalid jump destination -5. Invalid instruction +3. State modification during a static call +4. Insufficient stack items +5. Invalid jump destination +6. Invalid instruction -We would like to consider EVM code valid iff no execution of the program can lead to an exceptional halting state. In practice, we must test at runtime for conditions 1 and 2 — sufficient gas and sufficient stack. We don’t know how much gas there will be, we don’t know how deep a recursion may go, and analysis of stack depth even for non-recursive programs is nontrivial. All of the remaining conditions MUST be validated statically, in time and space quasi-linear in the size of the code. +We would like to consider EVM code valid iff no execution of the program can lead to an exceptional halting state. In practice, we must test at runtime for the first three conditions. We don’t know how much gas there will be, we don’t know how deep a recursion may go, analysis of stack depth even for non-recursive programs is nontrivial, and we don't know whether a call will be static. All of the remaining conditions MUST be validated statically, in time and space quasi-linear in the size of the code. #### Static Constraints on Valid Code @@ -109,7 +116,7 @@ We would like to consider EVM code valid iff no execution of the program can lea * The data stack MUST be consistently aligned: * The data stack height is * the absolute difference between the current `stack pointer` and the `stack pointer` on entry to the current subroutine. - * It MUST be the same for every reachable path through a given `PC` and + * It MUST be the same for every reachable path through a given `PC` and * MUST NOT exceed 1024. ## Rationale @@ -127,11 +134,12 @@ The constraints on valid code cover all of the exceptional halting states that w The `RJUMP`, `RJUMPI` and `RJUMPSUB` instructions take their *relative_offset* as immediate arguments, which cannot change at runtime. Having constant destinations for all jumps means that all jump destinations can be validated at initialization time, not runtime. Dynamic jumps can branch to any destination in the code, so exploitable quadratic "path explosions" are possible when traversing the control flow graph. Deprecating `JUMP` and `JUMPI` prevents this. Requiring a consistently aligned `data stack` + * prevents stack underflow * ensures that all calls to a subroutine have the same number of inputs and the same number of outputs and * ensures that stack height is bounded in the absence of recursion. -Requiring a consistently aligned `data stack` also allows some algorithms that traverse the control-flow graph -- including code validation and compilation -- to break cycles at joins, again preventing quadratic path explosion. When a traversal gets to a `PC` it has visited before it is either at the beginning of a loop or the entry to a function -- and the stacks will align. So we know that loops will not grow stack, and that the number of arguments to a subroutine will always be the same -- there is no need to traverse that path again. +Requiring a consistently aligned `data stack` also allows some algorithms that traverse the control-flow graph -- including code validation and compilation -- to break cycles at joins, again preventing quadratic path explosion. When a traversal gets to a `PC` it has visited before it is either at the beginning of a loop or the entry to a function. Since the stack height at that `PC` is constant we know that loops will not grow stack, and that the number of arguments to a subroutine will always be the same -- there may be no need to traverse that path again. _Note: The JVM and Wasm enforce similar constraints for similar reasons._ @@ -141,7 +149,7 @@ There are a few major designs for a subroutine facility, two of which are consid *1. Keep return addresses on a dedicated return stack.* Turing's design is often used by stack machines, including those for Forth, Java, Wasm, and others. The data stack is used for computation, with a dedicated stack for return addresses. A single instruction suffices to call, and another to return from a routine. -*2. Keep return addresses on the data stack.* This design is often used by register machines, including those from CDC, IBM, DEC, Intel, and ARM. The registers are used primarily for computation, and the stack maintains call frames for return addresses, arguments, and local variables. On the EVM there are no registers for computation, so using the stack for both purposes can cause the sort of inefficiencies we saw above. Pascal p-code does use this design, but as part of a complex calling convention with dedicated registers. +*2. Keep return addresses on the data stack.* This design is often used by register machines, including those from CDC, IBM, DEC, Intel, and ARM. The registers are used primarily for computation, and the stack maintains call frames for return addresses, arguments, and local variables. On the EVM there are no registers for computation, so using the stack for both purposes can cause the sort of inefficiencies we see below. Pascal p-code does use this design, but as part of a complex calling convention with dedicated registers. #### We prefer the dedicated return stack. @@ -155,7 +163,7 @@ There are a few major designs for a subroutine facility, two of which are consid ### Efficiency -We illustrate here how subroutine instructions can be used to reduce the complexity and gas costs of both ordinary and optimized subroutine calls compared to using `JUMP`. +We illustrate here how subroutine instructions can be used to reduce the complexity and gas costs of both ordinary and optimized subroutine calls compared to using `JUMP`. #### **Simple Subroutine Call** @@ -167,7 +175,7 @@ SQUARE: dup1 ; 3 gas mul ; 5 gas returnsub ; 3 gas - + CALL_SQUARE: push 0x02 ; 3 gas rjumpsub SQUARE ; 5 gas @@ -315,20 +323,20 @@ Error: at pc=0, op=RETURNSUB: invalid retsub In this example the RJUMPSUB is on the last byte of code. When the subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, and not exit with error -Bytecode: `0x5c035e5ff` (`RJUMP 3, RETURNSUB, RJUMPSUB -1`) +Bytecode: `0x5c00045e5fffff` (`RJUMP 4, RETURNSUB, RJUMPSUB -1`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| | 0 | RJUMP | 5 | [] | [] | -| 2 | RETURNSUB | 5 | [] | [] | -| 3 | RJUMPSUB | 5 | [] | [] | -| 5 | STOP | 0 | [] | [] | +| 3 | RETURNSUB | 5 | [] | [] | +| 4 | RJUMPSUB | 5 | [] | [] | +| 7 | STOP | 0 | [] | [] | Consumed gas: `15` -## Reference Implementation +## Reference Implementation -The following is a pseudo-Python implementation of an algorithm for predicating code validity. An equivalent algorithm must be run at initialization time. +The following is a pseudo-Python implementation of an algorithm for predicating code validity. An equivalent algorithm must be run at initialization time. This algorithm performs a symbolic execution of the program that recursively traverses the _code_, emulating its control flow and stack use and checking for violations of the rules above. @@ -400,7 +408,7 @@ def validate_code(code: bytes, pc: int, sp: int, bp: int) -> boolean: return false push(return_stack, pc + 3) pc = jumpdest - continue + continue elif opcode == RETURNSUB: diff --git a/EIPS/eip-2335.md b/EIPS/eip-2335.md index df4bca08588097..43ca6240e06a81 100644 --- a/EIPS/eip-2335.md +++ b/EIPS/eip-2335.md @@ -61,7 +61,7 @@ The decryption key is an intermediate key which is used both to verify the user- ### Password verification -The password verification verifies step verifies that the password is correct with respect to the `checksum.message`, `cipher.message`, and `kdf`. This is done by appending the `cipher.message` to the 2nd 16 bytes of the decryption key, obtaining its SHA256 hash and verifying whether it matches the `checksum.message`. +The password verification step verifies that the password is correct with respect to the `checksum.message`, `cipher.message`, and `kdf`. This is done by appending the `cipher.message` to the 2nd 16 bytes of the decryption key, obtaining its SHA256 hash and verifying whether it matches the `checksum.message`. #### Inputs @@ -114,7 +114,7 @@ The `path` indicates where in the key-tree a key originates from. It is a string ## UUID -The `uuid` provided in the keystore is a randomly generated UUID as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122). It is intended to be used as a 128-bit proxy for referring to a particular set of keys or account. +The `uuid` provided in the keystore is a randomly generated UUID as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122). It is used as a 128-bit proxy for referring to a particular set of keys or account. ## Version diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index 2e7d292dd2d1ad..3b41dc004631d9 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -4,8 +4,7 @@ title: Diamonds, Multi-Facet Proxy description: Create modular smart contract systems that can be extended after deployment. author: Nick Mudge (@mudgen) discussions-to: https://ethereum-magicians.org/t/discussion-for-eip2535-diamonds/10459/ -status: Last Call -last-call-deadline: 2022-10-03 +status: Final type: Standards Track category: ERC created: 2020-02-22 @@ -372,8 +371,7 @@ bytes4 functionSelector = bytes4(keccak256("myFunction(uint256)")); // get facet address of function address facet = ds.selectorToFacet[functionSelector]; bytes memory myFunctionCall = abi.encodeWithSelector(functionSelector, 4); -(bool success, uint result) = address(facet).delegatecall(myFunctionCall); -require(success, "myFunction failed"); +(bool success, bytes memory result) = address(facet).delegatecall(myFunctionCall); ``` 6. Instead of calling an external function defined in another facet you can instead create an internal function version of the external function. Add the internal version of the function to the facet that needs to use it. @@ -387,7 +385,7 @@ It is possible to create and deploy a set of facets that are reused by different The ability to use the same deployed facets for many diamonds reduces deployment costs. -Facets can also be designed for a specific diamond and not be reusable in other diamonds. +It is possible to implement facets in a way that makes them usable/composable/compatible with other facets. It is also possible to implement facets in a way that makes them not usable/composable/compatible with other facets. A function signature is the name of a function and its parameter types. Example function signature: `myfunction(uint256)`. A limitation is that two external functions with the same function signature can’t be added to the same diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. @@ -435,4 +433,3 @@ Security and domain experts can review the history of change of a diamond to det ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). - diff --git a/EIPS/eip-2537.md b/EIPS/eip-2537.md index 53731144681424..9a80865676204c 100644 --- a/EIPS/eip-2537.md +++ b/EIPS/eip-2537.md @@ -95,7 +95,7 @@ Base field element (Fp) is encoded as `64` bytes by performing BigEndian encodin For elements of the quadratic extension field (Fp2) encoding is byte concatenation of individual encoding of the coefficients totaling in `128` bytes for a total encoding. For an Fp2 element in a form `el = c0 + c1 * v` where `v` is formal quadratic non-residue and `c0` and `c1` are Fp elements the corresponding byte encoding will be `encode(c0) || encode(c1)` where `||` means byte concatenation (or one can use `bytes32[4]` or `uint256[4]` in terms of Solidity types). -*Note on the top `16` bytes being zero*: it's required that encoded element is "in a field" that means strictly `< modulus`. In BigEndian encoding it automatically means that for a modulus that is just `381` bit long top `16` bytes in `64` bytes encoding are zeroes and it **must** be checked if only a subslice of input data is used for actual decoding. +*Note on the top `16` bytes being zero*: it's required that the encoded element is "in a field" that means strictly `< modulus`. In BigEndian encoding it automatically means that for a modulus that is just `381` bit long top `16` bytes in `64` bytes encoding are zeroes and it **must** be checked if only a subslice of input data is used for actual decoding. If encodings do not follow this spec anywhere during parsing in the precompile the precompile *must* return an error. @@ -105,7 +105,7 @@ Points in either G1 (in base field) or in G2 (in extension field) are encoded as #### Point of infinity encoding: -Also referred as "zero point". For BLS12 curves point with coordinates `(0, 0)` (formal zeroes in Fp or Fp2) is *not* on the curve, so encoding of such point `(0, 0)` is used as a convention to encode point of infinity. +Also referred to as "zero point". For BLS12 curves point with coordinates `(0, 0)` (formal zeroes in Fp or Fp2) is *not* on the curve, so encoding of such point `(0, 0)` is used as a convention to encode point of infinity. #### Encoding of scalars for multiplication operation: @@ -250,11 +250,11 @@ Discounts table as a vector of pairs `[k, discount]`: Cost of the pairing operation is `43000*k + 65000` where `k` is a number of pairs. -#### Fp-to-G1 mappign operation +#### Fp-to-G1 mapping operation Fp -> G1 mapping is `5500` gas. -#### Fp2-to-G2 mappign operation +#### Fp2-to-G2 mapping operation Fp2 -> G2 mapping is `75000` gas diff --git a/EIPS/eip-2612.md b/EIPS/eip-2612.md index 51b0a61ac85282..eecb1f7dfc77b0 100644 --- a/EIPS/eip-2612.md +++ b/EIPS/eip-2612.md @@ -1,11 +1,10 @@ --- eip: 2612 -title: "EIP-20 Permit Extension: Signed Approvals" +title: Permit Extension for EIP-20 Signed Approvals description: EIP-20 approvals via EIP-712 secp256k1 signatures author: Martin Lundfall (@Mrchico) discussions-to: https://github.com/ethereum/EIPs/issues/2613 -status: Last Call -last-call-deadline: 2022-10-08 +status: Final type: Standards Track category: ERC created: 2020-04-13 @@ -36,7 +35,7 @@ While it may be tempting to introduce `*_by_signature` counterparts for every EI - they can be implemented using a combination of `permit` and additional helper contracts without loss of generality. ## Specification -Three new functions are added to the EIP-20 ABI: +Compliant contracts must implement 3 new functions in addition to EIP-20: ```sol function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external function nonces(address owner) external view returns (uint) diff --git a/EIPS/eip-2771.md b/EIPS/eip-2771.md index dd8142938fa8b2..117c47304a25e2 100644 --- a/EIPS/eip-2771.md +++ b/EIPS/eip-2771.md @@ -1,81 +1,44 @@ --- eip: 2771 title: Secure Protocol for Native Meta Transactions -author: Ronan Sandford (@wighawag), Liraz Siri (@lirazsiri), Dror Tirosh (@drortirosh), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Hadrien Croubois (@Amxx), Sachin Tomar (@tomarsachin2271), Patrick McCorry (@stonecoldpat), Nicolas Venturo (@nventuro), Fabian Vogelsteller (@frozeman) +description: A contract interface for receiving meta transactions through a trusted forwarder +author: Ronan Sandford (@wighawag), Liraz Siri (@lirazsiri), Dror Tirosh (@drortirosh), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Hadrien Croubois (@Amxx), Sachin Tomar (@tomarsachin2271), Patrick McCorry (@stonecoldpat), Nicolas Venturo (@nventuro), Fabian Vogelsteller (@frozeman), Pandapip1 (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/erc-2771-secure-protocol-for-native-meta-transactions/4488 -status: Stagnant +status: Review type: Standards Track category: ERC created: 2020-07-01 --- -## Simple Summary - -A contract interface for receiving meta transactions through a trusted -forwarder. - ## Abstract -This ERC defines a minimal contract-level protocol that a compliant Recipient -contract needs to support in order to be capable of accepting a meta -transaction through a compliant Forwarder contract that it trusts to help it -identify the address of the Transaction Signer. - -No EVM-level protocol changes are proposed or required. +This EIP defines a contract-level protocol for `Recipient` contracts to accept meta-transactions through trusted `Forwarder` contracts. No protocol changes are made. `Recipient` contracts are sent the effective `msg.sender` (referred to as `_msgSender()`) and `msg.data` (referred to as `_msgData()`) by appending additional calldata. ## Motivation -There is a growing interest in making it possible for Ethereum contracts to -accept calls from externally owned accounts that do not have ETH to pay for -gas. - -This can be accomplished with meta transactions, which are transactions that -have been: +There is a growing interest in making it possible for Ethereum contracts to accept calls from externally owned accounts that do not have ETH to pay for gas. Solutions that allow for third parties to pay for gas costs are called meta transactions. For the purposes of this EIP, meta transactions are transactions that have been authorized by a **Transaction Signer** and relayed by an untrusted third party that pays for the gas (the **Gas Relay**). -1. Authorized by the **Transaction Signer**. For example, signed by an - externally owned account. -2. Relayed by an untrusted third party that pays for the gas (the **Gas - Relay**) +## Specification -`msg.sender` is a transaction parameter that can be inspected by a contract to -determine who signed the transaction. The integrity of this parameter is -guaranteed by the Ethereum EVM, but for a meta transaction securing -`msg.sender` is insufficient. +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. -The problem is that for a contract that is not natively aware of meta -transactions, the `msg.sender` of the transaction will make it appear to be -coming from the **Gas Relay** and not the **Transaction Signer**. A secure -protocol for a contract to accept meta transactions needs to prevent the **Gas -Relay** from forging, modifying or duplicating requests by the **Transaction -Signer**. +### Definitions -## Specification +**Transaction Signer**: Signs & sends transactions to a Gas Relay -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](https://www.ietf.org/rfc/rfc2119.txt). +**Gas Relay**: Receives signed requests off-chain from Transaction Signers and pays gas to turn it into a valid transaction that goes through a Trusted Forwarder -Here is an example flow: +**Trusted Forwarder**: A contract trusted by the `Recipient` to correctly verify signatures and nonces before forwarding the request from Transaction Signers -![Example flow](../assets/eip-2771/example-flow.png) +**Recipient**: A contract that accepts meta-transactions through a Trusted Forwarder +### Example Flow -* **Transaction Signer** - entity that signs & sends to request to **Gas - Relay** -* **Gas Relay** - receives a signed request off-chain from **Transaction - Signer** and pays gas to turn it into a valid transaction that goes through -**Trusted Forwarder** -* **Trusted Forwarder** - a contract that is trusted by the `Recipient` to - correctly verify the signature and nonce before forwarding the request from -**Transaction Signer** -* **Recipient** - a contract that can securely accept meta-transactions - through a **Trusted Forwarder** by being compliant with this standard. +![Example flow](../assets/eip-2771/example-flow.png) ### Extracting The Transaction Signer address -The **Trusted Forwarder** is responsible for calling the **Recipient** contract -and MUST append the address of the **Transaction Signer** (20 bytes of data) to -the end of the call data. +The **Trusted Forwarder** is responsible for calling the **Recipient** contract and MUST append the address of the **Transaction Signer** (20 bytes of data) to the end of the call data. For example : @@ -83,15 +46,11 @@ For example : (bool success, bytes memory returnData) = to.call.value(value)(abi.encodePacked(data, from)); ``` -The **Recipient** contract can then extract the **Transaction Signer** address -by performing 3 operations: +The **Recipient** contract can then extract the **Transaction Signer** address by performing 3 operations: -1. Check that the **Forwarder** is trusted. How this is implemented is out of - the scope of this proposal. -2. Extract the **Transaction Signer** address from the last 20 bytes of the - call data and use that as the original `sender` of the transaction (instead of `msg.sender`) -3. If the `msg.sender` is not a trusted forwarder (or if the msg.data is - shorter than 20 bytes), then return the original `msg.sender` as it is. +1. Check that the **Forwarder** is trusted. How this is implemented is out of the scope of this proposal. +2. Extract the **Transaction Signer** address from the last 20 bytes of the call data and use that as the original `sender` of the transaction (instead of `msg.sender`) +3. If the `msg.sender` is not a trusted forwarder (or if the `msg.data` is shorter than 20 bytes), then return the original `msg.sender` as it is. The **Recipient** MUST check that it trusts the Forwarder to prevent it from extracting address data appended from an untrusted contract. This could result @@ -99,49 +58,53 @@ in a forged address. ### Protocol Support Discovery Mechanism -Unless a **Recipient** contract is being used by a particular frontend that -knows that this contract has support for native meta transactions, it would not -be possible to offer the user the choice of using meta-transaction to interact -with the contract. We thus need a mechanism by which the **Recipient** can let -the world know that it supports meta transactions. +Unless a **Recipient** contract is being used by a particular frontend that knows that this contract has support for native meta transactions, it would not be possible to offer the user the choice of using meta-transaction to interact with the contract. We thus need a mechanism by which the **Recipient** can let the world know that it supports meta transactions. -This is especially important for meta transactions to be supported at the Web3 -wallet level. Such wallets may not necessarily know anything about the -**Recipient** contract users may wish to interact with. +This is especially important for meta transactions to be supported at the Web3 wallet level. Such wallets may not necessarily know anything about the **Recipient** contract users may wish to interact with. -As a **Recipient** could trust forwarders with different interfaces and -capabilities (e.g., transaction batching, different message signing formats), -we need to allow wallets to discover which Forwarder is trusted. +As a **Recipient** could trust forwarders with different interfaces and capabilities (e.g., transaction batching, different message signing formats), we need to allow wallets to discover which Forwarder is trusted. -To provide this discovery mechanism a **Recipient** contract MUST implement -this function: +To provide this discovery mechanism a **Recipient** contract MUST implement this function: ```solidity -function isTrustedForwarder(address forwarder) external returns(bool); +function isTrustedForwarder(address forwarder) external view returns(bool); ``` -* That function MUST return true if the forwarder is trusted by the - Recipient. -* That function MUST return false if the forwarder is not trusted. -* That function MUST NOT throw a revert. +`isTrustedForwarder` MUST return `true` if the forwarder is trusted by the Recipient, otherwise it MUST return `false`. `isTrustedForwarder` MUST NOT revert. + +Internally, the **Recipient** MUST then accept a request from forwarder. -Internally, the **Recipient** MUST then accept a request from forwarder +`isTrustedForwarder` function MAY be called on-chain, and as such gas restrictions MUST be put in place. A Gas limit of 50k SHOULD be sufficient to making the decision either inside the contract, or delegating it to another contract and doing some memory access calculations, like querying a mapping. -That function can be called on-chain and as such gas restriction needs to be -put in place. +## Rationale -A Gas limit of 50k is enough for making the decision either inside the -contract, or delegating it to another contract and doing some memory access -calculations, like querying a mapping. +* Make it easy for contract developers to add support for meta + transactions by standardizing the simplest viable contract interface. +* Without support for meta transactions in the recipient contract, an externally owned + account can not use meta transactions to interact with the recipient contract. +* Without a standard contract interface, there is no standard way for a client + to discover whether a recipient supports meta transactions. +* Without a standard contract interface, there is no standard way to send a + meta transaction to a recipient. +* Without the ability to leverage a trusted forwarder every recipient contract + has to internally implement the logic required to accept meta transactions securely. +* Without a discovery protocol, there is no mechanism for a client to discover + whether a recipient supports a specific forwarder. +* Making the contract interface agnostic to the internal implementation + details of the trusted forwarder, makes it possible for a recipient contract + to support multiple forwarders with no change to code. +* `msg.sender` is a transaction parameter that can be inspected by a contract to determine who signed the transaction. The integrity of this parameter is guaranteed by the Ethereum EVM, but for a meta transaction securing `msg.sender` is insufficient. + * The problem is that for a contract that is not natively aware of meta transactions, the `msg.sender` of the transaction will make it appear to be coming from the **Gas Relay** and not the **Transaction Signer**. A secure protocol for a contract to accept meta transactions needs to prevent the **Gas Relay** from forging, modifying or duplicating requests by the **Transaction Signer**. +## Reference Implementation -### Recipient example +### Recipient Example ```solidity contract RecipientExample { function purchaseItem(uint256 itemId) external { address sender = _msgSender(); - ... perform the purchase for sender + // ... perform the purchase for sender } address immutable _trustedForwarder; @@ -165,45 +128,11 @@ contract RecipientExample { } ``` -## Rationale - -* Make it easy for contract developers to add support for meta - transactions by standardizing the simplest viable contract interface. - -* Without support for meta transactions in the recipient contract, an externally owned - account can not use meta transactions to interact with the recipient contract. - -* Without a standard contract interface, there is no standard way for a client - to discover whether a recipient supports meta transactions. - -* Without a standard contract interface, there is no standard way to send a - meta transaction to a recipient. - -* Without the ability to leverage a trusted forwarder every recipient contract - has to internally implement the logic required to accept meta transactions securely. - -* Without a discovery protocol, there is no mechanism for a client to discover - whether a recipient supports a specific forwarder. - -* Making the contract interface agnostic to the internal implementation - details of the trusted forwarder, makes it possible for a recipient contract - to support multiple forwarders with no change to code. - ## Security Considerations -A bad forwarder may allow forgery of the `msg.sender` returned from -`_msgSender()` and allow transactions to appear to be coming from any address. - -This means a recipient contract should be very careful which forwarder it -trusts and whether this can be modified. The power to change the forwarder -trusted by a recipient is equivalent to giving full control over the contract. -If this kind of control over the recipient is acceptable, it is recommended -that only the owner of the recipient contract be able to modify which forwarder -is trusted. Otherwise best to leave it unmodifiable, as in the example above. - -## Implementations +A malicious forwarder may forge the value of `_msgSender()` and effectively send transactions from any address. Therefore, `Recipient` contracts must be very careful in trusting forwarders. If a forwarder is upgradeable, then one must also trust that the contract won't perform a malicious upgrade. -An implementation of a base class for a recipient: [BaseRelayRecipient.sol](https://github.com/opengsn/forwarder/blob/master/contracts/BaseRelayRecipient.sol) +In addition, modifying which forwarders are trusted must be restricted, since an attacker could "trust" their own address to forward transactions, and therefore be able to forge transactions. It is recommended to have the list of trusted forwarders be immutable, and if this is not feasible, then only trusted contract owners should be able to modify it. ## Copyright diff --git a/EIPS/eip-3525.md b/EIPS/eip-3525.md index 47f62f12c4cd0c..5f0573edaf9c87 100644 --- a/EIPS/eip-3525.md +++ b/EIPS/eip-3525.md @@ -536,25 +536,25 @@ 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 mode (safe/unsafe) to the sender, and may cause some potential problem: +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: -1. In most situation the sender don't know how to choose between two kinds of transfer methods (safe/unsafe); -2. If the sender calls the `safeTransferFrom` method, the transfer may fail when the recipient contract didn't implements the callback function, even if that contract can receive and manipulate the token with no problem. +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. 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 need to 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 purpose of being notified after receiving values, in this function it can return certain pre-defined value to accept the transfer, or any other value to reject. +2. All EIP-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 allow value transfer from an address to itself, so that when a smart contract which implements `onERC3525Received` transfers value to itself, this function will also be called. So that the contract is responsible to choose 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 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. ### 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: -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. +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 @@ -566,9 +566,9 @@ As mentioned in the beginning, this EIP is backward compatible with EIP-721. ## 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 EIP-721 approval models, so that approving value should not affect EIP-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. +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. ## Copyright diff --git a/EIPS/eip-3540.md b/EIPS/eip-3540.md index 1f3d2f64e6043c..2baf88fc0424e6 100644 --- a/EIPS/eip-3540.md +++ b/EIPS/eip-3540.md @@ -16,6 +16,7 @@ requires: 3541 We introduce an extensible and versioned container format for the EVM with a once-off validation at deploy time. The version described here brings the tangible benefit of code and data separation, and allows for easy introduction of a variety of changes in the future. This change relies on the reserved byte introduced by [EIP-3541](./eip-3541.md). To summarise, EOF bytecode has the following layout: + ``` magic, version, (section_kind, section_size)+, 0,
``` @@ -28,19 +29,20 @@ Validating code during the contract creation process allows code versioning with The format described in this EIP introduces a simple and extensible container with a minimal set of changes required to both clients and languages, and introduces validation. -The first tangible feature it provides is separation of code and data. This separation is especially beneficial for on-chain code validators (like those utilised by layer-2 scaling tools, such as Optimism), because they can distinguish code and data (this includes deployment code and constructor arguments too). Currently they a) require changes prior to contract deployment; b) implement a fragile method; or c) implement an expensive and restrictive jump analysis. Code and data separation can result in ease of use and significant gas savings for such use cases. Additionally, various (static) analysis tools can also benefit, though off-chain tools can already deal with existing code, so the impact is smaller. +The first tangible feature it provides is separation of code and data. This separation is especially beneficial for on-chain code validators (like those utilised by layer-2 scaling tools, such as Optimism), because they can distinguish code and data (this includes deployment code and constructor arguments too). Currently, they a) require changes prior to contract deployment; b) implement a fragile method; or c) implement an expensive and restrictive jump analysis. Code and data separation can result in ease of use and significant gas savings for such use cases. Additionally, various (static) analysis tools can also benefit, though off-chain tools can already deal with existing code, so the impact is smaller. A non-exhaustive list of proposed changes which could benefit from this format: + - Including a `JUMPDEST`-table (to avoid analysis at execution time) and/or removing `JUMPDEST`s entirely. - Introducing static jumps (with relative addresses) and jump tables, and disallowing dynamic jumps at the same time. -- Requiring code section(s) to be terminated by `STOP`. (Assumptions like this can provide significant speed improvements in interpreters, such as a speed up of ~7% seen in [evmone](https://github.com/ethereum/evmone/pull/295).) -- Multi-byte opcodes without any workarounds. +- Requiring code section(s) to be terminated by `STOP`. (Assumptions like this can provide significant speed improvements in interpreters, such as a speed-up of ~7% seen in evmone (ethereum/evmone#295). +- Multibyte opcodes without any workarounds. - Representing functions as individual code sections instead of subroutines. - Introducing special sections for different use cases, notably Account Abstraction. ## Specification -*We use [RFC2119](https://tools.ietf.org/html/rfc2119) keywords in this section.* +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 guarantee that every EOF-formatted contract in the state is valid, we need to prevent already deployed (and not validated) contracts from being recognized as such format. This is achieved by choosing a byte sequence for the *magic* that doesn't exist in any of the already deployed contracts. @@ -52,9 +54,10 @@ The opcode `0xEF` is currently an undefined instruction, therefore: *It pops no ### Code validation -We introduce _code validation_ for new contract creation. To achieve this, we define a format called EVM Object Format (EOF), containing a version indicator, and a ruleset of validity tied to a given version. +We introduce *code validation* for new contract creation. To achieve this, we define a format called EVM Object Format (EOF), containing a version indicator, and a ruleset of validity tied to a given version. At `block.number == HF_BLOCK` new contract creation is modified: + - if *initcode* or *code* starts with the `MAGIC`, it is considered to be EOF formatted and will undergo validation specified in the following sections, - else if *code* starts with `0xEF`, creation continues to result in an exceptional abort (the rule introduced in EIP-3541), - otherwise code is considered *legacy code* and the following rules do not apply to it. @@ -65,24 +68,24 @@ EOF container is a binary format with the capability of providing the EOF versio The container starts with the EOF header: -| description | length | value | | -|-------------|----------|------------|-| -| magic | 2-bytes | 0xEF00 | | +| description | length | value | | +|-------------|----------|------------|--------------------| +| magic | 2-bytes | 0xEF00 | | | version | 1-byte | 0x01–0xFF | EOF version number | The EOF header is followed by at least one section header. Each section header contains two fields, `section_kind` and `section_size`. -| description | length | value | | -|--------------|---------|---------------|-| -| section_kind | 1-byte | 0x01–0xFF | Encoded as a 8-bit unsigned number. | +| description | length | value | | +|--------------|---------|---------------|-------------------------------------------------| +| section_kind | 1-byte | 0x01–0xFF | Encoded as a 8-bit unsigned number. | | section_size | 2-bytes | 0x0001–0xFFFF | Encoded as a 16-bit unsigned big-endian number. | -The list of section headers is terminated with the _section headers terminator byte_ `0x00`. +The list of section headers is terminated with the *section headers terminator byte* `0x00`. #### Container validation rules 1. `version` MUST NOT be `0`.[^1](#EOF-version-range-start-with-1) -2. `section_kind` MUST NOT be `0`. The value `0` is reserved for _section headers terminator byte_. +2. `section_kind` MUST NOT be `0`. The value `0` is reserved for *section headers terminator byte*. 3. `section_size` MUST NOT be `0`. If a section is empty its section header MUST be omitted. 4. There MUST be at least one section (and therefore section header). 5. Section data size MUST be equal to `section_size` declared in its header. @@ -94,19 +97,18 @@ The list of section headers is terminated with the _section headers terminator b The section kinds for EOF version 1 are defined as follows. The list may be extended in future versions. -| section_kind | meaning | -|--------------|------------| -| 0 | _reserved for section headers terminator byte_ | -| 1 | code | -| 2 | data | +| section_kind | meaning | +|--------------|------------------------------------------------| +| 0 | *reserved for section headers terminator byte* | +| 1 | code | +| 2 | data | #### EOF version 1 validation rules 1. In addition to general validation rules above, EOF version 1 bytecode conforms to the rules specified below: -- Exactly one code section MUST be present. -- The code section MUST be the first section. -- A single data section MAY follow the code section. - + - Exactly one code section MUST be present. + - The code section MUST be the first section. + - A single data section MAY follow the code section. 2. Any other version is invalid. (*Remark:* Contract creation code SHOULD set the section size of the data section so that the constructor arguments fit it.) @@ -117,7 +119,7 @@ For clarity, the *container* refers to the complete account code, while *code* r 1. `JUMPDEST`-analysis is only run on the *code*. 2. Execution starts at the first byte of the *code*, and `PC` is set to 0. -3. If `PC` goes outside of the code section bounds, execution aborts with failure. +3. If `PC` goes outside the code section bounds, execution aborts with failure. 4. `PC` returns the current position within the *code*. 5. `JUMP`/`JUMPI` uses an absolute offset within the *code*. 6. `CODECOPY`/`CODESIZE`/`EXTCODECOPY`/`EXTCODESIZE`/`EXTCODEHASH` keeps operating on the entire *container*. @@ -127,73 +129,82 @@ For clarity, the *container* refers to the complete account code, while *code* r For clarity, the *EOF prefix* together with a version number *n* is denoted as the *EOFn prefix*, e.g. *EOF1 prefix*. -1. If _initcode's container_ has EOF1 prefix it must be valid EOF1 code. -2. If _code's container_ has EOF1 prefix it must be valid EOF1 code. +1. If *initcode's container* has EOF1 prefix it must be valid EOF1 code. +2. If *code's container* has EOF1 prefix it must be valid EOF1 code. ## Rationale -EVM and/or account versioning has been discussed numerous times over the past years. This proposal aims to learn from them. See [this collection of previous proposals](https://ethereum-magicians.org/t/ethereum-account-versioning/3508) for a good starting point. +EVM and/or account versioning has been discussed numerous times over the past years. This proposal aims to learn from them. +See "Ethereum account versioning" on the Fellowship of Ethereum Magicians Forum for a good starting point. ### Execution vs. creation time validation This specification introduces creation time validation, which means: + - All created contracts with *EOFn* prefix are valid according to version *n* rules. This is very strong and useful property. The client can trust that the deployed code is well-formed. -- In future, this allows to serialize `JUMPDEST` map in the EOF container and eliminate the need of implicit `JUMPDEST` analysis required before execution. +- In the future, this allows to serialize `JUMPDEST` map in the EOF container and eliminate the need of implicit `JUMPDEST` analysis required before execution. - Or to completely remove the need for `JUMPDEST` instructions. - This helps with deprecating EVM instructions and/or features. - The biggest disadvantage is that deploy-time validation of EOF code must be enabled in two hard-forks. However, the first step ([EIP-3541](./eip-3541.md)) is already deployed in London. -The alternative is to have execution time validation for EOF. This is performed every single time a contract is executed, however clients may be able to cache validation results. This _alternative_ approach has the following properties: -- Because the validation is consensus-level execution step, it means the execution always requires the entire code. This makes _code merkleization impractical_. +The alternative is to have execution time validation for EOF. This is performed every single time a contract is executed, however clients may be able to cache validation results. This *alternative* approach has the following properties: + +- Because the validation is consensus-level execution step, it means the execution always requires the entire code. This makes *code merkleization impractical*. - Can be enabled via a single hard-fork. -- Better backwards compatibility: data contracts starting with the `0xEF` byte or the *EOF prefix* can be deployed. This is a dubious benefit however. +- Better backwards compatibility: data contracts starting with the `0xEF` byte or the *EOF prefix* can be deployed. This is a dubious benefit, however. ### Contract creation restrictions -The [Changes to contact creation semantics](#changes-to-contract-creation-semantics) section defines minimal set of restrictions related to the contract creation: if _initcode_ or _code_ has the EOF1 container prefix it must be validated. This adds two validation steps in the contract creation, any of it failing will result in contract creation failure. +The [Changes to contact creation semantics](#changes-to-contract-creation-semantics) section defines +minimal set of restrictions related to the contract creation: if *initcode* or *code* has the EOF1 +container prefix it must be validated. This adds two validation steps in the contract creation, +any of it failing will result in contract creation failure. -Since _initcode_ and _code_ are evaluated for EOF1 independently, number of interesting combinations are allowed: -- Create transaction with EOF1 _initcode_ can deploy legacy contract, -- EOF1 contract can execute `CREATE` instruction with legacy _initcode_ to create new legacy contract, -- Legacy contract can execute `CREATE` instruction with EOF1 _initcode_ to create new EOF1 contract, -- Legacy contract can execute `CREATE` instruction with EOF1 _initcode_ to create new legacy contract, +Since *initcode* and *code* are evaluated for EOF1 independently, number of interesting combinations are allowed: + +- Create transaction with EOF1 *initcode* can deploy legacy contract, +- EOF1 contract can execute `CREATE` instruction with legacy *initcode* to create new legacy contract, +- Legacy contract can execute `CREATE` instruction with EOF1 *initcode* to create new EOF1 contract, +- Legacy contract can execute `CREATE` instruction with EOF1 *initcode* to create new legacy contract, - etc. To limit the number of exotic bytecode version combinations, additional restrictions are considered, but currently are not part of the specification: -1. The EOF version of _initcode_ must much the version of _code_. +1. The EOF version of *initcode* must much the version of *code*. 2. An EOF1 contract must not create legacy contracts. -Finally, create transaction must be allowed to contain legacy _initcode_ and deploy legacy _code_ because otherwise there is no transition period allowing upgrading transaction signing tools. Deprecating such transactions may be considered in future. +Finally, create transaction must be allowed to contain legacy *initcode* and deploy legacy *code* because otherwise there is no transition period allowing upgrading transaction signing tools. Deprecating such transactions may be considered in the future. ### The MAGIC 1. The first byte `0xEF` was chosen because it is reserved for this purpose by [EIP-3541](./eip-3541.md). 2. The second byte `0x00` was chosen to avoid clashes with three contracts which were deployed on **Mainnet**: - - `EFF09f918bf09f9fa9` [0xca7bf67ab492b49806e24b6e2e4ec105183caa01](https://etherscan.io/address/0xca7bf67ab492b49806e24b6e2e4ec105183caa01) - - `EF` [0x897da0f23ccc5e939ec7a53032c5e80fd1a947ec](https://etherscan.io/address/0x897da0f23ccc5e939ec7a53032c5e80fd1a947ec) - - `EF` [0x6e51d4d9be52b623a3d3a2fa8d3c5e3e01175cd0](https://etherscan.io/address/0x6e51d4d9be52b623a3d3a2fa8d3c5e3e01175cd0) + - `0xca7bf67ab492b49806e24b6e2e4ec105183caa01`: `EFF09f918bf09f9fa9` + - `0x897da0f23ccc5e939ec7a53032c5e80fd1a947ec`: `EF` + - `0x6e51d4d9be52b623a3d3a2fa8d3c5e3e01175cd0`: `EF` 3. No contracts starting with `0xEF` bytes exist on public testnets: Goerli, Ropsten, Rinkeby, Kovan and Sepolia at their London fork block. ### EOF version range start with 1 -The version number 0 will never be used in EOF so we can call legacy code _EOF0_. Also implementations may use APIs where 0 version number denotes legacy code. +The version number 0 will never be used in EOF, so we can call legacy code *EOF0*. +Also, implementations may use APIs where 0 version number denotes legacy code. ### Section structure We have considered different questions for the sections: + - Streaming headers (i.e. `section_header, section_data, section_header, section_data, ...`) are used in some other formats (such as WebAssembly). They are handy for formats which are subject to editing (adding/removing sections). That is not a useful feature for EVM. One minor benefit applicable to our case is that they do not require a specific "header terminator". On the other hand they seem to play worse with code chunking / merkleization, as it is better to have all section headers in a single chunk. -- Whether to have a header terminator or to encode `number_of_sections` or `total_size_of_headers`. Both raise the question how large of a value these fields should be able to hold. While today there will be only two sections, in case each "EVM function" would become a separate code section, a fixed 8-bit field may not be big enough. A terminator byte seems to avoid these problems. -- Whether to encode `section_size` as a fixed 16-bit value or some kind of variable length field (e.g. [LEB128](https://en.wikipedia.org/wiki/LEB128)). We have opted for fixed size, because it simplifies client implementations, and 16-bit seems enough, because of the currently exposed code size limit of 24576 bytes (see [EIP-170](./eip-170.md) and [EIP-2677](./eip-2677.md)). Should this be limiting in the future, a new EOF version could change the format. Besides simplifying client implementations, not using LEB128 also greatly simplifies on-chain parsing. +- Whether to have a header terminator or to encode `number_of_sections` or `total_size_of_headers`. Both raise the question of how large of a value these fields should be able to hold. While today there will be only two sections, in case each "EVM function" would become a separate code section, a fixed 8-bit field may not be big enough. A terminator byte seems to avoid these problems. +- Whether to encode `section_size` as a fixed 16-bit value or some kind of variable length field (e.g. LEB128). We have opted for fixed size, because it simplifies client implementations, and 16-bit seems enough, because of the currently exposed code size limit of 24576 bytes (see [EIP-170](./eip-170.md) and [EIP-3860](./eip-3860.md)). Should this be limiting in the future, a new EOF version could change the format. Besides simplifying client implementations, not using LEB128 also greatly simplifies on-chain parsing. ### Data-only contracts The EOF prevents deploying contracts with arbitrary bytes (data-only contracts: their purpose is to store data not execution). **EOF1 requires** presence of a **code section** therefore the minimal overhead EOF data contract consist of a data section and one code section with single instruction. We recommend to use `INVALID` instruction in this case. In total there are 11 additional bytes required. ``` -EF0001 000001 01 00 FE +EF0001 010001 02 00 FE ``` ### PC starts with 0 at the code section @@ -301,52 +312,64 @@ S_DATA = 0x02 def is_eof(code: bytes) -> bool: return code.startswith(MAGIC) +class ValidationException(Exception): + pass -# Validate EOF code. +# Raises ValidationException on invalid code def validate_eof(code: bytes): # Check version - assert len(code) >= 3 and code[2] == VERSION + if len(code) < 3 or code[2] != VERSION: + raise ValidationException("invalid version") # Process section headers section_sizes = {S_CODE: 0, S_DATA: 0} pos = 3 while True: # Terminator not found - assert pos < len(code) + if pos >= len(code): + raise ValidationException("no section terminator") + section_id = code[pos] pos += 1 if section_id == S_TERMINATOR: break # Disallow unknown sections - assert section_id in section_sizes + if not section_id in section_sizes: + raise ValidationException("invalid section id") # Data section preceding code section - assert section_id != S_DATA or section_sizes[S_CODE] != 0 + if section_id == S_DATA and section_sizes[S_CODE] == 0: + raise ValidationException("data section preceding code section") # Multiple sections with the same id - assert section_sizes[section_id] == 0 + if section_sizes[section_id] != 0: + raise ValidationException("multiple sections with same id") # Truncated section size - assert (pos + 1) < len(code) + if (pos + 1) >= len(code): + raise ValidationException("truncated section size") section_sizes[section_id] = (code[pos] << 8) | code[pos + 1] pos += 2 # Empty section - assert section_sizes[section_id] != 0 + if section_sizes[section_id] == 0: + raise ValidationException("empty section") # Code section cannot be absent - assert section_sizes[S_CODE] != 0 + if section_sizes[S_CODE] == 0: + raise ValidationException("no code section") # The entire container must be scanned - assert len(code) == (pos + section_sizes[S_CODE] + section_sizes[S_DATA]) + if len(code) != (pos + section_sizes[S_CODE] + section_sizes[S_DATA]): + raise ValidationException("container size not equal to sum of section sizes") ``` ## Security Considerations Proposed validation rules can be checked at constant time, therefore it should not be easily attackable. This is subject to change with future extensions. -Currently *initcode* validation has no extra cost and the currently charged creation costs should be sufficient, however we consider adding an additional gas cost for contract creation. +Currently, *initcode* validation has no extra cost and the currently charged creation costs should be sufficient, however we consider adding a gas cost for contract creation. ## Copyright diff --git a/EIPS/eip-3670.md b/EIPS/eip-3670.md index 269086e6e2a0e0..cee7c58ac607ca 100644 --- a/EIPS/eip-3670.md +++ b/EIPS/eip-3670.md @@ -13,13 +13,22 @@ requires: 3540, 3860 ## Abstract -Introduce code validation at contract creation time for EOF formatted ([EIP-3540](./eip-3540.md)) contracts. Reject contracts which contain truncated `PUSH`-data or undefined instructions. Legacy bytecode (code which is not EOF formatted) is unaffected by this change. +Introduce code validation at contract creation time for EOF formatted ([EIP-3540](./eip-3540.md)) +contracts. Reject contracts which contain truncated `PUSH`-data or undefined instructions. +Legacy bytecode (code which is not EOF formatted) is unaffected by this change. ## Motivation -Currently existing contracts require no validation of correctness and EVM implementations can decide how they handle truncated bytecode or undefined instructions. This change aims to bring code validity into consensus, so that it becomes easier to reason about bytecode. Moreover, EVM implementations may require less paths to decide which instruction is valid in the current execution context. +Currently existing contracts require no validation of correctness and EVM implementations can decide +how they handle truncated bytecode or undefined instructions. This change aims to bring code +validity into consensus, so that it becomes easier to reason about bytecode. +Moreover, EVM implementations may require fewer paths to decide which instruction is valid in +the current execution context. -If it will be desired to introduce new instructions without bumping EOF version, having undefined instructions already deployed would mean such contracts potentially can be broken (since some of the instructions are changing their behaviour). Rejecting to deploy undefined instructions allows introducing new instructions with or without bumping the EOF version. +If it will be desired to introduce new instructions without bumping EOF version, having undefined +instructions already deployed would mean such contracts potentially can be broken (since some +instructions are changing their behaviour). Rejecting to deploy undefined instructions allows +introducing new instructions with or without bumping the EOF version. ### EOF1 forward compatibility @@ -35,7 +44,9 @@ The EOF1 format provides following forward compatibility properties: This feature is introduced on the very same block EIP-3540 is enabled, therefore every EOF1-compatible bytecode MUST be validated according to these rules. -At contract creation time both *initcode* and *code* are iterated instruction-by-instruction (the same process is used to perform "JUMPDEST-analysis"). Bytecode is deemed invalid if any of these conditions is true: +At contract creation time both *initcode* and *code* are iterated instruction-by-instruction (the same process is used to perform "JUMPDEST-analysis"). +Bytecode is deemed invalid if any of these conditions is true: + - it contains opcodes which are not currently assigned to an instruction (for the sake of assigned instructions, we count `INVALID` (0xfe) as assigned), - the last opcode (*terminating instruction*) is not `STOP` (0x00),`RETURN` (0xf3), `REVERT` (0xfd), `INVALID` (0xfe) or `SELFDESTRUCT` (0xff). @@ -53,11 +64,11 @@ In case *initcode* is invalid, gas for its execution is not deducted. In case *c ### Terminating instructions -An efficient interpreter loop would only need to rely on checking if a terminating instruction has been encountered, and if so stopping execution. Currently this is not possible in the EVM, because of the lack of requirement for a proper termination as well as allowing for truncated instructions, an interpreter must track and check these various conditions. +An efficient interpreter loop would only need to rely on checking if a terminating instruction has been encountered, and if so stopping execution. Currently, this is not possible in the EVM, because of the lack of requirement for a proper termination as well as allowing for truncated instructions, an interpreter must track and check these various conditions. ### Possibility for deprecation -The deprecated `CALLCODE` (0xf2) opcode may be dropped from the `valid_opcodes` list to prevent use of this instruction in future. Likewise `SELFDESTRUCT` (0xff) could also be rejected. Yet we decided not to mix such changes in. +The deprecated `CALLCODE` (0xf2) opcode may be dropped from the `valid_opcodes` list to prevent use of this instruction in the future. Likewise `SELFDESTRUCT` (0xff) could also be rejected. Yet we decided not to mix such changes in. ## Backwards Compatibility @@ -65,7 +76,7 @@ This change poses no risk to backwards compatibility, as it is introduced at the ## Test Cases -#### Contract creation +### Contract creation Each case should be tested for creation transaction, `CREATE` and `CREATE2`. @@ -73,7 +84,7 @@ Each case should be tested for creation transaction, `CREATE` and `CREATE2`. - Valid initcode returning invalid code - Valid initcode returning valid code -#### Valid codes +### Valid codes - EOF code containing `INVALID` - EOF codes ending with any of the terminating instructions @@ -82,7 +93,7 @@ Each case should be tested for creation transaction, `CREATE` and `CREATE2`. - Legacy code ending with incomplete PUSH instruction - Legacy code ending with any valid non-terminating instruction -#### Invalid codes +### Invalid codes - EOF code containing undefined instruction - EOF code ending with incomplete `PUSH` instruction diff --git a/EIPS/eip-4519.md b/EIPS/eip-4519.md index ee30f7bab8c4ce..f12a3a2ece8c29 100644 --- a/EIPS/eip-4519.md +++ b/EIPS/eip-4519.md @@ -4,7 +4,8 @@ title: Non-Fungible Tokens Tied to Physical Assets description: Interface for non-fungible tokens representing physical assets that can generate or recover their own accounts and obey users. author: Javier Arcenegui (@Hardblock-IMSE-CNM), Rosario Arjona (@RosarioArjona), Roberto Román , Iluminada Baturone (@lumi2018) discussions-to: https://ethereum-magicians.org/t/new-proposal-of-smart-non-fungible-token/7677 -status: Review +status: Last Call +last-call-deadline: 2022-11-30 type: Standards Track category: ERC created: 2021-12-03 @@ -12,22 +13,16 @@ requires: 165, 721 --- ## Abstract -This EIP proposes a standard interface for non-fungible tokens that represent physical assets, such as Internet of Things (IoT) devices. A SmartNFT is tied to a physical asset that can check if the tie is authentic or not. The SmartNFT can include an Ethereum address of the physical asset and, consequently, the physical asset can sign messages or transactions. The physical asset can operate with an operating mode that is defined by its SmartNFT with an attribute named state. The token state can define if the token owner or the token user can use the asset or not. A cryptographically secure mutual authentication process can be carried out between the physical asset and its owner or its user. SmartNFTs extend [ERC-721](./eip-721.md) non-fungible tokens, which only allow representing assets by a unique identifier, but not by an Ethereum address. Moreover, SmartNFTs extend ERC-721 NFTs to include users in addition to owners. + +This EIP standardizes an interface for non-fungible tokens representing physical assets, such as Internet of Things (IoT) devices. These NFTs are tied to physical assets and can verify the authenticity of the tie. They can include an Ethereum address of the physical asset, permitting physical assets to sign messages and transactions. Physical assets can operate with an operating mode defined by its corresponding NFT. ## Motivation -This SmartNFT was developed because the ERC-721 NFT does not include the users of an asset (only include the owners) and does not include the Ethereum address of the asset. Smart assets (for example, IoT devices) are increasing nowadays. If smart assets are tied to SmartNFTs then they can be managed in a secure and traceable way. The reason is that SmartNFTs, unlike ERC-721 NFTs, allow establishing secure communication channels between the physical asset and its owner and its user. In this way, assets, owners and users can be assured of exchanging information with trusted parties. - -**Secure Physical Asset Tied to a SmartNFT** -Current non-fungible tokens are associated with passive assets, either virtual or physical things, but they do not include any standardized mechanism to tie the non-fungible token to the asset. Tying physical assets to NFTs is interesting because the asset can know anytime its owner, user, operating mode, and how to establish secure communication channels with its owner and user. The assets, owners and users are identified by their Ethereum addresses and the Ethereum address of the asset can be obtained from a unique physical property of the physical asset (for example, using a physical unclonable function). The asset can be an active part in any transfer of ownership and use. In addition, the asset is smart, for example to not obey orders from a non-authorized user, or to be inoperative if a successful authentication with the user or the owner has not been fulfilled. -**User Management Mechanism** -SmartNFTs allow implementing a new and useful user management mechanism. In the last few years, many projects concerning assets sharing (for example, vehicles) have been created and developed. SmartNFTs incorporate the Ethereum address of the user as another attribute of the token in order to distinguish between the user, who employs the asset for a specific application, and the owner, who assigns the token to users. Hence, both users and owners of an asset can be traced. - -**Secure Key Exchange Mechanism** -The engagement of the asset with an owner or a user is carried out after a mutual authentication protocol (for example, based on elliptic curve Diffie-Hellman key exchange protocol). This protocol can be employed for a key agreement between the asset and its owner, in the one side, and between the asset and its user, in the other side. +This standard was developed because [EIP-721](./eip-721.md) only tracks ownership (not usage rights) and does not track the Ethereum addresses of the asset. The popularity of smart assets, such as IoT devices, is increasing. To permit secure and traceable management, these NFTs can be used to establish secure communication channels between the physical asset, its owner, and its user. ## Specification -The SmartNFT attributes `addressAsset` and `addressUser` are, respectively, the Ethereum addresses of the physical asset and the user. They are optional attributes but at least one of them should be used in a SmartNFT. In the case of using only the attribute `addressUser`, two states define if the token is assigned or not to a user. `Figure 1` shows these states in a flow chart. When a token is created, transferred or unassigned, the token state is set to `notAssigned`. If the token is assigned to a valid user, the state is set to `userAssigned`. + +The attributes `addressAsset` and `addressUser` are, respectively, the Ethereum addresses of the physical asset and the user. They are optional attributes but at least one of them should be used in an NFT. In the case of using only the attribute `addressUser`, two states define if the token is assigned or not to a user. `Figure 1` shows these states in a flow chart. When a token is created, transferred or unassigned, the token state is set to `notAssigned`. If the token is assigned to a valid user, the state is set to `userAssigned`. ![Figure 1 : Flow chart of the token states with `addressUser` defined (and `addressAsset` undefined)](../assets/eip-4519/images/Figure1.jpg) @@ -35,50 +30,50 @@ In the case of defining the attribute `addressAsset` but not the attribute `addr ![Figure 2 : Flow chart of the token states with `addressAsset` defined (and `addressUser` undefined)](../assets/eip-4519/images/Figure2.jpg) -Finally, if both the attributes `addressAsset` and `addressUser` are defined, the states of the SmartNFT define if the asset has been engaged or not with the owner or the user (`waitingForOwner`, `engagedWithOwner`, `waitingForUser` and `engagedWithUser`). The flow chart in `Figure 3` shows all the possible state changes. The states related to the owner are the same as in `Figure 2`. The difference is that, at the state `engagedWithOwner`, the token can be assigned to a user. If a user is assigned (the token being at states `engagedWithOwner`, `waitingForUser` or `engagedWithUser`), then the token changes its state to `waitingForUser`. Once the asset and the user authenticate each other, the state of the token is set to `engagedWithUser`, and the user is able to use the asset. +Finally, if both the attributes `addressAsset` and `addressUser` are defined, the states of the NFT define if the asset has been engaged or not with the owner or the user (`waitingForOwner`, `engagedWithOwner`, `waitingForUser` and `engagedWithUser`). The flow chart in `Figure 3` shows all the possible state changes. The states related to the owner are the same as in `Figure 2`. The difference is that, at the state `engagedWithOwner`, the token can be assigned to a user. If a user is assigned (the token being at states `engagedWithOwner`, `waitingForUser` or `engagedWithUser`), then the token changes its state to `waitingForUser`. Once the asset and the user authenticate each other, the state of the token is set to `engagedWithUser`, and the user is able to use the asset. ![Figure 3 : Flow chart of the token states with `addressUser` and `addressUser` defined](../assets/eip-4519/images/Figure3.jpg) -In order to complete the ownership transfer of a token, the new owner must carry out a mutual authentication process with the asset, which is off-chain with the asset and on-chain with the token, by using their Ethereum addresses. Similarly, a new user must carry out a mutual authentication process with the asset to complete a use transfer. SmartNFTs define how the authentication processes start and finish. These authentication processes allow deriving fresh session cryptographic keys for secure communication between assets and owners, and between assets and users. Therefore, the trustworthiness of the assets can be traced even if new owners and users manage them. +In order to complete the ownership transfer of a token, the new owner must carry out a mutual authentication process with the asset, which is off-chain with the asset and on-chain with the token, by using their Ethereum addresses. Similarly, a new user must carry out a mutual authentication process with the asset to complete a use transfer. NFTs define how the authentication processes start and finish. These authentication processes allow deriving fresh session cryptographic keys for secure communication between assets and owners, and between assets and users. Therefore, the trustworthiness of the assets can be traced even if new owners and users manage them. -When the SmartNFT is created or when the ownership is transferred, the token state is `waitingForOwner`. Assuming that the asset is an electronic physical asset, it saves in its memory the owner address and sets its operating mode to `waitingForOwner`. The owner generates a pair of keys using the elliptic curve secp256k1 and the primitive element P used on this curve: a secret key SKO_A and a Public Key PKO_A, so that PKO_A = SKO_A*P. To generate the shared key between the owner and the asset, KO, the public key of the asset, PKA, is employed as follows: +When the NFT is created or when the ownership is transferred, the token state is `waitingForOwner`. The asset sets its operating mode to `waitingForOwner`. The owner generates a pair of keys using the elliptic curve secp256k1 and the primitive element P used on this curve: a secret key $SK_{O_{-}A}$ and a Public Key $PK_{O_{-}A}$, so that $PK_{O_{-}A} = SK_{O_{-}A} * P$. To generate the shared key between the owner and the asset, $K_O$, the public key of the asset, $PK_A$, is employed as follows: -KO=PKA*SKO_A +$$K_O = PK_A*SK_{O_{-}A}$$ -Using the function `startOwnerEngagement`, the owner saves PKO_A as the attribute `dataEngagement` and the hash of KO as the attribute `hashK_OA` in the SmartNFT. The owner sends PKO_A signed to the asset. The asset checks the signature of the owner and, if signature is correct, the asset uses PKO_A to calculate: +Using the function `startOwnerEngagement`, $PK_{O_{-}A}$ is saved as the attribute `dataEngagement` and the hash of $K_O$ as the attribute `hashK_OA`. The owner sends request engagement to the asset, and the asset calculates: -KA = SKA*PKO_A +$$K_A = SK_A*PK_{O_{-}A}$$ -If everything is correctly done, KO and KA are the same since: +If everything is correctly done, $K_O$ and $K_A$ are the same since: -KO=PKA\*SKO_A=(SKA\*P)\*SKO_A= SKA\*(SKO_A\*P)=SKA\*PKO_A +$$K_O=PK_A\*SK_{O_{-}A}=(SK_A\*P)\*SK_{O_{-}A}= SK_A\*(SK_{O_{-}A}\*P)=SK_A\*PK_{O_{-}A}$$ -Using the function `ownerEngagement`, the asset sends the hash of KA obtained and if it is the same as the data in `hashK_OA`, then the state of the token changes to `engagedWithOwner` and the event `OwnerEngaged` is sent. Once the asset receives the event, it changes its operation mode to `engagedWithOwner`. This process is shown in `Figure 4`. From this moment, the asset can be managed by the owner and they can communicate in a secure way using the shared key. +Using the function `ownerEngagement`, the asset sends the hash of $K_A$, and if it is the same as the data in `hashK_OA`, then the state of the token changes to `engagedWithOwner` and the event `OwnerEngaged` are sent. Once the asset receives the event, it changes its operation mode to `engagedWithOwner`. This process is shown in `Figure 4`. From this moment, the asset can be managed by the owner and they can communicate in a secure way using the shared key. ![Figure 4: Steps in a successful owner and asset mutual authentication process](../assets/eip-4519/images/Figure4.jpg) -If the asset consults Ethereum and the state of its SmartNFT is `waitingForUser`, the asset (assuming it is an electronic physical asset) saves in its memory the user address and sets its operating mode to `waitingForUser`. Then, a mutual authentication process is carried out with the user, as already done with the owner. The user sends the transaction associated with the function `startUserEngagement`. As in `startOwnerEngagement`, this function saves the public key generated by the user, PKU_A, as the attribute `dataEngagement` and the hash of KU=PKA*SKU_A as the attribute `hashK_UA` in the SmartNFT. +If the asset consults Ethereum and the state of its NFT is `waitingForUser`, the asset (assuming it is an electronic physical asset) sets its operating mode to `waitingForUser`. Then, a mutual authentication process is carried out with the user, as already done with the owner. The user sends the transaction associated with the function `startUserEngagement`. As in `startOwnerEngagement`, this function saves the public key generated by the user, $PK_{U_{-}A}$, as the attribute `dataEngagement` and the hash of $K_U = PK_A * SK_{U_{-}A}$ as the attribute `hashK_UA` in the NFT. -The user sends PKU_A signed to the asset. The latter checks the signature of the user and, if signature is correct, the asset uses PKU_A to calculate: +The user sends request engagement and the asset calculates: -KA = SKA*PKU_A +$$K_A = SK_A*PK_{U_{-}A}$$ -If everything is correctly done, KU and KA are the same since: +If everything is correctly done, $K_U$ and $K_A$ are the same since: -KU=PKA\*SKU_A=(SKA\*P)\*SKU_A= SKA\*(SKU_A\*P)=SKA\*PKU_A +$$K_U=PK_A\*SK_{U_{-}A}=(SK_A\*P)\*SK_{U_{-}A}= SK_A\*(SK_{U_{-}A}\*P)=SK_A\*PK_{U_{-}A}$$ -Using the function `userEngagement`, the asset sends the hash of KA obtained and if it is the same as the data in `hashK_UA`, then the state of the token changes to `engagedWithUser` and the event `UserEngaged` is sent. Once the asset receives the event, it changes its operation mode to `engagedWithUser`. This process is shown in `Figure 5`. From this moment, the asset can be managed by the user and they can communicate in a secure way using the shared key. +Using the function `userEngagement`, the asset sends the hash of $K_A$ obtained and if it is the same as the data in `hashK_UA`, then the state of the token changes to `engagedWithUser` and the event `UserEngaged` is sent. Once the asset receives the event, it changes its operation mode to `engagedWithUser`. This process is shown in `Figure 5`. From this moment, the asset can be managed by the user and they can communicate in a secure way using the shared key. - ![Figure 5: Steps in a successful user and asset mutual authentication process](../assets/eip-4519/images/Fig5_rev.png) + ![Figure 5: Steps in a successful user and asset mutual authentication process](../assets/eip-4519/images/Figure5.jpg) -Since the establishment of a shared secret key is very important for a secure communication, SmartNFTs include the attributes +Since the establishment of a shared secret key is very important for a secure communication, NFTs include the attributes `hashK_OA`, `hashK_UA` and `dataEngagement`. The first two attributes define, respectively, the hash of the secret key shared between the asset and its owner and between the asset and its user. Assets, owners and users should check they are using the correct shared secret keys. The attribute `dataEngagement` defines the public data needed for the agreement. ```solidity pragma solidity ^0.8.0; - /// @title SmartNFT: Extension of ERC-721 Non-Fungible Token Standard. -/// Note: the ERC-165 identifier for this interface is 0x8a68abe3 - interface SmartNFT is ERC721/*,ERC165*/{ + /// @title EIP-4519 NFT: Extension of EIP-721 Non-Fungible Token Standard. +/// Note: the EIP-165 identifier for this interface is 0x8a68abe3 + interface EIP-4519 NFT is EIP721/*,EIP165*/{ /// @dev This emits when the NFT is assigned as utility of a new user. /// This event emits when the user of the token changes. /// (`_addressUser` == 0) when no user is assigned. @@ -93,21 +88,21 @@ pragma solidity ^0.8.0; event OwnerEngaged(uint256 indexed tokenId); /// @dev This emits when it is checked that the timeout has expired. - /// This event emits when the timestamp of the SmartNFT is not updated in timeout. + /// This event emits when the timestamp of the EIP-4519 NFT is not updated in timeout. event TimeoutAlarm(uint256 indexed tokenId); /// @notice This function defines how the NFT is assigned as utility of a new user (if "addressUser" is defined). - /// @dev Only the owner of the SmartNFT can assign a user. If "addressAsset" is defined, then the state of the token must be + /// @dev Only the owner of the EIP-4519 NFT can assign a user. If "addressAsset" is defined, then the state of the token must be /// "engagedWithOwner","waitingForUser" or "engagedWithUser" and this function changes the state of the token defined by "_tokenId" to /// "waitingForUser". If "addressAsset" is not defined, the state is set to "userAssigned". In both cases, this function sets the parameter /// "addressUser" to "_addressUser". - /// @param _tokenId is the tokenId of the SmartNFT tied to the asset. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. /// @param _addressUser is the address of the new user. function setUser(uint256 _tokenId, address _addressUser) external payable; /// @notice This function defines the initialization of the mutual authentication process between the owner and the asset. /// @dev Only the owner of the token can start this authentication process if "addressAsset" is defined and the state of the token is "waitingForOwner". /// The function does not change the state of the token and saves "_dataEngagement" /// and "_hashK_OA" in the parameters of the token. - /// @param _tokenId is the tokenId of the SmartNFT tied to the asset. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. /// @param _dataEngagement is the public data proposed by the owner for the agreement of the shared key. /// @param _hashK_OA is the hash of the secret proposed by the owner to share with the asset. function startOwnerEngagement(uint256 _tokenId, uint256 _dataEngagement, uint256 _hashK_OA) external payable; @@ -124,14 +119,14 @@ pragma solidity ^0.8.0; /// @dev Only the user of the token can start this authentication process if "addressAsset" and "addressUser" are defined and /// the state of the token is "waitingForUser". The function does not change the state of the token and saves "_dataEngagement" /// and "_hashK_UA" in the parameters of the token. - /// @param _tokenId is the tokenId of the SmartNFT tied to the asset. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. /// @param _dataEngagement is the public data proposed by the user for the agreement of the shared key. /// @param _hashK_UA is the hash of the secret proposed by the user to share with the asset. function startUserEngagement(uint256 _tokenId, uint256 _dataEngagement, uint256 _hashK_UA) external payable; /// @notice This function completes the mutual authentication process between the user and the asset. /// @dev Only the asset tied to the token can finish this authentication process provided that the state of the token is - /// "waitingForUser" and dataEngagement if different from 0. This function compares hashK_UA saved in + /// "waitingForUser" and dataEngagement is different from 0. This function compares hashK_UA saved in /// the token with hashK_A. If they are equal then the state of the token changes to "engagedWithUser", dataEngagement is set to 0, /// and the event "UserEngaged" is emitted. /// @param _hashK_A is the hash of the secret generated by the asset to share with the user. @@ -140,14 +135,14 @@ pragma solidity ^0.8.0; /// @notice This function checks if the timeout has expired. /// @dev Everybody can call this function to check if the timeout has expired. The event "TimeoutAlarm" is emitted /// if the timeout has expired. - /// @param _tokenId is the tokenId of the SmartNFT tied to the asset. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. /// @return true if timeout has expired and false in other case. function checkTimeout(uint256 _tokenId) external returns (bool); /// @notice This function sets the value of timeout. /// @dev Only the owner of the token can set this value provided that the state of the token is "engagedWithOwner", /// "waitingForUser" or "engagedWithUser". - /// @param _tokenId is the tokenId of the SmartNFT tied to the asset. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. /// @param _timeout is the value to assign to timeout. function setTimeout(uint256 _tokenId, uint256 _timeout) external; @@ -169,7 +164,7 @@ pragma solidity ^0.8.0; /// @notice This function lets know the user of the token from its tokenId. /// @dev Everybody can call this function. The code executed only reads from Ethereum. - /// @param _tokenId is the tokenId of the SmartNFT tied to the asset. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. /// @return user of the token from its _tokenId. function userOf(uint256 _tokenId) external view returns (address); @@ -195,30 +190,37 @@ pragma solidity ^0.8.0; ``` ## Rationale -The demand for SmartNFTs, which allow user management and a tie to a physical asset are growing (for example, in the context of the Internet of Things). Therefore, it is essential to establish a standard capable of including all these options working together or separately. The incorporation of an Ethereum address of the user or an Ethereum address of a physical asset to the SmartNFT is optional. However, it does not make sense that the SmartNFT does not include any of them because, in that case, the SmartNFT would be an ERC-721 token. Since some functions such as `startUserEngagement` are available only if both addresses are implemented, a single interface with all the options is proposed. -**SmartNFT** -This EIP proposes a non-fungible token tied to a physical asset. The asset is able to generate an Ethereum address and authenticate its user and its owner. Hence, the asset can be considered as a smart asset associated with an NFT. If the asset and the token are regarded as one thing, we can talk about a SmartNFT. +### Authentication -**Authentication** -This EIP proposes using the smart contract to verify the mutual authentication process between the physical asset and the owner or the user by verifying the hash of a shared key. +This EIP uses smart contracts to verify the mutual authentication process since smart contracts are trustless. + +### Tie Time -**Tie Time** This EIP proposes including the attribute timestamp (to register in Ethereum the last time that the physical asset checked the tie with its token) and the attribute timeout (to register the maximum delay time established for the physical asset to prove again the tie). These attributes avoid that a malicious owner or user could use the asset endlessly. + When the asset calls `updateTimestamp`, the smart contract must call `block.timestamp`, which provides current block timestamp as seconds since Unix epoch. For this reason, `timeout` must be provided in seconds. -**ERC-721-based** -[EIP-721](./eip-721.md) is the most commonly-used standard for generic NFTs. This EIP extends ERC-721 for backwards compatibility. +### EIP-721-based + +[EIP-721](./eip-721.md) is the most commonly-used standard for generic NFTs. This EIP extends EIP-721 for backwards compatibility. ## Backwards Compatibility -This standard is an extension of ERC-721. It is fully compatible with both of the commonly used optional extensions (`IERC721Metadata` and `IERC721Enumerable`) mentioned in the EIP-721 standard. + +This standard is an extension of EIP-721. It is fully compatible with both of the commonly used optional extensions (`IERC721Metadata` and `IERC721Enumerable`) mentioned in the EIP-721 standard. + ## Test Cases -The test cases presented in the paper shown below are available [here] (../assets/eip-4519/PoC_SmartNFT/README.md). + +The test cases presented in the paper shown below are available [here](../assets/eip-4519/PoC_SmartNFT/README.md). + ## Reference Implementation + A first version was presented in a paper of the Special Issue **Security, Trust and Privacy in New Computing Environments** of **Sensors** journal of **MDPI** editorial. The paper, entitled [Secure Combination of IoT and Blockchain by Physically Binding IoT Devices to Smart Non-Fungible Tokens Using PUFs](../assets/eip-4519/sensors-21-03119.pdf), was written by the same authors of this EIP. ## Security Considerations -In this EIP, a generic system has been proposed for the creation of non-fungible tokens tied to physical assets. A generic point of view based on the improvements of the current ERC-721 NFT is provided, such as the implementation of the user management mechanism, which does not affect the token's ownership. The physical asset should have the ability to generate an Ethereum address from itself in a totally random way so that only the asset is able to know the secret from which the Ethereum address is generated. In this way, identity theft is avoided and the asset can be proven to be completely genuine. In order to ensure this, it is recommended that only the manufacturer of the asset has the ability to create its associated token. In the case of an IoT device, the device firmware will be unable to share and modify the secret. Instead of storing the secrets, it is recommended that assets reconstruct their secrets from non-sensitive information such as the helper data associated with Physical Unclonable Functions (PUFs). Although a secure key exchange protocol based on elliptic curves has been proposed, the token is open to coexist with other types of key exchange. + +In this EIP, a generic system has been proposed for the creation of non-fungible tokens tied to physical assets. A generic point of view based on the improvements of the current EIP-721 NFT is provided, such as the implementation of the user management mechanism, which does not affect the token's ownership. The physical asset should have the ability to generate an Ethereum address from itself in a totally random way so that only the asset is able to know the secret from which the Ethereum address is generated. In this way, identity theft is avoided and the asset can be proven to be completely genuine. In order to ensure this, it is recommended that only the manufacturer of the asset has the ability to create its associated token. In the case of an IoT device, the device firmware will be unable to share and modify the secret. Instead of storing the secrets, it is recommended that assets reconstruct their secrets from non-sensitive information such as the helper data associated with Physical Unclonable Functions (PUFs). Although a secure key exchange protocol based on elliptic curves has been proposed, the token is open to coexist with other types of key exchange. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4834.md b/EIPS/eip-4834.md index e0d6739d0c0250..d497c34b9a54b5 100644 --- a/EIPS/eip-4834.md +++ b/EIPS/eip-4834.md @@ -4,8 +4,7 @@ title: Hierarchical Domains description: Extremely generic name resolution author: Pandapip1 (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/erc-4834-hierarchical-domains-standard/8388 -status: Last Call -last-call-deadline: 2022-09-30 +status: Review type: Standards Track category: ERC created: 2022-02-22 diff --git a/EIPS/eip-4844.md b/EIPS/eip-4844.md index 05cb53b8528e49..19dcd394a0958c 100644 --- a/EIPS/eip-4844.md +++ b/EIPS/eip-4844.md @@ -8,7 +8,7 @@ status: Draft type: Standards Track category: Core created: 2022-02-25 -requires: 1559, 2718, 2930 +requires: 1559, 2718, 2930, 4895 --- ## Abstract @@ -31,7 +31,7 @@ However, data sharding will still take a considerable amount of time to finish i This EIP provides a stop-gap solution until that point by implementing the _transaction format_ that would be used in sharding, but not actually sharding those transactions. Instead, the data from this transaction format is simply part of the beacon chain and is fully downloaded by all consensus nodes (but can be deleted after only a relatively short delay). -Compared to full data sharding, this EIP has a reduced cap on the number of these transactions that can be included, corresponding to a target of ~1 MB per block and a limit of ~2 MB. +Compared to full data sharding, this EIP has a reduced cap on the number of these transactions that can be included, corresponding to a target of ~0.25 MB per block and a limit of ~0.5 MB. ## Specification @@ -45,17 +45,18 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes | `BLOB_COMMITMENT_VERSION_KZG` | `Bytes1(0x01)` | | `POINT_EVALUATION_PRECOMPILE_ADDRESS` | `Bytes20(0x14)` | | `POINT_EVALUATION_PRECOMPILE_GAS` | `50000` | -| `MAX_BLOBS_PER_BLOCK` | `16` | -| `TARGET_BLOBS_PER_BLOCK` | `8` | -| `MAX_BLOBS_PER_TX` | `2` | -| `GASPRICE_UPDATE_FRACTION_PER_BLOB` | `64` | +| `MAX_DATA_GAS_PER_BLOCK` | `2**19` | +| `TARGET_DATA_GAS_PER_BLOCK` | `2**18` | +| `MIN_DATA_GASPRICE` | `1` | +| `DATA_GASPRICE_UPDATE_FRACTION` | `2225652` | | `MAX_VERSIONED_HASHES_LIST_SIZE` | `2**24` | | `MAX_CALLDATA_SIZE` | `2**24` | | `MAX_ACCESS_LIST_SIZE` | `2**24` | | `MAX_ACCESS_LIST_STORAGE_KEYS` | `2**24` | | `MAX_TX_WRAP_KZG_COMMITMENTS` | `2**24` | | `LIMIT_BLOBS_PER_TX` | `2**24` | -| `GAS_PER_BLOB` | `120000` | +| `DATA_GAS_PER_BLOB` | `2**17` | +| `SIMPLE_GAS_PER_BLOB` | `120000` | | `HASH_OPCODE_BYTE` | `Bytes1(0x49)` | | `HASH_OPCODE_GAS` | `3` | @@ -74,10 +75,12 @@ Compared to full data sharding, this EIP has a reduced cap on the number of thes Throughout this proposal we use cryptographic methods and classes defined in the corresponding [consensus 4844 specs](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844). Specifically, we use the following methods from [`polynomial-commitments.md`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/polynomial-commitments.md): + - [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/polynomial-commitments.md#verify_kzg_proof) - [`evaluate_polynomial_in_evaluation_form()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/polynomial-commitments.md#evaluate_polynomial_in_evaluation_form) We also use the following methods and classes from [`validator.md`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md): + - [`hash_to_bls_field()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md#hash_to_bls_field) - [`compute_powers()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md#compute_powers) - [`compute_aggregated_poly_and_commitment()`](https://github.com/ethereum/consensus-specs/blob/6c2b46ae3248760e0f6e52d61077d8b31e43ad1d/specs/eip4844/validator.md#compute_aggregated_poly_and_commitment) @@ -90,16 +93,18 @@ def kzg_to_versioned_hash(kzg: KZGCommitment) -> VersionedHash: return BLOB_COMMITMENT_VERSION_KZG + hash(kzg)[1:] ``` -Approximates `2 ** (numerator / denominator)`, with the simplest possible approximation that is continuous and has a continuous derivative: +Approximates `factor * e ** (numerator / denominator)` using Taylor expansion: ```python -def fake_exponential(numerator: int, denominator: int) -> int: - cofactor = 2 ** (numerator // denominator) - fractional = numerator % denominator - return cofactor + ( - fractional * cofactor * 2 + - (fractional ** 2 * cofactor) // denominator - ) // (denominator * 3) +def fake_exponential(factor: int, numerator: int, denominator: int) -> int: + i = 1 + output = 0 + numerator_accum = factor * denominator + while numerator_accum > 0: + output += numerator_accum + numerator_accum = (numerator_accum * numerator) // (denominator * i) + i += 1 + return output // denominator ``` ### New transaction type @@ -116,13 +121,14 @@ class SignedBlobTransaction(Container): class BlobTransaction(Container): chain_id: uint256 nonce: uint64 - priority_fee_per_gas: uint256 + max_priority_fee_per_gas: uint256 max_fee_per_gas: uint256 gas: uint64 to: Union[None, Address] # Address = Bytes20 value: uint256 data: ByteList[MAX_CALLDATA_SIZE] access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE] + max_fee_per_data_gas: uint256 blob_versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE] class AccessTuple(Container): @@ -135,10 +141,11 @@ class ECDSASignature(Container): s: uint256 ``` -The `priority_fee_per_gas` and `max_fee_per_gas` fields follow [EIP-1559](./eip-1559.md) semantics, +The `max_priority_fee_per_gas` and `max_fee_per_gas` fields follow [EIP-1559](./eip-1559.md) semantics, and `access_list` as in [`EIP-2930`](./eip-2930.md). [`EIP-2718`](./eip-2718.md) is extended with a "wrapper data", the typed transaction can be encoded in two forms, dependent on the context: + - Network (default): `TransactionType || TransactionNetworkPayload`, or `LegacyTransaction` - Minimal (as in execution payload): `TransactionType || TransactionPayload`, or `LegacyTransaction` @@ -154,12 +161,11 @@ 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: -* All hashes in `blob_versioned_hashes` must start with the byte `BLOB_COMMITMENT_VERSION_KZG` -* There may be at most `MAX_BLOBS_PER_TX` blob commitments in any single transaction. -* There may be at most `MAX_BLOBS_PER_BLOCK` total blob commitments in a valid block. -* There is an equal amount of versioned hashes, kzg commitments and blobs. -* The KZG commitments hash to the versioned hashes, i.e. `kzg_to_versioned_hash(kzg[i]) == versioned_hash[i]` -* The KZG commitments match the blob contents. (Note: this can be optimized with additional data, using a proof for a +- 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. +- The KZG commitments hash to the versioned hashes, i.e. `kzg_to_versioned_hash(kzg[i]) == versioned_hash[i]` +- The KZG commitments match the blob contents. (Note: this can be optimized with additional data, using a proof for a random evaluation at two points derived from the commitment and blob data) @@ -178,42 +184,44 @@ def get_origin(tx: SignedBlobTransaction) -> Address: ### Header extension -The current header encoding is extended with a new 256-bit unsigned integer field `excess_blobs`. This is the running -total of excess blobs included on chain since this EIP was activated. If the total number of blobs is below the -average, `excess_blobs` is capped at zero. +The current header encoding is extended with a new 256-bit unsigned integer field `excess_data_gas`. This is the running +total of excess data gas consumed on chain since this EIP was activated. If the total amount of data gas is below the +target, `excess_data_gas` is capped at zero. The resulting RLP encoding of the header is therefore: ``` rlp([ parent_hash, - ommers_hash, + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash coinbase, state_root, - tx_root, - receipt_root, - bloom, - difficulty, + txs_root, + receipts_root, + logs_bloom, + 0, # difficulty number, gas_limit, gas_used, - time, - extra, - mix_digest, - nonce, - base_fee, - excess_blobs + timestamp, + extradata, + prev_randao, + 0x0000000000000000, # nonce + base_fee_per_gas, + withdrawals_root, + excess_data_gas ]) ``` -The value of `excess_blobs` can be calculated using the parent header and number of blobs in the block. +The value of `excess_data_gas` can be calculated using the parent header and number of blobs in the block. ```python -def calc_excess_blobs(parent: Header, new_blobs: int) -> int: - if parent.excess_blobs + new_blobs < TARGET_BLOBS_PER_BLOCK: +def calc_excess_data_gas(parent: Header, new_blobs: int) -> int: + consumed_data_gas = new_blobs * DATA_GAS_PER_BLOB + if parent.excess_data_gas + consumed_data_gas < TARGET_DATA_GAS_PER_BLOCK: return 0 else: - return parent.excess_blobs + new_blobs - TARGET_BLOBS_PER_BLOCK + return parent.excess_data_gas + consumed_data_gas - TARGET_DATA_GAS_PER_BLOCK ``` ### Beacon chain validation @@ -227,6 +235,7 @@ with full sharding `is_data_available()` can be replaced by data-availability-sa Note that the consensus-layer is tasked with persisting the blobs for data availability, the execution-layer is not. The `ethereum/consensus-specs` repository defines the following beacon-node changes involved in this EIP: + - Beacon chain: process updated beacon blocks and ensure blobs are available. - P2P network: gossip and sync updated beacon block types and new blobs sidecars. - Honest validator: produce beacon blocks with blobs, publish the blobs sidecars. @@ -267,33 +276,48 @@ def point_evaluation_precompile(input: Bytes) -> Bytes: ### Gas price of blobs (Simplified version) -For early draft implementations, we simply change `get_blob_gas(parent)` to always return `GAS_PER_BLOB`. +___WARNING: This is only for testing___ + +For early draft implementations, we simply change `get_blob_gas(parent)` to always return `SIMPLE_GAS_PER_BLOB`. -### Gas price update rule (Full version) +### Gas accounting (Full version) -We propose a simple independent EIP-1559-style targeting rule to compute the gas cost of the transaction. -We use the `excess_blobs` header field to store persistent data needed to compute the cost. +We introduce data gas as a new type of gas. It is independent of normal gas and follows its own targeting rule, similar to EIP-1559. +We use the `excess_data_gas` header field to store persistent data needed to compute the data gas price. For now, only blobs are priced in data gas. ```python -def get_intrinsic_gas(tx: SignedBlobTransaction, parent: Header) -> int: - intrinsic_gas = 21000 # G_transaction - if tx.message.to == None: # i.e. if a contract is created - intrinsic_gas = 53000 - # EIP-2028 data gas cost reduction for zero bytes - intrinsic_gas += 16 * len(tx.message.data) - 12 * len(tx.message.data.count(0)) - # EIP-2930 Optional access lists - intrinsic_gas += 1900 * sum(len(entry.storage_keys) for entry in tx.message.access_list) + 2400 * len(tx.message.access_list) - # New additional gas cost per blob - intrinsic_gas += len(tx.message.blob_versioned_hashes) * get_blob_gas(parent) - return intrinsic_gas - -def get_blob_gas(header: Header) -> int: +def calc_data_fee(tx: SignedBlobTransaction, parent: Header) -> int: + return get_total_data_gas(tx) * get_data_gasprice(header) + +def get_total_data_gas(tx: SignedBlobTransaction) -> int: + return DATA_GAS_PER_BLOB * len(tx.message.blob_versioned_hashes) + +def get_data_gasprice(header: Header) -> int: return fake_exponential( - header.excess_blobs, - GASPRICE_UPDATE_FRACTION_PER_BLOB + MIN_DATA_GASPRICE, + header.excess_data_gas, + DATA_GASPRICE_UPDATE_FRACTION ) ``` +The block validity conditions are modified to include data gas checks: + +```python +def validate_block(block: Block) -> None: + ... + + for tx in block.transactions: + ... + + # the signer must be able to afford the transaction + assert signer(tx).balance >= tx.message.gas * tx.message.max_fee_per_gas + get_total_data_gas(tx) * tx.message.max_fee_per_data_gas + + # 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) +``` + +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. + ### Networking Transactions are presented as `TransactionType || TransactionNetworkPayload` on the execution layer network, @@ -345,7 +369,7 @@ def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper): ### On the path to sharding This EIP introduces blob transactions in the same format in which they are expected to exist in the final sharding specification. -This provides a temporary but significant scaling relief for rollups by allowing them to scale to 2 MB per slot, +This provides a temporary but significant scaling relief for rollups by allowing them to initially scale to 0.25 MB per slot, with a separate fee market allowing fees to be very low while usage of this system is limited. The core goal of rollup scaling stopgaps is to provide temporary scaling relief, @@ -363,26 +387,26 @@ do we implement 25% of the work on the way to full sharding, or 50%, or 75%? The work that is already done in this EIP includes: -* A new transaction type, of the exact same format that will need to exist in "full sharding" -* _All_ of the execution-layer logic required for full sharding -* _All_ of the execution / consensus cross-verification logic required for full sharding -* Layer separation between `BeaconBlock` verification and data availability sampling blobs -* Most of the `BeaconBlock` logic required for full sharding -* A self-adjusting independent gasprice for blobs. +- A new transaction type, of the exact same format that will need to exist in "full sharding" +- _All_ of the execution-layer logic required for full sharding +- _All_ of the execution / consensus cross-verification logic required for full sharding +- Layer separation between `BeaconBlock` verification and data availability sampling blobs +- Most of the `BeaconBlock` logic required for full sharding +- A self-adjusting independent gasprice for blobs The work that remains to be done to get to full sharding includes: -* A low-degree extension of the `blob_kzgs` in the consensus layer to allow 2D sampling -* An actual implementation of data availability sampling -* PBS (proposer/builder separation), to avoid requiring individual validators to process 32 MB of data in one slot -* Proof of custody or similar in-protocol requirement for each validator to verify a particular part of the sharded data in each block +- A low-degree extension of the `blob_kzgs` in the consensus layer to allow 2D sampling +- An actual implementation of data availability sampling +- PBS (proposer/builder separation), to avoid requiring individual validators to process 32 MB of data in one slot +- Proof of custody or similar in-protocol requirement for each validator to verify a particular part of the sharded data in each block This EIP also sets the stage for longer-term protocol cleanups: -* It adds an SSZ transaction type which is slightly gas-advantaged (1000 discount) to nudge people toward using it, +- It adds an SSZ transaction type which is slightly gas-advantaged (1000 discount) to nudge people toward using it, and paves the precedent that all new transaction types should be SSZ -* It defines `TransactionNetworkPayload` to separate network and block encodings of a transaction type -* Its (cleaner) gas price update rule could be applied to the primary basefee. +- It defines `TransactionNetworkPayload` to separate network and block encodings of a transaction type +- Its (cleaner) gas price update rule could be applied to the primary basefee ### How rollups would function @@ -408,17 +432,23 @@ allowing the point verification precompile to work with the new format. Rollups would not have to make any EVM-level changes to how they work; sequencers would simply have to switch over to using a new transaction type at the appropriate time. -### Blob gasprice update rule +### Data gasprice update rule -The blob gasprice update rule is intended to approximate the formula `blob_gas = 2**(excess_blobs / GASPRICE_UPDATE_FRACTION_PER_BLOB)`, -where `excess_blobs` is the total "extra" number of blobs that the chain has accepted relative to the "targeted" number (`TARGET_BLOBS_PER_BLOCK` per block). -Like EIP-1559, it's a self-correcting formula: as the excess goes higher, the `blob_gas` increases exponentially, reducing usage and eventually forcing the excess back down. +The data gasprice update rule is intended to approximate the formula `data_gasprice = MIN_DATA_GASPRICE * e**(excess_data_gas / DATA_GASPRICE_UPDATE_FRACTION)`, +where `excess_data_gas` is the total "extra" amount of data gas that the chain has consumed relative to the "targeted" number (`TARGET_DATA_GAS_PER_BLOCK` per block). +Like EIP-1559, it's a self-correcting formula: as the excess goes higher, the `data_gasprice` increases exponentially, reducing usage and eventually forcing the excess back down. The block-by-block behavior is roughly as follows. -If in block `N`, `blob_gas = G1`, and block `N` has `X` blobs, then in block `N+1`, `excess_blobs` increases by `X - TARGET_BLOBS_PER_BLOCK`, -and so the `blob_gas` of block `N+1` increases by a factor of `2**((X - TARGET_BLOBS_PER_BLOCK) / GASPRICE_UPDATE_FRACTION_PER_BLOB)`. +If block `N` consumes `X` data gas, then in block `N+1` `excess_data_gas` increases by `X - TARGET_DATA_GAS_PER_BLOCK`, +and so the `data_gasprice` of block `N+1` increases by a factor of `e**((X - TARGET_DATA_GAS_PER_BLOCK) / DATA_GASPRICE_UPDATE_FRACTION)`. Hence, it has a similar effect to the existing EIP-1559, but is more "stable" in the sense that it responds in the same way to the same total usage regardless of how it's distributed. +The parameter `DATA_GASPRICE_UPDATE_FRACTION` controls the maximum rate of change of the blob gas price. It is chosen to target a maximum change rate of `e(TARGET_DATA_GAS_PER_BLOCK / DATA_GASPRICE_UPDATE_FRACTION) ≈ 1.125` per block. + +### Throughput + +The values for `TARGET_DATA_GAS_PER_BLOCK` and `MAX_DATA_GAS_PER_BLOCK` are chosen to correspond to a target of 2 blobs (0.25 MB) and maximum of 4 blobs (0.5 MB) per block. These small initial limits are intended to minimize the strain on the network created by this EIP and are expected to be increased in future upgrades as the network demonstrates reliability under larger blocks. + ## Backwards Compatibility ### Blob non-accessibility @@ -429,17 +459,13 @@ instead, they go into the `BeaconBlockBody`. This means that there is now a part ### Mempool issues -Blob transactions are unique in that they have a variable intrinsic gas cost. Hence, a transaction that could be included in one block may be invalid for the next. -To prevent mempool attacks, we recommend a simple technique: only propagate transactions whose `gas` is at least twice the current minimum. - -Additionally, blob transactions have a large data size at the mempool layer, which poses a mempool DoS risk, +Blob transactions have a large data size at the mempool layer, which poses a mempool DoS risk, though not an unprecedented one as this also applies to transactions with large amounts of calldata. -The risk is that an attacker makes and publishes a series of large blob transactions with fees `f9 > f8 > ... > f1`, -where each fee is the 10% minimum increment higher than the previous, and finishes it off with a 21000-gas basic transaction with fee `f10`. -Hence, an attacker could impose millions of gas worth of load on the network and only pay 21000 gas worth of fees. -We recommend a simple solution: both for blob transactions and for transactions carrying a large amount of calldata, -increase the minimum increment for mempool replacement from 1.1x to 2x, decreasing the number of resubmissions an attacker can do at any given fee level by ~7x. +We recommend two solutions: + +- include a 1.1x (or potentially higher) data gasprice bump requirement to the mempool replacement rules +- modify the Ethereum Wire Protocol to stop automatically broadcasting large transactions ## Test Cases @@ -447,8 +473,8 @@ TBD ## Security Considerations -This EIP increases the storage requirements per Beacon block by a maximum of ~2 MB. -This is equal to the theoretical maximum size of a block today (30M gas / 16 gas per calldata byte = 1.875M bytes), and so it will not greatly increase worst-case bandwidth. +This EIP increases the storage requirements per Beacon block by a maximum of ~0.5 MB. +This is 4x larger than the theoretical maximum size of a block today (30M gas / 16 gas per calldata byte = 1.875M bytes), and so it will not greatly increase worst-case bandwidth. Post-merge, block times are expected to be static rather than an unpredictable Poisson distribution, giving a guaranteed period of time for large blocks to propagate. The _sustained_ load of this EIP is much lower than alternatives that reduce calldata costs, even if the calldata is limited, diff --git a/EIPS/eip-4895.md b/EIPS/eip-4895.md index d0f199df2de987..bcdb5c1e8e774a 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: Draft +status: Review type: Standards Track category: Core created: 2022-03-10 @@ -22,10 +22,10 @@ This EIP provides a way for validator withdrawals made on the beacon chain to en The architecture is "push"-based, rather than "pull"-based, where withdrawals are required to be processed in the execution layer as soon as they are dequeued from the consensus layer. Withdrawals are represented as a new type of object in the execution payload -- an "operation" -- that separates the withdrawals feature from user-level transactions. -This approach is more involved than the prior [EIP-4863](./eip-4863.md) but it cleanly separates this "system-level" operation from regular transactions. +This approach is more involved than the prior approach introducing a new transaction type but it cleanly separates this "system-level" operation from regular transactions. The separation simplifies testing (so facilitates security) by reducing interaction effects generated by mixing this system-level concern with user data. -Moreover, this approach is more complex than "pull"-based alternatives (e.g. [EIP-4788](./eip-4788.md) + user-space withdrawal contract) with respect to the core protocol but does provide tighter integration of a critical feature into the protocol itself. +Moreover, this approach is more complex than "pull"-based alternatives with respect to the core protocol but does provide tighter integration of a critical feature into the protocol itself. ## Specification @@ -40,12 +40,15 @@ Beginning with the execution timestamp `FORK_TIMESTAMP`, execution clients **MUS Define a new payload-level object called a `withdrawal` that describes withdrawals that have been validated at the consensus layer. `Withdrawal`s are syntactically similar to a user-level transaction but live in a different domain than user-level transactions. -`Withdrawal`s have three key pieces of information supplied from the consensus layer: -1. a monotonically increasing `index` as a `uint64` value that uniquely identifies each withdrawal -2. a recipient for the withdrawn ether `address` as a 20-byte value -3. an `amount` of ether given in wei as a 256-bit value. +`Withdrawal`s provide key information from the consensus layer: +1. a monotonically increasing `index`, starting from 0, as a `uint64` value that increments by 1 per withdrawal to uniquely identify each withdrawal +2. the `validator_index` of the validator on the consensus layer the withdrawal corresponds to +3. a recipient for the withdrawn ether `address` as a 20-byte value +4. an `amount` of ether given in wei as a 256-bit value. -`Withdrawal` objects are serialized as a RLP list according to the schema: `[index, address, amount]`. +*NOTE*: the `index` for each withdrawal is a global counter spanning the entire sequence of withdrawals. + +`Withdrawal` objects are serialized as a RLP list according to the schema: `[index, validator_index, address, amount]`. ### New field in the execution payload: withdrawals @@ -54,8 +57,8 @@ The execution payload gains a new field for the `withdrawals` which is an RLP li For example: ```python -withdrawal_0 = [index_0, address_0, amount_0] -withdrawal_1 = [index_1, address_1, amount_1] +withdrawal_0 = [index_0, validator_index_0, address_0, amount_0] +withdrawal_1 = [index_1, validator_index_1, address_1, amount_1] withdrawals = [withdrawal_0, withdrawal_1] ``` @@ -145,7 +148,7 @@ An entirely new type of object firewalls off generic EVM execution from this typ The maximum number of withdrawals that can reach the execution layer at a given time is bounded (enforced by the consensus layer) and this limit has been chosen so that any execution layer operational costs are negligible in the context of the broader payload execution. -This bound applies to both computational cost (only a few balance updates in the state) and storage/networking cost as the additional payload footprint is kept small (current parameterizations put the additional overhead at ~1% of current average payload size). +This bound applies to both compuggational cost (only a few balance updates in the state) and storage/networking cost as the additional payload footprint is kept small (current parameterizations put the additional overhead at ~1% of current average payload size). ### Why only balance updates? No general EVM execution? diff --git a/EIPS/eip-4955.md b/EIPS/eip-4955.md index 5f36bc54992b1c..87e045447fa20c 100644 --- a/EIPS/eip-4955.md +++ b/EIPS/eip-4955.md @@ -1,10 +1,10 @@ --- eip: 4955 -title: Vendor Specific Metadata Extension for Non-Fungible Tokens +title: Vendor Metadata Extension for NFTs description: Add a new field to NFT metadata to store vendor specific data author: Ignacio Mazzara (@nachomazzara) discussions-to: https://ethereum-magicians.org/t/eip-4955-non-fungible-token-metadata-namespaces-extension/8746 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-03-29 @@ -13,23 +13,21 @@ requires: 721, 1155 ## Abstract -The following standard allows for the implementation of a standard schema for NFTs metadata. The main goal is adding a new field namespaces to the JSON schema for NFTs +This EIP standardizes a schema for NFTs metadata to add new field namespaces to the JSON schema for [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) NFTs. ## Motivation -A standard schema allows wallets, marketplaces, metaverses, etc. to work with any NFT. +A standardized NFT metadata schema allows wallets, marketplaces, metaverses, and sililar applications to interoperate with any NFT. Applications such as NFT marketplaces and metaverses could usefully leverage NFTs by rendering them using custom 3D representations or any other new attributes. -This standard is inspired by the projects like marketplaces and metaverses which want to leverage the usage of NFTs by rendering them using custom 3d representations or any other new attributes. - -Some projects like Decentraland, TheSandbox, Cryptoavatars, etc. need their own 3d model in order to represent an NFT. These models are not compatible between them because of their esthetics and armatures. - -The lack of a property like the one proposed makes it almost impossible to generate interoperability of NFTs and keep the data decentralized. +Some projects like Decentraland, TheSandbox, Cryptoavatars, etc. need their own 3D model in order to represent an NFT. These models are not cross-compatible because of distinct aesthetics and data formats. ## 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. -**Proposed Schema** (subject to "caveats" below): +### Schema + +(subject to "caveats" below) A new property called `namespaces` is introduced. This property expects one object per project as shown in the example below. @@ -58,7 +56,8 @@ A new property called `namespaces` is introduced. This property expects one obje } ``` -**Example** +### Example + ```json { "name": "My NFT", @@ -95,18 +94,20 @@ There are many projects which need custom properties in order to display a curre The main differences between the projects that are rendering 3d NFTs (models) are: -- **Armatures** -For example, every metaverse uses its own armature. There is a standard for humanoids but it is not being used for every metaverse and not all the metaverses use humanoids. For example, Decentraland has a different esthetic than Cryptovoxels and TheSandbox. It means that every metaverse will need a different model and they may have the same extension (GLB, GLTF) +### Armatures + +Every metaverse uses its own armature. There is a standard for humanoids but it is not being used for every metaverse and not all the metaverses use humanoids. For example, Decentraland has a different aesthetic than Cryptovoxels and TheSandbox. It means that every metaverse will need a different model and they may have the same extension (GLB, GLTF) EIP 4955 Different Renders -- **Metadata (Representations Files)** +### Metadata (Representations Files) For example, every metaverse uses its own metadata representation files to make it work inside the engine depending on its game needs. This is how a wearable looks like in Decentraland in terms of the config file: -``` + +```json "data": { "replaces": [], "hides": [], @@ -154,13 +155,14 @@ This is how a wearable looks like in Decentraland in terms of the config file: "entities": 1 } ``` + `replaces`, `overrides`, `hides`, and different body shapes representation for the same asset are needed for Decentraland in order to render the 3D asset correctly. --- -Using `namespaces` instead of objects like the ones below make it easy for the specific vendor/third-parties to access and index the required models. Moreover, `styles` do not exist because there are no standards around for how an asset will be rendered. As I mentioned above, each metaverse for example uses its own armature and esthetic. There is no Decentraland-style or TheSandbox-style that other metaverses use. Each of them is unique and specific for the sake of the platform's reason of being. Projects like Cryptoavatars are trying to push different standards but without luck for the same reasons related to the uniquity of the armature/animations/metadata. +Using `namespaces` instead of objects like the ones below make it easy for the specific vendor/third-parties to access and index the required models. Moreover, `styles` do not exist because there are no standards around for how an asset will be rendered. As I mentioned above, each metaverse for example uses its own armature and aesthetic. There is no Decentraland-style or TheSandbox-style that other metaverses use. Each of them is unique and specific for the sake of the platform's reason of being. Projects like Cryptoavatars are trying to push different standards but without luck for the same reasons related to the uniquity of the armature/animations/metadata. -``` +```json { "id": "model", "type": "model/gltf+json", @@ -184,12 +186,16 @@ With `namespaces` each vendor will know how to render an asset by doing: fetch(metadata.namespaces["PROJECT_NAME"].uri).then(res => render(res)) ``` -The idea behind extending the ERC721 metadata schema is to have backward compatibility as much as possible with existing projects. Chances are that the asset's smart contracts are not upgradeable and therefore if a project wants to be compatible with this EIP, it will need to redeploy and migrate the current contracts. This is very time- and money-consuming. Creating a new token standard that stores the data needed on-chain: 3D models and config files are not the right paths. There are protocols already used for the ERC721 metadata standard like IPFS (the token URI is an IPFS hash). The idea is to leverage this and require as few changes as possible. Moreover, the current metadata standard uses a 2D representation field: `image`. It seems reasonable to have all the representations of an asset in the same place. +The idea behind extending the [EIP-721](./eip-721.md) metadata schema is to have backward compatibility as much as possible with existing projects. Chances are that the asset's smart contracts are not upgradeable and therefore if a project wants to be compatible with this EIP, it will need to redeploy and migrate the current contracts. This is very time- and money-consuming. Creating a new token standard that stores the data needed on-chain: 3D models and config files are not the right paths. There are protocols already used for the [EIP-721](./eip-721.md) metadata standard like IPFS (the token URI is an IPFS hash). The idea is to leverage this and require as few changes as possible. Moreover, the current metadata standard uses a 2D representation field: `image`. It seems reasonable to have all the representations of an asset in the same place. ## Backwards Compatibility Existing projects that can't modify the metadata response (schema), may be able to create a new smart contract that based on the `tokenId` returns the updated metadata schema. Of course, the projects may need to accept these linked smart contracts as valid in order to fetch the metadata by the `tokenURI` function. +## Security Considerations + +The same security considerations as with [EIP-721](./eip-721.md) apply related to using http gateways or IPFS for the tokenURI method. + ## 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). diff --git a/EIPS/eip-4973.md b/EIPS/eip-4973.md index a03e78cd84c5be..0a0d042b9ee14c 100644 --- a/EIPS/eip-4973.md +++ b/EIPS/eip-4973.md @@ -54,7 +54,7 @@ pragma solidity ^0.8.6; /// @title Account-bound tokens /// @dev See https://eips.ethereum.org/EIPS/eip-4973 -/// Note: the ERC-165 identifier for this interface is 0x5164cf47 +/// Note: the ERC-165 identifier for this interface is 0x8d7bac72 interface IERC4973 { /// @dev This emits when ownership of any ABT changes by any mechanism. /// This event emits when ABTs are given or equipped and unequipped diff --git a/EIPS/eip-5000.md b/EIPS/eip-5000.md index 1147dc4a76a8b8..26804d486a18b9 100644 --- a/EIPS/eip-5000.md +++ b/EIPS/eip-5000.md @@ -41,6 +41,7 @@ def muldiv(x, y, z): else: return ((x * y) // z) % (2**256) ``` + The cost of the instruction is 8 gas (aka `mid`), the same as for `addmod` and `mulmod`. ## Rationale @@ -50,9 +51,11 @@ The cost of the instruction is 8 gas (aka `mid`), the same as for `addmod` and ` All the arithmetic instructions in EVM handle division or modulo 0 specially: the instructions return 0. We have decided to break consistency in order to provide a flexible opcode, which can be used to detect wrapping behaviour. Alternate options include: + - Returning a flag for wrapping - Returning two stack items, higher and lower order bits - Compute the higher order 256 bits in EVM: + ```solidity /// Returns `hi` such that `x × y = hi × 2**256 + mul(x, y)` function hob(uint x, uint y) returns (uint hi) { @@ -111,4 +114,4 @@ TBA ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5006.md b/EIPS/eip-5006.md index dd2369c50b0508..996afb18a72154 100755 --- a/EIPS/eip-5006.md +++ b/EIPS/eip-5006.md @@ -45,10 +45,10 @@ interface IERC5006 { event CreateUserRecord( uint256 recordId, uint256 tokenId, - uint256 amount, + uint64 amount, address owner, address user, - uint64 expiry + uint64 expiry ); /** diff --git a/EIPS/eip-5023.md b/EIPS/eip-5023.md new file mode 100644 index 00000000000000..ae4c0fc0372109 --- /dev/null +++ b/EIPS/eip-5023.md @@ -0,0 +1,168 @@ +--- +eip: 5023 +title: Shareable Non-Fungible Token +description: An interface for creating value-holding tokens shareable by multiple owners +author: Jarno Marttila (@yaruno), Martin Moravek (@mmartinmo) +discussions-to: https://ethereum-magicians.org/t/new-nft-concept-shareable-nfts/8681 +status: Draft +type: Standards Track +category: ERC +created: 2022-01-28 +requires: 165 +--- + +## Abstract + +This EIP standardizes an interface for non-fungible value-holding shareable tokens. Shareability is accomplished by minting copies of existing tokens for new recipients. Sharing and associated events allow the construction of a graph describing who has shared what to which party. + + +## Motivation + +NFT standards such as [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) have been developed to standardize scarce digital resources. However, many non-fungible digital resources need not be scarce. + +We have attempted to capture positive externalities in ecosystems with new types of incentive mechanisms that exhibit anti-rival logic, serve as an unit of accounting and function as medium of sharing. We envision that shareable tokens can work both as incentives but also as representations of items that are typically digital in their nature and gain more value as they are shared. + +These requirements have set us to define shareable NFTs and more specifically a variation of shareable NFTs called non-transferable shareable NFTs. These shareable NFTs can be “shared” in the same way digital goods can be shared, at an almost zero technical transaction cost. We have utilized them to capture anti-rival value in terms of accounting positive externalities in an economic system. + +Typical NFT standards such as EIP-721 and EIP-1155 do not define a sharing modality. Instead ERC standards define interfaces for typical rival use cases such as token minting and token transactions that the NFT contract implementations should fulfil. The ‘standard contract implementations' may extend the functionalities of these standards beyond the definition of interfaces. The shareable tokens that we have designed and developed in our experiments are designed to be token standard compatible at the interface level. However the implementation of token contracts may contain extended functionalities to match the requirements of the experiments such as the requirement of 'shareability'. In reflection to standard token definitions, shareability of a token could be thought of as re-mintability of an existing token to another party while retaining the original version of it. + +Sharing is an interesting concept as it can be thought and perceived in different ways. For example, when we talk about sharing we can think about it is as digital copying, giving a copy of a digital resource while retaining a version by ourselves. Sharing can also be fractional or sharing could be about giving rights to use a certain resource. The concept of shareability and the context of shareability can take different forms and one might use different types of implementatins for instances of shareable tokens. Hence we haven't restricted that the interface should require any specific token type. + +Shareable tokens can be made non-transferable at the contract implementaiton level. Doing so, makes them shareable non-transferable tokens. In the reference implementation we have distilled a general case from our use cases that defines a shareable non-transferable NFTs using the shareable NFT interface. + +We believe that the wider audience should benefit from an abstraction level higher definition for shareability, such as this interface implementation, that defines minimum amount of functions that would be implemented to satisfy the concept of shareability. + +## 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. + +```solidity +/// Note: the ERC-165 identifier for this interface is 0xd763f0cf +interface IERC5023 is IERC165 { + + /// @dev This emits when a token is shared, reminted and given to another wallet that isn't function caller + event Share(address indexed from, address indexed to, uint256 indexed tokenId, uint256 derivedFromtokenId); + + /// @dev Shares, remints an existing token, gives a newly minted token a fresh token id, keeps original token at function callers possession and transfers newly minted token to receiver which should be another address than function caller. + function share(address to, uint256 tokenIdToBeShared) returns(uint256 newTokenId) external; + +} +``` + +Share event is expected to be emitted when function method share is succesfully called and a new token on basis of a given token id is minted and transferred to a recipient. + +## Rationale + +Current NFT standards define transferable non-fungible tokens, but not shareable non-fungible tokens. To be able to create shareable NFTs we see that existing NFT contracts could be extended with an interface which defines the basic principles of sharing, namely the Event of sharing and the function method of sharing. Definition of how transferability of tokens should be handled is left to the contract implementor. In case transfering is left enable shareable tokens behave similarily to the existing tokens, except when they are shared, a version of token is retained. In case transfering is disabled, shareable tokens become shareable non-transferable tokens, where they can be minted and given or shared to other people, but they cannot be transferred away. + +Imagine that Bob works together with Alice on a project. Bob earns an unique NFT indicating that he has made effort to the project, but Bob feels that his accomplishments are not only out of his own accord. Bob wants to share his token with Alice to indicate that also Alice deserves recognition of having put effort on their project. Bob initiates token sharing by calling `Share` method on the contract which has his token and indicates which one of his tokens he wishes to share and to whom by passing address and token id parameters. A new token is minted for Alice and a `Share` event is initiated to communicate that it was Bob whom shared his token to Alice by logging addresses who shared a token id to whose address and which token id was this new token derived from. + +Over time, a tree-like structures can be formed from the Share event information. If Bob shared to Alice, and Alice shared further to Charlie and Alice also shared to David a rudimentary tree structure forms out from sharing activity. This share event data can be later on utilized to gain more information of share activities that the tokens represent. + +``` +B -> A -> C + \ + > D +``` + +These tree structures can be further aggregated and collapsed to network representations e.g. social graphs on basis of whom has shared to whom over a span of time. E.g. if Bob shared a token to Alice, and Alice has shared a different token to Charlie and Bob has shared a token to Charlie, connections form between all these parties through sharing activities. +``` + B----A----C + \_______/ +``` + +## Backwards Compatibility + +TBD + +## Reference Implementation + +Following reference implementation demonstrates a general use case of one of our pilots. In this case a shareable non-transferable token represents a contribution done to a community that the contract owner has decided to merit with a token. Contract owner can mint a merit token and give it to a person. This token can be further shared by the receiver to other parties for example to share the received merit to others that have participated or influenced his contribution. + +``` +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./IERC5023.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/utils/Context.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract ShareableERC721 is ERC721URIStorage, Ownable, IERC5023 /* EIP165 */ { + + string baseURI; + + uint256 internal _currentIndex; + + constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {} + + function mint( + address account, + uint256 tokenId + ) external onlyOwner { + _mint(account, tokenId); + } + + function setTokenURI( + uint256 tokenId, + string memory tokenURI + ) external { + _setTokenURI(tokenId, tokenURI); + } + + function setBaseURI(string memory baseURI_) external { + baseURI = baseURI_; + } + + function _baseURI() internal view override returns (string memory) { + return baseURI; + } + + function share(address to, uint256 tokenIdToBeShared) external returns(uint256 newTokenId) { + require(to != address(0), "ERC721: mint to the zero address"); + require(_exists(tokenIdToBeShared), "ShareableERC721: token to be shared must exist"); + + require(msg.sender == ownerOf(tokenIdToBeShared), "Method caller must be the owner of token"); + + string memory _tokenURI = tokenURI(tokenIdToBeShared); + _mint(to, _currentIndex); + _setTokenURI(_currentIndex, _tokenURI); + + emit Share(msg.sender, to, _currentIndex, tokenIdToBeShared); + + return _currentIndex; + } + + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + revert('In this reference implementation tokens are not transferrable'); + } + + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + revert('In this reference implementation tokens are not transferrable'); + } +} + + + +``` +## Security Considerations + +TBD + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5164.md b/EIPS/eip-5164.md index bfbd0ba9e0c6d6..357e12cafb1244 100644 --- a/EIPS/eip-5164.md +++ b/EIPS/eip-5164.md @@ -2,9 +2,9 @@ eip: 5164 title: Cross-Chain Execution description: Defines an interface that supports execution across EVM networks. -author: Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT), Anna Carroll (@anna-carroll), Hadrien Croubois (@Amxx), Nam Chu Hoai (@nambrot), Georgios (@geogons), Theo Gonella (@mintcloud), Rafael Solari (@rsolari), Auryn Macmillan (@auryn-macmillan), Nathan Ginnever (@nginnever) +author: Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT) discussions-to: https://ethereum-magicians.org/t/eip-5164-cross-chain-execution/9658 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-06-14 @@ -12,11 +12,13 @@ created: 2022-06-14 ## Abstract -This specification defines a cross-chain execution interface for EVM-based blockchains. Developers will be able to have contracts call contracts on other chains. There are two parts to this spec: the "sending" interface and the "executing" interface. The "sending" interface specifies the interface to send a call to another chain; this interface must be implemented by bridges. The "executing" interface must be implemented by the application; it is an interface for executing cross-chain calls. This specification is agnostic of the bridge implementation; it simply standardizes the call interfaces. +This specification defines a cross-chain execution interface for EVM-based blockchains. Implementations of this specification will allow contracts on one chain to call contracts on another. + +The specification defines two components: the "Cross Chain Relayer" and the "Cross Chain Executor". The Cross Chain Relayer lives on the calling side, and the executor lives on the receiving side. Calls sent to Cross Chain Relayers will move through a transport layer to Cross Chain Executor(s), where they are executed. Implementations of this specification must implement both components. ## Motivation -Many Ethereum protocols need to coordinate state changes across multiple EVM-based blockchains. These chains often have native or third-party bridges that allow Ethereum contracts to execute code. However, bridges have different APIs so bridge integrations are custom. Each one affords different properties; with varying degrees of security, speed, and control. +Many Ethereum protocols need to coordinate state changes across multiple EVM-based blockchains. These chains often have native or third-party bridges that allow Ethereum contracts to execute code. However, bridges have different APIs so bridge integrations are custom. Each one affords different properties; with varying degrees of security, speed, and control. Defining a simple, common specification will increase code re-use and allow us to use common bridge implementations. ## Specification @@ -33,16 +35,22 @@ The `CrossChainExecutor` lives on the destination chain and executes relayed cal ### CrossChainRelayer -The `CrossChainRelayer` lives on the chain from which messages are sent. The Relayer's job is to broadcast the messages through a transport layer. +The `CrossChainRelayer` lives on the chain from which messages are sent. The Relayer's job is to broadcast calls through a transport layer to one or more `CrossChainExecutor` contracts. #### Methods **relayCalls** -`CrossChainRelayer`s MUST emit the `RelayedCalls` event when successfully called. +Will relay a batch of `Call` structs to be executed by any `CrossChainExecutor`(s) that execute calls from this Relayer. The `gasLimit` is used as a limit on the executing side. + +`CrossChainRelayer`s MUST emit the `RelayedCalls` event when a batch of calls is relayed. `CrossChainRelayer`s MUST increment a `nonce` so that each batch of calls can be uniquely identified. +`CrossChainRelayer`s MUST return the `nonce` to allow callers to track the batch of calls. + +`CrossChainRelayer`s SHOULD pass the `nonce` as well as the address of the `sender` in the call to `CrossChainExecutor` to uniquely identify the message on the receiving chain. + `CrossChainRelayer`s MAY require payment. ```solidity @@ -52,7 +60,7 @@ struct Call { } interface CrossChainRelayer { - function relayCalls(Call[] calldata calls, uint256 gasLimit) external payable; + function relayCalls(Call[] calldata calls, uint256 gasLimit) external payable returns (uint256 nonce); } ``` @@ -65,6 +73,9 @@ interface CrossChainRelayer { type: Call[] - name: gasLimit type: uint256 + outputs: + - name: nonce + type: uint256 ``` #### Events @@ -76,11 +87,10 @@ The `RelayedCalls` event MUST be emitted by the `CrossChainRelayer` when `relayC ```solidity interface CrossChainRelayer { event RelayedCalls( - uint256 indexed nonce, - address indexed sender, - CrossChainExecutor indexed executor, - Call[] calls, - uint256 gasLimit + uint256 indexed nonce, + address indexed sender, + Call[] calls, + uint256 gasLimit ); } ``` @@ -95,9 +105,6 @@ interface CrossChainRelayer { - name: sender indexed: true type: address - - name: executor - indexed: true - type: address - name: calls type: Call[] - name: gasLimit @@ -123,22 +130,58 @@ interface CrossChainRelayer { The `CrossChainExecutor` executes relayed calls. Developers must implement a `CrossChainExecutor` in order to execute messages on the receiving chain. -#### Authentication +The `CrossChainExecutor` will execute a nonce only once, but may execute nonces in any order. This specification makes no ordering guarantees, because messages may travel non-sequentially through the transport layer. + +#### Execution -`CrossChainRelayer`s SHOULD authenticate that the call has been performed by the bridge transport layer. +`CrossChainExecutor`s SHOULD authenticate that the call has been performed by the bridge transport layer. -`CrossChainRelayer`s SHOULD use [EIP-2771](./eip-2771.md) for authentication and append the address of the Transaction Signer (20 bytes of data) on the origin chain to the end of the call data being executed. +`CrossChainExecutor`s MUST NOT execute a batch of calls more than once. + +**Calldata** + +`CrossChainExecutor`s MUST append the ABI-packed (`nonce`, `sender`) to the calldata for each call being executed. It allows the receiver of the call to check the true `sender` of the transaction and use the `nonce` to apply any transaction ordering logic. + +```solidity +interface CrossChainExecutor { + bytes calldata = abi.encode(Call.data, nonce, sender); // Can also use abi.encodePacked +} +``` + +```yaml +- name: calldata + type: bytes + inputs: + - name: Call.data + type: bytes + - name: nonce + type: uint256 + - name: sender + type: address +``` #### Error handling +**CallsAlreadyExecuted** + +`CrossChainExecutor`s MUST revert if a batch of calls has already been executed and SHOULD emit `CallsAlreadyExecuted` custom error. + +```solidity +interface CrossChainExecutor { + error CallsAlreadyExecuted( + uint256 nonce + ); +} +``` + **CallFailure** -`CrossChainRelayer`s SHOULD revert with `CallFailure` if a call fails. +`CrossChainExecutor`s SHOULD revert with a `CallFailure` error if a call fails. ```solidity interface CrossChainExecutor { error CallFailure( - Call call, + uint256 callIndex, bytes errorData ); } @@ -154,8 +197,7 @@ interface CrossChainExecutor { interface CrossChainExecutor { event ExecutedCalls( CrossChainRelayer indexed relayer, - uint256 indexed nonce, - address indexed caller + uint256 indexed nonce ); } ``` @@ -170,23 +212,18 @@ interface CrossChainExecutor { - name: nonce indexed: true type: uint256 - - name: caller - indexed: true - type: address ``` -### ExecutorAware - -The `ExecutorAware` interface follows [EIP-2771](./eip-2771.md) and allows applications to verify where the call originated from. - ## Rationale -The Relayer address is passed to the executor so that the execution can easily be traced by a client using the relayer address and nonce. A third party just needs to be aware of a list of Relayers and a list of Executors and can trace execution across all of them. +The `CrossChainRelayer` can be coupled to one or more `CrossChainExecutor`. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling `relayCalls` without being aware of the `CrossChainExecutor` address. Messages can also be traced by a client using the data logged by the `ExecutedCalls` event. -Calls are relayed in batches because it is such a common action. Rather than have implementors take different approaches to encoding multiple calls into the `data` portion, this spec includes call batching to take away any guess work. +Calls are relayed in batches because it is such a common action. Rather than have implementors take different approaches to encoding multiple calls into the `data` portion, this specification includes call batching to take away any guess work. Some bridges may require payment in the native currency, so the `relayCalls` function is payable. +Bridges relay messages in various ways, applications should be aware of how the bridge they rely on operates and decide if they want to enforce transaction ordering by using the `nonce`. + ## Backwards Compatibility This specification is compatible with existing governance systems as it offers simple cross-chain execution. diff --git a/EIPS/eip-5187.md b/EIPS/eip-5187.md index 1cc1240580cb5e..bb1ff12a15deeb 100644 --- a/EIPS/eip-5187.md +++ b/EIPS/eip-5187.md @@ -12,68 +12,85 @@ requires: 165, 1155 --- ## Abstract -This standard is an extension of [EIP-1155](./eip-1155.md). It proposes introducing the concept of independent, multiple, and leasable rights of use to enable NFT to be leased out for different cycles while ownership remains with the owner. + +This standard is an extension of [EIP-1155](./eip-1155.md). It proposes to introduce separable, rentable, and transferable usage rights (in the form of NFT-IDs), enabling the property owner (the only NFT holder) to rent out the NFT to multiple users (ID holders) at the same time for different terms, and be withdrawn by smart contract upon expiration. + +The property owner always retains ownership and is able to transfer the NFT to others during the lease. + +The proposal also supports the sublease and renewal of the rental so that users can freely transfer the usage rights among each other and extend the lease term. Early return of NFTs can also be achieved by subletting the usage rights back to the property owners. ## Motivation -The traditional [EIP-721](./eip-721.md) and EIP-1155 focus more on ownership. However, NFTs as digital assets are more prominent in use than ownership. Taking artistic NFTs as an example, NFT artists may wish to rent out the use rights of their works to media companies for an allotted time, or NFT musicians may wish to make their music available to listeners as per playing duration. -Therefore, to better serve NFT developers to meet such needs and develop more sophisticated NFT products, we propose directly introducing rentable usage rights to complement the ERC standard. + +The well-accepted [EIP-721](./eip-721.md) and EIP-1155 standards focused on the ownership of unique assets, quite sensible in the time of NFTs being used primarily as arts and collectibles, or, you can say, as private property rights. +### First Step: "Expirable" NFTs +The advent of private ownership in the real world has promoted the vigorous development of the modern economy, and we believe that the usage right will be the first detachable right widely applied in the blockchain ecosystem. As NFTs are increasingly applied in rights, finance, games, and the Metaverse, the value of NFT is no longer simply the proof of ownership, but with limitless practice use scenarios. For example, artists may wish to rent out their artworks to media or audiences within specific periods, and game guilds may wish to rent out game items to new players to reduce their entry costs. + +The lease/rental of NFTs in the crypto space is not a new topic, but the implementation of leasing has long relied on over-collateralization, centralized custody, or pure trust, which significantly limits the boom of the leasing market. Therefore, a new type of "expirable" NFTs that can be automatically withdrawn upon expiration through smart contract is proposed, at the technical level, to eliminate those bottlenecks. Based on that, a new leasing model that is decentralized, collateral-free, and operated purely "on-chain" may disrupt the way people trade and use NFTs. Thus, this EIP proposal is here to create "expirable" NFTs compatible with EIP-1155. +### Then, Make Everything Transferable +The way we achieve leasing is to separate ownership and usage rights, and beyond that, we focus more on making them freely priced and traded after separation, which is impossible to happen in the traditional financial field. Imagine the below scenarios: i) as a landlord, you can sell your house in rental to others without affecting the tenancy, and your tenants will then pay rent to the new landlord; ii) as a tenant, you can sublet the house to others without the consent of the landlord, and even the one sublets can continue subletting the house until the lease term is close the last tenant can apply for a renewal of the lease. All of this can happen in the blockchain world, and that's the beauty of blockchain. Without permission, without trust, code is the law. + +Making ownership and usage rights transferable may further revolutionize the game rules in NFT's field, both in capital allocation and NFT development. Buying NFT ownership is more like investing in stocks, and the price is determined by market expectations of the project; renting the usage right is less speculative, so the price is easier to determine based on supply and demand. The ownership market and the usage-right market will function to meet the needs of target participants and achieve a balance that is conducive to the long-term and stable development of NFT projects. +Based on the above, we propose this EIP standard to complement the current EIP scopes and introduce those functions as new standards. ## 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. ```solidity pragma solidity ^0.8.0; -/// Note: the ERC-165 identifier for this interface is 0xd4613e9f. -interface IRental /* is IERC1155,IERC165 */ { +/// Note: the ERC-165 identifier for this interface is 0x6938e358. + interface IRental /* is IERC165,IERC1155 */ { /** * @notice This emits when user rent NFT - * @param id The id of the current token - * @param user The address to rent the NFT usage rights - * @param amount The amount of usage rights - * @param expires The specified period of time to rent + * - `id` The id of the current token + * - `user` The address to rent the NFT usage rights + * - `amount` The amount of usage rights + * - `expire` The specified period of time to rent **/ - event Rented( - uint256 indexed id, - address indexed user, - uint256 amount, - uint256 expires - ); + event Rented(uint256 indexed id,address indexed user,uint256 amount,uint256 expire); /** - * @notice This emits when the NFT owner takes back the usage rights from the tenant (the `user`) - * @param id The id of the current token - * @param user The address to rent the NFT's usage rights - * @param amount Amount of usage rights + * MUST trigger on any successful call to `renew(address user,uint256 id)` + * - `id` The id of the current token + * - `user` The user of the NFT + * - `expire` The new specified period of time to rent + **/ + event Renew(uint256 indexed id,address indexed user,uint256 expire); + + /** + * MUST trigger on any successful call to `renew(address user,uint256 id,uint256 expire)` + * - `id` The id of the current token + * - `from` The current user of the NFT + * - `to` The new user + **/ + event Sublet(uint256 indexed id,address indexed from,address to); + + /** + * @notice This emits when the NFT owner takes back the usage rights from the tenant (the `user`) + * - id The id of the current token + * - user The address to rent the NFT's usage rights + * - amount Amount of usage rights **/ event TakeBack(uint256 indexed id, address indexed user, uint256 amount); /** * @notice Function to rent out usage rights - * @param from The address to approve - * @param to The address to rent the NFT usage rights - * @param id The id of the current token - * @param amount The amount of usage rights - * @param expires The specified period of time to rent + * - from The address to approve + * - to The address to rent the NFT usage rights + * - id The id of the current token + * - amount The amount of usage rights + * - expire The specified period of time to rent **/ - function safeRent( - address from, - address to, - uint256 id, - uint256 amount, - uint256 expires - ) external; + function safeRent(address from,address to,uint256 id,uint256 amount,uint256 expire) external; /** * @notice Function to take back usage rights after the end of the tenancy - * @param user The address to rent the NFT's usage rights - * @param tokenId The id of the current token + * - user The address to rent the NFT's usage rights + * - tokenId The id of the current token **/ - function takeBack( - address user, - uint256 tokenId - ) external; + function takeBack(address user,uint256 tokenId) external; /** * @notice Return the NFT to the address of the NFT property right owner. @@ -84,18 +101,48 @@ interface IRental /* is IERC1155,IERC165 */ { * @notice Return the total supply amount of the current token **/ function totalSupply(uint256 id) external view returns (uint256); + + /** + * @notice Return expire The specified period of time to rent + **/ + function expireAt(uint256 id,address user) external view returns(uint256); + + /** + * extended rental period + * - `id` The id of the current token + * - `user` The user of the NFT + * - `expire` The new specified period of time to rent + **/ + function renew(address user,uint256 id,uint256 expire) external; + + /** + * transfer of usage right + * - `id` The id of the current token + * - `user` The user of the NFT + * - `expire` The new specified period of time to rent + **/ + function sublet(address to,uint256 id) external; } ``` + ## Rationale -There are two main benefits to creating this proposal. One is to make it possible to create NFTs with detachable usage rights, of which the transfer of usage rights is separated from the transfer of ownership. The NFT owner can execute the safeRent function to lease out the usage rights to other users and the takeBack function to retrieve the usage rights after the lease expiration. + +Implementing the proposal to create rentable NFTs has two main benefits. + +One is that NFTs with multiple usage rights allow NFT property owners to perform the safeRent function and rent out usage rights to multiple users at the same time. For each usage right leased and expires, the property owner can perform the takeBack function to retrieve the usage right. + +Another benefit is that the transfer of usage rights can be quite flexible. The user can transfer the usage rights to other users by calling the Sublet function during the lease period, and can also extend the lease period of the usage rights by asking the property owner to perform the Renewal function. It is worth mentioning that if the user sublet the NFT to the property owner, it will realize the early return of NFT before the end of the lease period. ## Backwards Compatibility + As mentioned at the beginning, this is an extension of EIP-1155. Therefore, it is fully backward compatible with EIP-1155. ## Security Considerations + Needs discussion. ## Copyright + Disclaimer of copyright and related rights through [CC0](../LICENSE.md). diff --git a/EIPS/eip-5202.md b/EIPS/eip-5202.md index f98456f41d1e5e..b7b35009adcdf8 100644 --- a/EIPS/eip-5202.md +++ b/EIPS/eip-5202.md @@ -4,7 +4,7 @@ title: Blueprint contract format description: Define a bytecode container format for indexing and utilizing blueprint contracts author: Charles Cooper (@charles-cooper), Edward Amor (@skellet0r) discussions-to: https://ethereum-magicians.org/t/erc-5202-standard-factory-contract-format/9851 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-06-23 diff --git a/EIPS/eip-5216.md b/EIPS/eip-5216.md index 9f692450bd2e29..1e16fd4ce598cb 100644 --- a/EIPS/eip-5216.md +++ b/EIPS/eip-5216.md @@ -4,7 +4,8 @@ title: EIP-1155 Approval By Amount Extension description: Extension for EIP-1155 secure approvals author: Iván Mañús (@ivanmmurciaua), Juan Carlos Cantó (@EscuelaCryptoES) discussions-to: https://ethereum-magicians.org/t/eip-erc1155-approval-by-amount/9898 -status: Review +status: Last Call +last-call-deadline: 2022-11-12 type: Standards Track category: ERC created: 2022-07-11 @@ -13,11 +14,11 @@ requires: 20, 165, 1155 ## Abstract -This specification defines standard functions for granular approval of [EIP-1155](./eip-1155.md) tokens by both `id` and `amount`. This EIP extends [EIP-1155](./eip-1155.md). +This EIP defines standard functions for granular approval of [EIP-1155](./eip-1155.md) tokens by both `id` and `amount`. This EIP extends [EIP-1155](./eip-1155.md). ## Motivation -[EIP-1155](./eip-1155.md)'s popularity means that multi-token management transactions occur on a daily basis. Although it can be used as a more comprehensive alternative to [EIP-721](./eip-721.md), it is most commonly used as intended: creating multiple `id`s, each with multiple tokens. While many projects interface with these semi-fungible tokens, by far the most common interactions are with NFT marketplaces. +[EIP-1155](./eip-1155.md)'s popularity means that multi-token management transactions occur on a daily basis. Although it can be used as a more comprehensive alternative to [EIP-721](./eip-721.md), EIP-1155 is most commonly used as intended: creating multiple `id`s, each with multiple tokens. While many projects interface with these semi-fungible tokens, by far the most common interactions are with NFT marketplaces. Due to the nature of the blockchain, programming errors or malicious operators can cause permanent loss of funds. It is therefore essential that transactions are as trustless as possible. EIP-1155 uses the `setApprovalForAll` function, which approves ALL tokens with a specific `id`. This system has obvious minimum required trust flaws. This EIP combines ideas from [EIP-20](./eip-20.md) and [EIP-721](./eip-721.md) in order to create a trust mechanism where an owner can allow a third party, such as a marketplace, to approve a limited (instead of unlimited) number of tokens of one `id`. diff --git a/EIPS/eip-5218.md b/EIPS/eip-5218.md index 266286e30bcb59..5c3222b821afd8 100644 --- a/EIPS/eip-5218.md +++ b/EIPS/eip-5218.md @@ -182,6 +182,8 @@ The `supportsInterface` method MUST return `true` when called with `0xac7b5ca9`. This EIP aims to allow tracing all licenses to an NFT to facilitate right management. The EIP-721 standard only logs the property but not the legal rights tethered to NFTs. Even when logging the license via the optional EIP-721 Metadata extension, sublicenses are not traceable, which doesn't comply with the transparency goals of Web3. Some implementations attempt to get around this limitation by minting NFTs to represent a particular license, such as the BAYC #6068 Royalty-Free Usage License. This is not an ideal solution because the linking between different licenses to an NFT is ambiguous. An auditor has to investigate all NFTs in the blockchain and inspect the metadata which hasn't been standardized in terms of sublicense relationship. To avoid these problems, this EIP logs all licenses to an NFT in a tree data structure, which is compatible with EIP-721 and allows efficient traceability. +This EIP attempts to tether NFTs with copyright licenses to the creative work by default and is not subject to the high legal threshold for copyright ownership transfers which require an explicit signature from the copyright owner. To transfer and track copyright ownership, one may possibly integrate EIP-5218 and [EIP-5289](./eip-5289.md) after careful scrutinizing and implement a smart contract that atomically (1) signs the legal contract via EIP-5289, and (2) transfers the NFT together with the copyright ownership via EIP-5218. Either both take place or both revert. + ## Backwards Compatibility This standard is compatible with the current EIP-721 standards: a contract can inherit from both EIP-721 and EIP-5218 at the same time. diff --git a/EIPS/eip-5298.md b/EIPS/eip-5298.md new file mode 100644 index 00000000000000..19a368e1aa05c1 --- /dev/null +++ b/EIPS/eip-5298.md @@ -0,0 +1,149 @@ +--- +eip: 5298 +title: ENS Trust to hold NFTs under ENS name +description: An interface for a smart contract acting as a "trust" that holds tokens by ENS name. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-eip-5198-ens-as-token-holder/10374 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-12 +requires: 137, 721, 1155 +--- + +## Abstract + +This EIP standardizes an interface for smart contracts to hold of [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) tokens on behalf of ENS domains. + +## Motivation + +Currently, if someone wants to receive a token, they have to set up a wallet address. This EIP decouples NFT ownership from wallet addresses. + +## Specification + +1. Compliant contracts MUST implement `ERC721TokenReceiver`, as defined in [EIP-721](./eip-721.md). +2. Compliant contracts implement the following interface: + +```solidity +interface IERC_ENS_TRUST is ERC721Receiver, ERC1155Receiver { + function claimTo(address to, bytes32 ensNode, address operator, uint256 tokenId) payable external; +} +``` + +3. `claimTo` MUST check if `msg.sender` is the owner of the ENS node (and/or approved by the domain in implementation-specific ways). The compliant contract then MUST make a call to the `safeTransferFrom` function of [EIP-721](./eip-712.md) or [EIP-1155](./eip-1155.md). + +## Rationale + +1. ENS was chosen because it is a well-established scoped ownership namespace. +This is nonetheless compatible with other scoped ownership namespaces. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Test Cases + +```ts +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; + +describe("FirstENSBankAndTrust", function () { + + describe("Receive and Claim Token", function () { + + it("Should ACCEPT/REJECT claimTo based on if ENS owner is msg.sender", async function () { + ... + // Steps of testing: + // mint to charlie + // charlie send to ENSTrust and recorded under bob.xinbenlvethsf.eth + // bob try to claimTo alice, first time it should be rejected + // bob then set the ENS record + // bob claim to alice, second time it should be accepted + + // mint to charlie + await erc721ForTesting.mint(charlie.address, fakeTokenId); + + // charlie send to ENSTrust and recorded under bob.xinbenlvethsf.eth + await erc721ForTesting.connect(charlie)["safeTransferFrom(address,address,uint256,bytes)"]( + charlie.address, firstENSBankAndTrust.address, + fakeTokenId, + fakeReceiverENSNamehash + ); + + // bob try to claimTo alice, first time it should be rejected + await expect(firstENSBankAndTrust.connect(bob).claimTo( + alice.address, + fakeReceiverENSNamehash, + firstENSBankAndTrust.address, + fakeTokenId + )) + .to.be.rejectedWith("ENSTokenHolder: node not owned by sender"); + + // bob then set the ENS record + await ensForTesting.setOwner( + fakeReceiverENSNamehash, bob.address + ); + + // bob claim to alice, second time it should be accepted + await expect(firstENSBankAndTrust.connect(bob).claimTo( + alice.address, + fakeReceiverENSNamehash, + erc721ForTesting.address, + fakeTokenId + )); + }); + }); +}); +``` + +## Reference Implementation + +```solidity +pragma solidity ^0.8.9; + +contract FirstENSBankAndTrust is IERC721Receiver, Ownable { + function getENS() public view returns (ENS) { + return ENS(ensAddress); + } + + function setENS(address newENSAddress) public onlyOwner { + ensAddress = newENSAddress; + } + + // @dev This function is called by the owner of the token to approve the transfer of the token + // @param data MUST BE the ENS node of the intended token receiver this ENSHoldingServiceForNFT is holding on behalf of. + function onERC721Received( + address operator, + address /*from*/, + uint256 tokenId, + bytes calldata data + ) external override returns (bytes4) { + require(data.length == 32, "ENSTokenHolder: last data field must be ENS node."); + // --- START WARNING --- + // DO NOT USE THIS IN PROD + // this is just a demo purpose of using extraData for node information + // In prod, you should use a struct to store the data. struct should clearly identify the data is for ENS + // rather than anything else. + bytes32 ensNode = bytes32(data[0:32]); + // --- END OF WARNING --- + + addToHolding(ensNode, operator, tokenId); // conduct the book keeping + return ERC721_RECEIVER_MAGICWORD; + } + + function claimTo(address to, bytes32 ensNode, address tokenContract uint256 tokenId) public { + require(getENS().owner(ensNode) == msg.sender, "ENSTokenHolder: node not owned by sender"); + removeFromHolding(ensNode, tokenContract, tokenId); + IERC721(tokenContract).safeTransferFrom(address(this), to, tokenId); + } +} +``` + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5375.md b/EIPS/eip-5375.md index a84b76bed8ca64..c0cd6cc4d0a580 100644 --- a/EIPS/eip-5375.md +++ b/EIPS/eip-5375.md @@ -4,7 +4,7 @@ title: NFT Author Information and Consent description: An extension of EIP-721 for NFT authorship and author consent. author: Samuele Marro (@samuelemarro), Luca Donno (@lucadonnoh) discussions-to: https://ethereum-magicians.org/t/eip-5375-nft-authorship/10182 -status: Review +status: Final type: Standards Track category: ERC created: 2022-07-30 @@ -13,14 +13,14 @@ requires: 55, 155, 712, 721, 1155 ## Abstract -This document outlines a standard to provide off-chain information in JSON format regarding NFT authors. Specifically, it adds a new field which provides both a list of author names/addresses and a proof of _authorship consent_, i.e. a proof that a certain author agreed to be named as the author of the NFT. Note that a proof of authorship consent is not a proof of authorship: an address could consent to be named as the author without actually being the author. +This EIP standardizes a JSON format for storing off-chain information about NFT authors. Specifically, it adds a new field which provides a list of author names, addresses, and proofs of _authorship consent_: proofs that the authors have agreed to be named as authors. Note that a proof of authorship _consent_ is not a proof of authorship: an address can consent without having authored the NFT. ## Motivation -There is currently no unified protocol to identify the author of an NFT. Some existing techniques include: +There is currently no standard to identify authors of an NFT, and existing techniques have issues: -- Using the mint transaction signer - - Requires that the minter and the author are the same +- Using the mint `tx.origin` or `msg.sender` + - Assumes that the minter and the author are the same - Does not support multiple authors - Using the first Transfer event for a given ID - Contract/minter can claim that someone else is the author without their consent @@ -29,7 +29,7 @@ There is currently no unified protocol to identify the author of an NFT. Some ex - Requires per-contract support by NFT platforms - Contract/minter can claim that someone else is the author without their consent -The first practice is the most common, mainly due to the fact that relying on minters to provide truthful information opens up avenues for frauds (e.g. selling an NFT while claiming that it was made by a famous artist). However, there are several situations where the minter and the author might not be the same, such as: +The first practice is the most common. However, there are several situations where the minter and the author might not be the same, such as: - NFTs minted by a contract - Lazy minting @@ -243,7 +243,7 @@ There are three reasons: ### Why repeat id, chainId and contractAddress? -In many cases, all three data can be derived from contextual information. However, requiring their inclusion in the JSON document ensures that author consent can be verified by only accessing the JSON document. +In many cases, this data can be derived from contextual information. However, requiring their inclusion in the JSON document ensures that author consent can be verified using only the JSON document. ### Why not implement a revocation system? @@ -259,10 +259,12 @@ Since the author only needs to sign an EIP-712 message, this protocol allows min - Obtain an EVM wallet; - Learn how to read and sign a EIP-712 message (which can often be simplified by using a Dapp) + without needing to: -- Obtain the chain's native token (e.g. through trading or bridging) -- Sign a transaction -- Understand the pricing mechanism of transactions + +- Obtain the chain's native token (e.g. through trading or bridging); +- Sign a transaction; +- Understand the pricing mechanism of transactions; - Verify if a transaction has been included in a block This reduces the technical barrier for authors, thus increasing the usability of NFTs, without requiring authors to hand over their keys to a tech-savvy intermediary. diff --git a/EIPS/eip-5484.md b/EIPS/eip-5484.md index 5d8d22fea07f69..a885f529885651 100644 --- a/EIPS/eip-5484.md +++ b/EIPS/eip-5484.md @@ -1,10 +1,10 @@ --- eip: 5484 title: Consensual Soulbound Tokens -description: Interface for special NFTS with immutable ownership and pre-determined immutable burn authorization +description: Interface for special NFTs with immutable ownership and pre-determined immutable burn authorization author: Buzz Cai (@buzzcai) discussions-to: https://ethereum-magicians.org/t/eip-5484-consensual-soulbound-tokens/10424 -status: Review +status: Final type: Standards Track category: ERC created: 2022-08-17 @@ -13,14 +13,17 @@ requires: 165, 721 ## Abstract -This EIP defines an interface extending [EIP-721](./eip-721.md) to create soulbound tokens. Before issuance, both parties (the issuer and the receiver), have to agree on who has the authorization to burn this token. Burn authorization is immutable after declaration. After its issuance, a soulbound token can't be transferred, but can be burned based on a predetermined immutable burn authorization. + +This EIP defines an interface extending [EIP-721](./eip-721.md) to create soulbound tokens. Before issuance, both parties (the issuer and the receiver), have to agree on who has the authorization to burn this token. Burn authorization is immutable after declaration. After its issuance, a soulbound token can't be transferred, but can be burned based on a predetermined immutable burn authorization. ## Motivation -The idea of soulbound tokens has gathered significant attention since its publishing. Without a standard interface, however, soulbound tokens are incompatible. It is hard to develop universal services targeting at soulbound tokens without minimal consensus on the implementation of the tokens. + +The idea of soulbound tokens has gathered significant attention since its publishing. Without a standard interface, however, soulbound tokens are incompatible. It is hard to develop universal services targeting at soulbound tokens without minimal consensus on the implementation of the tokens. This EIP envisions soulbound tokens as specialized NFTs that will play the roles of credentials, credit records, loan histories, memberships, and many more. In order to provide the flexibility in these scenarios, soulbound tokens must have an application-specific burn authorization and a way to distinguish themselves from regular EIP-721 tokens. ## 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. - The token MUST implement the following interfaces: @@ -28,13 +31,14 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL 1. [EIP-165](./eip-165.md)’s `ERC165` (`0x01ffc9a7`) 1. [EIP-721](./eip-721.md)’s `ERC721` (`0x80ac58cd`) -- `burnAuth` SHALL be presented to receiver before issuance. +- `burnAuth` SHALL be presented to receiver before issuance. - `burnAuth` SHALL be Immutable after issuance. - `burnAuth` SHALL be the sole factor that determines which party has the rights to burn token. - The issuer SHALL present token metadata to the receiver and acquire receiver's signature before issuance. - The issuer SHALL NOT change metadata after issuance. /// Note: the EIP-165 identifier for this interface is 0x0489b56f + ### Contract Interface ```solidity @@ -71,32 +75,42 @@ interface IERC5484 { ``` ## Rationale + ### Soulbound Token (SBTs) as an extension to EIP-721 -We believe that soulbound token serves as a specialized subset of the existing EIP-721 tokens. The advantage of such design is seamless compatibility of soulbound token with existing NFT services. Service providers can treat SBTs like NFTs and do not need to make drastic changes to their existing codebase. -### Non-Transferable -One problem with current soulbound token implementations that extend from [EIP-721](./eip-721.md) is that all transfer implementations throw errors. A much cleaner approach would be for transfer functions to still throw, but also enable third parties to check beforehand if the contract implements the soulbound interface to avoid calling transfer. +We believe that soulbound token serves as a specialized subset of the existing EIP-721 tokens. The advantage of such design is seamless compatibility of soulbound token with existing NFT services. Service providers can treat SBTs like NFTs and do not need to make drastic changes to their existing codebase. + +### Non-Transferable + +One problem with current soulbound token implementations that extend from [EIP-721](./eip-721.md) is that all transfer implementations throw errors. A much cleaner approach would be for transfer functions to still throw, but also enable third parties to check beforehand if the contract implements the soulbound interface to avoid calling transfer. ### Burn Authorization + We want maximum freedom when it comes to interface usage. A flexible and predetermined rule to burn is crucial. Here are some sample scenarios for different burn authorizations: -- `IssuerOnly`: Loan record + +- `IssuerOnly`: Loan record - `ReceiverOnly`: Paid membership - `Both`: Credentials -- `Neither`: Credit history +- `Neither`: Credit history Burn authorization is tied to specific tokens and immutable after issuance. It is therefore important to inform the receiver and gain receiver's consent before the token is issued. ### Issued Event + On issuing, an `Issued` event will be emitted alongside [EIP-721](./eip-721.md)'s `Transfer` event. This design keeps backward compatibility while giving clear signals to thrid-parties that this is a soulBound token issuance event. ### Key Rotations -A concern Ethereum users have is that soulbound tokens having immutable ownership discourage key rotations. This is a valid concern. Having a burnable soulbound token, however, makes key rotations achievable. The owner of the soulbound token, when in need of key rotations, can inform the issuer of the token. Then the party with burn authorization can burn the token while the issuer can issue a replica to the new address. + +A concern Ethereum users have is that soulbound tokens having immutable ownership discourage key rotations. This is a valid concern. Having a burnable soulbound token, however, makes key rotations achievable. The owner of the soulbound token, when in need of key rotations, can inform the issuer of the token. Then the party with burn authorization can burn the token while the issuer can issue a replica to the new address. ## Backwards Compatibility + This proposal is fully backward compatible with [EIP-721](./eip-721.md) ## Security Considerations -Needs discussion. + +There are no security considerations related directly to the implementation of this standard. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5489.md b/EIPS/eip-5489.md index 0586d78f447452..32b15663a83f87 100644 --- a/EIPS/eip-5489.md +++ b/EIPS/eip-5489.md @@ -1,10 +1,10 @@ --- eip: 5489 title: NFT Hyperlink Extension -description: NFT Hyperlink Extension tries to make NFTs as decentralized medium for Web3. +description: NFT Hyperlink Extension embeds hyperlinks onto NFTs, allowing users to click any hNFT and be transported to any url set by the owner. author: IronMan_CH (@coderfengyun) discussions-to: https://ethereum-magicians.org/t/eip-5489-nft-hyperlink-extension/10431 -status: Draft +status: Review type: Standards Track category: ERC created: 2022-08-16 @@ -15,24 +15,21 @@ requires: 165, 721 This EIP proposes a new extension for NFTs (non-fungible token, aka [EIP-721](./eip-721.md)): nft-hyperlink-extention (hNFT), embedding NFTs with hyperlinks, referred to as “hNFTs”. As owners of hNFTs, users may authorize a URL slot to a specific address which can be either an externally-owned account (EOA) or a contract address and hNFT owners are entitled to revoke that authorization at any time. The address which has slot authorization can manage the URL of that slot. -Industries like advertisement can put advertisement materials on these hyperlink slots. ## Motivation -As NFTs attract more and more user attention, NFTs have the potential to evolve towards becoming the decentralized medium for Web3. But there are some missing core components, such as how to attach, edit and exhibit highly customized information on this medium (NFT). For example, end users can’t attach extra rich texts, videos or images on NFTs, also there’s no way to exhibit these rich-content attachments on NFTs. Industries like advertisement eagerly look forward this kind of rich-content attachment ability on NFTs. -This proposal try to answer this question, it tries to use hyperlink as the main form of “highly customized attachment on NFT”, and also resolve how to attach, edit and exhibit these attachments on NFTs. +As NFTs attract more attention, they have the potential to become the primary medium of Web3. Currently, end users can’t attach rich texts, videos, or images to NFTs, and there’s no way to render these rich-content attachments. Many industries eagerly look forward to this kind of rich-content attachment ability. Attaching, editing, and displaying highly customized information can usefully be standardized. + +This EIP uses hyperlinks as the aforementioned form of “highly customized attachment on NFT”, and also specifies how to attach, edit, and display these attachments on NFTs. ## 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. -### New URI format - -This EIP adds a new URI scheme, "hnft://". This URI scheme enables redirecting or connecting activity between hNFTs. - ### Interface #### `IERC5489` + ```solidity // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; @@ -89,7 +86,9 @@ interface IERC5489 { ) external; /** - * @dev + * @dev Throws if `tokenId` is not a valid NFT. URIs are defined in RFC 3986. + * The URI MUST point to a JSON file that confirms to the "EIP5489 Metadata JSON schema". + * * returns the latest uri of an slot on a token, which is indicated by `tokenId`, `slotManagerAddr` */ function getSlotUri(uint256 tokenId, address slotManagerAddr) @@ -118,29 +117,56 @@ The `SlotUriUpdated` event MUSt be emitted when a slot's URI is changed. The `supportInterface` method MUST return true when called with `0x8f65987b`. ### Authentication + The `authorizeSlotTo`, `revokeAuthorization`, and `revokeAllAuthorizations` functions are authenticated if and only if the message sender is the owner of the token. ## Rationale ### Extends NFT with hyperlinks + URIs are used to represent the value of slots to ensure enough flexibility to deal with different use cases. ### Authorize slot to address + We use addresses to represent the key of slots to ensure enough flexibility to deal with all use cases. -### New URI format to enable redirect between hNFTs -This extension adds a new URI scheme, `hnft://` so that the URI pointed to by the NFT is resolvable. +### Metadata JSON schema + +```json +{ + "title": "AD Metadata", + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the slot's occupier. Consider making any images at a width between 48 and 1080 pixels and aspect ration between 1.91:1 and 4:5 inclusive. Suggest to show this as an thumbnail of the target resource" + }, + "description": { + "type": "string", + "description": "A paragraph which briefly introduce what is the target resource" + }, + "target": { + "type": "string", + "description": "A URI pointing to target resource, sugguest to follow 30X status code to support more redirections, the mime type and content rely on user's setting" + } + } +} +``` ## Backwards Compatibility + As mentioned in the specifications section, this standard can be fully EIP-721 compatible by adding an extension function set. In addition, new functions introduced in this standard have many similarities with the existing functions in EIP-721. This allows developers to easily adopt the standard quickly. ## Reference Implementation + You can find an implementation of this standard in [`ERC5489.sol`](../assets/eip-5489/contracts/ERC5489.sol). ## Security Considerations + No security considerations were found. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5507.md b/EIPS/eip-5507.md index 2e1fbd9e78a220..06743c467e8797 100644 --- a/EIPS/eip-5507.md +++ b/EIPS/eip-5507.md @@ -1,28 +1,65 @@ --- eip: 5507 -title: Refundable NFTs -description: Adds refund functionality to EIP-721 and EIP-1155 NFTs +title: Refundable Tokens +description: Adds refund functionality to EIP-20, EIP-721, and EIP-1155 tokens author: elie222 (@elie222), Pandapip1 (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/eip-5507-refundable-nfts/10451 status: Draft type: Standards Track category: ERC created: 2022-08-19 -requires: 165, 721, 1155 +requires: 20, 165, 721, 1155 --- ## Abstract -This EIP adds refund functionality for initial NFT offerings to [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md). Funds are held in escrow until a predetermined time before they are claimable. Until that predetermined time passes, users can receive a refund for NFTs they have purchased. +This EIP adds refund functionality for initial token offerings to [EIP-20](./eip-20.md), [EIP-721](./eip-721.md), and [EIP-1155](./eip-1155.md). Funds are held in escrow until a predetermined time before they are claimable. Until that predetermined time passes, users can receive a refund for tokens they have purchased. ## Motivation -The NFT space lacks accountability. For the health of the NFT ecosystem as a whole, better mechanisms to prevent rugpulls from happening are needed. Offering refunds provides greater protection for buyers and increases legitimacy for creators. +The NFT and token spaces lack accountability. For the health of the ecosystem as a whole, better mechanisms to prevent rugpulls from happening are needed. Offering refunds provides greater protection for buyers and increases legitimacy for creators. ## 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. +### EIP-20 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.4; + +import "IERC20.sol"; +import "IERC165.sol"; + +/// @notice Refundable EIP-20 tokens +/// @dev The EIP-165 identifier of this interface is `0xTODO` +interface IERC20Refund is ERC20, ERC165 { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _sender The person that requested a refund + /// @param _amount The amount of token (in terms of the smallest divisible unit) that was refunded + event Refund( + address indexed _sender, + uint256 indexed _amount + ); + + /// @notice As long as the refund is active, refunds the user + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// @param amount The `amount` to refund + function refund(uint256 amount) external; + + /// @notice Gets the refund price + /// @return wei The amount of ether (in wei) that would be refunded for a single token unit (10**decimals smallest divisible units) + function refundOf() external view returns (uint256 wei); + + /// @notice Gets the first block for which the refund is not active + /// @return block The block beyond which the token cannot be refunded + function refundDeadlineOf() external view returns (uint256 block); +} +``` + ### EIP-721 Refund Extension ```solidity @@ -48,7 +85,7 @@ interface IERC721Refund is ERC721, ERC165 { /// @notice As long as the refund is active for the given `tokenId`, refunds the user /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors /// @param tokenId The `tokenId` to refund - function refund(uint256 calldata tokenId) external; + function refund(uint256 tokenId) external; /// @notice Gets the refund price of the specific `tokenId` /// @param tokenId The `tokenId` to query @@ -90,11 +127,11 @@ interface IERC1155Refund is IERC1155, IERC165 { /// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors /// @param tokenId The `tokenId` to refund /// @param amount The amount of `tokenId` to refund - function refund(uint256 calldata tokenId, uint256 amount) external; + function refund(uint256 tokenId, uint256 amount) external; /// @notice Gets the refund price of the specific `tokenId` /// @param tokenId The `tokenId` to query - /// @return wei The amount of ether (in wei) that would be refunded + /// @return wei The amount of ether (in wei) that would be refunded for a single token function refundOf(uint256 tokenId) external view returns (uint256 wei); /// @notice Gets the first block for which the refund is not active for a given `tokenId` diff --git a/EIPS/eip-5528.md b/EIPS/eip-5528.md index f67b8bd2dab657..cb822cc106a584 100644 --- a/EIPS/eip-5528.md +++ b/EIPS/eip-5528.md @@ -4,8 +4,7 @@ title: Refundable Fungible Token description: Allows refunds for EIP-20 tokens by escrow smart contract author: StartfundInc (@StartfundInc) discussions-to: https://ethereum-magicians.org/t/eip-5528-refundable-token-standard/10494 -status: Last Call -last-call-deadline: 2022-10-16 +status: Final type: Standards Track category: ERC created: 2022-08-16 @@ -14,40 +13,41 @@ requires: 20 ## Abstract -This standard is an extension of [EIP-20](./eip-20.md). This specification provides a type of escrow service in the blockchain ecosystem, which includes the following capabilities. +This standard is an extension of [EIP-20](./eip-20.md). This specification defines a type of escrow service with the following flow: - The seller issues tokens. -- The seller creates an escrow smart contract with detailed escrow information. The information could include seller token contract address, buyer token contract address, lock period, exchange rate, the maximum number of buyers, minimum balance of buyers, additional escrow success conditions, etc. -- The seller funds seller tokens to the escrow contract. -- Buyers fund buyer tokens which are pre-defined in the escrow contract. +- The seller creates an escrow smart contract with detailed escrow information like contract addresses, lock period, exchange rate, additional escrow success conditions, etc. +- The seller funds seller tokens to the *Escrow Contract*. +- Buyers fund buyer tokens which are pre-defined in the *Escrow Contract*. - When the escrow status meets success, the seller can withdraw buyer tokens, and buyers can withdraw seller tokens based on exchange rates. - Buyers can withdraw (or refund) their funded token if the escrow process is failed or is in the middle of the escrow process. ## Motivation -Due to the nature of cryptocurrencies that guarantee anonymity, there is no way to get it back to the cryptocurrency that has already been paid. +Because of the pseudonymous nature of cryptocurrencies, there is no automatic recourse to recover funds that have already been paid. -To solve this problem, the Escrow service exists in the real world. However, it is challenging to implement an escrow service coordinated by a third-party arbitrator in a decentralized cryptocurrency ecosystem. To solve this, a smart contract was designed that acts as an escrow and devised a function where each token is sent back to the original wallet if the escrow is not completed. - -Escrow smart contract service should support refund EIP-20 tokens in the middle of the escrow process or when the operation fails. +In traditional finance, trusted escrow services solve this problem. In the world of decentralized cryptocurrency, however, it is possible to implement an escrow service without a third-party arbitrator. This standard defines an interface for smart contracts to act as an escrow service with a function where tokens are sent back to the original wallet if the escrow is not completed. ## Specification There are two types of contract for the escrow process: -- `Payable Contract`: The sellers and buyers use this token to fund the `Escrow Contract`. -- `Escrow Contract`: Defines the escrow policies and holds `Payable Contract`'s token for a certain period. - -This standard proposes interfaces on top of the [EIP-20](./eip-20.md) standard. +- *Payable Contract*: The sellers and buyers use this token to fund the *Escrow Contract*. This contract MUST override [EIP-20](./eip-20.md) interfaces. +- *Escrow Contract*: Defines the escrow policies and holds *Payable Contract*'s token for a certain period. This contract does not requires override [EIP-20](./eip-20.md) interfaces. ### Methods -#### constructor +#### `constructor` + +The *Escrow Contract* demonstrates details of escrow policies as none-mutable matter in constructor implementation. + +The *Escrow Contract* MUST define the following policies: -The `Escrow Contract` may define the following policies: +- Seller token contract address +- Buyer token contract address + +The *Escrow Contract* MAY define the following policies: -- MUST include seller token contract address -- MUST include buyer token contract address - Escrow period - Maximum (or minimum) number of investors - Maximum (or minimum) number of tokens to fund @@ -58,17 +58,16 @@ The `Escrow Contract` may define the following policies: Funds `_value` amount of tokens to address `_to`. -In the case of `Escrow Contract`: +In the case of *Escrow Contract*: - `_to` MUST be the user address. - - `msg.sender` MUST be the payable contract address. + - `msg.sender` MUST be the *Payable Contract* address. - MUST check policy validations. -In the case of `Payable Contract`: +In the case of *Payable Contract*: - - The address `_to` MUST be the escrow contract address. - - MUST call EIP-20's `_transfer` likely function. - - Before calling `_transfer` function, MUST call the same function of the escrow contract interface. The parameter `_to` MUST be `msg.sender` to recognize the user address in the escrow contract. + - The address `_to` MUST be the *Escrow Contract* address. + - MUST call the same function of the *Escrow Contract* interface. The parameter `_to` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*. ```solidity function escrowFund(address _to, uint256 _value) public returns (bool) @@ -78,17 +77,16 @@ function escrowFund(address _to, uint256 _value) public returns (bool) Refunds `_value` amount of tokens from address `_from`. -In the case of `Escrow Contract`: +In the case of *Escrow Contract*: - `_from` MUST be the user address. - - `msg.sender` MUST be the payable contract address. + - `msg.sender` MUST be the *Payable Contract* address. - MUST check policy validations. -In the case of `Payable Contract`: +In the case of *Payable Contract*: - - The address `_from` MUST be the escrow contract address. - - MUST call EIP-20's `_transfer` likely function. - - Before calling `_transfer` function, MUST call the same function of the escrow contract interface. The parameter `_from` MUST be `msg.sender` to recognize the user address in the escrow contract. + - The address `_from` MUST be the *Escrow Contract* address. + - MUST call the same function of the *Escrow Contract* interface. The parameter `_from` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*. ```solidity function escrowRefund(address _from, uint256 _value) public returns (bool) @@ -98,11 +96,12 @@ function escrowRefund(address _from, uint256 _value) public returns (bool) Withdraws funds from the escrow account. -In the case of `Escrow Contract`: +In the case of *Escrow Contract*: + - MUST check the escrow process is completed. - MUST send the remaining balance of seller and buyer tokens to `msg.sender`'s seller and buyer contract wallets. -In the case of `Payable Contract`, it is optional. +In the case of *Payable Contract*, it is optional. ```solidity function escrowWithdraw() public returns (bool) @@ -110,49 +109,157 @@ function escrowWithdraw() public returns (bool) ### Example of interface +This example demonstrates simple exchange of one seller and one buyer in one-to-one exchange rates. + ```solidity pragma solidity ^0.4.20; -interface IERC5528 is ERC20 { +interface IERC5528 { function escrowFund(address _to, uint256 _value) public returns (bool); - function escrowRefund(address to, uint256 amount) public returns (bool); + function escrowRefund(address _from, uint256 _value) public returns (bool); function escrowWithdraw() public returns (bool); } +contract PayableContract is IERC5528, IERC20 { + /* + General ERC20 implementations + */ + + function _transfer(address from, address to, uint256 amount) internal { + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + _balances[from] = fromBalance - amount; + _balances[to] += amount; + } + + function transfer(address to, uint256 amount) public returns (bool) { + address owner = msg.sender; + _transfer(owner, to, amount); + return true; + } + + function escrowFund(address _to, uint256 _value) public returns (bool){ + bool res = IERC5528(to).escrowFund(msg.sender, amount); + require(res, "Fund Failed"); + _transfer(msg.sender, to, amount); + return true; + } + + function escrowRefund(address _from, uint256 _value) public returns (bool){ + bool res = IERC5528(_from).escrowRefund(msg.sender, _value); + require(res, "Refund Failed"); + _transfer(_from, msg.sender, _value); + return true; + } +} + +contract EscrowContract is IERC5528 { + + enum State { Inited, Running, Success, Closed } + struct BalanceData { + address addr; + uint256 amount; + } + + address _addrSeller; + address _addrBuyer; + BalanceData _fundSeller; + BalanceData _fundBuyer; + EscrowStatus _status; + + constructor(address sellerContract, address buyerContract){ + _addrSeller = sellerContract; + _addrBuyer = buyerContract; + _status = State.Inited; + } + + function escrowFund(address _to, uint256 _value) public returns (bool){ + if(msg.sender == _addrSeller){ + require(_status.state == State.Running, "must be running state"); + _fundSeller.addr = _to; + _fundSeller.amount = _value; + _status = State.Success; + }else if(msg.sender == _addrBuyer){ + require(_status.state == State.Inited, "must be init state"); + _fundBuyer.addr = _to; + _fundBuyer.amount = _value; + _status = State.Running; + }else{ + require(false, "Invalid to address"); + } + return true; + } + + function escrowRefund(address _from, uint256 amount) public returns (bool){ + require(_status.state == State.Running, "refund is only available on running state"); + require(msg.sender == _addrBuyer, "invalid caller for refund"); + require(_fundBuyer.addr == _from, "only buyer can refund"); + require(_fundBuyer.amount >= amount, "buyer fund is not enough to refund"); + _fundBuyer.amount = _fundBuyer.amount - amount + return true; + } + + function escrowWithdraw() public returns (bool){ + require(_status.state == State.Success, "withdraw is only available on success state"); + uint256 common = MIN(_fundBuyer.amount, _fundSeller.amount); + + if(common > 0){ + _fundBuyer.amount = _fundBuyer.amount - common; + _fundSeller.amount = _fundSeller.amount - common; + + // Exchange + IERC5528(_addrSeller).transfer(_fundBuyer.addr, common); + IERC5528(_addrBuyer).transfer(_fundSeller.addr, common); + + // send back the remaining balances + if(_fundBuyer.amount > 0){ + IERC5528(_addrBuyer).transfer(_fundBuyer.addr, _fundBuyer.amount); + } + if(_fundSeller.amount > 0){ + IERC5528(_addrSeller).transfer(_fundSeller.addr, _fundSeller.amount); + } + } + + _status = State.Closed; + } + +} + ``` ## Rationale -The interfaces described in this EIP have been chosen to cover the refundable issue in the escrow operation. +The interfaces cover the escrow operation's refundable issue. The suggested 3 functions (`escrowFund`, `escrowRefund` and `escrowWithdraw`) are based on `transfer` function in EIP-20. -`escrowFund` send tokens to the escrow contract. The escrow contract can hold the contract in the escrow process or reject tokens if the policy does not meet. +`escrowFund` send tokens to the *Escrow Contract*. The *Escrow Contract* can hold the contract in the escrow process or reject tokens if the policy does not meet. -`escrowRefund` can be invoked in the middle of the escrow process or when the escrow process is failed. +`escrowRefund` can be invoked in the middle of the escrow process or when the escrow process fails. -`escrowWithdraw` allows users (sellers and buyers) to transfer tokens from the escrow account. When the escrow process is completed, the seller can get the buyer's token, and the buyers can get the seller's token. +`escrowWithdraw` allows users (sellers and buyers) to transfer tokens from the escrow account. When the escrow process completes, the seller can get the buyer's token, and the buyers can get the seller's token. ## Backwards Compatibility -This EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification. +The *Payable Contract* which implements this EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification. ## Test Cases [Unit test example by truffle](../assets/eip-5528/truffule-test.js). This test case demonstrates the following conditions for exchanging seller/buyer tokens. + - The exchange rate is one-to-one. - If the number of buyers reaches 2, the escrow process will be terminated(success). - Otherwise (not meeting success condition yet), buyers can refund (or withdraw) their funded tokens. ## Security Considerations -Since the escrow contract controls seller and buyer rights, flaws within the escrow contract will directly lead to unexpected behavior and potential loss of funds. +Since the *Escrow Contract* controls seller and buyer rights, flaws within the *Escrow Contract* will directly lead to unexpected behavior and potential loss of funds. ## Copyright diff --git a/EIPS/eip-5568.md b/EIPS/eip-5568.md new file mode 100644 index 00000000000000..dd5b21f852e697 --- /dev/null +++ b/EIPS/eip-5568.md @@ -0,0 +1,70 @@ +--- +eip: 5568 +title: Required Action Signals Using Revert Reasons +description: Signal to wallets that an action is needed by returning a custom revert code +author: Pandapip1 (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-5568-revert-signals/10622 +status: Draft +type: Standards Track +category: Interface +created: 2022-08-31 +requires: 140 +--- + +## Abstract + +This EIP introduces a minimalistic machine-readable (binary) format to signal to wallets that an action needs to be taken by the user using a well-known revert reason. This custom revert reason contains just enough data to be extendable by future EIPs and to take in arbitrary parameters (up to 64 kB of data). Example use cases could include approving a token for an exchange, sending an HTTP request, or requesting the user to rotate their keys after a certain period of time to enforce good hygiene. + +## Motivation + +Oftentimes, a smart contract needs to signal to a wallet that an action needs to be taken, such as to sign a transaction or send an HTTP request to a URL. Traditionally, this has been done by hard-coding the logic into the frontend, but this EIP allows the smart contract itself to request the action. + +This means that, for example, an exchange or a market can directly tell the wallet to approve the smart contract to spend the token, vastly simplifying the front-end code. + +## 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. + +### Custom Revert Reason + +To send a signal to a wallet, a compliant smart contract MUST revert with the following error: + +```solidity +error WalletSignal24(uint8 number_hint, uint24 instruction_id, bytes instruction_data) +``` + +The `number_hint` is an estimate of the number of signals that will be sent after the current signal. If a guess is availabe, `number_hint` MUST be that estimate. If a guess is unavailable, `number_hint` MUST be `0`. + +The `instruction_id` of an instruction defined by an EIP MUST be its EIP number unless there are exceptional circumstances (be reasonable). An EIP MUST define exactly zero or one `instruction_id`. The structure of the instruction data for any `instruction_id` MUST be defined by the EIP that defines the `instruction_id`. + +### Signal Response + +Before submitting a transaction to the mempool, it MUST be evaluated locally. If it reverts and the revert signature matches the custom error, then it MUST be treated as a signal. (It is RECOMMENDED for wallets to show a warning if the transaction reverts, even if the revert is not a signal). + +The `number_hint`, `instruction_id`, and `instruction_data` MUST be parsed from the revert data. It is RECOMMENDED for wallets to show a progress indicator using the `number_hint`. The instruction SHOULD be evaluated as per the relevant EIP. If the instruction is not supported by the wallet, it MUST display an error to the user indicating that is the case. The wallet MUST then re-evaluate the transaction, except if an instruction explicitly states that the transaction MUST NOT be re-evaluated. + +If an instruction is invalid, or the `number_hint`, `instruction_id`, and `instruction_data` cannot be parsed, then an error MUST be displayed to the user indicating that is the case. + +## Rationale + +This EIP was explicitly optimized for deployment gas cost and simplicity. It is expected that libraries will eventually be developed that makes sending and receiving signals more developer-friendly. + +## Backwards Compatibility + +### Human-Readable Revert Messages + +See [Revert Reason Collisions](#revert-reason-collisions). + +### [EIP-3668](./eip-3668.md) + +EIP-3668 can be used alongside this EIP, but it uses a different mechanism than this EIP. + +## Security Considerations + +### Revert Reason Collisions + +It is unlikely that the signature of the custom error matches any custom errors in the wild. In the case that it does, no harm is caused unless the data happen to be a valid instruction, which is even more unlikely. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5573.md b/EIPS/eip-5573.md new file mode 100644 index 00000000000000..cfbe42628a3783 --- /dev/null +++ b/EIPS/eip-5573.md @@ -0,0 +1,273 @@ +--- +eip: 5573 +title: Sign-In with Ethereum Capabilities, ReCaps +description: Mechanism on top of Sign-In with Ethereum for informed consent to delegate capabilities with an extensible scope mechanism +author: Oliver Terbu (@awoie), Jacob Ward (@cobward), Charles Lehner (@clehner), Sam Gbafa (@skgbafa), Wayne Chang (@wyc) +discussions-to: https://ethereum-magicians.org/t/eip-5573-siwe-recap +status: Draft +type: Standards Track +category: ERC +created: 2021-07-20 +requires: 4361 +--- + +## Abstract +[EIP-4361](./eip-4361.md), or Sign-In with Ethereum (SIWE), describes how Ethereum accounts authenticate with off-chain services. This proposal, known as ReCaps, describes a mechanism on top of SIWE to give informed consent to delegate capabilities with a certain extensible scope mechanism to an authorized delegee. How a delegee authenticates against the target resource is out of scope for this specification and depends on the implementation of the target resource. + +## Motivation + +SIWE ReCaps unlock integration of protocols and/or APIs for developers by reducing user friction, onchain state and increasing security by introducing informed consent and deterministic capability objects on top of Sign-In With Ethereum (EIP-4361). + +While SIWE focuses on authenticating the Ethereum account against the service (relying party or SIWE client) initiating the SIWE flow, there is no canonical way to interact with a third-party service (resource service) on behalf of the authenticated Ethereum account. For example, a relying party might want to interact with another service on behalf of the Ethereum account, for example a service that provides data storage for the Ethereum account. This specification introduces a mechanism, that allows the service (or more generally a delegee) to combine authentication and authorization of such while preserving security and optimizing UX. + +Note, this approach is a similar mechanism to combining OpenID Connect (SIWE auth) and OAuth2 (SIWE ReCap) whereas SIWE ReCap follows an Object Capability-based approach. + +## Specification + +This specification has three different audiences: +- Web3 application developers that want to integrate ReCaps to authenticate with any protocols and APIs that support object capabilities. +- Protocol or API developers that want to learn how to define their own ReCaps. +- Wallet implementers that want to improve the UI for ReCaps. + +### Terms and Definitions + +- ReCap - A SIWE Message complying with this specification, i.e., containing at least one ReCap URI in the `Resources` section and the corresponding human-readable ReCap Statement appended to the SIWE `statement`. +- ReCap URI - A type of URI under a certain namespace that resolves to a ReCap Details Object. +- ReCap Details Object - A JSON object describing the actions and optionally the resources associated with a ReCap Capability under a certain namespace. +- Resource Service (RS) - The entity that is providing third-party services for the Ethereum account. +- SIWE Client (SC) - The entity initiating the SIWE authentication and ReCap flow. +- Relying Party (RP) - same as SC in the context of authentication. + +### Overview + +This specification defines the following: +- ReCap SIWE Extension +- ReCap Capability + - ReCap URI Scheme + - ReCap Details Object Schema +- ReCap Translation Algorithm +- ReCap Verification + +### ReCap SIWE Extension + +A ReCap is an EIP-4361 message following a specific format that allows an Ethereum account to delegate a set of ReCap Capabilities to a delegee through informed consent. Each ReCap Capability MUST be represented by an entry in the `Resources` array of the SIWE message that MUST deterministically translate the ReCap Capability in human-readable form to the `statement` field in the SIWE message using the ReCap Translation Algorithm. + +The following SIWE message fields are used to further define (or limit) the scope of all ReCap Capabilities: +- The `URI` field MUST specify the intended delegee, e.g., `https://example.com`, `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK`. It is expected that the RS authenticates the delegee before invoking an action for the ReCap Capability. +- The `Issued At` field MUST be used to specify the issuance date of the ReCap Capabilities. +- If present, the `Expiration Time` field MUST be used as the expiration time of the ReCap Capabilities, i.e. the time at which the RS will no longer accept an invocation of the capabilities expressed in this form. +- If present, the `Not Before` field MUST be used as the time that has to expire before the RS starts accepting invocations of the capabilities expressed in the message. + +The following is a non-normative example of a SIWE message with the SIWE ReCap Extension: +```text +example.com wants you to sign in with your Ethereum account: +0x0000000000000000000000000000000000000000 + +I further authorize https://example.com to perform the following actions on my behalf: (1) example: read for any. (2) example: append, delete for my.resource.1. (3) example: append for my.resource.2, my.resource.3. + +URI: https://example.com +Version: 1 +Chain ID: 1 +Nonce: n-0S6_WzA2Mj +Issued At: 2022-06-21T12:00:00.000Z +Resources: +- urn:recap:example:eyJkZWYiOlsicmVhZCJdLCJ0YXIiOnsibXkucmVzb3VyY2UuMSI6WyJhcHBlbmQiLCJkZWxldGUiXSwibXkucmVzb3VyY2UuMiI6WyJhcHBlbmQiXSwibXkucmVzb3VyY2UuMyI6WyJhcHBlbmQiXX19 +``` + +#### ReCap Capability + +A ReCap Capability is identified by their ReCap URI that resolves to a ReCap Details Object which defines the associated actions and optional target resources. The scope of each ReCap Capability is attenuated by common fields in the SWIE message as described in the previous chapter, e.g., `URI`, `Issued At`, `Expiration Time`, `Not Before`. + +##### ReCap URI Scheme + +A ReCap URI starts with `urn:recap:` followed by the namespace discriminator, followed by `:` and the base64url-encoded payload of the ReCap Details Object. Note, the term base64url is defined in RFC4648 - Base 64 Encoding with URL and Filename Safe Alphabet. + +The following is a non-normative example of a ReCap Capability that uses the `example` namespace: +```text +urn:recap:example:eyJkZWZhdWx0QWN0aW9ucyI6WyJyZWFkIl0sInRhcmdldGVkQWN0aW9ucyI6eyJteS5yZXNvdXJjZS4xIjpbImFwcGVuZCIsImRlbGV0ZSJdLCJteS5yZXNvdXJjZS4yIjpbImFwcGVuZCJdLCJteS5yZXNvdXJjZS4zIjpbImFwcGVuZCJdfX0 +``` + +It is expected that RS implementers define their own namespace, e.g., `urn:recap:service:`. + +##### ReCap Details Object Schema + +The ReCap Details Object denotes which actions on which resources the delegee is authorized to invoke on behalf of the delegee for the validity period defined in the SIWE message. It can also contain additional information that the RS may require to verify a capability invocation. A ReCap Details Object MUST follow the following JSON Schema: + +```jsonc +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "def": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 + }, + "tar": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 + } + }, + "additionalProperties": false, + "minProperties": 1 + }, + "ext": { + "type": "object", + "minProperties": 1 + } + }, + "minProperties": 1, + "additionalProperties": false, + "dependentSchemas": { + "ext": { + "minProperties": 2 + } + } +} +``` + +A ReCap Details Object defines the following properties: +- `def`: (CONDITIONAL) If present, `def` MUST be a JSON array of string values with at least one entry where each value describes an action the delegee MAY invoke in the RS on behalf of the Ethereum account without tying the scope to a particular target. +- `tar`: (CONDITIONAL) If present, `tar` MUST be a JSON object with variable properties where each property is a JSON array of string values each describing an action the delegee MAY invoke in the RS on behalf of the Ethereum account on the target resource denoted by the property name. +- `ext`: (OPTIONAL) If present, `ext` MUST be a JSON object with variable properties. + +The following is a non-normative example of a ReCap Capability Object with `def`, `tar` and `ext`: +```jsonc +{ + "def":[ + "read" + ], + "tar":{ + "my.resource.1":[ + "append", + "delete" + ], + "my.resource.2":[ + "append" + ], + "my.resource.3":[ + "append" + ] + }, + "ext":{ + "parentCapability": "bafybeigk7ly3pog6uupxku3b6bubirr434ib6tfaymvox6gotaaaaaaaaa" + } +} +``` + +In the example above, the delegee is authorized to perform the action `read` independent of any resource, `append`, `delete` on resource `my.resource.1`, `append` on resource `my.resource.2` and `append` on `my.resource.3`. Note, the delegee can invoke each action invididually and independent from each other in the RS. Additionally the ReCap Capability Object contains some additional information that the RS will need during verification. The responsibility for defining the structure and semantics of this data lies with the RS. + +It is expected that RS implementers define which resources they want to expose through ReCap Details Objects and which actions they want to allow to invoke on them. + +#### ReCap Translation Algorithm + +After applying the ReCap Translation Algorithm on a given SIWE message that MAY include a pre-defined `statement`, the `recap-transformed-statement` in a ReCap SIWE message MUST conform to the following ABNF: +```text +recap-transformed-statement = statement recap-preamble 1*(" " recap-statement-entry ".") + ; see EIP-4361 for definition of input-statement +recap-preamble = "I further authorize " uri " to perform the following actions on my behalf:" + ; see EIP-4361 for definition of uri +recap-statement-entry = "(" number ") " recap-namespace ": " + recap-action *("," recap-action) "for" + ( "any" / ( recap-resource *(", " recap-resource) ) ) + ; see RFC8259 for definition of number +recap-namespace = string + ; see RFC8259 for definition of string +recap-action = string + ; see RFC8259 for definition of string +recap-resource = string + ; see RFC8259 for definition of string +``` + +The following algorithm or an algorithm that produces the same output MUST be performed to generate the SIWE ReCap Transformed Statement. + +Inputs: +- Let `uri` be the uri field of the input SIWE message conforming to EIP-4361. +- Let `recap-uris` be a non-empty array of ReCap URIs, which represent the ReCap Capabilities that are to be encoded in the SIWE message, and which contain ReCap Details Objects which conform to the ReCap Details Object Schema. +- [Optional] Let `statement` be the statement field of the input SIWE message conforming to EIP-4361. +Algorithm: +- Let `recap-transformed-statement` be an empty string value. +- If `statement` is present, do the following: + - Append the value of the `statement` field of `siwe` to `recap-transformed-statement`. + - Append a single space character `" "` to `recap-transformed-statement`. +- Append the following string to `recap-transformed-statement`: "I further authorize ". +- Append `uri` to `recap-transformed-statement`. +- Append the following string to `recap-transformed-statement`: " to perform the following actions on my behalf:". +- Let `numbering` be an integer starting with 1. +- For each entry in `recap-uris` (starting with the first entry), perform the following: + - Let `namespace` be the `namespace` in the ReCap URI entry and let `capDetails` be the base64url-decoded ReCap Details Object of the ReCap URI entry. + - Let `defaultActions` be the `def` JSON array in `capDetails`, where each value represents an action. + - If `defaultActions` is present, do the following: + - Let `actions` be the string concatenation of each action in the array with the delimiter `", "`. + - Append the string concatenation of `" ("`, `numbering`, `")"` to `recap-transformed-statement`. + - Append `namespace` concatenated with `": "` to `recap-transformed-statement`. + - Append `actions` to `recap-transformed-statement`. + - Append the string `" for any."` to `recap-transformed-statement`. + - Increase `numbering` by 1. + - Let `targetedActions` be the `tar` JSON object in `capDetails`, where each key-value pair represents the set of actions allowed for a target. + - If `targetedActions` is present, do the following: + - Let `actionSets` be an array of arrays of strings; + - For each key-value pair in `targetedActions`, ordered alphabetically by key, append the string array value to `actionSets`. + - For each array of strings `actionSet` in `actionSets`, do the following: + - Sort the strings in `actionSet` alphabetically. + - Let `actions` be the string concatenation of each action in the array with the delimiter `", "`. + - Let `targets` be the string concatenation of each key in `targetedActions` with the delimiter `", "`, for those keys such that the associated value (or any permutation of that value) is identical to `actionSet`. + - Append the string concatenation of `" ("`, `numbering`, `")"` to `recap-transformed-statement`. + - Append `namespace` concatenated with `": "` to `recap-transformed-statement`. + - Append `actions` to `recap-transformed-statement`. + - Append the string `" for "` to `recap-transformed-statement`. + - Append `targets` to `recap-transformed-statement`. + - Append the string `" ."` to `recap-transformed-statement`. + - Increase `numbering` by 1. +- Return `recap-transformed-statement`. + +#### ReCap Verification Algorithm + +The following algorithm or an algorithm that produces the same output MUST be performed to verify a SIWE ReCap. + +Inputs: +- Let `recap-siwe` be the input SIWE message conforming to EIP-4361 and this EIP. +- Let `siwe-signature` be the output of signing `recap-siwe`, as defined in EIP-4361. +Algorithm: +- Perform EIP-4361 signature verification with `recap-siwe` and `siwe-signature` as inputs. +- Let `uri` be the uri field of `recap-siwe`. +- Let `recap-uris` be an array of recap URIs taken in order from the resources field of `recap-siwe`, such that URIs which are not valid ReCap URIs are ignored. +- Let `recap-transformed-statement` be the result of performing the above `ReCap Translation Algorithm` with `uri` and `recap-uris` as input. +- Assert that the statement field of `recap-siwe` ends with `recap-transformed-statement`. + +### Implementer's Guide + +TBD + +#### Web3 Application Implementers + +TBD + +#### Wallet Implementers + +TBD + +#### Protocol or API Implementers + +TBD + +## Rationale + +TBD + +## Security Considerations + +Resource service implementer's should not consider ReCaps as bearer tokens but instead require to authenticate the delegee in addition. The process of authenticating the delegee against the resource service is out of scope of this specification and can be done in various different ways. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-5585.md b/EIPS/eip-5585.md new file mode 100644 index 00000000000000..3dd1e3cde058a6 --- /dev/null +++ b/EIPS/eip-5585.md @@ -0,0 +1,166 @@ +--- +eip: 5585 +title: EIP-721 NFT Authorization +description: Allows NFT owners to authorize other users to use their NFTs. +author: Veega Labs (@VeegaLabsOfficial), Sean NG (@ngveega), Tiger (@tiger0x), Fred (@apan), Fov Cao (@fovcao) +discussions-to: https://ethereum-magicians.org/t/nft-authorization-erc721-extension/10661 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-15 +requires: 721 +--- + +## Abstract + +This EIP separates an [EIP-721](./eip-721.md) NFT's ownership from its commercial usage rights to allow for the independent management of those rights. + +## Motivation + +Most NFTs have a simplified ownership verification mechanism, with a sole owner of an NFT. Under this model, other rights, such as display, or creating derivative works or distribution, are not possible to grant, limiting the value and commercialization of NFTs. Therefore, the separation of an NFT's ownership and user rights can enhance its commercial value. + +Commercial right is a broad concept based on the copyright, including the rights of copy, display, distribution, renting, commercial use, modify, reproduce and sublicense etc. With the development of the Metaverse, NFTs are becoming more diverse, with new use cases such as digital collections, virtual real estate, music, art, social media, and digital asset of all kinds. The copyright and authorization based on NFTs are becoming a potential business form. + +## 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. + +### Contract Interface + +```solidity +interface IERC5585 { + + struct UserRecord { + address user; + string[] rights; + uint expires + } + + /// @notice NFT holder authorizes all the rights to a user for a specified period of time + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT which is authorized + /// @param user The user to whom the NFT is authorized + /// @param duration The period of time the authorization lasts + function authorizeUser(uint256 tokenId, address user, uint duration) external; + + /// @notice NFT holder authorizes specific rights to a user for a specified period of time + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT which is authorized + /// @param user The user to whom the NFT is authorized + /// @param rights Rights autorised to the user, such as renting, distribution or display etc + /// @param duration The period of time the authorization lasts + function authorizeUser(uint256 tokenId, address user, string[] rights, uint duration) external; + + /// @notice NFT holder extends the duration of authorization + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT which has been authorized + /// @param user The user to whom the NFT has been authorized + /// @param duration The new duration of the authorization + function extendDuration(uint256 tokenId, address user, uint duration) external; + + /// @notice NFT holder updates the rights of authorization + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT which has been authorized + /// @param user The user to whom the NFT has been authorized + /// @param rights New rights autorised to the user + function updateUserRights(uint256 tokenId, address user, string[] rights) external; + + /// @notice Get the authorization expired time of the specified NFT and user + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT to get the user expires for + /// @param user The user who has been authorized + /// @return The authorization expired time + function getExpires(uint256 tokenId, address user) external view returns(uint); + + /// @notice Get the rights of the specified NFT and user + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT to get the rights + /// @param user The user who has been authorized + /// @return The rights has been authorized + function getUserRights(uint256 tokenId, address user) external view returns(string[]); + + /// @notice The contract owner can update the number of users that can be authorized per NFT + /// @param userLimit The number of users set by operators only + function updateUserLimit(unit256 userLimit) external onlyOwner; + + /// @notice resetAllowed flag can be updated by contract owner to control whether the authorization can be revoked or not + /// @param resetAllowed It is the boolean flag + function updateResetAllowed(bool resetAllowed) external onlyOwner; + + /// @notice Check if the token is available for authorization + /// @dev Throws if tokenId is not a valid NFT + /// @param tokenId The NFT to be checked the availability + /// @return true or false whether the NFT is available for authorization or not + function checkAuthorizationAvailability(uint256 tokenId) public view returns(bool); + + /// @notice Clear authorization of a specified user + /// @dev The zero address indicates there is no user. The function works when resetAllowed is true and it will throw exception when false + /// @param tokenId The NFT on which the authorization based + /// @param user The user whose authorization will be cleared + function resetUser(uint256 tokenId, address user) external; + + /// @notice This is an OPTIONAL function that the operator MAY call, he can set the starting time of staking as a reward of the authorization for each user + /// @dev The zero address indicates there is no user + /// @param user To which user the staking time will be set + /// @param stakingTime The starting time of the staking for each user + function updateStakingTime(address[] user, uint[] stakingTime) external; + + + /// @notice Emitted when the user of a NFT is changed or the authorization expires time is updated + /// param tokenId The NFT on which the authorization based + /// param indexed user The user to whom the NFT authorized + /// @param rights Rights autorised to the user + /// param expires The expires time of the authorization + event authorizeUser(uint256 indexed tokenId, address indexed user, string[] rights, uint expires); +} +``` + +The `authorizeUser(uint256 tokenId, address user, uint duration)` function MAY be implemented as `public` or `external`. + +The `authorizeUser(uint256 tokenId, address user, string[] rights; uint duration)` function MAY be implemented as `public` or `external`. + +The `extendDuration(uint256 tokenId, address user, uint duration)` function MAY be implemented as `public` or `external`. + +The `updateUserRights(uint256 tokenId, address user, string[] rights)` function MAY be implemented as `public` or `external`. + +The `getExpires(uint256 tokenId, address user)` function MAY be implemented as `pure` or `view`. + +The `getUserRights(uint256 tokenId, address user)` function MAY be implemented as pure and view. + +The `updateUserLimit(unit256 userLimit)` function MAY be implemented as`public` or `external`. + +The `updateResetAllowed(bool resetAllowed)` function MAY be implemented as `public` or `external`. + +The `checkAuthorizationAvailability(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `resetUser(uint256 tokenId, address user)` function MAY be implemented as `public` or `external`. + +The `updateStakingTime(address[] user, uint[] stakingTime)` function MAY be implemented as `public` or `external`. + +The `authorizeUser` event MUST be emittedwhen the user of a NFT is changed or the authorization expires time is updated. + +## Rationale + +First of all, NFT contract owner can set the maximum number of authorized users to each NFT and whether the NFT owner can cancel the authorization at any time to protect the interests of the parties involved. + +Secondly, this EIP combines the functions of staking and authorization, which means the NFT contract owner can update the number of authorized users to NFT owners depending on the period of staking. The function is optional, but it is a way to protect all parties from overhype and to ensure that the price of the NFT is more accurately to match its value. + +Thirdly, there is a resetAllowed flag to control the rights between the NFT owner and the users for the contract owner. If the flag is set to true, then the NFT owner can disable usage rights of all authorized users at any time. + +Fourthly, the rights within the user record struct is used to store what rights has been authorized to a user by the NFT owner, in other words, the NFT owner can authorize a user with specific rights and update it when necessary. + +Finally, this design can be seamlessly integrated with third parties. It is an extension of EIP-721, therefore it can be easily integrated into a new NFT project. Other projects can directly interact with these interfaces and functions to implement their own types of transactions. For example, an announcement platform could use this EIP to allow all NFT owners to make authorization or deauthorization at any time. + +## Backwards Compatibility + +This standard is compatible with [EIP-721](./eip-721.md) since it is an extension of it. + +## Security Considerations + +If someone buys an NFT within the duration of an authorization, they will not have to stake anything, providing no incentive to cancel the authorization. + +To solve this problem, the authorization fee paid by the users will be held in an escrow contract for a period of time depending on the duration of the authorization. For example, if the authorization duration is 12 months and the fee in total is 10 ETH, then if the NFT is transferred after 3 months, then only 2.5 ETH would be sent and the remaining 7.5 ETH would be refunded. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5593.md b/EIPS/eip-5593.md new file mode 100644 index 00000000000000..3f68a16f430d04 --- /dev/null +++ b/EIPS/eip-5593.md @@ -0,0 +1,87 @@ +--- +eip: 5593 +title: Restrict Ethereum Provider API Injection +description: Wallet guidance for restricting Ethereum Provider API access to secure contexts for improved privacy and security for wallet users. +author: Yan Zhu (@diracdeltas), Brian R. Bondy (@bbondy), Andrea Brancaleoni (@thypon), Kyle Den Hartog (@kdenhartog) +discussions-to: https://ethereum-magicians.org/t/rfc-limiting-provider-object-injection-to-secure-contexts/10670 +status: Draft +type: Standards Track +category: Interface +created: 2022-09-05 +requires: 1193 +--- + +## Abstract + +Historically the web platform has had a notion of “powerful” APIs like those defined in W3C's Geolocation specification and W3C's Mediastreams specification, which are subject to additional security restrictions such as those defined by W3C's secure contexts specification. Since the Ethereum Provider APIs allow dApp websites to request access to sensitive user data and to request use of user funds, new Ethereum Provider APIs generally should align to the security considerations defined by W3C's Secure Context specification in order to better protect the users data and users funds managed via the web. + +### Author's Note + +Unfortunately, because of a difference in interpretations by EIP editors of RFC 2119 terminology around linking in [EIP-1](./eip-1.md), the authors cannot directly link to other W3C specifications which this EIP builds upon. The author's attempted to provide as much context as possible within the text while complying with the editor bot in order to get this merged. If this policy is updated or further clarified before this EIP reaches final call in the future this EIP will be updated with links. + +## Motivation + +Wallets are oftentimes maintaining security and safety of users' funds that can be equivalent to large portions of money. For this reason, it's a good idea to restrict access to the Ethereum Provider APIs to align it with other powerful APIs on the web platform. This will assist in reducing the surface area that attacks can be conducted to access users funds or data. Additionally, by adding in restrictions we're reducing the surface area that malicious web pages could fingerprint the user via the Ethereum Provider APIs providing some additional privacy benefits. An example of a specific attack that's avoided by this is one where a malicious advertisement is loaded on a legitimate dApp that attempts to interact with a users wallet to maliciously request the user to access funds. With this EIP implemented the advertisement frame would be considered a third-party iframe and therefore would not have the Ethereum Provider API injected into it's sub frame because it's not a secure context. + +## 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. + +### Restrictions for providers + +The provider objects, e.g. `window.ethereum`, are expected to only inject the Ethereum Provider APIs in secure context when conforming with this specification. The following restrictions are REQUIRED for conformant wallets: + +- Provider objects MAY be accessible in private (incognito) windows. +- The origin MUST be a "potentially trustworthy origin" (defined in W3C's Secure Contexts specification in section 3.1) to have access to `window.ethereum`. This can be checked using `window.isSecureContext`, including inside iframes. + - Secure contexts include sites that are served from HTTPS but also HTTP `localhost`. + - The User Agent implementation MAY also support configured [potentially trustworthy origins] that would normally not be considered trustworthy if the user configures their User Agent to do so. See section 7.2 titled "Development Environments" of W3C's Secure Contexts specification for additional details. For example, in Chromium based User Agents this is done via the `chrome://flags/#unsafely-treat-insecure-origin-as-secure` flag. +- By default the Ethereum Provider APIs MUST NOT be exposed to third-party iframes. +- `window.ethereum` MUST be `undefined` in an iframe where `window.isSecureContext` returns `false` in that iframe. +- If the iframe is a third party to the top-level secure origin, it SHOULD be blocked. +- If the iframe is first-party to the top-level origin AND the `sandbox` attribute is set on the iframe, the provider object MUST be blocked. If the `sandbox` attribute is set to `sandbox="allow-same-origin"` it MUST be injected for a first party frame. + - Note `"allow-same-origin"` does nothing if the iframe is third-party. The case of the third party iframe is dictated by the usage of the `allow` attribute and the Permissions API as defined in the rule above. + +## Rationale + +By limiting the capabilities of where the Ethereum Provider APIs are being injected we can reduce the surface area of where attacks can be executed. Given the sensitivity of data that's passed to the Ethereum Provider APIs some basic levels of authentication and confidentiality should be met in order to ensure that request data is not being intercepted or tampered with. While there have been attempts to [limit request access via the wallet](./eip-2255.md) interface itself, there have not been limitations that have been set to where these Ethereum Provider APIs are expected to be or not be injected. Since the secure contexts web platform API is a well developed boundary that's been recommended by W3C and the fact that the Ethereum Provider APIs are extending the traditional web platform APIs, no other alternative solutions have been considered in order to extend current established prior art. + + +## Backwards Compatibility + +Wallet extensions SHOULD consider adding a "developer mode" toggle via a UX so that dApp developers have the capability to disable the insecure context (http) check for the `http://localhost:` origin only in the event that localhost does not return `true` for secure context. See section 5.2 of W3C's Secure Context specification for more details. This will allow dApp developers to be able to continue to host dApps on the localhost origin if a User Agent has chosen to not already consider localhost a secure context. All major User Agent implementations tested do consider localhost a secure context already. This toggle MUST be set to disabled by default. + +## Test Cases + +### Required Test Cases + +- Top level `http://a.com` -> blocked (insecure/top level) +- Top level `https://a.com` -> allowed +- Top level `https://a.com` with `