diff --git a/.gitattributes b/.gitattributes index 98eb0e8ece596f..a9a9d1eb55a62f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,7 @@ # GitHub highlighting for Solidity files # See https://github.com/github/linguist/pull/3973#issuecomment-357507741 *.sol linguist-language=Solidity + +# Force Linux line endings on all files +# Necessary for running eipw locally +* text=auto eol=lf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000000..21f1400916aee7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @eth-bot diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000000000..a34cc168a6aa00 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +Please review [EIP-1](https://eips.ethereum.org/EIPS/eip-1) for EIP guidelines. + + diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 92% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md index 61f357858bf11c..bfa769c05b4b6c 100644 --- a/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,12 +1,11 @@ - ATTENTION! If you would like to submit an EIP and it has already been written as a draft (see the [template](https://github.com/ethereum/EIPs/blob/master/eip-template.md) for an example), please submit it as a [Pull Request](https://github.com/ethereum/EIPs/pulls). If you are considering a proposal but would like to get some feedback on the idea before submitting a draft, then continue opening an Issue as a thread for discussion. Note that the more clearly and completely you state your idea the higher the quality of the feedback you are likely to receive. -Keep in mind the following guidelines from [EIP-1](./eip-1.md): +Keep in mind the following guidelines from [EIP-1](https://eips.ethereum.org/EIPS/eip-1): > Each EIP must have a champion - someone who writes the EIP using the style and format described below, shepherds the discussions in the appropriate forums, and attempts to build community consensus around the idea. The EIP champion (a.k.a. Author) should first attempt to ascertain whether the idea is EIP-able. Posting to the the Protocol Discussion forum or opening an Issue is the best way to go about this. > Vetting an idea publicly before going as far as writing a EIP is meant to save the potential author time. Asking the Ethereum community first if an idea is original helps prevent too much time being spent on something that is guaranteed to be rejected based on prior discussions (searching the Internet does not always do the trick). It also helps to make sure the idea is applicable to the entire community and not just the author. Just because an idea sounds good to the author does not mean it will work for most people in most areas where Ethereum is used. -> Once the champion has asked the Ethereum community as to whether an idea has any chance of acceptance, a draft EIP should be presented as a Pull Request. This gives the author a chance to flesh out the draft EIP to make properly formatted, of high quality, and to address initial concerns about the proposal. +> Once the champion has asked the Ethereum community as to whether an idea has any chance of acceptance, a draft EIP should be presented as a Pull Request. This gives the author a chance to flesh out the draft EIP to make properly formatted, of high quality, and to address initial concerns about the proposal. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000000000..59893b3a4fb322 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,29 @@ +name: Bug Report +description: File a bug report +labels: ['bug'] + +body: + - type: markdown + attributes: + value: Thanks for taking the time to fill out this bug report! + - type: input + id: pull-request + attributes: + label: Pull Request + description: Link to the pull request where the issue occurred + validations: + required: false + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000000..1b97141a73a3cf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Do you want to discuss EIP ideas or ask questions about the process? + url: https://ethereum-magicians.org/ + about: Use the Fellowship of Ethereum Magicians for EIP-related discussion! diff --git a/.github/ISSUE_TEMPLATE/propose-process-change.yml b/.github/ISSUE_TEMPLATE/propose-process-change.yml new file mode 100644 index 00000000000000..4d97a6ba8ab2c9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/propose-process-change.yml @@ -0,0 +1,13 @@ +name: Do you want to propose a change to the EIP process itself? +description: Use this if you want to propose changes to the EIP process +labels: ['enhancement'] + +body: + - type: textarea + id: proposed-change + attributes: + label: Proposed Change + description: What do you think should be different? + placeholder: Describe your proposed change here + validations: + required: true diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 00000000000000..64c927153bcf76 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base", + ":disableDependencyDashboard" + ], + "ignorePaths": [ + "**/assets/**" + ], + "ignoreDeps": [ + "Pandapip1/jekyll-label-action", + "ethereum/eipw-action", + "ethereum/eip-review-bot", + "ethereum/EIP-Bot" + ] +} diff --git a/.github/workflows/auto-merge-bot.yml b/.github/workflows/auto-merge-bot.yml deleted file mode 100644 index e82dfc491e7a3a..00000000000000 --- a/.github/workflows/auto-merge-bot.yml +++ /dev/null @@ -1,34 +0,0 @@ -on: [pull_request_target] -name: Auto-Merge Bot -jobs: - await_ci: - runs-on: ubuntu-latest - name: Await Travis CI Success - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Node.js Environment - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: await-ci - uses: ethereum/EIP-Bot@bd41acc24e173acbfbc831ca6e448f38f3e7bab6 # await-ci-pass - id: await-ci - with: - GITHUB-TOKEN: ${{ secrets.TOKEN }} - auto_merge_bot: - runs-on: ubuntu-latest - needs: [await_ci] - name: EIP Auto-Merge Bot - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Node.js Environment - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: auto-merge-bot - uses: ethereum/EIP-Bot@5ce39794a68effe78765601429912321847f277f # master - id: auto-merge-bot - with: - GITHUB-TOKEN: ${{ secrets.TOKEN }} diff --git a/.github/workflows/auto-review-bot.yml b/.github/workflows/auto-review-bot.yml new file mode 100644 index 00000000000000..ff14c259c200fc --- /dev/null +++ b/.github/workflows/auto-review-bot.yml @@ -0,0 +1,32 @@ +on: + workflow_run: + workflows: + - Auto Review Bot Trigger + types: + - completed + +name: Auto Review Bot +jobs: + auto-review-bot: + runs-on: ubuntu-latest + name: Run + steps: + - name: Fetch PR Number + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 + with: + name: pr-number + workflow: auto-review-trigger.yml + run_id: ${{ github.event.workflow_run.id }} + + - name: Save PR Number + id: save-pr-number + run: echo "pr=$(cat pr-number.txt)" >> $GITHUB_OUTPUT + + - name: Auto Review Bot + id: auto-review-bot + uses: ethereum/eip-review-bot@3e9905fcb72cf81ae9ed732df429c28b17e155b1 + continue-on-error: true + with: + token: ${{ secrets.TOKEN }} + config: config/eip-editors.yml + pr_number: ${{ steps.save-pr-number.outputs.pr }} diff --git a/.github/workflows/auto-review-trigger.yml b/.github/workflows/auto-review-trigger.yml new file mode 100644 index 00000000000000..45b815e390daeb --- /dev/null +++ b/.github/workflows/auto-review-trigger.yml @@ -0,0 +1,59 @@ +on: + pull_request_target: + pull_request_review: + workflow_dispatch: + inputs: + pr_number: + description: Pull Request Number + type: string + required: true + issue_comment: + types: + - created + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +name: Auto Review Bot Trigger +jobs: + trigger: + runs-on: ubuntu-latest + name: Trigger + steps: + - name: Write PR Number - PR Target + run: echo $PR_NUMBER > pr-number.txt + if: github.event_name == 'pull_request_target' && ((!endsWith(github.event.sender.login, '-bot') && !endsWith(github.event.sender.login, '[bot]')) || github.event.sender.login == 'renovate[bot]') + env: + PR_NUMBER: ${{ github.event.number }} + + - name: Write PR Number - PR Review + run: echo $PR_NUMBER > pr-number.txt + if: github.event_name == 'pull_request_review' && !endsWith(github.event.sender.login, '-bot') && !endsWith(github.event.sender.login, '[bot]') + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + + - name: Write PR Number - Workflow Dispatch + run: echo $PR_NUMBER > pr-number.txt + if: github.event_name == 'workflow_dispatch' + env: + PR_NUMBER: ${{ inputs.pr_number }} + + - name: Write PR Number - Comment Retrigger + run: echo $PR_NUMBER > pr-number.txt + if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@eth-bot rerun') + env: + PR_NUMBER: ${{ github.event.issue.number }} + + - name: Check File Existence + uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b + id: check_pr_number_exists + with: + files: pr-number.txt + + - name: Save PR Number + uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05 + if: steps.check_pr_number_exists.outputs.files_exists == 'true' + with: + name: pr-number + path: pr-number.txt diff --git a/.github/workflows/auto-stagnate-bot.yml b/.github/workflows/auto-stagnate-bot.yml new file mode 100644 index 00000000000000..25aca3bf9c9c97 --- /dev/null +++ b/.github/workflows/auto-stagnate-bot.yml @@ -0,0 +1,24 @@ +on: + schedule: + # A job that runs every sunday at 00:00 + - cron: '0 0 * * 0' + workflow_dispatch: + +name: Auto Stagnant Bot +jobs: + auto_merge_bot: + if: github.repository == 'ethereum/eips' + runs-on: ubuntu-latest + name: Auto Stagnant Bot + steps: + - name: Checkout + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f + - name: Setup Node.js Environment + uses: actions/setup-node@d98fa1113850e562f83c7fc3a89c0ecd7a87fbed + with: + node-version: '14' + - name: auto-stagnant-bot + uses: ethereum/EIP-Bot@b3ac0ba3600aea27157fc68d1e36c08cc5a6db77 # mark-eips-stale + id: auto-stagnant-bot + with: + GITHUB-TOKEN: ${{ secrets.TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000000..423bc26d7268fd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,144 @@ +name: Continuous Integration + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - 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 + runs-on: ubuntu-latest + + steps: + - name: Save PR number + env: + PR_NUMBER: ${{ github.event.number }} + PR_SHA: ${{ github.event.pull_request.head.sha }} + MERGE_SHA: ${{ github.sha }} + run: | + mkdir -p ./pr + echo $PR_NUMBER > ./pr/pr_number + echo $PR_SHA > ./pr/pr_sha + echo $MERGE_SHA > ./pr/merge_sha + + - name: Upload PR Number + uses: actions/upload-artifact@65d862660abb392b8c4a3d1195a2108db131dd05 + with: + name: pr_number + path: pr/ + + htmlproofer: + name: HTMLProofer + runs-on: ubuntu-20.04 + + steps: + - name: Checkout EIP Repository + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f + + - name: Install Ruby + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf + with: + ruby-version: 3.1.4 # 3.2 fails, see https://github.com/github/pages-gem/issues/879 + bundler-cache: true + + - name: Build Website + run: | + bundle exec jekyll doctor + bundle exec jekyll build + + - name: HTML Proofer + run: bundle exec htmlproofer --allow-missing-href --disable-external --assume-extension '.html' --log-level=:info --cache='{"timeframe":{"external":"6w"}}' --checks 'Links,Images,Scripts,OpenGraph' --no-check-sri --ignore-empty-alt --no-enforce_https ./_site + - name: DNS Validator + run: bundle exec github-pages health-check + + link-check: + name: Link Check + runs-on: ubuntu-latest + + steps: + - name: Checkout EIP Repository + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f + + - name: Link Checker + uses: gaurav-nelson/github-action-markdown-link-check@d53a906aa6b22b8979d33bc86170567e619495ec + with: + config-file: config/mlc_config.json + use-quiet-mode: no + use-verbose-mode: yes + check-modified-files-only: yes + + codespell: + name: CodeSpell + runs-on: ubuntu-latest + + steps: + - name: Checkout EIP Repository + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f + + - 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@57beb9f38f49d773d641ac555d1565c3b6a59938 + 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 + + eipw-validator: + name: EIP Walidator + runs-on: ubuntu-latest + + steps: + - name: Checkout EIP Repository + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f + + - uses: ethereum/eipw-action@b8de7ea9ad5cb842301e63898afb996c451c18cf + id: eipw + with: + token: ${{ secrets.GITHUB_TOKEN }} + unchecked: 1, 5069, 5757 + + markdownlint: + name: Markdown Linter + runs-on: ubuntu-latest + steps: + - name: Checkout EIP Repository + uses: actions/checkout@47fbe2df0ad0e27efb67a70beac3555f192b062f + + - 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@f5cf187ef11bd3a68a127321b794aa252ff23019 + if: steps.changed.outcome == 'success' + with: + command: config + globs: | + config/.markdownlint.yaml + ${{ env.CHANGED_FILES }} diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml deleted file mode 100644 index effc1d05ee0ad7..00000000000000 --- a/.github/workflows/greetings.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Greetings - -on: [pull_request, issues] - -jobs: - greeting: - runs-on: ubuntu-latest - steps: - - uses: actions/first-interaction@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: 'Since this is your first issue, we kindly remind you to check out [EIP-1](https://eips.ethereum.org/EIPS/eip-1) for guidance.' - pr-message: 'Since this is your first pull request, we kindly remind you to check out [EIP-1](https://eips.ethereum.org/EIPS/eip-1) for guidance.' diff --git a/.github/workflows/jekyll-label-bot.yml b/.github/workflows/jekyll-label-bot.yml new file mode 100644 index 00000000000000..549c28c9b4d2a2 --- /dev/null +++ b/.github/workflows/jekyll-label-bot.yml @@ -0,0 +1,20 @@ +on: + pull_request_target: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +name: Jekyll Label Bot +jobs: + jekyll-label-action: + name: Label + runs-on: ubuntu-latest + + steps: + - uses: Pandapip1/jekyll-label-action@d0fd82c3cd118140a50843906845fca8e59a8b9e + with: + token: ${{ secrets.GITHUB_TOKEN }} + config-path: config/.jekyll-labels.yml diff --git a/.github/workflows/post-ci.yml b/.github/workflows/post-ci.yml new file mode 100644 index 00000000000000..7057a97f3017cd --- /dev/null +++ b/.github/workflows/post-ci.yml @@ -0,0 +1,56 @@ +on: + workflow_run: + workflows: + - Continuous Integration + types: + - completed + +name: Post CI + +# This is adapted from https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run (2022-07-17) + +jobs: + on-failure: + runs-on: ubuntu-latest + steps: + - name: Fetch PR Data + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 + with: + name: pr_number + workflow: ci.yml + run_id: ${{ github.event.workflow_run.id }} + + - name: Save PR Data + id: save-pr-data + run: | + echo "pr_number=$(cat pr_number)" >> $GITHUB_OUTPUT + echo "pr_sha=$(cat pr_sha)" >> $GITHUB_OUTPUT + echo "merge_sha=$(cat merge_sha)" >> $GITHUB_OUTPUT + + - name: Add Comment + uses: marocchino/sticky-pull-request-comment@39c5b5dc7717447d0cba270cd115037d32d28443 + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + with: + number: ${{ steps.save-pr-data.outputs.pr_number }} + recreate: true + message: | + The commit ${{ steps.save-pr-data.outputs.pr_sha }} (as a parent of ${{ steps.save-pr-data.outputs.merge_sha }}) contains errors. + Please inspect the [Run Summary](https://github.com/ethereum/EIPs/pull/${{ steps.save-pr-data.outputs.pr_number }}/files) for details. + + - name: Add Waiting Label + uses: actions-ecosystem/action-add-labels@288072f1a3b596f4350fe135bcfe381a23abadef + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + with: + labels: w-ci + number: ${{ steps.save-pr-data.outputs.pr_number }} + repo: ${{ github.repository }} + github_token: ${{ github.token }} + + - name: Remove Waiting Label + uses: actions-ecosystem/action-remove-labels@d05162525702062b6bdef750ed8594fc024b3ed7 + if: ${{ github.event.workflow_run.conclusion != 'failure' }} + with: + labels: w-ci + number: ${{ steps.save-pr-data.outputs.pr_number }} + repo: ${{ github.repository }} + github_token: ${{ github.token }} diff --git a/.github/workflows/rerun-bot-pull-request-review.yml b/.github/workflows/rerun-bot-pull-request-review.yml deleted file mode 100644 index 3e8e498cd80477..00000000000000 --- a/.github/workflows/rerun-bot-pull-request-review.yml +++ /dev/null @@ -1,11 +0,0 @@ -on: - pull_request_review: - types: [submitted] -name: Rerun Bot -jobs: - rerun_bot_on_review: - runs-on: ubuntu-latest - name: Trigger Bot Rerun workflow_run - steps: - - name: Explanation - run: echo "this bot is used to trigger another workflow using the workflow_run github event; this is necessary because without it forked PRs do not have access to repo secret; normally this is circumvented using the pull_request_target event but because github actions.. a hack is required to allow the same behavior on pull_request_review; this work-around will no longer be necessary if github ever implements a pull_request_review_target or something similar" \ No newline at end of file diff --git a/.github/workflows/rerun-bot-workflow-run.yml b/.github/workflows/rerun-bot-workflow-run.yml deleted file mode 100644 index a93393304973d7..00000000000000 --- a/.github/workflows/rerun-bot-workflow-run.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Workflow run re-run auto-merge-bot on review -on: - workflow_run: - workflows: - - Rerun Bot - types: - - requested - -jobs: - rerun-bot: - runs-on: ubuntu-latest - name: Rerun Bot (workflow_run) - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Node.js Environment - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: auto-merge-bot - uses: ethereum/EIP-Bot@983960684e010eb6ad6e7bd168169e4d57a17cf8 # rerun-pull-request-target-on-review - id: rerun-auto-merge-bot - with: - GITHUB-TOKEN: ${{ secrets.TOKEN }} - WORKFLOW-ID: ${{github.event.workflow_run.id}} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6ea4c6afd1e422..03eaf1bb8c6d35 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,19 +1,35 @@ -name: "Mark stale PRs" +name: Mark stale PRs & Issues on: schedule: - # Run this every hour, so we are not spammed with changes at once. Later we could consider changing this to once a day. - - cron: "0 * * * *" + - cron: 0 0 * * * # Runs at 00:00 UTC every day. + +permissions: + issues: write + pull-requests: write jobs: stale: + if: github.repository == 'ethereum/eips' runs-on: ubuntu-latest + name: Mark Stale Issues steps: - - uses: actions/stale@v3 + - uses: actions/stale@03af7c36d33f4905e618fac0a1bb7e6d05f0d41b with: + # General repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-pr-message: 'There has been no activity on this pull request for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP 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-stale: 60 - days-before-close: 7 - stale-pr-label: 'stale' + ascending: true # Since we have so many issues, the stale bot finds it hard to keep track. This makes sure that at least the oldest are removed. + # Issue config + stale-issue-message: There has been no activity on this issue for 1 week. It will be closed after 3 months of inactivity. + 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 + 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-milestones: "Manual Merge Queue" + stale-pr-label: w-stale diff --git a/.gitignore b/.gitignore index bcebd7267ebdf9..8503a3cdadb0d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,13 @@ +# Website packaging _site .sass-cache .jekyll-metadata vendor + +# Editor files +.gitpod.yml +.DS_Store +/.idea + +# Secrets +.vercel diff --git a/.travis-ci.sh b/.travis-ci.sh deleted file mode 100755 index 0a3adfc9d57c97..00000000000000 --- a/.travis-ci.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -e # halt script on error - -HTMLPROOFER_OPTIONS="./_site --internal-domains=eips.ethereum.org --check-html --check-opengraph --report-missing-names --log-level=:debug --assume-extension --empty-alt-ignore --timeframe=6w --url-ignore=/EIPS/eip-1,EIPS/eip-1,/EIPS/eip-107,/EIPS/eip-858" - -if [[ $TASK = 'htmlproofer' ]]; then - bundle exec jekyll doctor - bundle exec jekyll build - bundle exec htmlproofer $HTMLPROOFER_OPTIONS --disable-external - - # Validate GH Pages DNS setup - bundle exec github-pages health-check -elif [[ $TASK = 'htmlproofer-external' ]]; then - bundle exec jekyll doctor - bundle exec jekyll build - bundle exec htmlproofer $HTMLPROOFER_OPTIONS --external_only -elif [[ $TASK = 'eip-validator' ]]; then - if [[ $(find . -maxdepth 1 -name 'eip-*' | wc -l) -ne 1 ]]; then - echo "only 'eip-template.md' should be in the root" - exit 1 - fi - eipv EIPS/ --ignore=title_max_length,missing_discussions_to --skip=eip-20-token-standard.md -elif [[ $TASK = 'codespell' ]]; then - codespell -q4 -I .codespell-whitelist -S ".git,Gemfile.lock,**/*.png,**/*.gif,**/*.jpg,**/*.svg,.codespell-whitelist,vendor,_site,_config.yml,style.css" -fi diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 78772718a5e3e1..00000000000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -sudo: false - -language: ruby - -before_install: - - gem install bundler -v '< 2' - -cache: - # Cache Ruby bundles - - bundler - - directories: - - $TRAVIS_BUILD_DIR/tmp/.htmlproofer #https://github.com/gjtorikian/html-proofer/issues/381 - - /usr/local/lib/python3.3/dist-packages/pip/ - -# Assume bundler is being used, therefore -# the `install` step will run `bundle install` by default. -script: "bash -ex .travis-ci.sh" - -env: - global: - - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer - -matrix: - fast_finish: true - include: - - rvm: 2.6.0 - env: TASK='htmlproofer' - - rvm: 2.6.0 - env: TASK='htmlproofer-external' - - language: rust - cache: cargo - before_script: - - cargo install eipv --version=0.1.1 - env: TASK='eip-validator' - - python: 3.3 - env: TASK='codespell' - before_script: "sudo pip install urllib3[secure] && sudo pip install codespell" - allow_failures: - - rvm: 2.6.0 - env: TASK='htmlproofer-external' - -notifications: - webhooks: - urls: - - https://ethlab-183014.appspot.com/merge/ - on_success: always - -addons: - apt: - packages: - "libcurl4-openssl-dev" # https://github.com/gjtorikian/html-proofer/issues/376#issuecomment-332767999 diff --git a/404.html b/404.html index c472b4ea0a7810..faf7e23559089f 100644 --- a/404.html +++ b/404.html @@ -18,7 +18,6 @@

404

-

Page not found :(

The requested page could not be found.

diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md index f0ae480de86254..4849fed2a926f3 100644 --- a/EIPS/eip-1.md +++ b/EIPS/eip-1.md @@ -21,10 +21,10 @@ For Ethereum implementers, EIPs are a convenient way to track the progress of th There are three types of EIP: -- A **Standards Track EIP** describes any change that affects most or all Ethereum implementations, such as—a change to the network protocol, a change in block or transaction validity rules, proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Ethereum. Standards Track EIPs consist of three parts—a design document, an implementation, and (if warranted) an update to the [formal specification]. Furthermore, Standards Track EIPs can be broken down into the following categories: +- A **Standards Track EIP** describes any change that affects most or all Ethereum implementations, such as—a change to the network protocol, a change in block or transaction validity rules, proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Ethereum. Standards Track EIPs consist of three parts—a design document, an implementation, and (if warranted) an update to the [formal specification](https://github.com/ethereum/yellowpaper). Furthermore, Standards Track EIPs can be broken down into the following categories: - **Core**: improvements requiring a consensus fork (e.g. [EIP-5](./eip-5.md), [EIP-101](./eip-101.md)), as well as changes that are not necessarily consensus critical but may be relevant to [“core dev” discussions](https://github.com/ethereum/pm) (for example, [EIP-90], and the miner/node strategy changes 2, 3, and 4 of [EIP-86](./eip-86.md)). - - **Networking**: includes improvements around [devp2p] ([EIP-8](./eip-8.md)) and [Light Ethereum Subprotocol], as well as proposed improvements to network protocol specifications of [whisper] and [swarm]. - - **Interface**: includes improvements around client [API/RPC] specifications and standards, and also certain language-level standards like method names ([EIP-6](./eip-6.md)) and [contract ABIs]. The label “interface” aligns with the [interfaces repo] and discussion should primarily occur in that repository before an EIP is submitted to the EIPs repository. + - **Networking**: includes improvements around [devp2p](https://github.com/ethereum/devp2p/blob/readme-spec-links/rlpx.md) ([EIP-8](./eip-8.md)) and [Light Ethereum Subprotocol](https://ethereum.org/en/developers/docs/nodes-and-clients/#light-node), as well as proposed improvements to network protocol specifications of [whisper](https://github.com/ethereum/go-ethereum/issues/16013#issuecomment-364639309) and [swarm](https://github.com/ethereum/go-ethereum/pull/2959). + - **Interface**: includes improvements around language-level standards like method names ([EIP-6](./eip-6.md)) and [contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html). - **ERC**: application-level standards and conventions, including contract standards such as token standards ([ERC-20](./eip-20.md)), name registries ([ERC-137](./eip-137.md)), URI schemes, library/package formats, and wallet formats. - A **Meta EIP** describes a process surrounding Ethereum or proposes a change to (or an event in) a process. Process EIPs are like Standards Track EIPs but apply to areas other than the Ethereum protocol itself. They may propose an implementation, but not to Ethereum's codebase; they often require community consensus; unlike Informational EIPs, they are more than recommendations, and users are typically not free to ignore them. Examples include procedures, guidelines, changes to the decision-making process, and changes to the tools or environment used in Ethereum development. Any meta-EIP is also considered a Process EIP. @@ -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,29 +49,29 @@ 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] to do this, but you can also use [one of the Ethereum Gitter chat rooms], [the Ethereum subreddit] or [the Issues section of this repository]. +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]. +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). -The AllCoreDevs call serve as a way for client implementers to do three things. First, to discuss the technical merits of EIPs. Second, to gauge what other clients will be implementing. Third, to coordinate EIP implementation for network upgrades. +The AllCoreDevs call serves as a way for client implementers to do three things. First, to discuss the technical merits of EIPs. Second, to gauge what other clients will be implementing. Third, to coordinate EIP implementation for network upgrades. These calls generally result in a "rough consensus" around what EIPs should be implemented. This "rough consensus" rests on the assumptions that EIPs are not contentious enough to cause a network split and that they are technically sound. -: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] thread for your EIP includes or links to as much of the community discussion as possible and that various stakeholders are well-represented. +: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: -![EIP Status Diagram](../assets/eip-1/EIP-process.png) +![EIP Status Diagram](../assets/eip-1/EIP-process-update.jpg) **Idea** - An idea that is pre-draft. This is not tracked within the EIP Repository. @@ -78,76 +79,74 @@ The following is the standardization process for all EIPs in all tracks: **Review** - An EIP Author marks an EIP as ready for and requesting Peer Review. -**Last Call** - This is the final review window for an EIP before moving to `FINAL`. An EIP editor will assign `Last Call` status and set a review end date (review-period-end), typically 14 days later. +**Last Call** - This is the final review window for an EIP before moving to `Final`. An EIP editor will assign `Last Call` status and set a review end date (`last-call-deadline`), typically 14 days later. -If this period results in necessary normative changes it will revert the EIP to `REVIEW`. +If this period results in necessary normative changes it will revert the EIP to `Review`. **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` 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`. +A PR moving an EIP from Last Call to Final SHOULD contain no changes other than the status update. Any content or editorial proposed change SHOULD be separate from this status-updating PR and committed prior to it. + +**Stagnant** - Any EIP in `Draft` or `Review` or `Last Call` if inactive for a period of 6 months or greater is moved to `Stagnant`. An EIP may be resurrected from this state by Authors or EIP Editors through moving it back to `Draft` or it's earlier status. If not resurrected, a proposal may stay forever in this status. >*EIP Authors are notified of any algorithmic change to the status of their EIP* **Withdrawn** - The EIP Author(s) have withdrawn the proposed EIP. This state has finality and can no longer be resurrected using this EIP number. If the idea is pursued at later date it is considered a new proposal. -**Living** - A special status for EIPs that are designed to be continually updated and not reach a state of finality. This includes most notably EIP-1. Any changes to these EIPs will move between `REVIEW` and `LIVING` states. +**Living** - A special status for EIPs that are designed to be continually updated and not reach a state of finality. This includes most notably EIP-1. ## What belongs in a successful EIP? 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), and the author details. See [below](./eip-1.md#eip-header-preamble) for details. -- Abstract - A short (~200 word) description of the technical issue being addressed. -- 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. EIP submissions without sufficient motivation may be rejected outright. -- 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://github.com/ethereum/wiki/wiki/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 may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion. -- Backwards Compatibility - All EIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The EIP must explain how the author proposes to deal with these incompatibilities. EIP submissions without a sufficient backwards compatibility treatise may be rejected outright. -- Test Cases - 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-###/`. -- Reference Implementation - An optional section that contains a reference/example implementation that people can use to assist in understanding or implementing this specification. +- 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. +- 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 (besu, erigon, ethereumjs, go-ethereum, nethermind, or others). +- 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. - 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. See the bottom of this EIP for an example copyright waiver. +- 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).` ## EIP Formats and Templates -EIPs should be written in [markdown] format. There is a [template](https://github.com/ethereum/EIPs/blob/master/eip-template.md) to follow. +EIPs should be written in [markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) format. There is a [template](https://github.com/ethereum/EIPs/blob/master/eip-template.md) to follow. ## EIP Header Preamble -Each EIP must begin with an [RFC 822](https://www.ietf.org/rfc/rfc822.txt) style header preamble, preceded and followed by three hyphens (`---`). This header is also termed ["front matter" by Jekyll](https://jekyllrb.com/docs/front-matter/). The headers must appear in the following order. Headers marked with "*" are optional and are described below. All other headers are required. - -` eip:` *EIP number* (this is determined by the EIP editor) - -` title:` *EIP title* +Each EIP must begin with an [RFC 822](https://www.ietf.org/rfc/rfc822.txt) style header preamble, preceded and followed by three hyphens (`---`). This header is also termed ["front matter" by Jekyll](https://jekyllrb.com/docs/front-matter/). The headers must appear in the following order. -` author:` *a list of the author's or authors' name(s) and/or username(s), or name(s) and email(s). Details are below.* +`eip`: *EIP number* -` * discussions-to:` *a url pointing to the official discussion thread* +`title`: *The EIP title is a few words, not a complete sentence* -` status:` *Draft, Review, Last Call, Final, Stagnant, Withdrawn, Living* +`description`: *Description is one full (short) sentence* -`* review-period-end:` *date review period ends* +`author`: *The list of the author's or authors' name(s) and/or username(s), or name(s) and email(s). Details are below.* -` type:` *Standards Track, Meta, or Informational* +`discussions-to`: *The url pointing to the official discussion thread* -` * category:` *Core, Networking, Interface, or ERC* (fill out for Standards Track EIPs only) +`status`: *Draft, Review, Last Call, Final, Stagnant, Withdrawn, Living* -` created:` *date created on* +`last-call-deadline`: *The date last call period ends on* (Optional field, only needed when status is `Last Call`) -` * updated:` *comma separated list of dates* +`type`: *One of `Standards Track`, `Meta`, or `Informational`* -` * requires:` *EIP number(s)* +`category`: *One of `Core`, `Networking`, `Interface`, or `ERC`* (Optional field, only needed for `Standards Track` EIPs) -` * replaces:` *EIP number(s)* +`created`: *Date the EIP was created on* -` * superseded-by:` *EIP number(s)* +`requires`: *EIP number(s)* (Optional field) -` * resolution:` *a url pointing to the resolution of this EIP* +`withdrawal-reason`: *A sentence explaining why the EIP was withdrawn.* (Optional field, only needed when status is `Withdrawn`) 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: @@ -157,55 +156,266 @@ or > Random J. User (@username) -if the email address or GitHub username is included, and - -> Random J. User - -if the email address is not given. +or -It is not possible to use both an email and a GitHub username at the same time. If important to include both, one could include their name twice, once with the GitHub username, and once with the email. +> Random J. User (@username) <address@dom.ain> -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. +if the email address and/or GitHub username is included, and -#### `resolution` header +> Random J. User -The `resolution` header is required for Standards Track EIPs only. It contains a URL that should point to an email message or other web resource where the pronouncement about the EIP is made. +if neither the email address nor the GitHub username are given. -#### `discussions-to` header +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. -While an EIP is a draft, a `discussions-to` header will indicate the mailing list or URL where the EIP is being discussed. As mentioned above, examples for places to discuss your EIP include [Ethereum topics on Gitter](https://gitter.im/ethereum/topics), an issue in this repo or in a fork of this repo, [Ethereum Magicians](https://ethereum-magicians.org/) (this is suitable for EIPs that may be contentious or have a strong governance aspect), and [Reddit r/ethereum](https://www.reddit.com/r/ethereum/). +### `discussions-to` header -No `discussions-to` header is necessary if the EIP is being discussed privately with the author. +While an EIP is a draft, a `discussions-to` header will indicate the URL where the EIP is being discussed. -As a single exception, `discussions-to` cannot point to GitHub pull requests. +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. -#### `updated` header +### `requires` header + +EIPs may have a `requires` header, indicating the EIP numbers that this EIP depends on. If such a dependency exists, this field is required. + +A `requires` dependency is created when the current EIP cannot be understood or implemented without a concept or technical element from another EIP. Merely mentioning another EIP does not necessarily create such a dependency. + +## Linking to External Resources -The `updated` header records the date(s) when the EIP was updated with "substantial" changes. This header is only valid for EIPs of Draft and Active status. +Other than the specific exceptions listed below, links to external resources **SHOULD NOT** be included. External resources may disappear, move, or change unexpectedly. -#### `requires` header +The process governing permitted external resources is described in [EIP-5757](./eip-5757.md). + +### Execution Client Specifications + +Links to the Ethereum Execution Client Specifications may be included using normal markdown syntax, such as: + +```markdown +[Ethereum Execution Client Specifications](https://github.com/ethereum/execution-specs/blob/9a1f22311f517401fed6c939a159b55600c454af/README.md) +``` + +Which renders to: + +[Ethereum Execution Client Specifications](https://github.com/ethereum/execution-specs/blob/9a1f22311f517401fed6c939a159b55600c454af/README.md) + +Permitted Execution Client Specifications URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^(https://github.com/ethereum/execution-specs/(blob|commit)/[0-9a-f]{40}/.*|https://github.com/ethereum/execution-specs/tree/[0-9a-f]{40}/.*)$ +``` + +### Consensus Layer Specifications + +Links to specific commits of files within the Ethereum Consensus Layer Specifications may be included using normal markdown syntax, such as: + +```markdown +[Beacon Chain](https://github.com/ethereum/consensus-specs/blob/26695a9fdb747ecbe4f0bb9812fedbc402e5e18c/specs/sharding/beacon-chain.md) +``` -EIPs may have a `requires` header, indicating the EIP numbers that this EIP depends on. +Which renders to: + +[Beacon Chain](https://github.com/ethereum/consensus-specs/blob/26695a9fdb747ecbe4f0bb9812fedbc402e5e18c/specs/sharding/beacon-chain.md) + +Permitted Consensus Layer Specifications URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^https://github.com/ethereum/consensus-specs/(blob|commit)/[0-9a-f]{40}/.*$ +``` + +### Networking Specifications + +Links to specific commits of files within the Ethereum Networking Specifications may be included using normal markdown syntax, such as: + +```markdown +[Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md) +``` + +Which renders as: + +[Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md) + +Permitted Networking Specifications URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^https://github.com/ethereum/devp2p/(blob|commit)/[0-9a-f]{40}/.*$ +``` + +### World Wide Web Consortium (W3C) + +Links to a W3C "Recommendation" status specification may be included using normal markdown syntax. For example, the following link would be allowed: + +```markdown +[Secure Contexts](https://www.w3.org/TR/2021/CRD-secure-contexts-20210918/) +``` + +Which renders as: + +[Secure Contexts](https://www.w3.org/TR/2021/CRD-secure-contexts-20210918/) + +Permitted W3C recommendation URLs MUST anchor to a specification in the technical reports namespace with a date, and so MUST match this regular expression: + +```regex +^https://www\.w3\.org/TR/[0-9][0-9][0-9][0-9]/.*$ +``` + +### Web Hypertext Application Technology Working Group (WHATWG) + +Links to WHATWG specifications may be included using normal markdown syntax, such as: + +```markdown +[HTML](https://html.spec.whatwg.org/commit-snapshots/578def68a9735a1e36610a6789245ddfc13d24e0/) +``` -#### `superseded-by` and `replaces` headers +Which renders as: -EIPs may also have a `superseded-by` header indicating that an EIP has been rendered obsolete by a later document; the value is the number of the EIP that replaces the current document. The newer EIP must have a `replaces` header containing the number of the EIP that it rendered obsolete. +[HTML](https://html.spec.whatwg.org/commit-snapshots/578def68a9735a1e36610a6789245ddfc13d24e0/) + +Permitted WHATWG specification URLs must anchor to a specification defined in the `spec` subdomain (idea specifications are not allowed) and to a commit snapshot, and so must match this regular expression: + +```regex +^https:\/\/[a-z]*\.spec\.whatwg\.org/commit-snapshots/[0-9a-f]{40}/$ +``` + +Although not recommended by WHATWG, EIPs must anchor to a particular commit so that future readers can refer to the exact version of the living standard that existed at the time the EIP was finalized. This gives readers sufficient information to maintain compatibility, if they so choose, with the version referenced by the EIP and the current living standard. + +### Internet Engineering Task Force (IETF) + +Links to an IETF Request For Comment (RFC) specification may be included using normal markdown syntax, such as: + +```markdown +[RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) +``` + +Which renders as: + +[RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) + +Permitted IETF specification URLs MUST anchor to a specification with an assigned RFC number (meaning cannot reference internet drafts), and so MUST match this regular expression: + +```regex +^https:\/\/www.rfc-editor.org\/rfc\/.*$ +``` + +### Bitcoin Improvement Proposal + +Links to Bitcoin Improvement Proposals may be included using normal markdown syntax, such as: + +```markdown +[BIP 38](https://github.com/bitcoin/bips/blob/3db736243cd01389a4dfd98738204df1856dc5b9/bip-0038.mediawiki) +``` + +Which renders to: + +[BIP 38](https://github.com/bitcoin/bips/blob/3db736243cd01389a4dfd98738204df1856dc5b9/bip-0038.mediawiki) + +Permitted Bitcoin Improvement Proposal URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^(https://github.com/bitcoin/bips/blob/[0-9a-f]{40}/bip-[0-9]+\.mediawiki)$ +``` + +### National Vulnerability Database (NVD) + +Links to the Common Vulnerabilities and Exposures (CVE) system as published by the National Institute of Standards and Technology (NIST) may be included, provided they are qualified by the date of the most recent change, using the following syntax: + +```markdown +[CVE-2023-29638 (2023-10-17T10:14:15)](https://nvd.nist.gov/vuln/detail/CVE-2023-29638) +``` + +Which renders to: + +[CVE-2023-29638 (2023-10-17T10:14:15)](https://nvd.nist.gov/vuln/detail/CVE-2023-29638) + +### Digital Object Identifier System + +Links qualified with a Digital Object Identifier (DOI) may be included using the following syntax: + +````markdown +This is a sentence with a footnote.[^1] + +[^1]: + ```csl-json + { + "type": "article", + "id": 1, + "author": [ + { + "family": "Jameson", + "given": "Hudson" + } + ], + "DOI": "00.0000/a00000-000-0000-y", + "title": "An Interesting Article", + "original-date": { + "date-parts": [ + [2022, 12, 31] + ] + }, + "URL": "https://sly-hub.invalid/00.0000/a00000-000-0000-y", + "custom": { + "additional-urls": [ + "https://example.com/an-interesting-article.pdf" + ] + } + } + ``` +```` + +Which renders to: + + + + +This is a sentence with a footnote.[^1] + +[^1]: + ```csl-json + { + "type": "article", + "id": 1, + "author": [ + { + "family": "Jameson", + "given": "Hudson" + } + ], + "DOI": "00.0000/a00000-000-0000-y", + "title": "An Interesting Article", + "original-date": { + "date-parts": [ + [2022, 12, 31] + ] + }, + "URL": "https://sly-hub.invalid/00.0000/a00000-000-0000-y", + "custom": { + "additional-urls": [ + "https://example.com/an-interesting-article.pdf" + ] + } + } + ``` + + + +See the [Citation Style Language Schema](https://resource.citationstyles.org/schema/v1.0/input/json/csl-data.json) for the supported fields. In addition to passing validation against that schema, references must include a DOI and at least one URL. + +The top-level URL field must resolve to a copy of the referenced document which can be viewed at zero cost. Values under `additional-urls` must also resolve to a copy of the referenced document, but may charge a fee. ## Linking to other EIPs -References to other EIPs should follow the format `EIP-N` where `N` is the EIP number you are referring to. Each EIP that is referenced in an EIP **MUST** be accompanied by a relative markdown link the first time it is referenced, and **MAY** be accompanied by a link on subsequent references. The link **MUST** always be done via relative paths so that the links work in this GitHub repository, forks of this repository, the main EIPs site, mirrors of the main EIP site, etc. For example, you would link to this EIP with `[EIP-1](./eip-1.md)`. +References to other EIPs should follow the format `EIP-N` where `N` is the EIP number you are referring to. Each EIP that is referenced in an EIP **MUST** be accompanied by a relative markdown link the first time it is referenced, and **MAY** be accompanied by a link on subsequent references. The link **MUST** always be done via relative paths so that the links work in this GitHub repository, forks of this repository, the main EIPs site, mirrors of the main EIP site, etc. For example, you would link to this EIP as `./eip-1.md`. ## Auxiliary Files @@ -221,15 +431,25 @@ If you are interested in assuming ownership of an EIP, send a message asking to The current EIP editors are -- Nick Johnson (@arachnid) +- Alex Beregszaszi (@axic) +- Gavin John (@Pandapip1) +- Greg Colvin (@gcolvin) +- Matt Garnett (@lightclient) +- Sam Wilson (@SamWilsn) +- Zainan Victor Zhou (@xinbenlv) +- Gajinder Singh (@g11tech) + +Emeritus EIP editors are + - Casey Detrio (@cdetrio) - Hudson Jameson (@Souptacular) -- Vitalik Buterin (@vbuterin) -- Nick Savers (@nicksavers) - Martin Becze (@wanderer) -- Greg Colvin (@gcolvin) -- Alex Beregszaszi (@axic) - Micah Zoltu (@MicahZoltu) +- Nick Johnson (@arachnid) +- Nick Savers (@nicksavers) +- Vitalik Buterin (@vbuterin) + +If you would like to become an EIP editor, please check [EIP-5069](./eip-5069.md). ## EIP Editor Responsibilities @@ -243,10 +463,8 @@ If the EIP isn't ready, the editor will send it back to the author for revision, Once the EIP is ready for the repository, the EIP editor will: -- Assign an EIP number (generally the PR number or, if preferred by the author, the Issue # if there was discussion in the Issues section of this repository about this EIP) - -- Merge the corresponding pull request - +- Assign an EIP number (generally incremental; editors can reassign if number sniping is suspected) +- Merge the corresponding [pull request](https://github.com/ethereum/EIPs/pulls) - Send a message back to the EIP author with the next step. Many EIPs are written and maintained by developers with write access to the Ethereum codebase. The EIP editors monitor EIP changes, and correct any structure, grammar, spelling, or markup mistakes we see. @@ -255,32 +473,34 @@ The editors don't pass judgment on EIPs. We merely do the administrative & edito ## Style Guide -When referring to an EIP by number, it should be written in the hyphenated form `EIP-X` where `X` is the EIP's assigned number. +### Titles + +The `title` field in the preamble: + +- 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. + +### EIP numbers + +When referring to an EIP with a `category` of `ERC`, it must be written in the hyphenated form `ERC-X` where `X` is that EIP's assigned number. When referring to EIPs with any other `category`, it must be written in the hyphenated form `EIP-X` where `X` is that EIP's assigned number. + +### RFC 2119 and RFC 8174 + +EIPs are encouraged to follow [RFC 2119](https://www.ietf.org/rfc/rfc2119.html) and [RFC 8174](https://www.ietf.org/rfc/rfc8174.html) for terminology and to insert the following at the beginning of the Specification 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. ## History -This document was derived heavily from [Bitcoin's BIP-0001] written by Amir Taaki which in turn was derived from [Python's PEP-0001]. In many places text was simply copied and modified. Although the PEP-0001 text was written by Barry Warsaw, Jeremy Hylton, and David Goodger, they are not responsible for its use in the Ethereum Improvement Process, and should not be bothered with technical questions specific to Ethereum or the EIP. Please direct all comments to the EIP editors. - -### Bibliography - -[devp2p]: https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol -[Light Ethereum Subprotocol]: https://github.com/ethereum/wiki/wiki/Light-client-protocol -[whisper]: https://github.com/ethereum/go-ethereum/wiki/Whisper-Overview -[swarm]: https://github.com/ethereum/go-ethereum/pull/2959 -[API/RPC]: https://github.com/ethereum/wiki/wiki/JSON-RPC -[contract ABIs]: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI -[interfaces repo]: https://github.com/ethereum/interfaces -[the Ethereum subreddit]: https://www.reddit.com/r/ethereum/ -[one of the Ethereum Gitter chat rooms]: https://gitter.im/ethereum/ -[pull request]: https://github.com/ethereum/EIPs/pulls -[formal specification]: https://github.com/ethereum/yellowpaper -[the Issues section of this repository]: https://github.com/ethereum/EIPs/issues -[markdown]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet -[Bitcoin's BIP-0001]: https://github.com/bitcoin/bips -[Python's PEP-0001]: https://www.python.org/dev/peps/ -[the Ethereum Magicians forum]: https://ethereum-magicians.org/ -[AllCoreDevs agenda GitHub Issue]: https://github.com/ethereum/pm/issues +This document was derived heavily from [Bitcoin's BIP-0001](https://github.com/bitcoin/bips) written by Amir Taaki which in turn was derived from [Python's PEP-0001](https://peps.python.org/). In many places text was simply copied and modified. Although the PEP-0001 text was written by Barry Warsaw, Jeremy Hylton, and David Goodger, they are not responsible for its use in the Ethereum Improvement Process, and should not be bothered with technical questions specific to Ethereum or the EIP. Please direct all comments to the EIP editors. ## 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-101.md b/EIPS/eip-101.md index 91dd8d86c108c8..19053a64bd2e2b 100644 --- a/EIPS/eip-101.md +++ b/EIPS/eip-101.md @@ -1,8 +1,8 @@ --- eip: 101 title: Serenity Currency and Crypto Abstraction -author: Vitalik Buterin -status: Draft +author: Vitalik Buterin (@vbuterin) +status: Stagnant type: Standards Track category: Core created: 2015-11-15 diff --git a/EIPS/eip-1010.md b/EIPS/eip-1010.md index b6bf09987c68d9..794fee16428eb8 100644 --- a/EIPS/eip-1010.md +++ b/EIPS/eip-1010.md @@ -3,7 +3,7 @@ eip: 1010 title: Uniformity Between 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B and 0x15E55EF43efA8348dDaeAa455F16C43B64917e3c author: Anderson Wesley (@andywesley) discussions-to: https://github.com/andywesley/EIPs/issues/1 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-04-18 @@ -65,5 +65,4 @@ consistently among all clients as intended by the proposal process will be suffi to ensure that backwards compatibility is not a concern. ## 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-1011.md b/EIPS/eip-1011.md index 3e3087b1f5e7b3..84c3844bf10f0e 100644 --- a/EIPS/eip-1011.md +++ b/EIPS/eip-1011.md @@ -1,7 +1,7 @@ --- eip: 1011 title: Hybrid Casper FFG -status: Draft +status: Stagnant type: Standards Track category: Core author: Danny Ryan (@djrtwo), Chih-Cheng Liang (@ChihChengLiang) @@ -395,7 +395,7 @@ Below is a table of deposit sizes with associated annual interest rate and appro #### Gas Changes Normal block transactions cannot affect casper `vote` validation results, but casper `vote` validation results can affect normal block transaction execution. Due to this asymmetrical relationship, `vote` transactions can be processed in parallel with normal block transactions if `vote` transactions are placed after all normal block transactions. Because `vote` transactions can be processed in parallel to normal block transactions, `vote` transactions cost 0 gas for validators, ensuring that validators can submit votes even in highly congested or high gas-price periods. -`vote_gas_used` is introduced to ensure that `vote` transactions do not put an undue burden on block processing. The additional overhead from `vote` transactions is capped at the same limit as normal block transactions so that, when run in parallel, neither sets of transactions exceeds the overhead defined by the `block_gas_limit`. +`vote_gas_used` is introduced to ensure that `vote` transactions do not put an undue burden on block processing. The additional overhead from `vote` transactions is capped at the same limit as normal block transactions so that, when run in parallel, neither sets of transactions exceed the overhead defined by the `block_gas_limit`. The call to `initialize_epoch` at the beginning of each epoch requires 0 gas so that this protocol state transition does not take any gas allowance away from normal transactions. @@ -437,4 +437,4 @@ This setting is suggested default disabled because the block producer will almos This EIP is not forward compatible and introduces backwards incompatibilities in the state, fork choice rule, block reward, transaction validity, and gas calculations on certain transactions. Therefore, all changes should be included in a scheduled hardfork at `HYBRID_CASPER_FORK_BLKNUM`. ## 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-1013.md b/EIPS/eip-1013.md index ea3f55cb486425..0c7b04a82b7f54 100644 --- a/EIPS/eip-1013.md +++ b/EIPS/eip-1013.md @@ -17,7 +17,7 @@ This meta-EIP specifies the changes included in the Ethereum hardfork named Cons - Codename: Constantinople - Aliases: Metropolis/Constantinople, Metropolis part 2 - Activation: - - `Block >= 7_280_000` on the Ethereum mainnet + - `Block >= 7_280_000` on the Ethereum Mainnet - `Block >= 4,230,000` on the Ropsten testnet - `Block >= 9_200_000` on the Kovan testnet - `Block >= 3_660_663` on the Rinkeby testnet @@ -35,4 +35,4 @@ This meta-EIP specifies the changes included in the Ethereum hardfork named Cons ## 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-1015.md b/EIPS/eip-1015.md index e21c7f17201663..829cc00f3ffb5b 100644 --- a/EIPS/eip-1015.md +++ b/EIPS/eip-1015.md @@ -3,7 +3,7 @@ eip: 1015 title: Configurable On Chain Issuance author: Alex Van de Sande discussions-to: https://ethereum-magicians.org/t/eip-dynamic-block-rewards-with-governance-contract/204 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-04-20 @@ -26,9 +26,9 @@ Moving to PoS has been on the roadmap since day 0 for ethereum, along with a red [EIP-960](https://github.com/ethereum/EIPs/issues/960), Vitalik's not so jokey april's fool has been taken seriously. It proposes the issuance to be slowly reduced until it reaches 120 million ether. One of the main counterpoints by Vlad can be simplified by [we don't know enough to know what that ether can be used for](https://medium.com/@Vlad_Zamfir/against-vitaliks-fixed-supply-eip-eip-960-18e182a7e5bd) and Vitalik's counterpoint is that [reducing emissions can be a way to reduce future abuse of these funds by finding a schelling point at 0](https://medium.com/@VitalikButerin/to-be-clear-im-not-necessarily-wedded-to-a-finite-supply-cap-a7aa48ab880c). Issuance has already been reduced once, from 5 ether to the current 3 ether per block. The main point of a hard cap is that a lot of people consider *not issuing* as having a positive contribution, that can outweigh other actions. Burning ether is also a valid issuance decision. -#### Asics and advantadges of PoW +#### Asics and advantages of PoW -[EIP-960](https://eips.ethereum.org/EIPS/eip-969) proposes a change in algorithm to avoid mining being dominated by ASICS. Counter arguments by Phil Daian argue among others than [resisting economies of scale is futile and there might be specific security advantadges to specialized hardware](https://pdaian.com/blog/anti-asic-forks-considered-harmful/). One of the main arguments for PoW mining, even when it doesn't provide security, it is useful as a fair distribution mechanism, that **PoW allows any person with a computer, internet access and electricity to obtain currency without having to deal with government imposed currency controls**. +[EIP-960](https://eips.ethereum.org/EIPS/eip-969) proposes a change in algorithm to avoid mining being dominated by ASICS. Counter arguments by Phil Daian argue among others than [resisting economies of scale is futile and there might be specific security advantages to specialized hardware](https://pdaian.com/blog/anti-asic-forks-considered-harmful/). One of the main arguments for PoW mining, even when it doesn't provide security, it is useful as a fair distribution mechanism, that **PoW allows any person with a computer, internet access and electricity to obtain currency without having to deal with government imposed currency controls**. #### Recovery Forks @@ -116,4 +116,4 @@ A lot of things are suggested in this EIP, so I would like to propose these ques ## 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-1046.md b/EIPS/eip-1046.md index ab29e214b35045..c4e38566b38400 100644 --- a/EIPS/eip-1046.md +++ b/EIPS/eip-1046.md @@ -1,20 +1,19 @@ --- eip: 1046 -title: ERC20 Metadata Extension -author: Tommy Nicholas (@tomasienrbc), Matt Russo (@mateosu), John Zettler (@JohnZettler), Matt Condon (@shrugs) -discussions-to: https://www.reddit.com/r/raredigitalart/comments/8hfh1g/erc20_metadata_extension_eip_1046/ -status: Draft +title: tokenURI Interoperability +description: Extends ERC-20 with an ERC-721-like tokenURI, and extends ERC-721 and ERC-1155 with interoperability +author: Tommy Nicholas (@tomasienrbc), Matt Russo (@mateosu), John Zettler (@JohnZettler), Matt Condon (@shrugs), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-1046-erc-20-metadata-extension/13036 +status: Final type: Standards Track category: ERC created: 2018-04-13 -requires: 20 +requires: 20, 721, 1155 --- -## Simple Summary -Optionally extend ERC20 token interface to support the same metadata standard as ERC721 tokens. - ## Abstract -The ERC721 standard introduced the `tokenURI` parameter for non-fungible tokens to handle metadata such as: + +[ERC-721](./eip-721.md) introduced a `tokenURI` function for non-fungible tokens to handle miscellaneous metadata such as: - thumbnail image - title @@ -22,66 +21,201 @@ The ERC721 standard introduced the `tokenURI` parameter for non-fungible tokens - special asset properties - etc. -Metadata is critical for assets such as crypto-collectibles and video game assets to have real utility and value. However, not all crypto-collectibles and gaming assets will be non-fungible. It is critical for fungible ERC20 tokens to have a metadata standard like that of ERC721 tokens. Standardization of metadata between ERC20 and ERC721 will simplify development of dApps and infrastructure that must support both fungible and non-fungible assets. +This ERC adds a `tokenURI` function to [ERC-20](./eip-20.md), and extends [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) to enable interoperability between all three types of token URI. ## Motivation -The ERC721 standard was created to support the creation of perfectly unique, 1-of-1, non-divisible tokens known as "non-fungible tokens". -The initial use case for the ERC721 standard was to support the creation of crypto-collectibles and gaming assets, initially for the ["Crypto Kitties"](https://www.cryptokitties.co/) collectibles game. The success of Crypto Kitties catalyzed significant application development to support the display of ERC721 assets using the `tokenURI` metadata parameter. +See the note about the metadata extension in [ERC-721](./eip-721.md#rationale). The same arguments apply to ERC-20. + +Being able to use similar mechanisms to extract metadata for ERC-20, ERC-721, ERC-1155, and future standards is useful for determining: -However, not all crypto-collectibles and gaming assets need to be unique and non-fungible. Gaming assets (items, weapons, characters), crypto-artworks with non-unique "prints", and more will function more like traditional ERC20 tokens with a fungible `supply`. Many applications such as wallets, exchanges, games, etc. will want to support both fungible and non-fungible assets containing similar metadata. This proposal will extend the ERC20 standard to optionally include a nearly identical `tokenURI` parameter supporting the same JSON metadata schema as the ERC721 standard. +- What type of token a contract is (if any); +- How to display a token to a user, either in an asset listing page or on a dedicated token page; and +- Determining the capabilities of the token ## Specification -The **metadata extension** will be OPTIONAL for ERC20 contracts. This allows your smart contract to be interrogated for its name and for details about the assets which your tokens represent. +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. + +### Interoperability Metadata + +The following TypeScript interface is used in later sections: + +```typescript +/** + * Interoperability metadata. + * This can be extended by other proposals. + * + * All fields MUST be optional. + * **Not every field has to be a boolean.** Any optional JSON-serializable object can be used by extensions. + */ +interface InteroperabilityMetadata { + /** + * This MUST be true if this is ERC-1046 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as an ERC-20 token. + **/ + erc1046?: boolean | undefined; + + /** + * This MUST be true if this is ERC-721 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as a ERC-721 token. + **/ + erc721?: boolean | undefined; + + /** + * This MUST be true if this is ERC-1155 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as a ERC-1155 token. + **/ + erc1155?: boolean | undefined; +} +``` + +### ERC-20 Extension + +#### ERC-20 Interface Extension + +Compliant contracts MUST implement the following Solidity interface: + +```solidity +pragma solidity ^0.8.0; + +/// @title ERC-20 Metadata Extension +interface ERC20TokenMetadata /* is ERC20 */ { + /// @notice Gets an ERC-721-like token URI + /// @dev The resolved data MUST be in JSON format and support ERC-1046's ERC-20 Token Metadata Schema + function tokenURI() external view returns (string); +} +``` + +#### ERC-20 Token Metadata Schema + +The resolved JSON of the `tokenURI` described in the ERC-20 Interface Extension section MUST conform to the following TypeScript interface: + +```typescript +/** + * Asset Metadata + */ +interface ERC20TokenMetadata { + /** + * Interoperabiliy, to differentiate between different types of tokens and their corresponding URIs. + **/ + interop: InteroperabilityMetadata; + + /** + * The name of the ERC-20 token. + * If the `name()` function is present in the ERC-20 token and returns a nonempty string, these MUST be the same value. + */ + name?: string; + + /** + * The symbol of the ERC-20 token. + * If the `symbol()` function is present in the ERC-20 token and returns a nonempty string, these MUST be the same value. + */ + symbol?: string; + + /** + * The decimals of the ERC-20 token. + * If the `decimals()` function is present in the ERC-20 token, these MUST be the same value. + * Defaults to 18 if neither this parameter nor the ERC-20 `decimals()` function are present. + */ + decimals?: number; + + /** + * Provides a short one-paragraph description of the ERC-20 token, without any markup or newlines. + */ + description?: string; + + /** + * A URI pointing to a resource with mime type `image/*` that represents this token. + * If the image is a bitmap, it SHOULD have a width between 320 and 1080 pixels + * The image SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive. + */ + image?: string; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represents this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels + * Images SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive. + */ + images?: string[]; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represent an icon for this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels, and MUST have a height equal to its width + * Images MUST have an aspect ratio of 1:1, and use a transparent background + */ + icons?: string[]; +} +``` + +### ERC-721 Extension + +#### Extension to the ERC-721 Metadata Schema + +Contracts that implement ERC-721 and use its token metadata URI SHOULD to use the following TypeScript extension to the metadata URI: + +```typescript +interface ERC721TokenMetadataInterop extends ERC721TokenMetadata { + /** + * Interoperabiliy, to avoid confusion between different token URIs + **/ + interop: InteroperabilityMetadata; +} +``` + +### ERC-1155 Extension + +#### ERC-1155 Interface Extension + +[ERC-1155](./eip-1155.md)-compliant contracts using the metadata extension SHOULD implement the following Solidity interface: ```solidity -/// @title ERC-20 optional metadata extension -interface TokenMetaData /* is ERC20 */ { +pragma solidity ^0.8.0; - /// @notice A distinct Uniform Resource Identifier (URI) for a given token. +/// @title ERC-1155 Metadata URI Interoperability Extension +interface ERC1155TokenMetadataInterop /* is ERC1155Metadata */ { + /// @notice Gets an ERC-1046-compliant ERC-1155 token URI + /// @dev The resolved data MUST be in JSON format and support ERC-1046's Extension to the ERC-1155 Token Metadata Schema + /// This MUST be the same uri as the `uri()` function function tokenURI() external view returns (string); } ``` -This is the "Token Metadata JSON Schema" referenced above. - -```json -{ - "title": "Asset Metadata", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Identifies the asset to which this token represents", - }, - "description": { - "type": "string", - "description": "Describes the asset to which this token represents", - }, - "image": { - "type": "string", - "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.", - } - } +#### Extension to the ERC-1155 Metadata Schema + +Contracts that implement ERC-1155 and use its token metadata URI are RECOMMENDED to use the following extension to the metadata URI. Contracts that implement the interface described in the ERC-1155 Interface Extension section MUST use the following TypeScript extension: + +```typescript +interface ERC1155TokenMetadataInterop extends ERC1155TokenMetadata { + /** + * Interoperabiliy, to avoid confusion between different token URIs + **/ + interop: InteroperabilityMetadata; } ``` -The token's name() and symbol() getters should be preferred over the name and/or symbol properties in the tokenURI JSON. +### Miscellaneous Recommendations + +To save gas, it is RECOMMENDED for compliant contracts not to implement the optional `name()`, `symbol()`, or `decimals()` functions, and instead to only include them in the metadata URI. Additionally, if the decimals is `18`, then it is NOT RECOMMENDED to include the `decimals` field in the metadata. ## Rationale -This proposal will make adding metadata to ERC20 tokens straightforward for developers with minimal-to-no disruption to the overall ecosystem. By using the same parameter name and by consolidating the underlying Token JSON Metadata Standard, developers will confidently understand how to add and interpret token metadata between ERC20 and ERC721 tokens. + +This ERC makes adding metadata to ERC-20 tokens more straightforward for developers, with minimal to no disruption to the overall ecosystem. Using the same parameter name makes it easier to reuse code. + +Additionally, the recommendations not to use ERC-20's `name`, `symbol`, and `decimals` functions save gas. + +Built-in interoperability is useful as otherwise it might not be easy to differentiate the type of the token. Interoperability could be done using [ERC-165](./eip-165.md), but static calls are time-inefficient for wallets and websites, and is generally inflexible. Instead, including interoperability data in the token URI increases flexibility while also giving a performance increase. ## Backwards Compatibility -This EIP is fully backwards compatible as its implementation simply extends the functionality of ERC20 tokens and is optional. -## Test Cases -TO-DO +This EIP is fully backwards compatible as its implementation simply extends the functionality of ERC-20 tokens and is optional. Additionally, it makes backward compatible recommendations for ERC-721 and ERC-1155 tokens. -## Implementation +## Security Considerations -- [Rare Art Labs](https://rareart.io) (WIP) -- [Open Zeppelin](https://github.com/OpenZeppelin/zeppelin-solidity) (WIP) +### Server-Side Request Forgery (SSRF) + +Wallets should be careful about making arbitrary requests to URLs. As such, it is recommended for wallets to sanitize the URI by whitelisting specific schemes and ports. A vulnerable wallet could be tricked into, for example, modifying data on a locally-hosted redis database. ## 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-1051.md b/EIPS/eip-1051.md index 309f4137743ae9..c6b1a7ef34afb6 100644 --- a/EIPS/eip-1051.md +++ b/EIPS/eip-1051.md @@ -3,7 +3,7 @@ eip: 1051 title: Overflow checking for the EVM author: Nick Johnson discussions-to: https://ethereum-magicians.org/t/eip-arithmetic-overflow-detection-for-the-evm/261 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-05-02 @@ -56,4 +56,4 @@ TBD TBD ## 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-1052.md b/EIPS/eip-1052.md index 6dc6015e16a40e..73e00bb8041e0c 100644 --- a/EIPS/eip-1052.md +++ b/EIPS/eip-1052.md @@ -66,7 +66,7 @@ There are no backwards compatibility concerns. 5. The `EXTCODEHASH` of an account that selfdestructed in the current transaction. 6. The `EXTCODEHASH` of an account that selfdestructed and later the selfdestruct has been reverted. 7. The `EXTCODEHASH` of an account created in the current transaction. -8. The `EXTCODEHASH` of an account that has been newly create and later the creation has been reverted. +8. The `EXTCODEHASH` of an account that has been newly created and later the creation has been reverted. 9. The `EXTCODEHASH` of an account that firstly does not exist and later is empty. 10. The `EXTCODEHASH` of an empty account that is going to be cleared by the state clearing rule. @@ -75,4 +75,4 @@ There are no backwards compatibility concerns. TBD ## 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-1056.md b/EIPS/eip-1056.md index a600cb92fdc87a..cb468d8eb173b4 100644 --- a/EIPS/eip-1056.md +++ b/EIPS/eip-1056.md @@ -5,7 +5,7 @@ author: Pelle Braendgaard , Joel Torstensson , Andrea Lanfranchi (@AndreaLanfranchi), Michael Carter (@bitsbetrippin), IfDefElse discussions-to: https://ethereum-magicians.org/t/eip-progpow-a-programmatic-proof-of-work/272 -status: Review +status: Stagnant type: Standards Track category: Core created: 2018-05-02 @@ -568,8 +568,8 @@ Machine-readable test vectors (T.B.D) The reference ProgPoW mining implementation is located at [the @ifdefelse ProgPOW repository](https://github.com/ifdefelse/ProgPOW). -## License and Copyright +## Copyright -The ProgPoW algorithm and this specification are a new work. Copyright and related rights are waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). The reference ProgPoW mining implementation located at [ProgPOW](https://github.com/ifdefelse/ProgPOW) is a derivative of ethminer so retains the GPL license. diff --git a/EIPS/eip-1062.md b/EIPS/eip-1062.md index 022467747dc0e0..0f304b6cb2c8b7 100644 --- a/EIPS/eip-1062.md +++ b/EIPS/eip-1062.md @@ -3,7 +3,7 @@ eip: 1062 title: Formalize IPFS hash into ENS(Ethereum Name Service) resolver author: Phyrex Tsai , Portal Network Team discussions-to: https://ethereum-magicians.org/t/eip-1062-formalize-ipfs-hash-into-ens-ethereum-name-service-resolver/281 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-02 @@ -79,6 +79,6 @@ The use case can be implemented as browser extension. Users can easily download The workable implementation repository : [https://github.com/PortalNetwork/portal-network-browser-extension](https://github.com/PortalNetwork/portal-network-browser-extension) ## 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-1066.md b/EIPS/eip-1066.md index 9f057326ec6833..5b29e2dd32c981 100644 --- a/EIPS/eip-1066.md +++ b/EIPS/eip-1066.md @@ -3,7 +3,7 @@ eip: 1066 title: Status Codes author: Brooklyn Zelenka (@expede), Tom Carchrae (@carchrae), Gleb Naumenko (@naumenkogs) discussions-to: https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/ -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-05 @@ -595,4 +595,4 @@ Reference cases and helper libraries (Solidity and JS) can be found at: ## 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-107.md b/EIPS/eip-107.md index d6a79c2b53cf3e..ed70e8f5f3767f 100644 --- a/EIPS/eip-107.md +++ b/EIPS/eip-107.md @@ -3,19 +3,19 @@ eip: 107 title: safe "eth_sendTransaction" authorization via html popup author: Ronan Sandford (@wighawag) created: 2016-06-05 -status: Draft +status: Stagnant type: Standards Track category: Interface --- -Abstract -======== +## Abstract + This draft EIP describes the details of an authorization method that if provided by rpc enabled ethereum nodes would allow regular websites to send transactions (via ```eth_sendTransaction```) without the need to enable CORS. Instead, user would be asked to confirm the transaction via an html popup. Every read only rpc call the dapp wants to perform is redirected to an invisible iframe from the node's domain and for every transaction that the dapp wish to execute, an html popup is presented to the user to allow him/her to cancel or confirm the transaction. This allows the dapp to connect to the node's rpc api without being granted any kind of privileges. This allows users to safely interact with dapps running in their everyday web browser while their accounts are unlocked. In case the account is not unlocked, and the node has allowed the "personal" api via rpc,the html page also allow the user to enter their password to unlock the account for the scope of the transaction. -Motivation -========== +## Motivation + Currently, if a user navigates to a dapp running on a website using her/his everyday browser, the dapp will by default have no access to the rpc api for security reasons. The user will have to enable CORS for the website's domain in order for the dapp to work. Unfortunately if the user does so, the dapp will be able to send transactions from any unlocked account without the need for any user consent. In other words, not only does the user need to change the node's default setting, but the user is also forced to trust the dapp in order to use it. This is of course not acceptable and forces existing dapps to rely on the use of workarounds like: - if the transaction is a plain ether transfer, the user is asked to enter it in a dedicated trusted wallet like "Mist" - For more complex case, the user is asked to enter the transaction manually via the node command line interface. @@ -25,27 +25,27 @@ This proposal aims to provide a safe and user friendly alternative. Here are some screenshots of the provided implementation of that html popup: -Account unlocked : ------------------ +### Account unlocked + When the account is already unlocked, the user is presented with the following popup for every transaction that the dapp attempts to make: ![](../assets/eip-107/authorization.png) -Account locked and no "personal" api exposed via rpc: ------------------ +### Account locked and no "personal" api exposed via rpc: + When the account is locked, and the node does not provide access to account unlocking via its rpc interface, the following popup will be presented. This is not ideal since this requires the user to know how to unlock an account: ![](../assets/eip-107/authorization-locked.png) -Account locked but node exposing the "personal" api via rpc : ------------------ +### Account locked but node exposing the "personal" api via rpc : + A better option is to ask the user for their password, but this is only possible if the node allows access to the "personal" api via rpc. In such case, the following dialog will be presented to the user so he/she can accept the transaction by providing the password required to unlock the account: ![](../assets/eip-107/authorization-password.png) -Specification -============= +## Specification + In order for the mechanism to work, the node needs to serve an html file via http at the url \/authorization.html This file will then be used by the dapp in 2 different modes (invisible iframe and popup window). @@ -90,22 +90,22 @@ the error object cannot be a javascript Error object due to postMessage limitati ``` -Rationale -========= +## Rationale + The design for that proposal was chosen for its simplicity and security. A previous idea was to use an oauth-like protocol in order for the user to accept or deny a transaction request. It would have required deeper code change in the node and some geth contributors argues that such change did not fit into geth code base as it would have required dapp aware code. The current design, instead has a very simple implementation (self contained html file that can be shared across node's implementation) and its safeness is guarantess by browsers' cross domain policies. The use of iframe/ window was required to have both security and user friendliness. The invisible iframe allows the dapp to execute read only calls without the need for user input, and the window ensures user approval before making a call. While we could have made it without the window mode by making the iframe confirmation use the native browser ```window.confirm``` dialog, this would have prevented the use of a more elegant confirmation popup that the current design allows. It also happens to be that the ```window.confirm``` is not safe in some browsers, as it gives focus to the accept option and can be triggered automatically (https://bugs.chromium.org/p/chromium/issues/detail?id=260653). -Implementations -=============== +## Implementations + In order to implement this design, the following html file or an equivalent one needs to be served at the url \/authorization.html That's it. -``` +```html @@ -612,6 +612,7 @@ That's it. ``` + ## 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-1077.md b/EIPS/eip-1077.md index 3d37476ce9b424..1efe6a144ddf8b 100644 --- a/EIPS/eip-1077.md +++ b/EIPS/eip-1077.md @@ -3,7 +3,7 @@ eip: 1077 title: Gas relay for contract calls author: Alex Van de Sande , Ricardo Guilherme Schmidt (@3esmit) discussions-to: https://ethereum-magicians.org/t/erc1077-and-1078-the-magic-of-executable-signed-messages-to-login-and-do-actions/351 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-04 @@ -217,7 +217,7 @@ Is also interest of relayers to maintaining a private reputation of contracts th ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). ## References diff --git a/EIPS/eip-1078.md b/EIPS/eip-1078.md index 9c40dd81321361..7990475fbcd98b 100644 --- a/EIPS/eip-1078.md +++ b/EIPS/eip-1078.md @@ -3,7 +3,7 @@ eip: 1078 title: Universal login / signup using ENS subdomains author: Alex Van de Sande discussions-to: https://ethereum-magicians.org/t/erc1077-and-1078-the-magic-of-executable-signed-messages-to-login-and-do-actions/351 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-04 @@ -118,4 +118,4 @@ This scheme would allow much more lighter apps, that don't require to hold ether * [Universal Logins talk at UX Unconf, Toronto](https://www.youtube.com/watch?v=qF2lhJzngto) ## 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-1080.md b/EIPS/eip-1080.md index a60a52c976bd90..d704fc840655ea 100644 --- a/EIPS/eip-1080.md +++ b/EIPS/eip-1080.md @@ -3,7 +3,7 @@ eip: 1080 title: Recoverable Token author: Bradley Leatherwood discussions-to: https://ethereum-magicians.org/t/erc-1080-recoverabletoken-standard/364 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-02 @@ -181,4 +181,4 @@ event AccountFrozen(address indexed reported) Pending. ## 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-1081.md b/EIPS/eip-1081.md index 754966e2a5d404..f73b2c7011410b 100644 --- a/EIPS/eip-1081.md +++ b/EIPS/eip-1081.md @@ -3,7 +3,7 @@ eip: 1081 title: Standard Bounties author: Mark Beylin , Kevin Owocki , Ricardo Guilherme Schmidt (@3esmit) discussions-to: https://gitter.im/bounties-network/Lobby -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-14 @@ -118,4 +118,4 @@ A reference implementation can be found here: https://github.com/Bounties-Networ **Although this code has been tested, it has not yet been audited or bug-bountied, so we cannot make any assertions about it's correctness, nor can we presently encourage it's use to hold funds on the Ethereum mainnet.** ## 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-1087.md b/EIPS/eip-1087.md index fcf5cca50fd93d..5df422325d5d5c 100644 --- a/EIPS/eip-1087.md +++ b/EIPS/eip-1087.md @@ -3,7 +3,7 @@ eip: 1087 title: Net gas metering for SSTORE operations author: Nick Johnson (@arachnid) discussions-to: https://ethereum-magicians.org/t/eip-net-storage-gas-metering-for-the-evm/383 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-05-17 @@ -78,4 +78,4 @@ No contract should see an increase in gas cost for this change, and many will se TBD ## 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-1102.md b/EIPS/eip-1102.md index cf26f0006dfa3a..6e7e743c164f0a 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -3,7 +3,7 @@ eip: 1102 title: Opt-in account exposure author: Paul Bouchon , Erik Marks (@rekmarks) discussions-to: https://ethereum-magicians.org/t/eip-1102-opt-in-provider-access/414 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2018-05-04 @@ -135,4 +135,4 @@ The MetaMask team [has implemented](https://github.com/MetaMask/metamask-extensi ## 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-1108.md b/EIPS/eip-1108.md index a1feaa7d00db0d..85bc480489800f 100644 --- a/EIPS/eip-1108.md +++ b/EIPS/eip-1108.md @@ -136,4 +136,4 @@ Both the Parity and Geth clients have already implemented cryptographic librarie @vbuterin independently proposed a similar reduction after this EIP was originally created, with similar rationale, as [ethereum/EIPs#1187](https://github.com/ethereum/EIPs/issues/1187). ## 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-1109.md b/EIPS/eip-1109.md index d666a5f2090641..f9df787b1e56fa 100644 --- a/EIPS/eip-1109.md +++ b/EIPS/eip-1109.md @@ -3,7 +3,7 @@ eip: 1109 title: PRECOMPILEDCALL opcode (Remove CALL costs for precompiled contracts) author: Jordi Baylina (@jbaylina) discussions-to: https://ethereum-magicians.org/t/eip-1109-remove-call-costs-for-precompiled-contracts/447 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-05-22 @@ -84,4 +84,4 @@ Old contracts that call precompiled smart contracts with the `CALL` method, will Not implemented yet. ## 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-1123.md b/EIPS/eip-1123.md index b0ea91705bde6a..06ca527c46d66b 100644 --- a/EIPS/eip-1123.md +++ b/EIPS/eip-1123.md @@ -7,7 +7,6 @@ status: Withdrawn type: Standards Track category: ERC created: 2018-06-01 -replaces: 190 --- This ERC has been abandoned in favor of the EthPM V3 smart contract packaging standard defined in [ERC-2678](./eip-2678.md) @@ -1879,5 +1878,4 @@ and the Ethereum community at large. 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-1129.md b/EIPS/eip-1129.md index bce6407fb963d7..ca6acdcd754975 100644 --- a/EIPS/eip-1129.md +++ b/EIPS/eip-1129.md @@ -3,7 +3,7 @@ eip: 1129 title: Standardised DAPP announcements author: Jan Turk (@ThunderDeliverer) discussions-to: https://ethereum-magicians.org/t/eip-sda-standardised-dapp-announcements/508?u=thunderdeliverer -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-05-31 @@ -141,4 +141,4 @@ The proposed version is deployed on Ropsten testnet all of the information can b ## Implementation ## 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-1132.md b/EIPS/eip-1132.md index d80c41c7332799..373a0dd4c3ffe3 100644 --- a/EIPS/eip-1132.md +++ b/EIPS/eip-1132.md @@ -4,7 +4,7 @@ title: Extending ERC20 with token locking capability author: nitika-goel type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2018-06-03 discussions-to: https://github.com/ethereum/EIPs/issues/1132 --- @@ -149,4 +149,4 @@ Test cases are available at [https://github.com/nitika-goel/lockable-token](http - [GovBlocks](https://govblocks.io) Project specific implementation available at https://github.com/somish/govblocks-protocol/blob/Locking/contracts/GBTStandardToken.sol ## 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-1153.md b/EIPS/eip-1153.md index e4668f5732ac7b..6f0be621c76a05 100644 --- a/EIPS/eip-1153.md +++ b/EIPS/eip-1153.md @@ -1,39 +1,48 @@ --- eip: 1153 title: Transient storage opcodes -author: Alexey Akhunov (@AlexeyAkhunov) +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: Draft +status: Last Call +last-call-deadline: 2022-12-08 type: Standards Track category: Core created: 2018-06-15 +requires: 2200, 3529 --- -## Simple Summary -Support for efficient transient storage in EVM. It is like regular storage (`SLOAD`/`SSTORE`), but with the lifetime limited to one Ethereum transaction. -Notable use case is efficient reentrancy lock. - ## Abstract -This proposal introduces transient storage, which behaves similar to storage, -but the updates will only persist within one Ethereum transaction. Transient storage is accessible to smart contracts via new opcodes: `TLOAD` and `TSTORE` (“T” stands for Transient). + +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:" + +``` +TLOAD (0x5c) +TSTORE (0x5d) +``` ## 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 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. -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”). Since 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. +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. + +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 unlocked by this EIP include: -1. Reentrancy lock -2. Passing error codes and messages from the execution frames up the execution stack -3. More generic libraries that use callbacks, for example generalised sorting with functions `Less` and `Swap` defined. -4. 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`. +Potential use cases enabled or improved by this EIP include: + +1. Reentrancy locks +2. On-chain computable CREATE2 addresses: constructor arguments are read from the factory contract instead of passed as part of init code hash +3. Single transaction [ERC-20](./eip-20.md) approvals, e.g. `#temporaryApprove(address spender, uint256 amount)` +4. Fee-on-transfer contracts: pay a fee to a token contract to unlock transfers for the duration of a transaction +5. "Till" pattern: allowing users to perform all actions as part of a callback, and checking the "till" is balanced at the end +6. Proxy call metadata: pass additional metadata to an implementation contract without using calldata, e.g. values of immutable proxy constructor arguments + +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` and `TSTORE`. + +Two new opcodes are added to EVM, `TLOAD` (`0x5c`) and `TSTORE` (`0x5d`). (Note that previous drafts of this EIP specified the values `0xb3` and `0xb4` for `TLOAD` and `TSTORE` respectively to avoid conflict with other EIPs. The conflict has since been removed.) They use the same arguments on stack as `SLOAD` (`0x54`) and `SSTORE` (`0x55`). @@ -43,33 +52,202 @@ They use the same arguments on stack as `SLOAD` (`0x54`) and `SSTORE` (`0x55`). Addressing is the same as `SLOAD` and `SSTORE`. i.e. each 32-byte address points to a unique 32-byte word. -Gas cost for both is 8 units of gas, regardless of values stored. +Gas cost for `TSTORE` is the same as a warm `SSTORE` of a dirty slot (i.e. original value is not new value and is not current value, currently 100 gas), and gas cost of `TLOAD` is the same as a hot `SLOAD` (value has been read before, currently 100 gas). Gas cost cannot be on par with memory access due to transient storage's interactions with reverts. -The effects of transient storage are discarded at the end of the transaction. +All values in transient storage are discarded at the end of the transaction. -Transient storage is private to the contract that owns it, in the same way as "regular" storage is. Only owning contract frames may access their transient storage. And when they do, all the frames access the same transient store, in the same way as "regular" storage, but unlike "memory". +Transient storage is private to the contract that owns it, in the same way as persistent storage. Only owning contract frames may access their transient storage. And when they do, all the frames access the same transient store, in the same way as persistent storage, but unlike memory. -When transient storage is used in the context of `DELEGATECALL` or `CALLCODE`, then the owning contract of the transient storage is the contract that issued `DELEGATECALL` or `CALLCODE` instruction (the caller). When transient storage is used in the context of `CALL` or `STATICCALL`, then the owning contract of the transient storage is the contract that is the target of the `CALL` or `STATICCALL` instruction (the callee). +When transient storage is used in the context of `DELEGATECALL` or `CALLCODE`, then the owning contract of the transient storage is the contract that issued `DELEGATECALL` or `CALLCODE` instruction (the caller) as with persistent storage. When transient storage is used in the context of `CALL` or `STATICCALL`, then the owning contract of the transient storage is the contract that is the target of the `CALL` or `STATICCALL` instruction (the callee). -Transient storage does not interact with reverts or invalid transactions, that means if a frame reverts, its effects on the transient storage remain until the end of the transaction. +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`, it will result in an exception instead of performing the modification. `TLOAD` is allowed within the context of a `STATICCALL`. ## Rationale -There is a proposal to alleviate the cost of inter-frame communication by reducing the cost of `SSTORE` when it modifies the same item multiple times within the same transaction (EIP-1087). -Relative cons of the transient storage: new opcodes; new code in the clients; new concept for the yellow paper (more to update); requires separation of concerns (persistence and inter-frame communication) when programming. +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. -Relative pros of the transient storage: cheaper to use; does not change the semantics of the existing operations; very simple gas accounting rules; +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. -## Backwards Compatibility -This EIP requires a hard fork to implement. +There is a known objection to the word-addressed storage-like interface of the `TSTORE` and `TLOAD` opcodes since transient storage is more akin to memory than storage in lifecycle. A byte-addressed memory-like interface is another option. The storage-like word-addressed interface is preferred due to the usefulness of mappings in combination with the transaction-scoped memory region. Often times, you will need to keep transient state with arbitrary keys, such as in the [ERC-20](./eip-20.md) temporary approval use case which uses a mapping of `(owner, spender)` to `allowance`. Mappings are difficult to implement using linear memory, and linear memory must also have dynamic gas costs. It is also more complicated to handle reverts with a linear memory. It is possible to have a memory-like interface while the underlying implementation uses a map to allow for storage in arbitrary offsets, but this would result in a third memory-storage hybrid interface that would require new code paths in compilers. + +Some think that a unique transaction identifier may obviate the need for transient storage as described in this EIP. This is a misconception: a transaction identifier used in combination with regular storage has all the same issues that motivate this EIP. The two features are orthogonal. + +Relative cons of this transient storage EIP: -Since this EIP does not change semantics of any existing opcodes, it does not pose risk of backwards incompatibility for existing deployed contracts. +- Does not address transient usages of storage in existing contracts +- New code in the clients +- New concept for the yellow paper (more to update) -## Test Cases -TBD +Relative pros of this transient storage EIP: -## Implementation -Most straightforward implementation would be a dictionary (map), similar to what exists for the ‘dirty’ storage, with the difference that it gets re-initialised at the start of each transaction, and does not get persisted. +- 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 +- Does not change the semantics of the existing operations +- No need to clear storage slots after usage +- Simpler gas accounting rules +- 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)` +- On revert all entries are reverted up to the last checkpoint, in reverse - `O(N)` where `N` = number of journal entries since last checkpoint + +```typescript +interface JournalEntry { + addr: string + key: string + prevValue: string +} + +type Journal = JournalEntry[] + +type Checkpoints = Journal['length'][] + +interface Current { + [addr: string]: { + [key: string]: string + } +} + +const EMPTY_VALUE = '0x0000000000000000000000000000000000000000000000000000000000000000' + +class TransientStorage { + /** + * The current state of transient storage. + */ + private current: Current = {} + /** + * All changes are written to the journal. On revert, we apply the changes in reverse to the last checkpoint. + */ + private journal: Journal = [] + /** + * The length of the journal at the time of each checkpoint + */ + private checkpoints: Checkpoints = [0] + + /** + * Returns the current value of the given contract address and key + * @param addr The address of the contract + * @param key The key of transient storage for the address + */ + public get(addr: string, key: string): string { + return this.current[addr]?.[key] ?? EMPTY_VALUE + } + + /** + * Set the current value in the map + * @param addr the address of the contract for which the key is being set + * @param key the slot to set for the address + * @param value the new value of the slot to set + */ + public put(addr: string, key: string, value: string) { + this.journal.push({ + addr, + key, + prevValue: this.get(addr, key), + }) + + this.current[addr] = this.current[addr] ?? {} + this.current[addr][key] = value; + } + + /** + * Commit all the changes since the last checkpoint + */ + public commit(): void { + if (this.checkpoints.length === 0) throw new Error('Nothing to commit') + this.checkpoints.pop() // The last checkpoint is discarded. + } + + /** + * To be called whenever entering a new context. If revert is called after checkpoint, all changes made after the latest checkpoint are reverted. + */ + public checkpoint(): void { + this.checkpoints.push(this.journal.length) + } + + /** + * Revert transient storage to the state from the last call to checkpoint + */ + public revert() { + const lastCheckpoint = this.checkpoints.pop() + if (typeof lastCheckpoint === 'undefined') throw new Error('Nothing to revert') + + for (let i = this.journal.length - 1; i >= lastCheckpoint; i--) { + const {addr, key, prevValue} = this.journal[i] + // we can assume it exists, since it was written in the journal + this.current[addr][key] = prevValue + } + this.journal.splice(lastCheckpoint, this.journal.length - lastCheckpoint) + } +} +``` + +The worst case time complexity can be produced by writing the maximum number of keys that can fit in one block, and then reverting. In this case, the client is required to do twice as many writes to apply all the entries in the journal. However, the same case applies to the state journaling implementation of existing clients, and cannot be DOS'd with the following code. + +```solidity +pragma solidity =0.8.13; + +contract TryDOS { + uint256 slot; + + constructor() { + slot = 1; + } + + function tryDOS() external { + uint256 i = 1; + while (gasleft() > 5000) { + unchecked { + slot = i++; + } + } + revert(); + } +} +``` + +## 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: + +``` +30M gas * 1 TSTORE / 100 gas * 32 bytes / 1 TSTORE * 1MB / 2^20 bytes ~= 9.15MB +``` + +Given the same amount of gas, the maximum amount of memory that can be allocated in a single context by `MSTORE` is ~3.75MB: + +``` +30M gas = 3x + x^2 / 512 => x = ~123,169 32-byte words +~123,169 words * 32 bytes/word * 1MB / 2^20 bytes = 3.75MB +``` + +However, if you only spend 1M gas allocating memory in each context, and make calls to reset the memory expansion cost, you can allocate ~700KB per million gas, for a total of ~20MB of memory allocated: + +``` +1M gas = 3x + x^2 / 512 => x = ~21,872 32-byte words +30M gas * ~21,872 words / 1M gas * 32 bytes/word * 1MB / 2^20 bytes = ~20MB +``` + +Smart contract developers should understand the lifetime of transient storage variables before use. Because transient storage is automatically cleared at the end of the transaction, smart contract developers may be tempted to avoid clearing slots as part of a call in order to save gas. However, this could prevent further interactions with the contract in the same transaction (e.g. in the case of re-entrancy locks) or cause other bugs, so smart contract developers should be careful to _only_ leave transient storage slots with nonzero values when those slots are intended to be used by future calls within the same transaction. Otherwise, these opcodes behave exactly the same as `SSTORE` and `SLOAD`, so all the usual security considerations apply especially in regard to reentrancy risk. + +Smart contract developers may also be tempted to use transient storage as an alternative to in-memory mappings. They should be aware that transient storage is not discarded when a call returns or reverts, as is memory, and should prefer memory for these use cases so as not to create unexpected behavior on reentrancy in the same transaction. The necessarily high cost of transient storage over memory should already discourage this usage pattern. Most usages of in-memory mappings can be better implemented with key-sorted lists of entries, and in-memory mappings are rarely required in smart contracts (i.e. the author knows of no known use cases in production). ## 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-1154.md b/EIPS/eip-1154.md index 159bab8f2cc1f8..417ae863f31b72 100644 --- a/EIPS/eip-1154.md +++ b/EIPS/eip-1154.md @@ -107,4 +107,4 @@ For data which mutates over time, the `id` field may be structured to specify "w * [Tidbit](https://github.com/levelkdev/tidbit) tracks this EIP. ## 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-1155.md b/EIPS/eip-1155.md index 5a509bfc7f1505..42ca3a7e5bcb25 100644 --- a/EIPS/eip-1155.md +++ b/EIPS/eip-1155.md @@ -1,7 +1,7 @@ --- eip: 1155 -title: ERC-1155 Multi Token Standard -author: Witek Radomski , Andrew Cooke , Philippe Castonguay , James Therien , Eric Binet , Ronan Sandford +title: Multi Token Standard +author: Witek Radomski , Andrew Cooke , Philippe Castonguay (@phabc) , James Therien , Eric Binet , Ronan Sandford (@wighawag) type: Standards Track category: ERC status: Final @@ -399,7 +399,7 @@ Example of such a URI: `https://token-cdn-domain/{id}.json` would be replaced wi #### Metadata Extensions -The optional `ERC1155Metadata_URI` extension can be identified with the (ERC-165 Standard Interface Detection)[./eip-165.md]. +The optional `ERC1155Metadata_URI` extension can be identified with the [ERC-165 Standard Interface Detection](./eip-165.md). If the optional `ERC1155Metadata_URI` extension is included: * The ERC-165 `supportsInterface` function MUST return the constant value `true` if `0x0e89341c` is passed through the `interfaceID` argument. @@ -669,9 +669,9 @@ uint128 indexNFT = 50; uint256 baseTokenFT = 54321 << 128; -balanceOf(baseTokenNFT, msg.sender); // Get balance of the base token for non-fungible set 12345 (this MAY be used to get balance of the user for all of this token set if the implementation wishes as a convenience). -balanceOf(baseTokenNFT + indexNFT, msg.sender); // Get balance of the token at index 50 for non-fungible set 12345 (should be 1 if user owns the individual non-fungible token or 0 if they do not). -balanceOf(baseTokenFT, msg.sender); // Get balance of the fungible base token 54321. +balanceOf(msg.sender, baseTokenNFT); // Get balance of the base token for non-fungible set 12345 (this MAY be used to get balance of the user for all of this token set if the implementation wishes as a convenience). +balanceOf(msg.sender, baseTokenNFT + indexNFT); // Get balance of the token at index 50 for non-fungible set 12345 (should be 1 if user owns the individual non-fungible token or 0 if they do not). +balanceOf(msg.sender, baseTokenFT); // Get balance of the fungible base token 54321. ``` Note that 128 is an arbitrary number, an implementation MAY choose how they would like this split to occur as suitable for their use case. An observer of the contract would simply see events showing balance transfers and mints happening and MAY track the balances using that information alone. @@ -707,4 +707,4 @@ Another simple way to represent non-fungibles is to allow a maximum value of 1 f - [ERC-1155: A new standard for The Sandbox](https://medium.com/sandbox-game/erc-1155-a-new-standard-for-the-sandbox-c95ee1e45072) ## 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-1167.md b/EIPS/eip-1167.md index b460151ea271cc..ebb2d0a3278c3d 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -112,4 +112,4 @@ Proxy deployment can be further optimized by installing the master contract at a This saves 4 bytes of proxy contract size (savings on each deployment) and has zero impact on runtime gas costs. ## 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-1175.md b/EIPS/eip-1175.md index 6da0dc669e80bd..133c3993ff53f6 100644 --- a/EIPS/eip-1175.md +++ b/EIPS/eip-1175.md @@ -3,7 +3,7 @@ eip: 1175 title: Wallet & shop standard for all tokens (erc20) author: Jet Lim (@Nitro888) discussions-to: https://github.com/ethereum/EIPs/issues/1182 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-06-21 @@ -14,7 +14,7 @@ requires: 20 ## Simple Summary Make wallets and shops created from certified contracts make erc20 tokens easy to use for commerce. -![wallet](/assets/eip-1175/wallet.png) +![wallet](../assets/eip-1175/wallet.png) ## Abstract The mutual trust between the wallet and the shop created by the authenticated contract allows you to pay for and purchase items at a simple process. @@ -25,7 +25,7 @@ New standards with improvements have been released, but the majority of tokens c And if you only reuse the store interface, you can also trade using `payUnsafe (address _shop, uint256 _item)`. ## Specification -![workflow](/assets/eip-1175/workflow.png) +![workflow](../assets/eip-1175/workflow.png) ## WalletCenter ### Methods #### createWallet @@ -530,4 +530,4 @@ contract WalletCenter { } ``` ## 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-1178.md b/EIPS/eip-1178.md index 0bcf6814fc20fd..06a740ce42c3c7 100644 --- a/EIPS/eip-1178.md +++ b/EIPS/eip-1178.md @@ -3,7 +3,7 @@ eip: 1178 title: Multi-class Token Standard author: Albert Chon discussions-to: https://github.com/ethereum/EIPs/issues/1179 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-06-22 @@ -152,5 +152,5 @@ Adoption of the MCFT standard proposal would not pose backwards compatibility is A sample implementation can be found [here](https://github.com/achon22/ERC-1178/blob/master/erc1178-sample.sol) ## 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-1185.md b/EIPS/eip-1185.md index 88b9672799e43e..77964d90c4e817 100644 --- a/EIPS/eip-1185.md +++ b/EIPS/eip-1185.md @@ -1,26 +1,29 @@ --- eip: 1185 title: Storage of DNS Records in ENS +description: A system to store and retrieve DNS records within the ENS contract. author: Jim McDonald (@mcdee) -status: Draft +discussions-to: https://ethereum-magicians.org/t/eip1185-dns-resolver-profile-for-ens/1589 +status: Review type: Standards Track category: ERC created: 2018-06-26 requires: 137 -discussions-to: https://ethereum-magicians.org/t/eip1185-dns-resolver-profile-for-ens/1589 --- ## Abstract + This EIP defines a resolver profile for ENS that provides features for storage and lookup of DNS records. This allows ENS to be used as a store of authoritative DNS information. ## Motivation + ENS is a highly desirable store for DNS information. It provides the distributed authority of DNS without conflating ownership and authoritative serving of information. With ENS, the owner of a domain has full control over their own DNS records. Also, ENS has the ability (through smart contracts) for a domain's subdomains to be irrevocably assigned to another entity. ## Specification -The resolver profile to support DNS on ENS follows the resolver specification as defined in #137. +The resolver profile to support DNS on ENS follows the resolver specification as defined in [ERC-137](./eip-137.md). -Traditionally, DNS is a zone-based system in that all of the records for a zone are kept together in the same file. This has the benefit of simplicity and atomicity of zone updates, but when transposed to ENS can result in significant gas costs for simple changes. As a result, the resolver works on the basis of record sets. A record set is uniquely defined by the tuple (domain, name, resource record type), for example the tuple (example.com, www.example.com, A) defines the record set of A records for the name www.example.com in the domain example.com. A record set can contain 0 or more values, for example if www.example.com has A records 1.2.3.4 and 5.6.7.8 then the aforementioned tuple will have two values. +Traditionally, DNS is a zone-based system in that all of the records for a zone are kept together in the same file. This has the benefit of simplicity and atomicity of zone updates, but when transposed to ENS can result in significant gas costs for simple changes. As a result, the resolver works on the basis of record sets. A record set is uniquely defined by the tuple `(domain, name, resource record type)`, for example the tuple `(example.com, www.example.com, A)` defines the record set of `A` records for the name `www.example.com` in the domain `example.com`. A record set can contain 0 or more values, for example if `www.example.com` has `A` records `1.2.3.4` and `5.6.7.8` then the aforementioned tuple will have two values. The choice to work at the level of record sets rather than zones means that this specification cannot completely support some features of DNS, such as zone transfers and DNSSEC. It would be possible to build a different resolver profile that works at the zone level, however it would be very expensive to carry out updates and so is not considered further for this EIP. @@ -31,9 +34,11 @@ The DNS resolver interface consists of two functions to set DNS information and `setDNSRecords()` sets, updates or clears 1 or more DNS records for a given node. It has function signature `0x0af179d7`. The arguments for the function are as follows: - - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in [ERC-137](./eip-137.md) - data: 1 or more DNS records in DNS wire format. Any record that is supplied without a value will be cleared. Note that all records in the same RRset should be contiguous within the data; if not then the later RRsets will overwrite the earlier one(s) + ### clearDNSZone(bytes32 node) `clearDNSZone()` removes all DNS records for the domain. It has function signature `0xad5780af`. @@ -41,16 +46,18 @@ The arguments for the function are as follows: Although it is possible to clear records individually with `setDNSRecords()` as described above this requires the owner to know all of the records that have been set (as the resolver has no methods to iterate over the records for a given domain), and might require multiple transactions. `clearDNSZone()` removes all zone information in a single operation. The arguments for the function is as follows: - - node: the namehash of the fully-qualified domain in ENS for which to clear the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to clear the records. Namehashes are defined in [ERC-137](./eip-137.md) ### dnsRecords(bytes32 node, bytes32 name, uint16 resource) view returns (bytes) `dnsRecords()` obtains the DNS records for a given node, name and resource. It has function signature `0x2461e851`. The arguments for the function are as follows: - - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in [ERC-137](./eip-137.md) - name: the `keccak256()` hash of the name of the record in DNS wire format. - - resource: the resource record ID. Resource record IDs are defined in https://en.wikipedia.org/wiki/List\_of\_DNS\_record\_types + - resource: the resource record ID. Resource record IDs are defined in RFC1035 and subsequent RFCs. The function returns all matching records in DNS wire format. If there are no records present the function will return nothing. @@ -58,23 +65,208 @@ The function returns all matching records in DNS wire format. If there are no r `hasDNSRecords()` reports if there are any records for the provided name in the domain. It has function signature `0x4cbf6ba4`. -This function is needed by DNS resolvers when working with wildcard resources as defined in https://tools.ietf.org/html/rfc4592 +This function is needed by DNS resolvers when working with wildcard resources as defined in RFC4592. The arguments for the function are as follows: - - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in [ERC-137](./eip-137.md) - name: the `keccak256()` hash of the name of the record in DNS wire format. The function returns `true` if there are any records for the provided node and name, otherwise `false`. -## Backwards compatibility +## Rationale + +DNS is a federated system of naming, and the higher-level entities control availability of everything beneath them (_e.g._ `.org` controls the availability of `ethereum.org`). A decentralized version of DNS would not have this constraint, and allow lookups directly for any domain with relevant records within ENS. + +## Backwards Compatibility + Not applicable. -## Implementation -The reference implementation of the DNS resolver is at https://github.com/wealdtech/wealdtech-solidity/blob/master/contracts/ens/DNSResolver.sol +## Reference Implementation + +The reference implementation of the DNS resolver is as follows: + +```solidity +pragma solidity ^0.7.4; +import "../ResolverBase.sol"; +import "@ensdomains/dnssec-oracle/contracts/RRUtils.sol"; + +abstract contract DNSResolver is ResolverBase { + using RRUtils for *; + using BytesUtils for bytes; + + bytes4 constant private DNS_RECORD_INTERFACE_ID = 0xa8fa5682; + bytes4 constant private DNS_ZONE_INTERFACE_ID = 0x5c47637c; -https://github.com/wealdtech/ethereal.git can be used to test the functionality of the resolver with the "dns set", "dns get" and "dns clear" commands. -## Test Cases -Test cases for the DNS resolver are at https://github.com/wealdtech/wealdtech-solidity/blob/master/test/ens/DNSResolver.js + // DNSRecordChanged is emitted whenever a given node/name/resource's RRSET is updated. + event DNSRecordChanged(bytes32 indexed node, bytes name, uint16 resource, bytes record); + // DNSRecordDeleted is emitted whenever a given node/name/resource's RRSET is deleted. + event DNSRecordDeleted(bytes32 indexed node, bytes name, uint16 resource); + // DNSZoneCleared is emitted whenever a given node's zone information is cleared. + event DNSZoneCleared(bytes32 indexed node); + + // DNSZonehashChanged is emitted whenever a given node's zone hash is updated. + event DNSZonehashChanged(bytes32 indexed node, bytes lastzonehash, bytes zonehash); + + // Zone hashes for the domains. + // A zone hash is an ERC-1577 content hash in binary format that should point to a + // resource containing a single zonefile. + // node => contenthash + mapping(bytes32=>bytes) private zonehashes; + + // Version the mapping for each zone. This allows users who have lost + // track of their entries to effectively delete an entire zone by bumping + // the version number. + // node => version + mapping(bytes32=>uint256) private versions; + + // The records themselves. Stored as binary RRSETs + // node => version => name => resource => data + mapping(bytes32=>mapping(uint256=>mapping(bytes32=>mapping(uint16=>bytes)))) private records; + + // Count of number of entries for a given name. Required for DNS resolvers + // when resolving wildcards. + // node => version => name => number of records + mapping(bytes32=>mapping(uint256=>mapping(bytes32=>uint16))) private nameEntriesCount; + + /** + * Set one or more DNS records. Records are supplied in wire-format. + * Records with the same node/name/resource must be supplied one after the + * other to ensure the data is updated correctly. For example, if the data + * was supplied: + * a.example.com IN A 1.2.3.4 + * a.example.com IN A 5.6.7.8 + * www.example.com IN CNAME a.example.com. + * then this would store the two A records for a.example.com correctly as a + * single RRSET, however if the data was supplied: + * a.example.com IN A 1.2.3.4 + * www.example.com IN CNAME a.example.com. + * a.example.com IN A 5.6.7.8 + * then this would store the first A record, the CNAME, then the second A + * record which would overwrite the first. + * + * @param node the namehash of the node for which to set the records + * @param data the DNS wire format records to set + */ + function setDNSRecords(bytes32 node, bytes calldata data) external authorised(node) { + uint16 resource = 0; + uint256 offset = 0; + bytes memory name; + bytes memory value; + bytes32 nameHash; + // Iterate over the data to add the resource records + for (RRUtils.RRIterator memory iter = data.iterateRRs(0); !iter.done(); iter.next()) { + if (resource == 0) { + resource = iter.dnstype; + name = iter.name(); + nameHash = keccak256(abi.encodePacked(name)); + value = bytes(iter.rdata()); + } else { + bytes memory newName = iter.name(); + if (resource != iter.dnstype || !name.equals(newName)) { + setDNSRRSet(node, name, resource, data, offset, iter.offset - offset, value.length == 0); + resource = iter.dnstype; + offset = iter.offset; + name = newName; + nameHash = keccak256(name); + value = bytes(iter.rdata()); + } + } + } + if (name.length > 0) { + setDNSRRSet(node, name, resource, data, offset, data.length - offset, value.length == 0); + } + } + + /** + * Obtain a DNS record. + * @param node the namehash of the node for which to fetch the record + * @param name the keccak-256 hash of the fully-qualified name for which to fetch the record + * @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types + * @return the DNS record in wire format if present, otherwise empty + */ + function dnsRecord(bytes32 node, bytes32 name, uint16 resource) public view returns (bytes memory) { + return records[node][versions[node]][name][resource]; + } + + /** + * Check if a given node has records. + * @param node the namehash of the node for which to check the records + * @param name the namehash of the node for which to check the records + */ + function hasDNSRecords(bytes32 node, bytes32 name) public view returns (bool) { + return (nameEntriesCount[node][versions[node]][name] != 0); + } + + /** + * Clear all information for a DNS zone. + * @param node the namehash of the node for which to clear the zone + */ + function clearDNSZone(bytes32 node) public authorised(node) { + versions[node]++; + emit DNSZoneCleared(node); + } + + /** + * setZonehash sets the hash for the zone. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param hash The zonehash to set + */ + function setZonehash(bytes32 node, bytes calldata hash) external authorised(node) { + bytes memory oldhash = zonehashes[node]; + zonehashes[node] = hash; + emit DNSZonehashChanged(node, oldhash, hash); + } + + /** + * zonehash obtains the hash for the zone. + * @param node The ENS node to query. + * @return The associated contenthash. + */ + function zonehash(bytes32 node) external view returns (bytes memory) { + return zonehashes[node]; + } + + function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { + return interfaceID == DNS_RECORD_INTERFACE_ID || + interfaceID == DNS_ZONE_INTERFACE_ID || + super.supportsInterface(interfaceID); + } + + function setDNSRRSet( + bytes32 node, + bytes memory name, + uint16 resource, + bytes memory data, + uint256 offset, + uint256 size, + bool deleteRecord) private + { + uint256 version = versions[node]; + bytes32 nameHash = keccak256(name); + bytes memory rrData = data.substring(offset, size); + if (deleteRecord) { + if (records[node][version][nameHash][resource].length != 0) { + nameEntriesCount[node][version][nameHash]--; + } + delete(records[node][version][nameHash][resource]); + emit DNSRecordDeleted(node, name, resource); + } else { + if (records[node][version][nameHash][resource].length == 0) { + nameEntriesCount[node][version][nameHash]++; + } + records[node][version][nameHash][resource] = rrData; + emit DNSRecordChanged(node, name, resource, rrData); + } + } +} +``` + +## Security Considerations + +Security of this solution would be dependent on security of the records within the ENS domain. This degenenrates to the security of the key(s) which have authority over that domain. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1186.md b/EIPS/eip-1186.md index 7443ea22ff54ef..b97cf5aa52dcd4 100644 --- a/EIPS/eip-1186.md +++ b/EIPS/eip-1186.md @@ -3,7 +3,7 @@ eip: 1186 title: RPC-Method to get Merkle Proofs - eth_getProof author: Simon Jentzsch , Christoph Jentzsch discussions-to: https://github.com/ethereum/EIPs/issues/1186 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2018-06-24 @@ -135,4 +135,4 @@ Since this only adds a new Method there are no issues with Backwards Compatibili TODO: Tests still need to be implemented, but the core function creating the proof already exists inside the clients and are well tested. ## 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-1191.md b/EIPS/eip-1191.md index c78b3f9c22fb15..18208804ed19cb 100644 --- a/EIPS/eip-1191.md +++ b/EIPS/eip-1191.md @@ -3,7 +3,7 @@ eip: 1191 title: Add chain id to mixed-case checksum address encoding author: Juliano Rizzo (@juli) status: Last Call -review-period-end: 2019-11-18 +last-call-deadline: 2019-11-18 type: Standards Track category: ERC created: 2018-03-18 @@ -27,9 +27,6 @@ Convert the address using the same algorithm defined by EIP-55 but if a register Benefits: - By means of a minimal code change on existing libraries, users are protected from losing funds by mixing addresses of different Ethereum based networks. -## Backwards Compatibility -This proposal is fully backward compatible. The checksum calculation is changed only for new networks that choose to adopt this EIP and add their chain numbers to the Adoption Table included in this document. - ## Implementation ```python #!/usr/bin/python3 @@ -100,9 +97,9 @@ for chainid, cases in test_cases.items(): for addr in cases: assert ( addr == eth_checksum_encode(addr,chainid) ) ``` -## Adoption +## Usage -### Adoption Table +### Usage Table | Network | Chain id | Supports this EIP | |-|-|-| @@ -110,8 +107,7 @@ for chainid, cases in test_cases.items(): | RSK Testnet | 31 | Yes | ### Implementation Table - -| Project | Adopted this EIP | Implementation | +| Project | EIP Usage | Implementation | |-|-|-| | MyCrypto | Yes | [JavaScript](https://github.com/MyCryptoHQ/MyCrypto/blob/develop/common/utils/formatters.ts#L126) | | MyEtherWallet | Yes | [JavaScript](https://github.com/MyEtherWallet/MyEtherWallet/blob/73c4a24f8f67c655749ac990c5b62efd92a2b11a/src/helpers/addressUtils.js#L22) | @@ -123,5 +119,5 @@ for chainid, cases in test_cases.items(): ## 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-1193.md b/EIPS/eip-1193.md index 890310376eb783..01cabe1365fb83 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -323,7 +323,7 @@ Instead, Providers should support RPC methods for explicitly requesting account ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). ## Appendix I: Consumer-Facing API Documentation diff --git a/EIPS/eip-1202.md b/EIPS/eip-1202.md index f979466c18a804..9ab0f843a315da 100644 --- a/EIPS/eip-1202.md +++ b/EIPS/eip-1202.md @@ -1,69 +1,119 @@ --- 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 +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 partitipation 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 + +1. Compliant contracts MUST implement the `IERC1202Core` below ```solidity -pragma solidity ^0.5.8; - - -/** - * - Multiple issue - * - Multiple selection - * - Ordered multiple result - **/ -contract ERC1202 { - - // Vote with an option. The caller needs to handle success or not - function vote(uint issueId, uint option) public returns (bool success); - function setStatus(uint issueId, bool isOpen) public returns (bool success); - - function issueDescription(uint issueId) public view returns (string desc); - function availableOptions(uint issueId) public view returns (uint[] options); - function optionDescription(uint issueId, uint option) public view returns (string desc); - function ballotOf(uint issueId, address addr) public view returns (uint option); - function weightOf(uint issueId, address addr) public view returns (uint weight); - function getStatus(uint issueId) public view returns (bool isOpen); - function weightedVoteCountsOf(uint issueId, uint option) public view returns (uint count); - function topOptions(uint issueId, uint limit) public view returns (uint[] topOptions_); - - event OnVote(uint issueId, address indexed _from, uint _value); - event OnStatusChange(uint issueId, bool newIsOpen); +interface IERC1202Core { + event VoteCast( + address indexed voter, + uint256 indexed proposalId, + uint8 support, + uint256 weight, + string reason, + bytes extraParams + ); + + function castVote( + uint256 proposalId, + uint8 support, + uint256 weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable returns; + + function castVoteFrom( + address from, + uint256 proposalId, + uint8 support, + uint256 weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable returns; + + function execute(uint256 proposalId, bytes memory extraParams) payable external; +} +``` + +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 +interface IERC1202MultiVote { + event MultiVoteCast( + address indexed voter, + uint256 indexed proposalId, + uint8[] support, + uint256[] weight, + string reason, + bytes extraParams + ); + + function castMultiVote( + uint256 proposalId, + uint8[] support, + uint256[] weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable; +} +``` + +### Getting Info: Voting Period, Eligibility, Weight + +```solidity +interface IERC1202Info { + function votingPeriodFor(uint256 proposalId) external view returns (uint256 startPointOfTime, uint256 endPointOfTime); + function eligibleVotingWeight(uint256 proposalId, address voter) external view returns (uint256); } ``` @@ -71,77 +121,40 @@ contract ERC1202 { 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: - - * It's possible to check someone's vote directly given an address. If implementor don't want to make it so easiy, 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. - - - **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. +### 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: -## Backward Compatibility -There is no backward compatibility issue we are aware of. +- 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. -## Simple Code Examples -### Example 1: Simplest Version: Single Issue Yes/No Question Per Smart Contract Address Per Non-Weighted Vote +- 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. - - [Source Code](https://github.com/xinbenlv/eip-1202-draft/blob/master/contracts/simple-version/SimplestVote1202.sol) - - [Deployment (Ropsten)](https://ropsten.etherscan.io/address/0x067e76ddd9c67f7ae606b18d881545512d4b680c#code) +### Weights -### Example 2: TokenVote with Simple Interface with Weight Assigned by Token and Pre-registered Snapshot of Token-Holders - - [Source Code](https://github.com/xinbenlv/eip-1202-draft/blob/master/contracts/simple-version/TokenVote1202.sol) - - [Deployment (Ropsten)](https://ropsten.etherscan.io/address/0x5bd007a224fe8820b19cc0bce8e241f4752ce74d#code) +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. -### Example 3: TokenVote with Advanced Interface - - [Source Code](https://github.com/xinbenlv/eip-1202-draft/blob/master/contracts/advanced-version/AdvancedTokenVote1202.sol) - - [Deployment (Ropsten)](https://ropsten.etherscan.io/address/0xfd8b3be5f9db4662d1c9269f948345b46e37fd26#code) +## 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`? - - 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. - -The third consideration is non-trivialness. 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. +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? -## Bibliography -### Related EIPs - - [EIP-20: ERC-20 Token Standard (a.k.a. ERC-20)](./eip-20.md) - - [EIP-165: Standard Interface Detection](./eip-165.md) - - [EIP-721: Non-Fungible Token Standard(a.k.a. ERC-721)](./eip-721.md) - - [EIP-735: ERC: Claim Holder](https://github.com/ethereum/EIPs/issues/735) - - [EIP-780: ERC: Ethereum Claims Registry](https://github.com/ethereum/EIPs/issues/780) - - [EIP-777: A New Advanced Token Standard](./eip-777.md) - - [EIP-897: ERC DelegateProxy](./eip-897.md) - - [EIP-1155: Crypto Item Standard](./eip-1155.md) - - [EIP-1178: Multi-class Token Standard](./eip-1178.md) - - [EIP-1167: Minimal Proxy Contract](./eip-1167.md) - - [EIP-1203: Multi-class Token Standard(ERC-20 Extension)](./eip-1203.md) - -### Worthnoting Projects - - [Ethereum DAO: How to build a DEMOCRACY on the blockchain](https://www.ethereum.org/dao) - - [Carbon Vote](http://carbonvote.com/) - - [Paper: A Smart Contract for Boardroom Voting with Maximum Voter Privacy](https://eprint.iacr.org/2017/110.pdf) - *Suggested by @aodhgan* - - [Private Voting for TCR](https://blog.enigma.co/private-voting-for-tcrs-with-enigma-b441b5d4fa7b) - - [Consensus/PLCR implementation](https://github.com/ConsenSys/PLCRVoting) - - [Aragon Voting App](https://wiki.aragon.org/dev/apps/voting/) - -## Acknowledgement -We appreciate Ansley, Andrew, Fred from Enigma, Fan and Raullen from IoTex for sharing us their use cases. we also appreciate the valuable input for designing an EIP from distinguished community members including: @frozeman, @fulldecent, @bingen, @aodhgan. - -## Work Directory -The drafting and revision of EIP-1202 is conducted at [GitHub/xinbenlv/eip-1202](https://github.com/xinbenlv/eip-1202) +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. ## 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-1203.md b/EIPS/eip-1203.md index 12ccfba22872df..dc971789dd83a7 100644 --- a/EIPS/eip-1203.md +++ b/EIPS/eip-1203.md @@ -3,7 +3,7 @@ eip: 1203 title: ERC-1203 Multi-Class Token Standard (ERC-20 Extension) author: Jeff Huang , Min Zu discussions-to: https://github.com/ethereum/EIPs/issues/1203 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-07-01 @@ -227,4 +227,4 @@ The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203 - ERC-1178 Multi-class Token Standard. ./eip-1178.md ## 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-1207.md b/EIPS/eip-1207.md index 5f769b7564a307..9b9fb0ca34b92c 100644 --- a/EIPS/eip-1207.md +++ b/EIPS/eip-1207.md @@ -3,7 +3,7 @@ eip: 1207 title: DAuth Access Delegation Standard author: Xiaoyu Wang (@wxygeek), Bicong Wang (@Wangbicong) discussions-to: https://github.com/ethereum/EIPs/issues/1207 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-07-10 @@ -166,4 +166,4 @@ Following is the DAuth Interface implementation. Furthermore, the example implem ## 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-1227.md b/EIPS/eip-1227.md index be566ead875d12..f731b13d77bba3 100644 --- a/EIPS/eip-1227.md +++ b/EIPS/eip-1227.md @@ -3,7 +3,7 @@ eip: 1227 title: Defuse Difficulty Bomb and Reset Block Reward author: SmeargleUsedFly (@SmeargleUsedFly) discussions-to: https://github.com/ethereum/EIPs/issues/1227 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-07-18 @@ -61,4 +61,4 @@ Forthcoming. Forthcoming. ## 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-1234.md b/EIPS/eip-1234.md index c4363d36dbb59e..d689697918d03f 100644 --- a/EIPS/eip-1234.md +++ b/EIPS/eip-1234.md @@ -58,4 +58,4 @@ Test cases shall be created once the specification is to be accepted by the deve The implementation in it's logic does not differ from [EIP-649](./eip-649.md); an implementation for Parity-Ethereum is available in [parity-ethereum#9187](https://github.com/paritytech/parity-ethereum/pull/9187). ## 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-1240.md b/EIPS/eip-1240.md index f20570a91fb2b1..e9c787cc4866f7 100644 --- a/EIPS/eip-1240.md +++ b/EIPS/eip-1240.md @@ -37,4 +37,4 @@ Test cases shall be created once the specification is to be accepted by the deve The yellow paper implements this change in https://github.com/ethereum/yellowpaper/pull/710 ## 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-1261.md b/EIPS/eip-1261.md index d8224f77635652..e2bf12a7cd9cbe 100644 --- a/EIPS/eip-1261.md +++ b/EIPS/eip-1261.md @@ -4,7 +4,7 @@ title: Membership Verification Token (MVT) author: Chaitanya Potti (@chaitanyapotti), Partha Bhattacharya (@pb25193) type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2018-07-14 requires: 165, 173 discussions-to: https://github.com/ethereum/EIPs/issues/1261 @@ -387,4 +387,4 @@ Membership Verification Token ERC1261 -- a reference implementation ## 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 8241665759def7..39f9e4e057b925 100644 --- a/EIPS/eip-1271.md +++ b/EIPS/eip-1271.md @@ -1,27 +1,23 @@ --- eip: 1271 title: Standard Signature Validation Method for Contracts +description: Standard way to verify a signature when the account is a smart contract author: Francisco Giordano (@frangio), Matt Condon (@shrugs), Philippe Castonguay (@PhABC), Amir Bandeali (@abandeali1), Jorge Izquierdo (@izqui), Bertrand Masius (@catageek) discussions-to: https://github.com/ethereum/EIPs/issues/1271 -status: Draft +status: Final type: Standards Track category: ERC created: 2018-07-25 --- -## Simple Summary -Many blockchain based applications allow users to sign off-chain messages instead of directly requesting users to do an on-chain transaction. This is the case for decentralized exchanges with off-chain orderbooks like [0x](https://0xproject.com/) and [etherdelta](https://etherdelta.com/). These applications usually assume that the message will be signed by the same address that owns the assets. However, one can hold assets directly in their regular account (controlled by a private key) *or* in a smart contract that acts as a wallet (e.g. a multisig contract). The current design of many smart contracts prevent contract based accounts from interacting with them, since contracts do not possess private keys and therefore can not directly sign messages. The proposal here outlines a standard way for contracts to verify if a provided signature is valid when the account is a contract. - ## Abstract -Externally Owned Accounts (EOA) can sign messages with their associated private keys, but currently contracts cannot. This is a problem for many applications that implement signature based off-chain methods, since contracts can't easily interact with them as they do not possess a private key. Here, we propose a standard way for any contracts to verify whether a signature on a behalf of a given contract is valid. +Externally Owned Accounts (EOA) can sign messages with their associated private keys, but currently contracts cannot. We propose a standard way for any contracts to verify whether a signature on a behalf of a given contract is valid. This is possible via the implementation of a `isValidSignature(hash, signature)` function on the signing contract, which can be called to validate a signature. ## Motivation -In the future, it is likely that many users will hold their assets in a smart contract instead of holding them in their externally owned account directly since contracts can improve user experience significantly while providing extra security. This means that contracts using signature based functions should not assume that a given address can provide ECDSA signatures. Otherwise, identity based contracts and contracts holding assets may not be able to interact with functions requiring ECDSA signatures directly. - -Here, we use the term *smart account* to refer to any contract that acts as an account, which can include identity based methods (e.g. [ERC-725](./eip-725.md) & [ERC-1078](./eip-1078.md)), asset ownership (e.g. Multisigs, proxy contracts) and/or executable signed messages methods (e.g. [ERC-1077)](https://github.com/alexvandesande/EIPs/blob/ee2347027e94b93708939f2e448447d030ca2d76/EIPS/eip-1077.md). This terminology is important for the reader to better distinguish a contract that acts as an account (e.g. a multisig, wallet or [Gnosis Safe](https://github.com/gnosis/safe-contracts) contract) and a contract that does not act as an account but requires signatures. +There are and will be many contracts that want to utilize signed messages for validation of rights-to-move assets or other purposes. In order for these contracts to be able to support non Externally Owned Accounts (i.e., contract owners), we need a standard mechanism by which a contract can indicate whether a given signature is valid or not on its behalf. -One example of an application that requires addresses to provide signatures would be decentralized exchanges with off-chain orderbook, where buy/sell orders are signed messages (see [0x](https://0xproject.com/) and [etherdelta](https://etherdelta.com/) for examples). In these applications, EOAs sign orders, signaling their desire to buy/sell a given asset and giving explicit permissions to the exchange smart contracts to conclude a trade via an ECDSA signature. When it comes to contracts however, ECDSA signature is not possible since contracts do not possess a private key. In the first version of the 0x protocol, smart contracts could not generate buy/sell orders for this very reason, as the `maker` needed to both own the assets *and* sign the order via ECDSA method. This was revised in their protocol version 2 (see below). +One example of an application that requires signatures to be provided would be decentralized exchanges with off-chain orderbook, where buy/sell orders are signed messages. In these applications, EOAs sign orders, signaling their desire to buy/sell a given asset and giving explicit permissions to the exchange smart contracts to conclude a trade via a signature. When it comes to contracts however, regular signatures are not possible since contracts do not possess a private key, hence this proposal. ## Specification @@ -36,9 +32,9 @@ contract ERC1271 { bytes4 constant internal MAGICVALUE = 0x1626ba7e; /** - * @dev Should return whether the signature provided is valid for the provided data + * @dev Should return whether the signature provided is valid for the provided hash * @param _hash Hash of the data to be signed - * @param _signature Signature byte array associated with _data + * @param _signature Signature byte array associated with _hash * * MUST return the bytes4 magic value 0x1626ba7e when function passes. * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5) @@ -53,33 +49,114 @@ contract ERC1271 { } ``` -`isValidSignature` can call arbitrary methods to validate a given signature, which could be context dependent (e.g. time based or state based), EOA dependent (e.g. signers authorization level within smart wallet), signature scheme Dependent (e.g. ECDSA, multisig, BLS), etc. +`isValidSignature` can call arbitrary methods to validate a given signature, which could be context dependent (e.g. time based or state based), EOA dependent (e.g. signers authorization level within smart wallet), signature scheme Dependent (e.g. ECDSA, multisig, BLS), etc. +This function should be implemented by contracts which desire to sign messages (e.g. smart contract wallets, DAOs, multisignature wallets, etc.) Applications wanting to support contract signatures should call this method if the signer is a contract. ## Rationale +We believe the name of the proposed function to be appropriate considering that an *authorized* signers providing proper signatures for a given data would see their signature as "valid" by the signing contract. Hence, a signed action message is only valid when the signer is authorized to perform a given action on the behalf of a smart wallet. -Such a function is important because it allows *other contracts* to validate signed messages on the behalf of the smart wallet. This is necessary because not all signed messages will first pass by the smart wallet as in ERC-1077, since signatures can be requested by independent contracts. Action based signed messages do not require this method for external contracts since the action is `smart wallet A -> Contract C` (e.g. owner of smart wallet `A` wants to transfer tokens `T` to contract `C`), but when the action is in the opposite direction (`Contract A -> SmartAccount`) this external function is necessary (e.g. `contract A` requires smart wallet `A` to transfer tokens `T` when event `E` is triggered). +Two arguments are provided for simplicity of separating the hash signed from the signature. A bytes32 hash is used instead of the unhashed message for simplicity, since contracts could expect a certain hashing function that is not standard, such as with [EIP-712](./eip-712.md). -We believe the name of the proposed function to be appropriate considering that an *authorized* signers providing proper signatures for a given data would see their signature as "valid" by the smart wallet. Hence, an signed action message is only valid when the signer is authorized to perform a given action on the behalf of a smart wallet. +`isValidSignature()` should not be able to modify states in order to prevent `GasToken` minting or similar attack vectors. Again, this is to simplify the implementation surface of the function for better standardization and to allow off-chain contract queries. -Two arguments are provided for simplicity of separating the data from the signature, but both could be concatenated in a single byte array if community prefers this. +The specific return value is expected to be returned instead of a boolean in order to have stricter and simpler verification of a signature. -`isValidSignature()` should not be able to modify states in order to prevent `GasToken` minting or similar attack vectors. A gas limit being passed is being considered, but could prevent some interesting use cases like large multisignatures or more costly validation and cryptographic schemes. +## Backwards Compatibility +This EIP is backward compatible with previous work on signature validation since this method is specific to contract based signatures and not EOA signatures. +## Reference Implementation -## Backwards Compatibility +Example implementation of a signing contract: -This EIP is backward compatible with previous work on signature validation since this method is specific to contract based signatures and not EOA signatures. +```solidity + + /** + * @notice Verifies that the signer is the owner of the signing contract. + */ + function isValidSignature( + bytes32 _hash, + bytes calldata _signature + ) external override view returns (bytes4) { + // Validate signatures + if (recoverSigner(_hash, _signature) == owner) { + return 0x1626ba7e; + } else { + return 0xffffffff; + } + } + + /** + * @notice Recover the signer of hash, assuming it's an EOA account + * @dev Only for EthSign signatures + * @param _hash Hash of message that was signed + * @param _signature Signature encoded as (bytes32 r, bytes32 s, uint8 v) + */ + function recoverSigner( + bytes32 _hash, + bytes memory _signature + ) internal pure returns (address signer) { + require(_signature.length == 65, "SignatureValidator#recoverSigner: invalid signature length"); + + // Variables are not scoped in Solidity. + uint8 v = uint8(_signature[64]); + bytes32 r = _signature.readBytes32(0); + bytes32 s = _signature.readBytes32(32); + + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + // + // Source OpenZeppelin + // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol + + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + revert("SignatureValidator#recoverSigner: invalid signature 's' value"); + } + + if (v != 27 && v != 28) { + revert("SignatureValidator#recoverSigner: invalid signature 'v' value"); + } + + // Recover ECDSA signer + signer = ecrecover(_hash, v, r, s); + + // Prevent signer from being 0x0 + require( + signer != address(0x0), + "SignatureValidator#recoverSigner: INVALID_SIGNER" + ); + + return signer; + } +``` + +Example implementation of a contract calling the isValidSignature() function on an external signing contract ; + +```solidity + function callERC1271isValidSignature( + address _addr, + bytes32 _hash, + bytes calldata _signature + ) external view { + bytes4 result = IERC1271Wallet(_addr).isValidSignature(_hash, _signature); + require(result == 0x1626ba7e, "INVALID_SIGNATURE"); + } +``` -## Implementation +## Security Considerations +Since there are no gas-limit expected for calling the isValidSignature() function, it is possible that some implementation will consume a large amount of gas. It is therefore important to not hardcode an amount of gas sent when calling this method on an external contract as it could prevent the validation of certain signatures. -Existing implementations : +Note also that each contract implementing this method is responsible to ensure that the signature passed is indeed valid, otherwise catastrophic outcomes are to be expected. -* ERC725 [implemented in the ERC725Account](https://github.com/ERC725Alliance/ERC725/blob/master/implementations/contracts/ERC725/ERC725Account.sol#L73-L90). -* The 0x project [implemented this method](https://github.com/0xProject/0x-monorepo/blob/05b35c0fdcbca7980d4195e96ec791c1c2d13398/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol#L187) in their protocol version 2. -* Zeppelin is [in the process](https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1104) of implementing this method. ## 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-1276.md b/EIPS/eip-1276.md index d5bcc711b3a4c0..f13ba7e3bd2f70 100644 --- a/EIPS/eip-1276.md +++ b/EIPS/eip-1276.md @@ -5,7 +5,7 @@ author: EOS Classic (@eosclassicteam) discussions-to: https://ethereum-magicians.org/t/eip-1276-eliminate-difficulty-bomb-and-adjust-block-reward-on-constantinople-shift/908 type: Standards Track category: Core -status: Draft +status: Stagnant created: 2018-07-31 --- @@ -60,4 +60,4 @@ Test cases shall be created once the specification is to be accepted by the deve The implementation shall be created once the specification is to be accepted by the developers or implemented by the clients. ## 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-1283.md b/EIPS/eip-1283.md index e1ebfa6d43f9c0..cdc8d222b04013 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -332,4 +332,4 @@ We always start at state X. The first SSTORE can: ## 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-1285.md b/EIPS/eip-1285.md index 16b5a992176d52..7d6d20525b5360 100644 --- a/EIPS/eip-1285.md +++ b/EIPS/eip-1285.md @@ -3,7 +3,7 @@ eip: 1285 title: Increase Gcallstipend gas in the CALL opcode author: Ben Kaufman , Adam Levi discussions-to: https://ethereum-magicians.org/t/eip-1285-increase-gcallstipend-gas-in-the-call-opcode/941 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-08-01 @@ -39,4 +39,4 @@ This EIP requires a backwards incompatible change for the ``Gcallstipend`` gas p ## 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-1295.md b/EIPS/eip-1295.md index 33fb4a453c5097..4eb207482cdd5d 100644 --- a/EIPS/eip-1295.md +++ b/EIPS/eip-1295.md @@ -3,7 +3,7 @@ eip: 1295 title: Modify Ethereum PoW Incentive Structure and Delay Difficulty Bomb author: Brian Venturo (@atlanticcrypto) discussions-to: https://github.com/atlanticcrypto/Discussion/issues/1 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-08-05 @@ -86,4 +86,4 @@ Test cases shall be created once the specification is to be accepted by the deve Forthcoming. ## 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-1319.md b/EIPS/eip-1319.md index 94991c0a9c877c..92ab5560e002b7 100644 --- a/EIPS/eip-1319.md +++ b/EIPS/eip-1319.md @@ -4,7 +4,7 @@ title: Smart Contract Package Registry Interface author: Piper Merriam , Christopher Gewecke , g. nicholas d'andrea , Nick Gheorghita (@njgheorghita) type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2018-08-13 discussions-to: https://github.com/ethereum/EIPs/issues/1319 --- @@ -173,4 +173,4 @@ No existing standard exists for package registries. The package registry current A reference implementation of this proposal is in active development at the EthPM organization on GitHub [here](https://github.com/ethpm/escape-truffle). ## 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-1328.md b/EIPS/eip-1328.md index 63d61278f0fafd..ddc15279daa58c 100644 --- a/EIPS/eip-1328.md +++ b/EIPS/eip-1328.md @@ -1,21 +1,18 @@ --- eip: 1328 -title: WalletConnect Standard URI Format -author: ligi , Pedro Gomes +title: WalletConnect URI Format +description: Define URI format for initiating connections between applications and wallets +author: ligi (@ligi), Pedro Gomes (@pedrouid) +discussions-to: https://ethereum-magicians.org/t/wallet-connect-eip/850 +status: Review type: Standards Track category: ERC -status: Draft created: 2018-08-15 -discussions-to: https://ethereum-magicians.org/t/wallet-connect-eip/850 --- -## Simple Summary - -A standard to create WalletConnect URIs to initiate connections between applications and wallets. - ## Abstract -This standard defines how the data to connect some application and a wallet can be encoded with a URI. This URI can then be shown either as a QR code or for mobile to mobile as a link. +This standard defines how the data to connect some application and a wallet can be encoded with a URI. This URI can then be shown either as a QR code or as a link. ## Specification @@ -28,27 +25,48 @@ WalletConnect request URI with the following parameters: version = 1*DIGIT parameters = parameter *( "&" parameter ) parameter = key "=" value - key = "bridge" / "key" + key = STRING value = STRING ### Semantics -Required parameters are dependent on the Walletconnect protocol version which currently includes the `key`, hex string of symmetric key, and `bridge`, encoded url of the bridge used for establishing the connection. +Required parameters are dependent on the WalletConnect protocol version: + +For WalletConnect v1.0 protocol (`version`=`1`) the parameters are: + +- `key` - symmetric key used for encryption +- `bridge` - url of the bridge server for relaying messages + +For WalletConnect v2.0 protocol (`version`=`2`) the parameters are: + +- `symKey` - symmetric key used for encrypting messages over relay +- `methods` - jsonrpc methods supported for pairing topic +- `relay-protocol` - transport protocol for relaying messages +- `relay-data` - (optional) transport data for relaying messages + ### Example ``` +# 1.0 wc:8a5e5bdc-a0e4-4702-ba63-8f1a5655744f@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=41791102999c339c844880b23950704cc43aa840f3739e365323cda4dfa89e7a + +# 2.0 +wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303&methods=[wc_sessionPropose],[wc_authRequest,wc_authBatchRequest]" ``` ## Rationale -The need for this ERC stems from the discussion to move away from JSON format used in the alpha version of the WalletConnect protocol which makes for very inneficient parsing of the intent of the QR code, making it easier to create better QR code parsers APIs for Wallets to implement. Also by using a URI instead of a JSON inside the QR-Code the Android Intent system can be leveraged. +This proposal moves away from the JSON format used in the alpha version of the WalletConnect protocol because it suffered from very inefficient parsing of the intent of the QR code, thereby making it easier to create better QR code parsers APIs for wallets to implement. Also by using a URI instead of JSON inside the QR-Code the Android Intent system can be leveraged. + +## Backwards Compatibility + +Versioning is required as part of the syntax for this URI specification to allow the WalletConnect protocol to evolve and allow backwards-compatibility whenever a new version is introduced. -## References +## Security Considerations -1. WalletConnect Technical Specification, https://docs.walletconnect.org/tech-spec +URIs should be shared between user devices or applications and no sensitive data is shared within the URI that could compromise the communication or would allow control of the user's private keys. ## 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-1337.md b/EIPS/eip-1337.md index 5db84b8bcaeb6a..665756b47070da 100644 --- a/EIPS/eip-1337.md +++ b/EIPS/eip-1337.md @@ -4,7 +4,7 @@ title: Subscriptions on the blockchain author: Kevin Owocki , Andrew Redden , Scott Burke , Kevin Seagraves , Luka Kacil , Štefan Šimec , Piotr Kosiński (@kosecki123), ankit raj , John Griffin , Nathan Creswell discussions-to: https://ethereum-magicians.org/t/eip-1337-subscriptions-on-the-blockchain/4422 type: Standards Track -status: Draft +status: Stagnant category: ERC created: 2018-08-01 requires: 20, 165 @@ -244,4 +244,4 @@ TBD TBD ## 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-1344.md b/EIPS/eip-1344.md index f10106a0016d3d..02e46aebc80e6e 100644 --- a/EIPS/eip-1344.md +++ b/EIPS/eip-1344.md @@ -48,4 +48,4 @@ A reference implementation for the Trinity Python client is [here](https://githu An example implementation of a trustless chain ID oracle was implemented [here](https://github.com/fubuloubu/chain-id-oracle/blob/master/ChainIdOracle.vy). ## 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-1352.md b/EIPS/eip-1352.md index 11416bbad847a2..55c1a52a3ebd80 100644 --- a/EIPS/eip-1352.md +++ b/EIPS/eip-1352.md @@ -3,7 +3,7 @@ eip: 1352 title: Specify restricted address range for precompiles/system contracts author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-1352-specify-restricted-address-range-for-precompiles-system-contracts/1151 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-07-27 @@ -37,4 +37,4 @@ N/A N/A ## 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-1363.md b/EIPS/eip-1363.md index 6175a7bcce8e5c..d4984c37998c9f 100644 --- a/EIPS/eip-1363.md +++ b/EIPS/eip-1363.md @@ -1,6 +1,6 @@ --- eip: 1363 -title: ERC-1363 Payable Token +title: Payable Token author: Vittorio Minacori (@vittominacori) discussions-to: https://github.com/ethereum/eips/issues/1363 status: Final @@ -205,4 +205,4 @@ Changing an allowance with the `approveAndCall` methods brings the risk that som One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards ([EIP-20#issuecomment-263524729](https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)). ## 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-1380.md b/EIPS/eip-1380.md index f8476e1f4673b6..6be8374064b7b8 100644 --- a/EIPS/eip-1380.md +++ b/EIPS/eip-1380.md @@ -3,7 +3,7 @@ eip: 1380 title: Reduced gas cost for call to self author: Alex Beregszaszi (@axic), Jacques Wagener (@jacqueswww) discussions-to: https://ethereum-magicians.org/t/eip-1380-reduced-gas-cost-for-call-to-self/1242 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-08-31 @@ -54,4 +54,4 @@ TBA 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-1386.md b/EIPS/eip-1386.md index 481c0b18a207e1..a83217050eabf2 100644 --- a/EIPS/eip-1386.md +++ b/EIPS/eip-1386.md @@ -3,7 +3,7 @@ eip: 1386 title: Attestation management contract author: Weiwu Zhang , James Sangalli discussions-to: https://github.com/ethereum/EIPs/issues/1386 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-09-08 diff --git a/EIPS/eip-1387.md b/EIPS/eip-1387.md index 360ab3de2b59a2..1061ffec26dc8e 100644 --- a/EIPS/eip-1387.md +++ b/EIPS/eip-1387.md @@ -3,7 +3,7 @@ eip: 1387 title: Merkle Tree Attestations with Privacy enabled author: Weiwu Zhang , James Sangalli discussions-to: https://github.com/ethereum/EIPs/issues/1387 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-09-08 diff --git a/EIPS/eip-1388.md b/EIPS/eip-1388.md index 1dba3a254f2e1a..ed05a6407d55bc 100644 --- a/EIPS/eip-1388.md +++ b/EIPS/eip-1388.md @@ -3,7 +3,7 @@ eip: 1388 title: Attestation Issuers Management List author: Weiwu Zhang , James Sangalli discussions-to: https://github.com/ethereum/EIPs/issues/1388 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-09-08 diff --git a/EIPS/eip-140.md b/EIPS/eip-140.md index 8469cb072dfe44..e907550930fe6a 100644 --- a/EIPS/eip-140.md +++ b/EIPS/eip-140.md @@ -55,4 +55,4 @@ should: ## 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-141.md b/EIPS/eip-141.md index 2cc65835cd9301..429e38acef1e66 100644 --- a/EIPS/eip-141.md +++ b/EIPS/eip-141.md @@ -26,4 +26,4 @@ This instruction was never used and therefore has no effect on past contracts. ## 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-1417.md b/EIPS/eip-1417.md index aa9f51c36821d1..1bf50edd79b978 100644 --- a/EIPS/eip-1417.md +++ b/EIPS/eip-1417.md @@ -4,7 +4,7 @@ title: Poll Standard author: Chaitanya Potti (@chaitanyapotti), Partha Bhattacharya (@pb25193) type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2018-09-16 requires: 165, 1261 discussions-to: https://github.com/ethereum/EIPs/issues/1417 @@ -280,4 +280,4 @@ Voting Standard -- a reference implementation ## 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-1418.md b/EIPS/eip-1418.md index 398adaa3f87e09..90047bb4de5906 100644 --- a/EIPS/eip-1418.md +++ b/EIPS/eip-1418.md @@ -1,53 +1,48 @@ --- eip: 1418 title: Blockchain Storage Rent Payment +description: At each block, deduct value from every account based on the quantity of storage used by that account. author: William Entriken (@fulldecent) -discussions-to: https://github.com/ethereum/EIPs/issues/1418 +discussions-to: https://ethereum-magicians.org/t/eip-1418-storage-rent/10737 status: Draft type: Standards Track category: Core created: 2018-09-16 +requires: 1559 --- -# Simple Summary +## Abstract -At each block, deduct an amount of value from every account based on the quantity of storage used by that account. +At each block, deduct an amount of value ("rent") from every account based on the quantity of storage used by that account. -# Abstract +## Motivation -The most naive implementation would be to simply loop through every account on each block and deduct a certain fee. We show that a better implementation could achieve reasonable performance. Also we review practical considerations of switching to a fee-based rent system. - -In other words, `product=0; while(factor1--)product+= factor2;` is slow, but equivalently `product = factor1 * factor2` is fast. And we can reason about both at the same time. +Ethereum is a public utility and we are underpricing the long-term costs of storage. Storage cost can be approximately modeled as bytes × time. -# Motivation +## Specification -Ethereum is a public utility and we are underpricing the long-term costs of storage. Storage cost can be approximately modeled as bytes × time. +**Updated transaction type** -# Specification +A new transaction type is introduced. Whereas [EIP-1559](./eip-1559.md) introduced warm access for contract state, this new type introduces warm access for contract code. **New state variables (per account)** -* rent -- σ[a]_r -- an amount of value, in Wei -* rentLastPaid -- σ[a]_p -- a block number that is set when: - * Value is transferred into an account - * Code is set for an account (CREATE) - * An account's storage is updated (SSTORE) -* storageWords -- σ[a]_w -- number of words in storage -* rentEvictBlock -- σ[a]_e -- the block number when this account will be destructed - * Note: it is possible that a client could implement the Yellow Paper without storing this value explicitly. It can be calculated simply on demand. +* **σ[a]_rent** -- an amount of value, in Wei, this is a signed value +* **σ[a]_storageWords** -- number of words in storage **New constants** -* RENT_WORD_COST -- The rent cost, in Wei, paid for each word-block -* RENT_ACCOUNT_COST -- The rent cost, in Wei, paid for each account-block -* RENT_STIPEND -- The amount of rent, in Wei, given to accounts when touched +* **`RENT_WORD_COST`** -- The rent cost, in Wei, paid for each word-block +* **`RENT_ACCOUNT_COST`** -- The rent cost, in Wei, paid for each account-block +* **`FORK_BLOCK`** – When implementation starts **New opcodes** -* RENTBALANCE(address) -- G_BALANCE -- Similar to BALANCE -* SENDRENT(address, amount) -- G_BASE -- Convert value to rent and send to account - 1. σ[account]_rent += amount - 2. σ[msg.sender]_balance -= amount +* **`RENTBALANCE(address)`** -- G_BALANCE -- Similar to `BALANCE` + * This returns the logical `σ[a]_rent` value which is defined to reduce each block. It is possible for the implementation to calculate this value using the recommended implementation variables, rather than storing and updating `σ[a]_rent` every block for every account. +* **`SENDRENT(address, amount)`** -- G_BASE -- Convert value to rent and send to account + 1. `σ[account]_rent` += amount + 2. `σ[msg.sender]_balance` -= amount **Updated opcodes** @@ -55,64 +50,76 @@ A new subroutine, paying for rent, is established as such: ```pseudocode PAYRENT(account) - ASSERT(σ[account]_rentEviction >= NUMBER) // TODO: I'm not sure if should be > or >= blocks_to_pay = NUMBER - σ[account]_rentLastPaid - cost_per_block = RENT_ACCOUNT_COST + RENT_WORD_COST * ⌈∥σ[account]_code∥ / 32⌉ + RENT_WORD_COST * σ[a]_storageWords + cost_per_block = RENT_ACCOUNT_COST + RENT_WORD_COST * (⌈∥σ[account]_code∥ / 32⌉ + * σ[a]_storageWords) rent_to_pay = blocks_to_pay * cost_per_block σ[account]_rent -= rent_to_pay + if σ[account]_rent < 0 + σ[account]_value += σ[account]_rent + σ[account]_rent = 0 + end + if σ[account]_value < 0 + σ[account]_rent = σ[account]_value + σ[account]_value = 0 + end σ[account]_rentLastPaid = NUMBER σ[account]_rentEvictBlock = NUMBER + ⌊σ[account]_rent / cost_per_block⌋ END PAYRENT ``` -* SSTORE(account, key, value) +* **`SSTORE(account, key, value)`** * Perform PAYRENT(account) - * Set σ[account]_rent = MAX(σ[account]_rent, RENT_STIPEND) + * If `account` is evicted (i.e. `NUMBER` > `σ[account]_rentEvictBlock`) then transaction fails unless using the new transaction type and sufficient proofs are included to validate the old storage root and calculate the new root. * Do normal SSTORE operation - * If the old value was zero for this [account, key] and the new value is non-zero, then σ[account]_storageSize++ - * If the old value was non-zero for this [account, key] and the new value is zero, then σ[account]_storageSize-- -* CALL (and derivatives) - * If value > 0 then perform PAYRENT(account) + * If the old value was zero for this [account, key] and the new value is non-zero, then `σ[account]_storageWords++` + * If the old value was non-zero for this [account, key] and the new value is zero, then `σ[account]_storageWords--`, and if the result is negative then set to zero +* **`SLOAD(account, key)`** + * If `account` is evicted (i.e. `NUMBER` > `σ[account]_rentEvictBlock`) then transaction fails unless using the new transaction type and sufficient proofs are included to validate the existing storage root and the existing storage value. + * Do normal SLOAD operation. +* **`CALL (and derivatives)`** + * If the target block is evicted (i.e. `NUMBER` > `σ[account]_rentEvictBlock`) then transaction fails unless using the new transaction type and sufficient proof is included to validate the existing code. * Do normal CALL operation -* CREATE - * Set σ[account]_rent = MAX(σ[account]_rent, RENT_STIPEND) - * Set σ[account]_rentLastPaid = HEIGHT +* **`CREATE`** + * Set σ[account]_rentLastPaid = NUMBER * Do normal CREATE operation + * `σ[account]_storageWord = 0` * Note: it is possible there is a pre-existing rent balance here -**Updated substate** - -The substate tuple is defined as: +**New built-in contract** -> A ≡ (As, Al, At, Ar) +* `PAYRENT(address, amount)` -- Calls `PAYRENT` opcode + * This is a convenience for humans to send Ether from their accounts and turn it into rent. Note that simple accounts (CODESIZE == 0) cannot call arbitrary opcodes, they can only call CREATE or CALL. + * The gas cost of PAYRENT will be 10,000 or lower if possible. -This includes A_t, "the set of touched accounts, of which the empty ones are deleted at the end of a transaction". +**Calculating `σ[account]_storageWord` for existing accounts** -This definition is updated to: "the set of touched accounts, of which the empty ones or evicted ones (BLOCK >= σ[a]_rentEvictBlock) are deleted at the end of a transaction" +DRAFT... -// TODO: I'm not sure if that should be > or >= +It is not an acceptable upgrade if on the fork block it is necessary for only archive nodes to participate which know the full storage amount for each account. -**New built-in contract** +An acceptable upgrade will be if the required `σ[account]_storageWord` can be calculated (or estimated) incrementally based on new transaction activity. -* PAYRENT(address, amount) -- Calls PAYRENT opcode +DRAFT: I think it is possible to make such an acceptable upgrade using an unbiased estimator -*This is a convenience for humans to send Ether from their accounts and turn it into rent. Note that simple accounts (CODESIZE == 0) cannot call arbitrary opcodes, they can only call CREATE or CALL.* +* add one bit of storage per `SSTORE` for legacy accounts on the first access of a given key +* add log(n) bits for each trie level +* assume that storage keys are a random variable -The gas cost of PAYRENT will be 10,000. +To think more about... **No changes to current opcode gas costs.** -# Rationale +## Rationale **No call** -A contract will not know or react to the receipt of rent. This is okay. Workaround: if a contract really needed to know who provided rent payments then it could create a function in its ABI to attribute these payments. It is already possible to send payments to a contract without attribution by using SELFDESTRUCT. +A contract will not know or react to the receipt of rent. This is okay. Workaround: if a contract really needed to know who provided rent payments then it could create a function in its ABI to attribute these payments. It is already possible to send payments to a contract without attribution by using `SELFDESTRUCT`. Other blockchains like TRON allow to transfer value to a contract without performing a call. **Eviction responsibility / lazy evaluation** -The specification gives responsibility for eviction to the consensus clients. This is the most predictable behavior because it happens exactly when it should. Also there need not be any incentive mechanism (refund gas, bounty) for outside participants (off chain) to monitor accounts and request removal. +The specification gives responsibility for eviction to the consensus clients. This is the most predictable behavior because it happens exactly when it should. Also there need not be any incentive mechanism (refund gas, bounty) for outside participants (off-chain) to monitor accounts and request removal. -This adds a computational responsibility to the clients to track eviction dates. This is possible in efficient time (at double the memory) using a double-ended priority queue (one for addressing by account address, the other for addressing by eviction date). There may be other ways of implementing this with different time-memory guarantees. +It is possible that an arbitrary number of accounts will be evicted in one block. That doesn't matter. Client implementations do not need to track which accounts are evicted, consensus is achieved just by agreeing on the conditions under which an account would be evicted. **No converting rent to value** @@ -120,67 +127,69 @@ Ether converted to rent cannot be converted back. Anybody that works in accounti **Accounts pay rent** -Yes, they pay rent. It costs money to keep their balances so we charge them rent. +Yes, they pay rent. It costs resources to account for their balances so we charge them rent. -**You can lose all your money** +**Why do you need a separate rent account?** -Yes, if you do not pay rent for your account or contract then you lose it all. User education is required. +Because anybody/everybody can contribute to the rent account. If you depend on a contract, you should contribute to its rent. -Alternative: spend value (Ether balance) when rent is depleted - * Rename rentEvictBlock to rentUsingValueBlock - * Update eviction calculation to include RENT + VALUE. Also update CALL (and friends) operations to recalculate eviction date when value is transferred. This is the new rentEvictBlock. - * Update CALL (and friends), RENTBALANCE and SENDRENT operations. If HEIGHT >= rentUsingValueBlock then proceed as if rent started paying using value. +But the contract can spend all of its value. -This alternative is a good idea, if there is support I can include this part formally in the specification. The specification is a little complicated so I like the informal definition above until we have some consent around it. +By maintaining a separate rent and value balance, this allows people to contribute to the rent while being confident that this is allowing the contract to stay around. -Alternative2: do not have a separate rent account -- directly deduct rent from value - * Every time the state is updated (including receiving value) you get a rent subsidity - * Need to review invariants of existing contracts to see what problems and broken assumptions this will cause in real life +NOTE: cloning. With this EIP, it may become feasible to allow storage cloning. Yes really. Because the new clone will be paying rent. See other EIP, I think made by Augur team. -**Permanent removal** +### Economics & constants -All state about an account is destructed during eviction. The data cannot be recovered. That's the point. +An `SSTORE` executed in 2015 cost 20,000 gas and has survived about 6 million blocks. The gas price has been around 1 ~ 50 Gwei. So basically 4,000 Wei per block per word so far. Maybe storing an account is 10 times more intensive than storing a word. But actually `G_transaction` is 21,000 and `G_sstore` is 20,000 so these are similar and they can both create new accounts / words. -Hint to implementers: make sure this works: +How about: -1. Send value to a new account (gets stipend) -2. Pay rent to that account -3. Wait until after the rent expires (account is gone) -4. Send value to that account (gets stipend again) -5. Deploy a contract (CREATE) to that account (stipend gets topped off) +* `RENT_WORD_COST` -- 4,000 Wei +* `RENT_ACCOUNT_COST` -- 4,000 Wei +* `FORK_BLOCK` – when implementation starts -# Rationale -- economics & constants +The rent is priced in cold, hard Ether. It is not negotiated by clients, it is not dynamic. -An `SSTORE` executed in 2015 cost 20,000 gas and has survived about 6 million blocks. The gas price has been around 1 ~ 50 Gwei. So basically 4,000 Wei per block per word so far. Maybe storing an account is 10 times more intensive than storing a word. But actually G_transaction is 21,000 and G_sstore is 20,000 so these are similar and they can both create new accounts / words. +A future EIP may change this pricing to be dynamic. For example to notarize a block, notaries may be required to prove they have the current storage dataset (excluding evictions). Additionally, they may also prove they have the dataset plus evictions to earn an additional fee. The relative storage of the evicted accounts, and the other accounts versus the value of the additional fee may be used as a feedback mechanism to set a market price for storage. -How about: +FYI, there are about 15B words in the Ethereum Mainnet dataset and about 100M total Ether mined. This means if all Ether was spent on storage at current proposed prices it would be 400 terabyte-years of storage. I'm not sure if it is helpful to look at it that way. -* RENT_WORD_COST -- 4,000 Wei -* RENT_ACCOUNT_COST -- 4,000 Wei -* RENT_STIPEND -- 4,000 Wei * 360 days worth of blocks +## Backwards Compatibility -The rent is priced in cold, hard Ether. It is not negotiated by clients, it is not dynamic. It is linear. Why is this a good idea? Because right now Ethereum is a system with multiple free variables -- Ether/gas price, gas/opcodes costs, Ether/block reward. [Add some note here about reducing a system of equations...] So the end result is that we can peg one of the values and it will be okay. +EIP-1559 already introduces a mechanism for nodes to participate without recording the full network state and for clients to warm cache with storage data in their type 2 transactions. -By setting the RENT price in Ether and by having the existing gas prices set based on the floating rate, there is an implicit price of ~4 gwei set into the Yellow Paper. In other words, if in the future the price of gas goes to 1 Ether then people will be upset because they will say "I paid 20,000 gas for an SSTORE" but I only got 360 days of stipend. If I paid for the rent directly I would have gotten enough rent to last until the Sun explodes." I acknowledge this complaint and do not think it is sufficient to warrant dismissing this proposal. - -Q: There is a finite-ish amount of Ether and this proposal introduces a word-price in Ether, do math for me. A: The current size of Ethereum is about ~1 TB, maybe half of that is branch nodes. So that's like 15B words. There is about 100M Ether mined. The answer is that all the Ether can be spent on 400,000 terabyte-years of storage. I'm not sure if it is helpful to look at it that way. +Users will need to be educated. -# Backwards compatibility +Many smart contracts allow anybody to use an arbitrary amount of storage in them. This can limit the usefulness of deploying this proposal on an existing chain. -There is a 360-day transition period (related to the RENT_STIPEND). This requires a hard fork. On the block of the fork, every account is immediately funded with enough rent to pay for ~ 360 days' worth of their current storage requirements. The formal implementation is that this new rule is applied if any existing account has σ[account]_rentLastPaid = 0. Therefore this can be implemented by clients lazily or eagerly. +**Recommended implementation variables (per account)** -Preexisting accounts which increase their storage needs will evict sooner than 360 days. +* **σ[a]_rentLastPaid** -- a block number that is set when: + * Value is transferred into an account (`CREATE`, `CALL`, `SELFDESTRUCT`) + * Code is set for an account (`CREATE`) + * An account's storage is updated (`SSTORE`) + * This begins with a logical value of `FORK_BLOCK` for all accounts -Users will need to be educated. +* **σ[a]_rentEvictBlock** -- the block number when this account will be evicted -# Test Cases +**Storage note** -TO BE ADDED +For every account that is evicted, clients may choose to delete that storage from disk. A future EIP may make an incentive to keep this extra data for a fee. A future EIP may create a mechanism for clients to exchange information about these storage states. -# Implementation +## Security Considerations -TO BE ADDED +Many smart contracts allow anybody to use an arbitrary amount of storage in them. -# Copyright +## Copyright Copyright and related rights waived via CC0. + + diff --git a/EIPS/eip-1438.md b/EIPS/eip-1438.md index 3d13ac80e9ff5a..9572193f173eee 100644 --- a/EIPS/eip-1438.md +++ b/EIPS/eip-1438.md @@ -3,7 +3,7 @@ eip: 1438 title: dApp Components (avatar) & Universal Wallet author: Jet Lim (@Nitro888) discussions-to: https://ethresear.ch/t/avatar-system-and-universal-wallet-for-ethereum-address/3473 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-09-21 @@ -25,7 +25,7 @@ While many projects are under development in an open source way, they are simply #### The following avatar store, badge system, and universal wallet are kind of examples about component dApp. -![intro](/assets/eip-1438/intro.png) +![intro](../assets/eip-1438/intro.png) ## Specification ### 1. Avatar @@ -38,11 +38,11 @@ The avatar's information & assets are stored in the event log part of the block - Assets are SVG format. (compressed with gzip) - avatar information data is json (compressed with msgpack) -![avatar](/assets/eip-1438/avatar.png) +![avatar](../assets/eip-1438/avatar.png) ** The avatar assets from [Avataaars](https://github.com/fangpenlin/avataaars) developed by [Fang-Pen Lin](https://twitter.com/fangpenlin), the original avatar is designed by [Pablo Stanley](https://twitter.com/pablostanley). ### 2. Universal Wallet -![wallet](/assets/eip-1438/wallet.png) +![wallet](../assets/eip-1438/wallet.png) #### 2.1. ERC20 interface ``` js contract ERC20Interface { @@ -139,4 +139,4 @@ contract UniversalWallet is _Base { - https://github.com/Nitro888/dApp-Alliance ## 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-1444.md b/EIPS/eip-1444.md index 4977ed7de67e0a..9c8e0d93e39b5c 100644 --- a/EIPS/eip-1444.md +++ b/EIPS/eip-1444.md @@ -3,7 +3,7 @@ eip: 1444 title: Localized Messaging with Signal-to-Text author: Brooklyn Zelenka (@expede), Jennifer Cooper (@jenncoop) discussions-to: https://ethereum-magicians.org/t/eip-1444-localized-messaging-with-signal-to-text/ -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-09-23 @@ -19,7 +19,7 @@ An on-chain system for providing user feedback by converting machine-efficient c ## Motivation -There are many cases where an end user needs feedback or instruction from a smart contact. Directly exposing numeric codes does not make for good UX or DX. If Ethereum is to be a truly global system usable by experts and lay persons alike, systems to provide feedback on what happened during a transaction are needed in as many languages as possible. +There are many cases where an end user needs feedback or instruction from a smart contract. Directly exposing numeric codes does not make for good UX or DX. If Ethereum is to be a truly global system usable by experts and lay persons alike, systems to provide feedback on what happened during a transaction are needed in as many languages as possible. Returning a hard-coded string (typically in English) only serves a small segment of the global population. This standard proposes a method to allow users to create, register, share, and use a decentralized collection of translations, enabling richer messaging that is more culturally and linguistically diverse. @@ -319,4 +319,4 @@ contract LocalizationPreference { ``` ## 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-145.md b/EIPS/eip-145.md index 55ae9c39cebce8..f2bfed54a325fc 100644 --- a/EIPS/eip-145.md +++ b/EIPS/eip-145.md @@ -374,4 +374,4 @@ Filled Tests: ## 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-1450.md b/EIPS/eip-1450.md index c905b7a2d4f783..4e6e0daae175d7 100644 --- a/EIPS/eip-1450.md +++ b/EIPS/eip-1450.md @@ -3,7 +3,7 @@ eip: 1450 title: ERC-1450 A compatible security token for issuing and trading SEC-compliant securities author: John Shiple (@johnshiple), Howard Marks , David Zhang discussions-to: https://ethereum-magicians.org/t/erc-proposal-ldgrtoken-a-compatible-security-token-for-issuing-and-trading-sec-compliant-securities/1468 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-09-25 @@ -323,4 +323,4 @@ Test cases are available at [https://github.com/StartEngine/ldgr_smart_contracts A reference implementation is available at [https://github.com/StartEngine/ldgr_smart_contracts](https://github.com/StartEngine/ldgr_smart_contracts). ## Copyright Waiver -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-1459.md b/EIPS/eip-1459.md index e559fc2bd8110a..b59df0eddbd370 100644 --- a/EIPS/eip-1459.md +++ b/EIPS/eip-1459.md @@ -1,21 +1,22 @@ --- eip: 1459 title: Node Discovery via DNS -author: Felix Lange , Péter Szilágyi +description: Scheme for authenticated updateable Ethereum node lists via DNS. +author: Felix Lange (@fjl), Péter Szilágyi (@karalabe) +discussions-to: https://github.com/ethereum/devp2p/issues/50 +status: Draft type: Standards Track category: Networking -status: Draft created: 2018-09-26 requires: 778 -discussions-to: https://github.com/ethereum/devp2p/issues/50 --- -# Abstract +## Abstract This document describes a scheme for authenticated, updateable Ethereum node lists retrievable via DNS. -# Motivation +## Motivation Many Ethereum clients contain hard-coded bootstrap node lists. Updating those lists requires a software update. The current lists are small, giving the client @@ -30,7 +31,7 @@ can't join the DHT due to restrictive network policy. DNS-based node lists may also be useful to Ethereum peering providers because their customers can configure the client to use the provider's list. -# Specification +## Specification A 'node list' is a list of 'node records' [as defined by EIP-778](./eip-778.md) of arbitrary length. Lists @@ -41,7 +42,7 @@ in order to verify the list. To refer to a DNS node list, clients use a URL with 'enrtree' scheme. The URL contains the DNS name on which the list can be found as well as the public key that signed the list. The public key is contained in the username part of the -URL and is the base32 encoding of the compressed 32-byte binary public key. +URL and is the base32 encoding (RFC-4648) of the compressed 32-byte binary public key. Example: @@ -51,7 +52,7 @@ This URL refers to a node list at the DNS name 'nodes.example.org' and is signed by the public key `0x049f88229042fef9200246f49f94d9b77c4e954721442714e85850cb6d9e5daf2d880ea0e53cb3ac1a75f9923c2726a4f941f7d326781baa6380754a360de5c2b6` -## DNS Record Structure +### DNS Record Structure The nodes in a list are encoded as a merkle tree for distribution via the DNS protocol. Entries of the merkle tree are contained in DNS TXT records. The root @@ -65,7 +66,7 @@ where nodes and links subtrees. - `sequence-number` is the tree's update sequence number, a decimal integer. - `signature` is a 65-byte secp256k1 EC signature over the keccak256 hash of the - record content, excluding the `sig=` part, encoded as URL-safe base64. + record content, excluding the `sig=` part, encoded as URL-safe base64 (RFC-4648). Further TXT records on subdomains map hashes to one of three entry types. The subdomain name of any entry is the base32 encoding of the (abbreviated) @@ -89,17 +90,15 @@ packets. This limits the number of hashes that can be placed into an Example in zone file format: -```text -; name ttl class type content -@ 60 IN TXT enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA -C7HRFPF3BLGF3YR4DY5KX3SMBE 86900 IN TXT enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org -JWXYDBPXYWG6FX3GMDIBFA6CJ4 86900 IN TXT enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24 -2XS2367YHAXJFGLZHVAWLQD4ZY 86900 IN TXT enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA -H4FHT4B454P6UXFD7JCYQ5PWDY 86900 IN TXT enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI -MHTDO6TMUBRIA2XWG5LUDACK24 86900 IN TXT enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o -``` + ; name ttl class type content + @ 60 IN TXT enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA + C7HRFPF3BLGF3YR4DY5KX3SMBE 86900 IN TXT enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org + JWXYDBPXYWG6FX3GMDIBFA6CJ4 86900 IN TXT enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24 + 2XS2367YHAXJFGLZHVAWLQD4ZY 86900 IN TXT enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA + H4FHT4B454P6UXFD7JCYQ5PWDY 86900 IN TXT enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI + MHTDO6TMUBRIA2XWG5LUDACK24 86900 IN TXT enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o -## Client Protocol +### Client Protocol To find nodes at a given DNS name, say "mynodes.org": @@ -124,7 +123,7 @@ Client implementations should avoid downloading the entire tree at once during normal operation. It's much better to request entries via DNS when-needed, i.e. at the time when the client is looking for peers. -# Rationale +## Rationale ### Why DNS? @@ -162,12 +161,12 @@ enable client implementations to sync these trees independently. A client wanting to get as many nodes as possible will sync the link tree first and add all linked names to the sync horizon. -# References +## Security Considerations -1. The base64 and base32 encodings used to represent binary data are defined in - [RFC 4648](https://tools.ietf.org/html/rfc4648). No padding is used for base64 - and base32 data. +Discovery via DNS is less secure than via DHT, because it relies on a trusted +party to publish the records regularly. The actor could easily eclipse +bootstrapping nodes by only publishing node records that it controls. -# Copyright +## 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-1462.md b/EIPS/eip-1462.md index 6893c3878b889f..9bb902088f7839 100644 --- a/EIPS/eip-1462.md +++ b/EIPS/eip-1462.md @@ -3,7 +3,7 @@ eip: 1462 title: Base Security Token author: Maxim Kupriianov , Julian Svirsky discussions-to: https://ethereum-magicians.org/t/erc-1462-base-security-token/1501 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-10-01 @@ -114,4 +114,4 @@ This EIP is fully backwards compatible as its implementation extends the functio ## 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-1470.md b/EIPS/eip-1470.md index ee705c4685617c..2ac90314f46e85 100644 --- a/EIPS/eip-1470.md +++ b/EIPS/eip-1470.md @@ -3,7 +3,7 @@ eip: 1470 title: Smart Contract Weakness Classification (SWC) author: Gerhard Wagner (@thec00n) discussions-to: https://github.com/ethereum/EIPs/issues/1469 -status: Draft +status: Stagnant type: Informational created: 2018-09-18 --- @@ -101,4 +101,4 @@ properties: The Smart Contract Weakness Classification registry located in this [GitHub repository](https://github.com/SmartContractSecurity/SWC-registry) uses the SWC scheme proposed in this EIP. A GitHub Pages rendered version is also available [here](https://smartcontractsecurity.github.io/SWC-registry/). ## 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-1474.md b/EIPS/eip-1474.md index 82bbce978b7ef5..b3834dc8c8d5b4 100644 --- a/EIPS/eip-1474.md +++ b/EIPS/eip-1474.md @@ -3,7 +3,7 @@ eip: 1474 title: Remote procedure call specification author: Paul Bouchon , Erik Marks (@rekmarks) discussions-to: https://ethereum-magicians.org/t/eip-remote-procedure-call-specification/1537 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2018-10-02 @@ -2251,4 +2251,4 @@ The current generation of Ethereum clients includes several implementations that ## 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-1482.md b/EIPS/eip-1482.md index 9f25ec464320ab..90b8bfa20d201d 100644 --- a/EIPS/eip-1482.md +++ b/EIPS/eip-1482.md @@ -3,7 +3,7 @@ eip: 1482 title: Define a maximum block timestamp drift author: Maurelian (@Maurelian) discussions-to: https://ethereum-magicians.org/t/define-a-maximum-block-timestamp-drift/1556 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-10-09 @@ -55,4 +55,4 @@ These would be important to have. _The implementations must be completed before any EIP is given status "Final", but it need not be completed before the EIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details. _ ## 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-1484.md b/EIPS/eip-1484.md index d5777e8ca8dec4..396602186bb305 100644 --- a/EIPS/eip-1484.md +++ b/EIPS/eip-1484.md @@ -3,7 +3,7 @@ eip: 1484 title: Digital Identity Aggregator author: Anurag Angara , Andy Chorlian , Shane Hampton , Noah Zinsmeister discussions-to: https://github.com/ethereum/EIPs/issues/1495 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-10-12 @@ -541,4 +541,4 @@ interface IdentityRegistryInterface { - [ERC-1056 Identities](./eip-1056.md) ## 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-1485.md b/EIPS/eip-1485.md index bbddd55bb5abfc..724623bcb9402d 100644 --- a/EIPS/eip-1485.md +++ b/EIPS/eip-1485.md @@ -1,200 +1,200 @@ ---- -eip: 1485 -title: TEthashV1 -author: trustfarm , trustfarm -discussions-to: https://ethereum-magicians.org/t/anti-eth-asic-mining-eip-1488-pr/1807 -status: Draft -type: Standards Track -category: Core -created: 2018-11-01 ---- - -## Simple Summary -This EIP modifies ethash in order to break ASIC miners specialized for the current ethash mining algorithm. - -## Abstract -This EIP pursue "obsolete current ASIC miners" by modifying PoW algorithm in a very low risk manner and update to latest hash algorithm from deprecated FNV Hash algorithms. - -Following TEthashV1 algorithm suggests safe transition of PoW algorithms and secure the FNV Algorithm in MIX Parts. - -## Motivation -Provide original Ethash proof of work verification with minimal set of changes by updating FNV0 algorithm - -## Specification - -#### 1. Reference materials on ETHASH FNV0 - -#### Where FNV Applied on ETHASH - -- In [ETHASH](https://github.com/ethereum/wiki/wiki/Ethash) , FNV Hash is used on - * 1) On data aggregation function, MIX parts. - - * Ethash Algorithm - - ``` - Header + Nonce - | - Keccak - | - **[MIX 0]** --> **[DAG Page]** - | | - Mixing <--| - ... - | - **[Mix 63]** - | - |-----> Mix64 [Process] ---> Mix Digest [32B] - ``` - - * FNV used in DAG Generation - and Mixing for random access or DAG Page. - -#### 2. Current applied Ethash FNV hash implementation is deprecated now. - -[FNV-0_hash (deprecated)](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-0_hash_(deprecated)) - - It is a simple way of hashing algorithm - - ``` - hash = 0 - for each byte_of_data to be hashed - hash = hash × FNV_prime - hash = hash XOR octet_of_data - return hash - ``` - - When analysed FNV-0 , there's very weak [avalanche effect](https://simple.wikipedia.org/wiki/Avalanche_effect), when hash input changes on 1~2bits. refer [FNV-Analysis reference section](https://github.com/tao-foundation/FNV-Analysis#how-to-test-and-analysis-reference-test-code) - - We need to research and apply newer FNV hash or short message hash algorithm. - -#### 3. FNV1A hash algorithm description - -Previous proposed algorithm based on FNV1 [EIP-1355](./eip-1355.md) - -There's a implementation that looks like "Missing Offset Bias" at **FNV1A**. - -Quotation of [original algorithm FNV1A](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash) -``` -use hash offset -FNV-1a hash -The FNV-1a hash differs from the FNV-1 hash by only the order in which the multiply and XOR is performed:[8][10] - - hash = FNV_offset_basis - for each byte_of_data to be hashed - hash = hash XOR byte_of_data - hash = hash × FNV_prime - return hash -``` - -FNV_offset_basis and computation order change of xor and multiplication Makes one more xor and multiply computation, but more secure hash effects than FNV0. -and make dispersion boundary condition (0, even number, ..) by using of Prime Number. - -#### 4. Real Implementation for FNV1A - -Consider real computation resources, in TEthashV1 uses hash byte_of_data to 4bytes aligned data. - -In TETHashV1, Adapts fully follow the FNV1A implementation. - - - TETHASHV1 FNV1A implementation - -Following are reference implementation of FNV1A adapted in TETHashV1. - -```cpp -// Reference Pseudo c/cpp implementation - -#define FNV_PRIME 0x01000193U -#define FNV_OFFSET_BASIS 0x811c9dc5U - -#define fnv1a(x, y) ((((FNV_OFFSET_BASIS^(x))*FNV_PRIME) ^ (y)) * FNV_PRIME) -#define fnv1a_reduce(a,b,c,d) (fnv1a(fnv1a(fnv1a(a, b), c), d)) -``` - -Another Byte aligned implementation of FNV1A , call to FNV1c - -```cpp -#define FNV_PRIME 0x01000193U -#define FNV_OFFSET_BASIS 0x811c9dc5U - -#define fnv1i(x) ( (( (( (( \ - ( ((FNV_OFFSET_BASIS)^( ((x)>>24)&0x000000ff )) * FNV_PRIME) \ - ^ (((x)>>16 )&0x000000ff)) * FNV_PRIME) \ - ^ (((x)>>8 )&0x000000ff)) * FNV_PRIME) \ - ^ (((x) )&0x000000ff)) * FNV_PRIME) \ - ) -#define fnv1c(x, y) ((fnv1i(x) ^ (y)) * FNV_PRIME) -``` - -#### 5. [FNV-Analysis](https://github.com/tao-foundation/FNV-Analysis) -FNV Mix Algorithm Analysis for TEthashV1 - -#### How to test and analysis reference test code. - -You can compile it with simple in terminal. -No additional library needs, - -```sh -gcc -o fnvtest fnvcltest.c -``` - -And You can execute it -``` -fnvtest - -F(00,00)::VEC(0, 0, ffffffff, 0):: FNV :00000000, DF=00000000(00) DS(00000000), FNV1 :00000000, DF=00000000(00) DS(00000000), FNV1a:117697cd, DF=117697cd(17) DS(117697cd), FNV1c:1210d00f, DF=127f8dbf(20) DS(11a1725f), F___RC=efe1b9c4, DF:efe1b9c4(19) , F1__RC=deb68dfe, DF:deb68dfe(22) , F1A_RC=99bad28b, DF:99bad28b(17) , F1C_RC=e29fa497, DF:e29fa497(18) -F(00,01)::VEC(0, 1, ffffffff, 0):: FNV :00000001, DF=00000001(01) DS(00000001), FNV1 :01000193, DF=01000193(06) DS(01000193), FNV1a:1076963a, DF=010001f7(09) DS(01000193), FNV1c:1110ce7c, DF=03001e73(11) DS(01000193), F___RC=fefffe6d, DF:111e47a9(14) , F1__RC=d9fd8597, DF:074b0869(12) , F1A_RC=72c287e0, DF:eb78556b(19) , F1C_RC=6b6991ef, DF:89f63578(17) -F(00,02)::VEC(0, 2, ffffffff, 0):: FNV :00000002, DF=00000003(02) DS(00000001), FNV1 :02000326, DF=030002b5(08) DS(01000193), FNV1a:0f7694a7, DF=1f00029d(11) DS(01000193), FNV1c:1410d335, DF=05001d49(09) DS(030004b9), F___RC=d8fd8404, DF:26027a69(13) , F1__RC=9b16d24c, DF:42eb57db(19) , F1A_RC=c17f0ecb, DF:b3bd892b(18) , F1C_RC=a5be8e78, DF:ced71f97(21) -F(00,03)::VEC(0, 3, ffffffff, 0):: FNV :00000003, DF=00000001(01) DS(00000001), FNV1 :030004b9, DF=0100079f(10) DS(01000193), FNV1a:0e769314, DF=010007b3(09) DS(01000193), FNV1c:1310d1a2, DF=07000297(09) DS(01000193), F___RC=b2fb099b, DF:6a068d9f(16) , F1__RC=5c301f01, DF:c726cd4d(17) , F1A_RC=94cf402e, DF:55b04ee5(16) , F1C_RC=aea1a025, DF:0b1f2e5d(17) -F(00,04)::VEC(0, 4, ffffffff, 0):: FNV :00000004, DF=00000007(03) DS(00000001), FNV1 :0400064c, DF=070002f5(10) DS(01000193), FNV1a:0d769181, DF=03000295(07) DS(01000193), FNV1c:0e10c9c3, DF=1d001861(09) DS(050007df), F___RC=8cf88f32, DF:3e0386a9(14) , F1__RC=1d496bb6, DF:417974b7(17) , F1A_RC=89401d59, DF:1d8f5d77(20) , F1C_RC=e4e96c7c, DF:4a48cc59(13) -F(00,05)::VEC(0, 5, ffffffff, 0):: FNV :00000005, DF=00000001(01) DS(00000001), FNV1 :050007df, DF=01000193(06) DS(01000193), FNV1a:0c768fee, DF=01001e6f(11) DS(01000193), FNV1c:0d10c830, DF=030001f3(09) DS(01000193), F___RC=66f614c9, DF:ea0e9bfb(20) , F1__RC=de62b86b, DF:c32bd3dd(19) , F1A_RC=346e222c, DF:bd2e3f75(21) , F1C_RC=502e5f82, DF:b4c733fe(20) -F(00,06)::VEC(0, 6, ffffffff, 0):: FNV :00000006, DF=00000003(02) DS(00000001), FNV1 :06000972, DF=03000ead(10) DS(01000193), FNV1a:0b768e5b, DF=070001b5(09) DS(01000193), FNV1c:1010cce9, DF=1d0004d9(10) DS(030004b9), F___RC=40f39a60, DF:26058ea9(13) , F1__RC=9f7c0520, DF:411ebd4b(16) , F1A_RC=b376a527, DF:8718870b(13) , F1C_RC=1241a9a4, DF:426ff626(17) -F(00,07)::VEC(0, 7, ffffffff, 0):: FNV :00000007, DF=00000001(01) DS(00000001), FNV1 :07000b05, DF=01000277(08) DS(01000193), FNV1a:0a768cc8, DF=01000293(06) DS(01000193), FNV1c:0f10cb56, DF=1f0007bf(15) DS(01000193), F___RC=1af11ff7, DF:5a028597(13) , F1__RC=609551d5, DF:ffe954f5(22) , F1A_RC=14293bea, DF:a75f9ecd(21) , F1C_RC=49d34bba, DF:5b92e21e(16) -F(00,08)::VEC(0, 8, ffffffff, 0):: FNV :00000008, DF=0000000f(04) DS(00000001), FNV1 :08000c98, DF=0f00079d(12) DS(01000193), FNV1a:09768b35, DF=030007fd(12) DS(01000193), FNV1c:1a10dca7, DF=150017f1(12) DS(0b001151), F___RC=f4eea58e, DF:ee1fba79(21) , F1__RC=21ae9e8a, DF:413bcf5f(19) , F1A_RC=eeebb7a5, DF:fac28c4f(17) , F1C_RC=7da04f47, DF:347304fd(16) -F(00,09)::VEC(0, 9, ffffffff, 0):: FNV :00000009, DF=00000001(01) DS(00000001), FNV1 :09000e2b, DF=010002b3(07) DS(01000193), FNV1a:087689a2, DF=01000297(07) DS(01000193), FNV1c:1910db14, DF=030007b3(10) DS(01000193), F___RC=ceec2b25, DF:3a028eab(14) , F1__RC=e2c7eb3f, DF:c36975b5(18) , F1A_RC=54e1aef8, DF:ba0a195d(15) , F1C_RC=d425e1af, DF:a985aee8(16) -F(00,0a)::VEC(0, a, ffffffff, 0):: FNV :0000000a, DF=00000003(02) DS(00000001), FNV1 :0a000fbe, DF=03000195(07) DS(01000193), FNV1a:0776880f, DF=0f0001ad(10) DS(01000193), FNV1c:1c10dfcd, DF=050004d9(08) DS(030004b9), F___RC=a8e9b0bc, DF:66059b99(15) , F1__RC=a3e137f4, DF:4126dccb(15) , F1A_RC=213fcd63, DF:75de639b(20) , F1C_RC=7e1d2751, DF:aa38c6fe(18) -F(00,0b)::VEC(0, b, ffffffff, 0):: FNV :0000000b, DF=00000001(01) DS(00000001), FNV1 :0b001151, DF=01001eef(12) DS(01000193), FNV1a:0676867c, DF=01000e73(09) DS(01000193), FNV1c:1b10de3a, DF=070001f7(11) DS(01000193), F___RC=82e73653, DF:2a0e86ef(16) , F1__RC=64fa84a9, DF:c71bb35d(19) , F1A_RC=5598ce46, DF:74a70325(14) , F1C_RC=6400c630, DF:1a1de161(14) -F(00,0c)::VEC(0, c, ffffffff, 0):: FNV :0000000c, DF=00000007(03) DS(00000001), FNV1 :0c0012e4, DF=070003b5(10) DS(01000193), FNV1a:057684e9, DF=03000295(07) DS(01000193), FNV1c:1610d65b, DF=0d000861(07) DS(050007df), F___RC=5ce4bbea, DF:de038db9(17) , F1__RC=2613d15e, DF:42e955f7(18) , F1A_RC=6a220ff1, DF:3fbac1b7(20) , F1C_RC=6e781da4, DF:0a78db94(15) -F(00,0d)::VEC(0, d, ffffffff, 0):: FNV :0000000d, DF=00000001(01) DS(00000001), FNV1 :0d001477, DF=01000693(07) DS(01000193), FNV1a:04768356, DF=010007bf(11) DS(01000193), FNV1c:1510d4c8, DF=03000293(07) DS(01000193), F___RC=36e24181, DF:6a06fa6b(17) , F1__RC=e72d1e13, DF:c13ecf4d(18) , F1A_RC=168d4944, DF:7caf46b5(19) , F1C_RC=65bbcfa1, DF:0bc3d205(13) -F(00,0e)::VEC(0, e, ffffffff, 0):: FNV :0000000e, DF=00000003(02) DS(00000001), FNV1 :0e00160a, DF=0300027d(09) DS(01000193), FNV1a:037681c3, DF=07000295(08) DS(01000193), FNV1c:1810d981, DF=0d000d49(09) DS(030004b9), F___RC=10dfc718, DF:263d8699(15) , F1__RC=a8466ac8, DF:4f6b74db(20) , F1A_RC=93e667bf, DF:856b2efb(19) , F1C_RC=76f80ee3, DF:1343c142(11) -F(00,0f)::VEC(0, f, ffffffff, 0):: FNV :0000000f, DF=00000001(01) DS(00000001), FNV1 :0f00179d, DF=01000197(07) DS(01000193), FNV1a:02768030, DF=010001f3(08) DS(01000193), FNV1c:1710d7ee, DF=0f000e6f(13) DS(01000193), F___RC=eadd4caf, DF:fa028bb7(17) , F1__RC=695fb77d, DF:c119ddb5(17) , F1A_RC=0f485682, DF:9cae313d(17) , F1C_RC=3667e8dc, DF:409fe63f(18) -F(00,10)::VEC(0, 10, ffffffff, 0):: FNV :00000010, DF=0000001f(05) DS(00000001), FNV1 :10001930, DF=1f000ead(13) DS(01000193), FNV1a:01767e9d, DF=0300fead(14) DS(01000193), FNV1c:0210b6df, DF=15006131(09) DS(1500210f), F___RC=c4dad246, DF:2e079ee9(17) , F1__RC=2a790432, DF:4326b34f(16) , F1A_RC=d10adebd, DF:de42883f(16) , F1C_RC=1ce48e12, DF:2a8366ce(15) -``` - -`F(00,01)` : is input x,y - -`VEC(0, 1, ffffffff, 0)` : is `fnv_reduce` input vector (a,b,c,d) - -`FNV :00000001, DF=00000001(01) DS(00000001)` : - * `FNV(00,01)` result is 00000001 , - * `DF` : is changed bitcounts, compared with previous outputs, in this case prev[00,00] current[00,01] input is 1bit changed, and output result 1bit changed. - * `DS` : is distances of previous result and current result , ABS(prev_fnvresult,current_fnvresult). - -** Basically, `DF` is higher is best on hash algorithm. - -`F___RC=fefffe6d, DF:111e47a9(14)` : `fnv_reduce = fnv(fnv(fnv(a,b),c),d) ` result is fefffe6d , and Different Bits counts are `14` bits. - - -## Rationale - -In case of ethash algorithm, it can't prevent ASIC forever. - -And, current ethash algorithm's FNV function is deprecated. - -So, It needs to be upgraded and it will make current ethash based ASICs obsolete. - -And current TETHASHV1 FNV1A implementation is based on most of ethash , which is verified for a long time. - -Another propose of big differencing the Ethash algorithm need to crypto analysis for a long times and need to GPU code optimization times. - -**Verification and Optimization timeline Examples** - -original ethminer (2015) -> claymore optimized miner (2016) [1year] - -genoil ethminer (2015) -> ethereum-mining/ethminer (2017) [2year] - -## Test Results:: - -Tethash miner has 2~3% of hashrate degrade on GPU, due to more core computation time. - -## Copyright - -This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/). +--- +eip: 1485 +title: TEthashV1 +author: trustfarm , trustfarm +discussions-to: https://ethereum-magicians.org/t/anti-eth-asic-mining-eip-1488-pr/1807 +status: Stagnant +type: Standards Track +category: Core +created: 2018-11-01 +--- + +## Simple Summary +This EIP modifies ethash in order to break ASIC miners specialized for the current ethash mining algorithm. + +## Abstract +This EIP pursue "obsolete current ASIC miners" by modifying PoW algorithm in a very low risk manner and update to latest hash algorithm from deprecated FNV Hash algorithms. + +Following TEthashV1 algorithm suggests safe transition of PoW algorithms and secure the FNV Algorithm in MIX Parts. + +## Motivation +Provide original Ethash proof of work verification with minimal set of changes by updating FNV0 algorithm + +## Specification + +#### 1. Reference materials on ETHASH FNV0 + +#### Where FNV Applied on ETHASH + +- In [ETHASH](https://github.com/ethereum/wiki/wiki/Ethash) , FNV Hash is used on + * 1) On data aggregation function, MIX parts. + + * Ethash Algorithm + + ``` + Header + Nonce + | + Keccak + | + **[MIX 0]** --> **[DAG Page]** + | | + Mixing <--| + ... + | + **[Mix 63]** + | + |-----> Mix64 [Process] ---> Mix Digest [32B] + ``` + + * FNV used in DAG Generation + and Mixing for random access or DAG Page. + +#### 2. Current applied Ethash FNV hash implementation is deprecated now. + +[FNV-0_hash (deprecated)](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-0_hash_(deprecated)) + + It is a simple way of hashing algorithm + + ``` + hash = 0 + for each byte_of_data to be hashed + hash = hash × FNV_prime + hash = hash XOR octet_of_data + return hash + ``` + + When analysed FNV-0 , there's very weak [avalanche effect](https://simple.wikipedia.org/wiki/Avalanche_effect), when hash input changes on 1~2bits. refer [FNV-Analysis reference section](https://github.com/tao-foundation/FNV-Analysis#how-to-test-and-analysis-reference-test-code) + + We need to research and apply newer FNV hash or short message hash algorithm. + +#### 3. FNV1A hash algorithm description + +Previous proposed algorithm based on FNV1 [EIP-1355](./eip-1355.md) + +There's a implementation that looks like "Missing Offset Bias" at **FNV1A**. + +Quotation of [original algorithm FNV1A](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash) +``` +use hash offset +FNV-1a hash +The FNV-1a hash differs from the FNV-1 hash by only the order in which the multiply and XOR is performed:[8][10] + + hash = FNV_offset_basis + for each byte_of_data to be hashed + hash = hash XOR byte_of_data + hash = hash × FNV_prime + return hash +``` + +FNV_offset_basis and computation order change of xor and multiplication Makes one more xor and multiply computation, but more secure hash effects than FNV0. +and make dispersion boundary condition (0, even number, ..) by using of Prime Number. + +#### 4. Real Implementation for FNV1A + +Consider real computation resources, in TEthashV1 uses hash byte_of_data to 4bytes aligned data. + +In TETHashV1, Adapts fully follow the FNV1A implementation. + + - TETHASHV1 FNV1A implementation + +Following are reference implementation of FNV1A adapted in TETHashV1. + +```cpp +// Reference Pseudo c/cpp implementation + +#define FNV_PRIME 0x01000193U +#define FNV_OFFSET_BASIS 0x811c9dc5U + +#define fnv1a(x, y) ((((FNV_OFFSET_BASIS^(x))*FNV_PRIME) ^ (y)) * FNV_PRIME) +#define fnv1a_reduce(a,b,c,d) (fnv1a(fnv1a(fnv1a(a, b), c), d)) +``` + +Another Byte aligned implementation of FNV1A , call to FNV1c + +```cpp +#define FNV_PRIME 0x01000193U +#define FNV_OFFSET_BASIS 0x811c9dc5U + +#define fnv1i(x) ( (( (( (( \ + ( ((FNV_OFFSET_BASIS)^( ((x)>>24)&0x000000ff )) * FNV_PRIME) \ + ^ (((x)>>16 )&0x000000ff)) * FNV_PRIME) \ + ^ (((x)>>8 )&0x000000ff)) * FNV_PRIME) \ + ^ (((x) )&0x000000ff)) * FNV_PRIME) \ + ) +#define fnv1c(x, y) ((fnv1i(x) ^ (y)) * FNV_PRIME) +``` + +#### 5. [FNV-Analysis](https://github.com/tao-foundation/FNV-Analysis) +FNV Mix Algorithm Analysis for TEthashV1 + +#### How to test and analysis reference test code. + +You can compile it with simple in terminal. +No additional library needs, + +```sh +gcc -o fnvtest fnvcltest.c +``` + +And You can execute it +``` +fnvtest + +F(00,00)::VEC(0, 0, ffffffff, 0):: FNV :00000000, DF=00000000(00) DS(00000000), FNV1 :00000000, DF=00000000(00) DS(00000000), FNV1a:117697cd, DF=117697cd(17) DS(117697cd), FNV1c:1210d00f, DF=127f8dbf(20) DS(11a1725f), F___RC=efe1b9c4, DF:efe1b9c4(19) , F1__RC=deb68dfe, DF:deb68dfe(22) , F1A_RC=99bad28b, DF:99bad28b(17) , F1C_RC=e29fa497, DF:e29fa497(18) +F(00,01)::VEC(0, 1, ffffffff, 0):: FNV :00000001, DF=00000001(01) DS(00000001), FNV1 :01000193, DF=01000193(06) DS(01000193), FNV1a:1076963a, DF=010001f7(09) DS(01000193), FNV1c:1110ce7c, DF=03001e73(11) DS(01000193), F___RC=fefffe6d, DF:111e47a9(14) , F1__RC=d9fd8597, DF:074b0869(12) , F1A_RC=72c287e0, DF:eb78556b(19) , F1C_RC=6b6991ef, DF:89f63578(17) +F(00,02)::VEC(0, 2, ffffffff, 0):: FNV :00000002, DF=00000003(02) DS(00000001), FNV1 :02000326, DF=030002b5(08) DS(01000193), FNV1a:0f7694a7, DF=1f00029d(11) DS(01000193), FNV1c:1410d335, DF=05001d49(09) DS(030004b9), F___RC=d8fd8404, DF:26027a69(13) , F1__RC=9b16d24c, DF:42eb57db(19) , F1A_RC=c17f0ecb, DF:b3bd892b(18) , F1C_RC=a5be8e78, DF:ced71f97(21) +F(00,03)::VEC(0, 3, ffffffff, 0):: FNV :00000003, DF=00000001(01) DS(00000001), FNV1 :030004b9, DF=0100079f(10) DS(01000193), FNV1a:0e769314, DF=010007b3(09) DS(01000193), FNV1c:1310d1a2, DF=07000297(09) DS(01000193), F___RC=b2fb099b, DF:6a068d9f(16) , F1__RC=5c301f01, DF:c726cd4d(17) , F1A_RC=94cf402e, DF:55b04ee5(16) , F1C_RC=aea1a025, DF:0b1f2e5d(17) +F(00,04)::VEC(0, 4, ffffffff, 0):: FNV :00000004, DF=00000007(03) DS(00000001), FNV1 :0400064c, DF=070002f5(10) DS(01000193), FNV1a:0d769181, DF=03000295(07) DS(01000193), FNV1c:0e10c9c3, DF=1d001861(09) DS(050007df), F___RC=8cf88f32, DF:3e0386a9(14) , F1__RC=1d496bb6, DF:417974b7(17) , F1A_RC=89401d59, DF:1d8f5d77(20) , F1C_RC=e4e96c7c, DF:4a48cc59(13) +F(00,05)::VEC(0, 5, ffffffff, 0):: FNV :00000005, DF=00000001(01) DS(00000001), FNV1 :050007df, DF=01000193(06) DS(01000193), FNV1a:0c768fee, DF=01001e6f(11) DS(01000193), FNV1c:0d10c830, DF=030001f3(09) DS(01000193), F___RC=66f614c9, DF:ea0e9bfb(20) , F1__RC=de62b86b, DF:c32bd3dd(19) , F1A_RC=346e222c, DF:bd2e3f75(21) , F1C_RC=502e5f82, DF:b4c733fe(20) +F(00,06)::VEC(0, 6, ffffffff, 0):: FNV :00000006, DF=00000003(02) DS(00000001), FNV1 :06000972, DF=03000ead(10) DS(01000193), FNV1a:0b768e5b, DF=070001b5(09) DS(01000193), FNV1c:1010cce9, DF=1d0004d9(10) DS(030004b9), F___RC=40f39a60, DF:26058ea9(13) , F1__RC=9f7c0520, DF:411ebd4b(16) , F1A_RC=b376a527, DF:8718870b(13) , F1C_RC=1241a9a4, DF:426ff626(17) +F(00,07)::VEC(0, 7, ffffffff, 0):: FNV :00000007, DF=00000001(01) DS(00000001), FNV1 :07000b05, DF=01000277(08) DS(01000193), FNV1a:0a768cc8, DF=01000293(06) DS(01000193), FNV1c:0f10cb56, DF=1f0007bf(15) DS(01000193), F___RC=1af11ff7, DF:5a028597(13) , F1__RC=609551d5, DF:ffe954f5(22) , F1A_RC=14293bea, DF:a75f9ecd(21) , F1C_RC=49d34bba, DF:5b92e21e(16) +F(00,08)::VEC(0, 8, ffffffff, 0):: FNV :00000008, DF=0000000f(04) DS(00000001), FNV1 :08000c98, DF=0f00079d(12) DS(01000193), FNV1a:09768b35, DF=030007fd(12) DS(01000193), FNV1c:1a10dca7, DF=150017f1(12) DS(0b001151), F___RC=f4eea58e, DF:ee1fba79(21) , F1__RC=21ae9e8a, DF:413bcf5f(19) , F1A_RC=eeebb7a5, DF:fac28c4f(17) , F1C_RC=7da04f47, DF:347304fd(16) +F(00,09)::VEC(0, 9, ffffffff, 0):: FNV :00000009, DF=00000001(01) DS(00000001), FNV1 :09000e2b, DF=010002b3(07) DS(01000193), FNV1a:087689a2, DF=01000297(07) DS(01000193), FNV1c:1910db14, DF=030007b3(10) DS(01000193), F___RC=ceec2b25, DF:3a028eab(14) , F1__RC=e2c7eb3f, DF:c36975b5(18) , F1A_RC=54e1aef8, DF:ba0a195d(15) , F1C_RC=d425e1af, DF:a985aee8(16) +F(00,0a)::VEC(0, a, ffffffff, 0):: FNV :0000000a, DF=00000003(02) DS(00000001), FNV1 :0a000fbe, DF=03000195(07) DS(01000193), FNV1a:0776880f, DF=0f0001ad(10) DS(01000193), FNV1c:1c10dfcd, DF=050004d9(08) DS(030004b9), F___RC=a8e9b0bc, DF:66059b99(15) , F1__RC=a3e137f4, DF:4126dccb(15) , F1A_RC=213fcd63, DF:75de639b(20) , F1C_RC=7e1d2751, DF:aa38c6fe(18) +F(00,0b)::VEC(0, b, ffffffff, 0):: FNV :0000000b, DF=00000001(01) DS(00000001), FNV1 :0b001151, DF=01001eef(12) DS(01000193), FNV1a:0676867c, DF=01000e73(09) DS(01000193), FNV1c:1b10de3a, DF=070001f7(11) DS(01000193), F___RC=82e73653, DF:2a0e86ef(16) , F1__RC=64fa84a9, DF:c71bb35d(19) , F1A_RC=5598ce46, DF:74a70325(14) , F1C_RC=6400c630, DF:1a1de161(14) +F(00,0c)::VEC(0, c, ffffffff, 0):: FNV :0000000c, DF=00000007(03) DS(00000001), FNV1 :0c0012e4, DF=070003b5(10) DS(01000193), FNV1a:057684e9, DF=03000295(07) DS(01000193), FNV1c:1610d65b, DF=0d000861(07) DS(050007df), F___RC=5ce4bbea, DF:de038db9(17) , F1__RC=2613d15e, DF:42e955f7(18) , F1A_RC=6a220ff1, DF:3fbac1b7(20) , F1C_RC=6e781da4, DF:0a78db94(15) +F(00,0d)::VEC(0, d, ffffffff, 0):: FNV :0000000d, DF=00000001(01) DS(00000001), FNV1 :0d001477, DF=01000693(07) DS(01000193), FNV1a:04768356, DF=010007bf(11) DS(01000193), FNV1c:1510d4c8, DF=03000293(07) DS(01000193), F___RC=36e24181, DF:6a06fa6b(17) , F1__RC=e72d1e13, DF:c13ecf4d(18) , F1A_RC=168d4944, DF:7caf46b5(19) , F1C_RC=65bbcfa1, DF:0bc3d205(13) +F(00,0e)::VEC(0, e, ffffffff, 0):: FNV :0000000e, DF=00000003(02) DS(00000001), FNV1 :0e00160a, DF=0300027d(09) DS(01000193), FNV1a:037681c3, DF=07000295(08) DS(01000193), FNV1c:1810d981, DF=0d000d49(09) DS(030004b9), F___RC=10dfc718, DF:263d8699(15) , F1__RC=a8466ac8, DF:4f6b74db(20) , F1A_RC=93e667bf, DF:856b2efb(19) , F1C_RC=76f80ee3, DF:1343c142(11) +F(00,0f)::VEC(0, f, ffffffff, 0):: FNV :0000000f, DF=00000001(01) DS(00000001), FNV1 :0f00179d, DF=01000197(07) DS(01000193), FNV1a:02768030, DF=010001f3(08) DS(01000193), FNV1c:1710d7ee, DF=0f000e6f(13) DS(01000193), F___RC=eadd4caf, DF:fa028bb7(17) , F1__RC=695fb77d, DF:c119ddb5(17) , F1A_RC=0f485682, DF:9cae313d(17) , F1C_RC=3667e8dc, DF:409fe63f(18) +F(00,10)::VEC(0, 10, ffffffff, 0):: FNV :00000010, DF=0000001f(05) DS(00000001), FNV1 :10001930, DF=1f000ead(13) DS(01000193), FNV1a:01767e9d, DF=0300fead(14) DS(01000193), FNV1c:0210b6df, DF=15006131(09) DS(1500210f), F___RC=c4dad246, DF:2e079ee9(17) , F1__RC=2a790432, DF:4326b34f(16) , F1A_RC=d10adebd, DF:de42883f(16) , F1C_RC=1ce48e12, DF:2a8366ce(15) +``` + +`F(00,01)` : is input x,y + +`VEC(0, 1, ffffffff, 0)` : is `fnv_reduce` input vector (a,b,c,d) + +`FNV :00000001, DF=00000001(01) DS(00000001)` : + * `FNV(00,01)` result is 00000001 , + * `DF` : is changed bitcounts, compared with previous outputs, in this case prev[00,00] current[00,01] input is 1bit changed, and output result 1bit changed. + * `DS` : is distances of previous result and current result , ABS(prev_fnvresult,current_fnvresult). + +** Basically, `DF` is higher is best on hash algorithm. + +`F___RC=fefffe6d, DF:111e47a9(14)` : `fnv_reduce = fnv(fnv(fnv(a,b),c),d) ` result is fefffe6d , and Different Bits counts are `14` bits. + + +## Rationale + +In case of ethash algorithm, it can't prevent ASIC forever. + +And, current ethash algorithm's FNV function is deprecated. + +So, It needs to be upgraded and it will make current ethash based ASICs obsolete. + +And current TETHASHV1 FNV1A implementation is based on most of ethash , which is verified for a long time. + +Another propose of big differencing the Ethash algorithm need to crypto analysis for a long times and need to GPU code optimization times. + +**Verification and Optimization timeline Examples** + +original ethminer (2015) -> claymore optimized miner (2016) [1year] + +genoil ethminer (2015) -> ethereum-mining/ethminer (2017) [2year] + +## Test Results:: + +Tethash miner has 2~3% of hashrate degrade on GPU, due to more core computation time. + +## Copyright + +This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/). diff --git a/EIPS/eip-1491.md b/EIPS/eip-1491.md index 2905d797018660..9007dcb0bee6a2 100644 --- a/EIPS/eip-1491.md +++ b/EIPS/eip-1491.md @@ -3,7 +3,7 @@ eip: 1491 title: Human Cost Accounting Standard (Like Gas but for humans) author: Iamnot Chris (@cohabo) discussions-to: https://github.com/freeworkculture/kazini/issues/11 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-10-12 @@ -525,4 +525,4 @@ interface IERC_HUCAP_TRACKUSERS_EXTENSION { [WIP] ## 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-1504.md b/EIPS/eip-1504.md index cb7eb3d3a6b11d..afed611194173d 100644 --- a/EIPS/eip-1504.md +++ b/EIPS/eip-1504.md @@ -1,9 +1,9 @@ --- eip: 1504 -title: ERC-1504 Upgradable Smart Contract +title: Upgradable Smart Contract author: Kaidong Wu , Chuqiao Ren , Ruthia He , Yun Ma , Xuanzhe Liu discussions-to: https://github.com/ethereum/EIPs/issues/1503 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-10-17 @@ -348,4 +348,4 @@ We have been very inclusive in this process and invite anyone with questions or ## 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-152.md b/EIPS/eip-152.md index 167f93c4d176c3..80aa7085c3dbdf 100644 --- a/EIPS/eip-152.md +++ b/EIPS/eip-152.md @@ -264,4 +264,4 @@ PrecompiledBlake2F/testVectors2bX_580 1 131 7.633 ## 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-1523.md b/EIPS/eip-1523.md index 3a08cab63f19cd..2469e83d69d4a9 100644 --- a/EIPS/eip-1523.md +++ b/EIPS/eip-1523.md @@ -3,7 +3,7 @@ eip: 1523 title: Standard for Insurance Policies as ERC-721 Non Fungible Tokens author: Christoph Mussenbrock (@christoph2806) discussions-to: https://github.com/ethereum/EIPs/issues/1523 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-10-10 @@ -73,36 +73,40 @@ In analogy to the “ERC721 Metadata JSON Schema”, the tokenURI **MUST** point "type": "string", "description": "Describes the asset to which this NFT represents", }, - "carrier": { - "type": "string", - "description": "Describes the carrier which takes the primary risk", - }, - "risk": { - "type": "string", - "description": "Describes the risk", - }, - "parameters": { - "type": "string", - "description": "Describes further parameters characterizing the risk", - }, - "status": { - "type": "string", - "description": "Defines the status of the policy, e.g. Applied, Underwritten, Claimed, Paid out, etc." - } + \[additional parameters according to the following table\] } } ``` +### Additional parameters for the metadata JSON Schema + +| Parameter | Type | Mandatory | Description | +| ------------- | ------------- | ----------| ---------------------------------------------------------------------------------- | +| carrier | string | yes | Describes the carrier which takes the primary risk | +| risk | string | yes | Describes the risk | +| status | string | yes | Describes the status of the policy, e.g. applied for, underwritten, expired | +| parameters | string | no | Describes further parameters characterizing the risk | +| terms | string | no | Describes legal terms & conditions which apply for this policy | +| premium | string | no | A string representation of the premium, **MAY** contain currency denominator | +| sum_insured | string | no | A string representation of the sum insured, **MAY** contain currency denominator | + +Parameters which are mandatory **MUST** be included in the metadata JSON. Other parameters **MAY** be included. However, the proposed optional parameters **SHOULD** be used for the intended purpose, so e.g. if the premium amount would be included in the metadata, the parameter name **SHOULD** be "premium". +All parameters **MAY** be plain text or **MAY** also be URIs pointing to resources which contain the respective information, and which **MAY** be protected by an authentication mechanism. + ## Rationale Insurance policies form an important class of financial assets, and it is natural to express those assets as a class of non-fungible tokens which adhere to the established ERC-721 standard. -We propose a standard for the accompanying metadata structures which are needed to uniquely define an insurance policy. +We propose a standard for the accompanying metadata structures which are needed to uniquely define an insurance policy. Standardization is key because we expect decentralized insurance to receive widespread adoption and it is crucial to establish a unified standard to enable composability and the creation of universal toolsets. +We therefore propose a standardized naming scheme for the different parameters describing an insurance policy. We propose three mandatory parameters which need to be included in every NFT and further parameters which **MAY** be used, and for which we only standardize the naming conventions. +### Mandatory parameters While policies can have a multitude of possible properties, it is common that policies are issued by some entity, which is basically the entity responsible for paying out claims. Second, an insurance policy is typically related to a specific risk. Some risks are unique, but there are cases where many policies share the same risk (e.g. all flight delay policies for the same flight). In general, the relation of policies to risks is a many-to-one relation with the special case of a one-to-one relation. -Third, most policies need more parameters to characterize the risk and other features, like premium, period etc. -Forth, a policy has a lifecycle of different statuses. +Third, a policy has a lifecycle of different statuses. Therefore the NFT We believe that those four properties are necessary to describe a policy. For many applications, those properties may be even sufficient. + +### Optional parameters +Most policies need more parameters to characterize the risk and other features, like premium, period etc. The naming conventions are listed in the above table. However, any implementation **MAY** chose to implement more properties. ### On-chain vs. off-chain metadata @@ -118,4 +122,4 @@ Therefore, we require that the ```tokenURI``` **MUST** point to a JSON with the ## Implementation ## 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-1538.md b/EIPS/eip-1538.md index 6eeb8735b470bc..afa991a7e7961d 100644 --- a/EIPS/eip-1538.md +++ b/EIPS/eip-1538.md @@ -465,4 +465,4 @@ A reference implementation of this standard is given in the [transparent-contrac ## 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-1559.md b/EIPS/eip-1559.md index 743a14514f9662..3a99f1e777a932 100644 --- a/EIPS/eip-1559.md +++ b/EIPS/eip-1559.md @@ -3,8 +3,7 @@ eip: 1559 title: Fee market change for ETH 1.0 chain author: Vitalik Buterin (@vbuterin), Eric Conner (@econoar), Rick Dudley (@AFDudley), Matthew Slipper (@mslipper), Ian Norden (@i-norden), Abdelhamid Bakhta (@abdelhamidbakhta) discussions-to: https://ethereum-magicians.org/t/eip-1559-fee-market-change-for-eth-1-0-chain/2783 -status: Last Call -review-period-end: 2021-07-14 +status: Final type: Standards Track category: Core created: 2019-04-13 @@ -15,7 +14,7 @@ requires: 2718, 2930 A transaction pricing mechanism that includes fixed-per-block network fee that is burned and dynamically expands/contracts block sizes to deal with transient congestion. ## Abstract -We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `0x02 || rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, access_list, signatureYParity, signatureR, signatureS])`. +We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])`. There is a base fee per gas in protocol, which can move up or down each block according to a formula which is a function of gas used in parent block and gas target (block gas limit divided by elasticity multiplier) of parent block. The algorithm results in the base fee per gas increasing when blocks are above the gas target, and decreasing when blocks are below the gas target. @@ -44,11 +43,11 @@ As of `FORK_BLOCK_NUMBER`, a new [EIP-2718](./eip-2718.md) transaction is introd The intrinsic cost of the new transaction is inherited from [EIP-2930](./eip-2930.md), specifically `21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count`. -The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, access_list, signatureYParity, signatureR, signatureS])`. +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])`. -The `signatureYParity, signatureR, signatureS` elements of this transaction represent a secp256k1 signature over `keccak256(0x02 || rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, access_list]))`. +The `signature_y_parity, signature_r, signature_s` elements of this transaction represent a secp256k1 signature over `keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))`. -The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulativeGasUsed, logsBloom, logs])`. +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`. *Note: `//` is integer division, round down.* ```python @@ -192,7 +191,7 @@ class World(ABC): else: gas_used_delta = parent_gas_target - parent_gas_used base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta // parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR - expected_base_fee_per_gas = max(parent_base_fee_per_gas - base_fee_per_gas_delta, 0) + expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta assert expected_base_fee_per_gas == block.base_fee_per_gas, 'invalid block: base fee not correct' # execute transactions and do gas accounting @@ -206,9 +205,19 @@ class World(ABC): signer.balance -= transaction.amount assert signer.balance >= 0, 'invalid transaction: signer does not have enough ETH to cover attached value' + # the signer must be able to afford the transaction + assert signer.balance >= transaction.gas_limit * transaction.max_fee_per_gas # ensure that the user was willing to at least pay the base fee assert transaction.max_fee_per_gas >= block.base_fee_per_gas + + # Prevent impossibly large numbers + assert transaction.max_fee_per_gas < 2**256 + # Prevent impossibly large numbers + assert transaction.max_priority_fee_per_gas < 2**256 + # The total must be the larger of the two + assert transaction.max_fee_per_gas >= transaction.max_priority_fee_per_gas + # priority fee is capped because the base fee is filled first priority_fee_per_gas = min(transaction.max_priority_fee_per_gas, transaction.max_fee_per_gas - block.base_fee_per_gas) # signer pays both the priority fee and the base fee @@ -301,23 +310,20 @@ The datastructure that is passed into keccak256 to calculate the block hash is c ### GASPRICE Previous to this change, `GASPRICE` represented both the ETH paid by the signer per gas for a transaction as well as the ETH received by the miner per gas. As of this change, `GASPRICE` now only represents the amount of ETH paid by the signer per gas, and the amount a miner was paid for the transaction is no longer accessible directly in the EVM. -## Test Cases -TODO - ## Security Considerations ### Increased Max Block Size/Complexity This EIP will increase the maximum block size, which could cause problems if miners are unable to process a block fast enough as it will force them to mine an empty block. Over time, the average block size should remain about the same as without this EIP, so this is only an issue for short term size bursts. It is possible that one or more clients may handle short term size bursts poorly and error (such as out of memory or similar) and client implementations should make sure their clients can appropriately handle individual blocks up to max size. ### Transaction Ordering -With most people not competing on priority fees and instead using a baseline fee to get included, transaction ordering now depends on individual client internal implementation details such as how they store the transactions in memory. It is recommended that transactions with the same priority fee be sorted by time the transaction was received to protect the network from spamming attacks where the attacker throws a bunch of transactions into the pending pool in order to ensure that at least one lands in a favorable position. Miners should still prefer higher tip transactions over those with a lower tip, purely from a selfish mining perspective. +With most people not competing on priority fees and instead using a baseline fee to get included, transaction ordering now depends on individual client internal implementation details such as how they store the transactions in memory. It is recommended that transactions with the same priority fee be sorted by time the transaction was received to protect the network from spamming attacks where the attacker throws a bunch of transactions into the pending pool in order to ensure that at least one lands in a favorable position. Miners should still prefer higher gas premium transactions over those with a lower gas premium, purely from a selfish mining perspective. ### Miners Mining Empty Blocks -It is possible that miners will mine empty blocks until such time as the base fee is very low and then proceed to mine half full blocks and revert to sorting transactions by the priority fee. While this attack is possible, it is not a particularly stable equilibrium as long as mining is decentralized. Any defector from this strategy will be more profitable than a miner participating in the attack for as long as the attack continues (even after the base fee reached 0). Since any miner can anonymously defect from a cartel, and there is no way to prove that a particular miner defected, the only feasible way to execute this attack would be to control 50% or more of hashing power. If an attacker had exactly 50% of hashing power, they would make no money from priority fee while defectors would make double the money from priority fees. For an attacker to turn a profit, they need to have some amount over 50% hashing power, which means they can instead execute double spend attacks or simply ignore any other miners which is a far more profitable strategy. +It is possible that miners will mine empty blocks until such time as the base fee is very low and then proceed to mine half full blocks and revert to sorting transactions by the priority fee. While this attack is possible, it is not a particularly stable equilibrium as long as mining is decentralized. Any defector from this strategy will be more profitable than a miner participating in the attack for as long as the attack continues (even after the base fee reached 0). Since any miner can anonymously defect from a cartel, and there is no way to prove that a particular miner defected, the only feasible way to execute this attack would be to control 50% or more of hashing power. If an attacker had exactly 50% of hashing power, they would make no Ether from priority fee while defectors would make double the Ether from priority fees. For an attacker to turn a profit, they need to have some amount over 50% hashing power, which means they can instead execute double spend attacks or simply ignore any other miners which is a far more profitable strategy. Should a miner attempt to execute this attack, we can simply increase the elasticity multiplier (currently 2x) which requires they have even more hashing power available before the attack can even be theoretically profitable against defectors. ### ETH Burn Precludes Fixed Supply -By burning the base fee, we can no longer guarantee a fixed token supply. This could result in economic instability as the long term supply of ETH will no longer be constant over time. While a valid concern, it is difficult to quantify how much of an impact this will have. If more is burned on base fee than is generated in mining rewards then ETH will be deflationary and if more is generated in mining rewards than is burned then ETH will be inflationary. Since we cannot control user demand for block space, we cannot assert at the moment whether ETH will end up inflationary or deflationary, so this change causes the core developers to lose some control over Ethereum's long term monetary policy. +By burning the base fee, we can no longer guarantee a fixed Ether supply. This could result in economic instability as the long term supply of ETH will no longer be constant over time. While a valid concern, it is difficult to quantify how much of an impact this will have. If more is burned on base fee than is generated in mining rewards then ETH will be deflationary and if more is generated in mining rewards than is burned then ETH will be inflationary. Since we cannot control user demand for block space, we cannot assert at the moment whether ETH will end up inflationary or deflationary, so this change causes the core developers to lose some control over Ether's long term quantity. ## 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-1571.md b/EIPS/eip-1571.md index 88655fe8fb0940..5338fd0088d32c 100644 --- a/EIPS/eip-1571.md +++ b/EIPS/eip-1571.md @@ -3,7 +3,7 @@ eip: 1571 title: EthereumStratum/2.0.0 author: Andrea Lanfranchi (@AndreaLanfranchi), Pawel Bylica (@chfast), Marius Van Der Wijden (@MariusVanDerWijden) discussions-to: https://github.com/AndreaLanfranchi/EthereumStratum-2.0.0/issues -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2018-11-09 @@ -548,4 +548,4 @@ Where `params` is an object which holds these members for values of the **whole The client will eventually take internal actions to reset/restart it's workers. ## 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-1577.md b/EIPS/eip-1577.md index f73fd507eeb0cc..c555e53fe11c35 100644 --- a/EIPS/eip-1577.md +++ b/EIPS/eip-1577.md @@ -4,7 +4,7 @@ title: contenthash field for ENS author: Dean Eigenmann , Nick Johnson type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2018-11-13 --- @@ -113,4 +113,4 @@ There are also implementations in multiple languages to encode and decode `conte * [Python](https://github.com/filips123/ContentHashPy) ## 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-158.md b/EIPS/eip-158.md index 6dde3019a62ef7..905e1d368f4c88 100644 --- a/EIPS/eip-158.md +++ b/EIPS/eip-158.md @@ -1,12 +1,11 @@ --- eip: 158 title: State clearing -author: Vitalik Buterin +author: Vitalik Buterin (@vbuterin) type: Standards Track category: Core status: Final created: 2016-10-16 -superseded-by: 161 --- # Specification diff --git a/EIPS/eip-1581.md b/EIPS/eip-1581.md index 1c5808fc5d8200..d4634d745bc145 100644 --- a/EIPS/eip-1581.md +++ b/EIPS/eip-1581.md @@ -1,17 +1,14 @@ --- eip: 1581 title: Non-wallet usage of keys derived from BIP-32 trees +description: A derivation path structure for BIP32 trees to generate key pairs not meant to hold crypto assets. author: Michele Balistreri (@bitgamma) discussions-to: https://ethereum-magicians.org/t/non-wallet-usage-of-keys-derived-from-bip-32-trees/1817 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-11-13 --- - -## Simple Summary -This EIP describes a derivation path structure for [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) wallets to be used for non-wallet key pairs. - ## Abstract BIP32 defines a way to generate hierarchical trees of keys which can be derived from a common master key. BIP32 and [BIP44](https://https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) defines the usage of these keys as wallets. In this EIP we describe the usage of such keys outside the scope of the blockchain defining a logical tree for key usage which can coexist (and thus share the same master) with existing BIP44 compatible wallets. @@ -49,4 +46,4 @@ As an example, let's assume we have a key with key type 4' and a key_index repre The structure proposed above follows the BIP43 generic structure and is similar to the widely adopted BIP44 specification. ## 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-1588.md b/EIPS/eip-1588.md index f25760a697adb4..f7e4202c16d3a7 100644 --- a/EIPS/eip-1588.md +++ b/EIPS/eip-1588.md @@ -2,7 +2,7 @@ eip: 1588 title: "Hardfork Meta: Ethereum ProgPoW" author: Ikmyeong Na (@naikmyeong) -status: Draft +status: Stagnant type: Meta created: 2018-11-16 requires: 1057 @@ -23,4 +23,4 @@ This meta-EIP specifies the changes included in the alternative Ethereum hardfor ## 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-1592.md b/EIPS/eip-1592.md index 9345541329cf99..dd97e594393df0 100644 --- a/EIPS/eip-1592.md +++ b/EIPS/eip-1592.md @@ -5,7 +5,7 @@ author: Cyril Lapinte , Laurent Aapro , Dror Tirosh , Alex Forshtat discussions-to: https://github.com/yoav-tabookey/EIPs/issues/1 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-11-18 @@ -89,7 +89,7 @@ Glossary of terms used in the processes below: * `Sender` - an external address with a valid key pair but no ETH to pay for gas. * `Relay` - a node holding ETH in an external address, listed in RelayHub and relaying transactions from Senders to RelayHub for a fee. -![Sequence Diagram](/assets/eip-1613/sequence.png) +![Sequence Diagram](../assets/eip-1613/sequence.png) The process of registering/refreshing a `Relay`: @@ -297,4 +297,4 @@ Dapps adding gas station network support remain backwards compatible with their A working implementation of the [**gas stations network**](https://github.com/tabookey-dev/tabookey-gasless) is being developed by **TabooKey**. It consists of `RelayHub`, `RelayRecipient`, `web3 hooks`, an implementation of a gas station inside `geth`, and sample dapps using the gas stations network. ## 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-1616.md b/EIPS/eip-1616.md index aca47691cb6a0f..52107d4a0a95cd 100644 --- a/EIPS/eip-1616.md +++ b/EIPS/eip-1616.md @@ -1,9 +1,9 @@ --- eip: 1616 -title: ERC-1616 Attribute Registry Standard +title: Attribute Registry Standard author: 0age (@0age), Santiago Palladino (@spalladino), Leo Arias (@elopio), Alejo Salles (@fiiiu), Stephane Gosselin (@thegostep) discussions-to: https://github.com/ethereum/EIPs/issues/1616 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-11-23 @@ -384,4 +384,4 @@ contract AttributeRegistry is AttributeRegistryInterface { ``` ## 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-162.md b/EIPS/eip-162.md index e3a86f87e46e0e..03764438559841 100644 --- a/EIPS/eip-162.md +++ b/EIPS/eip-162.md @@ -245,4 +245,4 @@ This document borrows heavily from several sources: - 2017-03-13 Update timelines for bidding and reveal periods ## 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-1620.md b/EIPS/eip-1620.md index cd22385d8dbcb7..694c9d6a1cabcf 100644 --- a/EIPS/eip-1620.md +++ b/EIPS/eip-1620.md @@ -1,9 +1,9 @@ --- eip: 1620 -title: ERC-1620 Money Streaming +title: Money Streaming author: Paul Berg (@PaulRBerg) discussions-to: https://github.com/ethereum/EIPs/issues/1620 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-11-24 @@ -293,4 +293,4 @@ There could be a way to avoid running two different streams in parallel. That is Many thanks to @mmilton41 for countless brainstorming sessions. We have been doing research on the topic of money streaming for quite a while within the context of @ChronosProtocol. In August this year, we published the first version of our white paper describing a Plasma approach. However, in the meantime, we realised that it would be much more [fun](https://twitter.com/PaulRBerg/status/1056595919116910592) and easier to start small on Ethereum itself and sidechains like [xDai](https://blockscout.com/poa/dai). ## 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-1633.md b/EIPS/eip-1633.md index 7fddba9bbff121..17461d4dbddb55 100644 --- a/EIPS/eip-1633.md +++ b/EIPS/eip-1633.md @@ -3,7 +3,7 @@ eip: 1633 title: Re-Fungible Token Standard (RFT) author: Billy Rennekamp (@okwme), Dan Long , Kiryl Yermakou , Nate van der Ende discussions-to: https://github.com/ethereum/EIPs/issues/1634 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2018-11-18 @@ -171,4 +171,4 @@ interface RFT /* is ERC20, ERC165 */ { TBD ## 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-165.md b/EIPS/eip-165.md index 1998ffa47f8bfb..77e6cbcf3938d3 100644 --- a/EIPS/eip-165.md +++ b/EIPS/eip-165.md @@ -1,6 +1,6 @@ --- eip: 165 -title: ERC-165 Standard Interface Detection +title: Standard Interface Detection author: Christian Reitwießner , Nick Johnson , Fabian Vogelsteller , Jordi Baylina , Konrad Feldmeier , William Entriken type: Standards Track category: ERC @@ -232,4 +232,4 @@ With three or more supported interfaces (including ERC165 itself as a required s ## 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-1679.md b/EIPS/eip-1679.md index 0c968c1e181b69..a6bf39fa837eb3 100644 --- a/EIPS/eip-1679.md +++ b/EIPS/eip-1679.md @@ -18,7 +18,7 @@ This meta-EIP specifies the changes included in the Ethereum hardfork named Ista - Codename: Istanbul ### Activation - - `Block >= 9,069,000` on the Ethereum mainnet + - `Block >= 9,069,000` on the Ethereum Mainnet - `Block >= 6,485,846` on the Ropsten testnet - `Block >= 14,111,141` on the Kovan testnet - `Block >= 5,435,345` on the Rinkeby testnet @@ -39,4 +39,4 @@ This meta-EIP specifies the changes included in the Ethereum hardfork named Ista ## 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-1681.md b/EIPS/eip-1681.md index 28c6b2fca4a720..a17f2c9dc8d497 100644 --- a/EIPS/eip-1681.md +++ b/EIPS/eip-1681.md @@ -3,7 +3,7 @@ eip: 1681 title: Temporal Replay Protection author: Martin Holst Swende (@holiman) discussions-to: https://ethereum-magicians.org/t/temporal-replay-protection/2355 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-01-08 @@ -87,5 +87,5 @@ user actively wants to start using it. Secondary security impacts are that the addition of a timestamp would make the transactions a little bit larger. ## 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-1682.md b/EIPS/eip-1682.md index 48213b3499321d..3a0f0a5ee13217 100644 --- a/EIPS/eip-1682.md +++ b/EIPS/eip-1682.md @@ -217,4 +217,4 @@ TBA 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-170.md b/EIPS/eip-170.md index d2448f2f0e9bf4..632f216a62e340 100644 --- a/EIPS/eip-170.md +++ b/EIPS/eip-170.md @@ -12,12 +12,13 @@ created: 2016-11-04 [Spurious Dragon](./eip-607.md) ### Parameters +- `MAX_CODE_SIZE`: `0x6000` (`2**14 + 2**13`) - `FORK_BLKNUM`: 2,675,000 -- `CHAIN_ID`: 1 (main net) +- `CHAIN_ID`: 1 (Mainnet) ### Specification -If `block.number >= FORK_BLKNUM`, then if contract creation initialization returns data with length of **more than** `0x6000` (`2**14 + 2**13`) bytes, contract creation fails with an out of gas error. +If `block.number >= FORK_BLKNUM`, then if contract creation initialization returns data with length of **more than** `MAX_CODE_SIZE` bytes, contract creation fails with an out of gas error. ### Rationale diff --git a/EIPS/eip-1702.md b/EIPS/eip-1702.md index 9a3f1607d66b86..bfcc9a76b829cb 100644 --- a/EIPS/eip-1702.md +++ b/EIPS/eip-1702.md @@ -238,4 +238,4 @@ The source of this specification can be found at ## 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-1706.md b/EIPS/eip-1706.md index f7ac77a38fec87..b392f65b080662 100644 --- a/EIPS/eip-1706.md +++ b/EIPS/eip-1706.md @@ -4,13 +4,12 @@ title: Disable SSTORE with gasleft lower than call stipend author: Alex Forshtat , Yoav Weiss discussions-to: https://github.com/alex-forshtat-tbk/EIPs/issues/1 status: Withdrawn +withdrawal-reason: The authors prefer [EIP-2200](./eip-2200.md) type: Standards Track category: Core created: 2019-01-15 requires: 1283 -superseded-by: 2200 --- -> :information_source: **[EIP-2200] has superseded [EIP-1706].** :information_source: ## Simple Summary The proposal that had been accepted changes security properties of a large portion of an existing contract code base that may be infeasible to update and validate. This proposal will make the old assumptions hold even after a network upgrade. @@ -60,4 +59,4 @@ TODO ## Implementation TODO ## 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-1710.md b/EIPS/eip-1710.md index f18e9f5f6db774..91503f8d78d9d1 100644 --- a/EIPS/eip-1710.md +++ b/EIPS/eip-1710.md @@ -3,7 +3,7 @@ eip: 1710 title: URL Format for Web3 Browsers author: Bruno Barbieri (@brunobar79) discussions-to: https://ethereum-magicians.org/t/standarize-url-format-for-web3-browsers/2422 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-01-13 @@ -56,4 +56,4 @@ The proposed format attempts to solve the problem of vendor specific protocols f ## 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-1716.md b/EIPS/eip-1716.md index 00b98d0f832ff6..031f20d3bc3dce 100644 --- a/EIPS/eip-1716.md +++ b/EIPS/eip-1716.md @@ -17,7 +17,7 @@ This meta-EIP specifies the changes included in the Ethereum hardfork that remov - Codename: Petersburg - Aliases: St. Petersfork, Peter's Fork, Constantinople Fix - Activation: - - `Block >= 7_280_000` on the Ethereum mainnet + - `Block >= 7_280_000` on the Ethereum Mainnet - `Block >= 4_939_394` on the Ropsten testnet - `Block >= 10_255_201` on the Kovan testnet - `Block >= 4_321_234` on the Rinkeby testnet @@ -36,4 +36,4 @@ If `Petersburg` is defined with an earlier block number than `Constantinople`, t ## 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-173.md b/EIPS/eip-173.md index bc77693e94c801..e79033eb5262d3 100644 --- a/EIPS/eip-173.md +++ b/EIPS/eip-173.md @@ -1,19 +1,15 @@ --- eip: 173 title: Contract Ownership Standard -author: Nick Mudge , Dan Finlay +description: A standard interface for ownership of contracts +author: Nick Mudge (@mudgen), Dan Finlay discussions-to: https://github.com/ethereum/EIPs/issues/173 type: Standards Track category: ERC -status: Last Call -review-period-end: 2020-09-06 +status: Final created: 2018-06-07 --- -## Simple Summary - -A standard interface for ownership of contracts. - ## Abstract This specification defines standard functions for owning or controlling a contract. @@ -55,8 +51,7 @@ interface ERC173 /* is ERC165 */ { interface ERC165 { /// @notice Query if a contract implements an interface /// @param interfaceID The interface identifier, as specified in ERC-165 - /// @dev Interface identification is specified in ERC-165. This function - /// uses less than 30,000 gas. + /// @dev Interface identification is specified in ERC-165. /// @return `true` if the contract implements `interfaceID` and /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceID) external view returns (bool); @@ -87,7 +82,7 @@ Here are other schemes that were considered: This standard does not exclude the above ownership schemes or other schemes from also being implemented in the same contract. For example a contract could implement this standard and also implement the other schemes so that ownership could be managed and transferred in multiple ways. This standard does provide a simple ownership scheme that is backwards compatible, is light-weight and simple to implement, and can be widely adopted and depended on. -This standard can be extended by other standards to add additional ownership functionality. For example [EIP-2767](./eip-2767.md) uses and extends this standard by adding decentralized contract ownership governance. +This standard can be (and has been) extended by other standards to add additional ownership functionality. ## Security Considerations @@ -97,13 +92,6 @@ If the address returned by `owner()` is an externally owned account then its pri Many existing contracts already implement this standard. -## Implementations - -* [OpenZeppelin's implementation of Ownable](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol) -* [0xcert ownable](https://github.com/0xcert/ethereum-erc721/blob/master/src/contracts/ownership/ownable.sol) -* [FriendlyUser Ownable](https://github.com/FriendlyUser/solidity-smart-contracts//blob/v0.2.0/contracts/other/CredVert/Ownable.sol) - - ## 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-1753.md b/EIPS/eip-1753.md index ef92881b3aed63..09cb027eebd047 100644 --- a/EIPS/eip-1753.md +++ b/EIPS/eip-1753.md @@ -2,7 +2,7 @@ eip: 1753 title: Smart Contract Interface for Licences author: Lucas Cullen (@BitcoinBrisbane), Kai Yeung (@CivicKai), Anna Crowley , Caroline Marshall , Katrina Donaghy -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-02-06 @@ -167,18 +167,16 @@ Smart contracts can be used to embed regulatory requirements with respect to the ### Solidity Example ```solidity interface EIP1753 { - string public name; - uint256 public totalSupply; - function grantAuthority(address who) public; - function revokeAuthority(address who) public; + function grantAuthority(address who) external; + function revokeAuthority(address who) external; function hasAuthority(address who) external view returns (bool); function issue(address who, uint256 from, uint256 to) external; - function revoke(address who) public; + function revoke(address who) external; function hasValid(address who) external view returns (bool); - function purchase(uint256 validFrom, uint256 validTo) public payable; + function purchase(uint256 validFrom, uint256 validTo) external payable; } pragma solidity ^0.5.3; @@ -210,7 +208,7 @@ contract EIP is EIP1753 { delete _authorities[who]; } - function hasAuthority(address who) external view returns (bool) { + function hasAuthority(address who) public view returns (bool) { return _authorities[who] == true; } @@ -224,12 +222,12 @@ contract EIP is EIP1753 { } function hasValid(address who) external view returns (bool) { - return _holders[who].start > now && _holders[who].end < now; + return _holders[who].validFrom > now && _holders[who].validTo < now; } function purchase(uint256 validFrom, uint256 validTo) external payable { require(msg.value == 1 ether, "Incorrect fee"); - issue(msg.sender, from, to); + issue(msg.sender, validFrom, validTo); } modifier onlyOwner() { @@ -245,4 +243,4 @@ contract EIP is EIP1753 { ``` ## 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-1761.md b/EIPS/eip-1761.md index bd62772bc14a4b..141836829d66a0 100644 --- a/EIPS/eip-1761.md +++ b/EIPS/eip-1761.md @@ -1,10 +1,10 @@ --- eip: 1761 -title: ERC-1761 Scoped Approval Interface +title: Scoped Approval Interface author: Witek Radomski , Andrew Cooke , James Therien , Eric Binet type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2019-02-18 discussions-to: https://github.com/ethereum/EIPs/issues/1761 requires: 165 @@ -172,4 +172,4 @@ The Scope Metadata JSON Schema was added in order to support human-readable scop - [GitHub - ERC-1155 Discussion Thread](https://github.com/ethereum/EIPs/issues/1155) ## 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-1767.md b/EIPS/eip-1767.md index 84c83bc114ca6a..616604100df8c3 100644 --- a/EIPS/eip-1767.md +++ b/EIPS/eip-1767.md @@ -3,7 +3,7 @@ eip: 1767 title: GraphQL interface to Ethereum node data author: Nick Johnson (@arachnid), Raúl Kripalani (@raulk), Kris Shinn (@kshinn) discussions-to: https://ethereum-magicians.org/t/graphql-interface-to-ethereum-node-data/2710 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2019-02-14 @@ -415,4 +415,4 @@ TBD. - Work in progress in [Parity](https://github.com/paritytech/parity-ethereum/issues/10933) ## 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-1775.md b/EIPS/eip-1775.md index 9eb79ee299c163..3ee41df430a617 100644 --- a/EIPS/eip-1775.md +++ b/EIPS/eip-1775.md @@ -3,7 +3,7 @@ eip: 1775 title: App Keys, application specific wallet accounts author: Vincent Eli (@Bunjin), Dan Finlay (@DanFinlay) discussions-to: https://ethereum-magicians.org/t/eip-erc-app-keys-application-specific-wallet-accounts/2742 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-02-20 @@ -193,4 +193,4 @@ MetaMask team, Christian Lundkvist, Counterfactual team, Liam Horne, Erik Bryn, ## 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-1803.md b/EIPS/eip-1803.md index 855b51242ec329..2d0e50c1b93b03 100644 --- a/EIPS/eip-1803.md +++ b/EIPS/eip-1803.md @@ -5,7 +5,7 @@ author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-1803-rename-opcodes-for-clarity/3345 type: Standards Track category: Interface -status: Draft +status: Stagnant created: 2017-07-28 requires: 141 --- @@ -39,4 +39,4 @@ Renaming `SHA3` was previously proposed by [EIP-59](https://github.com/ethereum/ ## 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-181.md b/EIPS/eip-181.md index 8d2ae33f5a3622..3c7a2311de824c 100644 --- a/EIPS/eip-181.md +++ b/EIPS/eip-181.md @@ -206,4 +206,4 @@ This registrar, written in Solidity, implements the specifications outlined abov } ## 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-1812.md b/EIPS/eip-1812.md index 7b496dadd2f493..8ff5f0759d7e79 100644 --- a/EIPS/eip-1812.md +++ b/EIPS/eip-1812.md @@ -3,7 +3,7 @@ eip: 1812 title: Ethereum Verifiable Claims author: Pelle Braendgaard (@pelle) discussions-to: https://ethereum-magicians.org/t/erc-1812-ethereum-verifiable-claims/2814 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-03-03 @@ -439,4 +439,4 @@ There is a repo with a few example verifiers and consuming smart contracts writt * [RevocationRegistry.sol](https://github.com/uport-project/eip712-claims-experiments/blob/master/contracts/RevocationRegistry.sol) ## 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-1820.md b/EIPS/eip-1820.md index 641555ff3064ef..09e2dc4bbc253d 100644 --- a/EIPS/eip-1820.md +++ b/EIPS/eip-1820.md @@ -8,7 +8,6 @@ type: Standards Track category: ERC requires: 165, 214 created: 2019-03-04 -replaces: 820 --- > :information_source: **[ERC-1820] has superseded [ERC-820].** :information_source: @@ -340,7 +339,9 @@ The contract has the address above for every chain on which it is deployed.
Raw metadata of ./contracts/ERC1820Registry.sol
-{
+
+```json
+{
         "compiler": {
           "version": "0.5.3+commit.10d17f24"
         },
@@ -656,7 +657,9 @@ The contract has the address above for every chain on which it is deployed.
           }
         },
         "version": 1
-      }
+      }
+```
+
 
@@ -906,7 +909,7 @@ Please check the [0xjac/ERC1820] repository for the full test suite. The implementation is available in the repo: [0xjac/ERC1820]. ## Copyright -Copyright and related rights waived via [CC0]. +Copyright and related rights waived via [CC0](../LICENSE.md). [EIP-155]: ./eip-155.md [ERC-165]: ./eip-165.md @@ -922,7 +925,6 @@ Copyright and related rights waived via [CC0]. [ERC165 Cache]: #erc165-cache [Nick's article]: https://medium.com/@weka/how-to-send-ether-to-11-440-people-187e332566b7 [0xjac/ERC1820]: https://github.com/0xjac/ERC1820 -[CC0]: https://creativecommons.org/publicdomain/zero/1.0/ [Nick]: https://github.com/Arachnid/ [William Entriken]: https://github.com/fulldecent [ENS]: https://ens.domains/ diff --git a/EIPS/eip-1822.md b/EIPS/eip-1822.md index ec1f7aeb83de1d..8280b196ad5c8d 100644 --- a/EIPS/eip-1822.md +++ b/EIPS/eip-1822.md @@ -3,42 +3,12 @@ eip: 1822 title: Universal Upgradeable Proxy Standard (UUPS) author: Gabriel Barros , Patrick Gallagher discussions-to: https://ethereum-magicians.org/t/eip-1822-universal-upgradeable-proxy-standard-uups -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-03-04 --- -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Simple Summary](#simple-summary) -- [Abstract](#abstract) -- [Motivation](#motivation) -- [Terminology](#terminology) -- [Specification](#specification) - - [Proxy Contract](#proxy-contract) - - [Functions](#functions) - - [`fallback`](#fallback) - - [`constructor`](#constructor) - - [Proxiable Contract](#proxiable-contract) - - [Functions](#functions-1) - - [`proxiable`](#proxiable) - - [`updateCodeAddress`](#updatecodeaddress) -- [Pitfalls when using a proxy](#pitfalls-when-using-a-proxy) - - [Separating Variables from Logic](#separating-variables-from-logic) - - [Restricting dangerous functions](#restricting-dangerous-functions) -- [Examples](#examples) - - [Owned](#owned) - - [ERC-20 Token](#erc-20-token) - - [Proxy Contract](#proxy-contract-1) - - [Token Logic Contract](#token-logic-contract) -- [References](#references) -- [Copyright](#copyright) - - ## Simple Summary Standard upgradeable proxy contract. @@ -60,7 +30,7 @@ The following describes a standard for proxy contracts which is universally comp - **Logic Contract** - The contract **B** which contains the logic used by Proxy Contract **A** - **Proxiable Contract** - Inherited in Logic Contract **B** to provide the upgrade functionality -

diagram

+![](../assets/eip-1822/proxy-diagram.png) ## Specification @@ -306,7 +276,7 @@ contract LibraryLock is LibraryLockDataLayout { } } -contact ERC20DataLayout is LibraryLockDataLayout { +contract ERC20DataLayout is LibraryLockDataLayout { uint256 public totalSupply; mapping(address=>uint256) public tokens; } @@ -343,7 +313,7 @@ contract MyToken is ERC20DataLayout, ERC20, Owned, Proxiable, LibraryLock { ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [1]: https://github.com/maraoz/solidity-proxy/blob/master/contracts/Dispatcher.sol [2]: https://blog.gnosis.pm/solidity-delegateproxy-contracts-e09957d0f201 diff --git a/EIPS/eip-1829.md b/EIPS/eip-1829.md index 49e82e894b5064..8a2a27a389cb80 100644 --- a/EIPS/eip-1829.md +++ b/EIPS/eip-1829.md @@ -3,7 +3,7 @@ eip: 1829 title: Precompile for Elliptic Curve Linear Combinations author: Remco Bloemen discussions-to: https://ethereum-magicians.org/t/ewasm-precompile-for-general-elliptic-curve-math/2581 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-03-06 @@ -146,5 +146,5 @@ This EIP overlaps in scope with * [EIP-1108](./eip-1108.md): Optimize ecadd and ecmul for altbn128. ## 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-1844.md b/EIPS/eip-1844.md index 1245ecd5ce6f94..a80e696c7b20cb 100644 --- a/EIPS/eip-1844.md +++ b/EIPS/eip-1844.md @@ -3,7 +3,7 @@ eip: 1844 title: ENS Interface Discovery author: Nick Johnson (@arachnid) discussions-to: https://ethereum-magicians.org/t/ens-interface-discovery/2924 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-03-15 @@ -61,4 +61,4 @@ TBD The PublicResolver in the [ensdomains/resolvers](https://github.com/ensdomains/resolvers/) repository implements this interface. ## 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-1872.md b/EIPS/eip-1872.md index fe840d1497e495..7fcab2afc6d2c0 100644 --- a/EIPS/eip-1872.md +++ b/EIPS/eip-1872.md @@ -3,7 +3,7 @@ eip: 1872 title: Ethereum Network Upgrade Windows author: Danno Ferrin (@shemnon) discussions-to: https://ethereum-magicians.org/t/eip-1872-ethereum-network-upgrade-windows/2993 -status: Draft +status: Stagnant type: Meta created: 2018-03-25 --- @@ -213,5 +213,4 @@ follows: ## 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-1884.md b/EIPS/eip-1884.md index a16dc9cdba0669..3698c203becf8c 100644 --- a/EIPS/eip-1884.md +++ b/EIPS/eip-1884.md @@ -155,6 +155,6 @@ func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, - In many cases, a recipient of `ether` from a `CALL` will want to issue a `LOG`. The `LOG` operation costs `375` plus `375` per topic. If the `LOG` also wants to do an `SLOAD`, this change may make some such transfers fail. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [eip-150]: ./eip-150.md diff --git a/EIPS/eip-1890.md b/EIPS/eip-1890.md index c927937226bf34..8c3a38b76f303e 100644 --- a/EIPS/eip-1890.md +++ b/EIPS/eip-1890.md @@ -50,4 +50,4 @@ This EIP makes no changes to existing state transitions. Existing consensus test Reference implementations are included for the Trinity, go-ethereum, and parity clients. ## 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-1895.md b/EIPS/eip-1895.md index 60fe433aac6e03..5c520839e0774b 100644 --- a/EIPS/eip-1895.md +++ b/EIPS/eip-1895.md @@ -3,7 +3,7 @@ eip: 1895 title: Support for an Elliptic Curve Cycle author: Alexandre Belling discussions-to: https://ethresear.ch/t/reducing-the-verification-cost-of-a-snark-through-hierarchical-aggregation/5128 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-03-31 @@ -157,4 +157,4 @@ Proper benchmarks will be done in order to make this choice and to price the ope ## 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-1898.md b/EIPS/eip-1898.md index 8a387deddcaae2..0bb8bc4d7c5ae6 100644 --- a/EIPS/eip-1898.md +++ b/EIPS/eip-1898.md @@ -1,25 +1,26 @@ --- eip: 1898 -title: Add `blockHash` to JSON-RPC methods which accept a default block parameter. +title: Add `blockHash` to defaultBlock methods +description: Add `blockHash` option to JSON-RPC methods that currently support defaultBlock parameter. author: Charles Cooper (@charles-cooper) +discussions-to: https://ethereum-magicians.org/t/eip-1898-add-blockhash-option-to-json-rpc-methods-that-currently-support-defaultblock-parameter/11757 +status: Review type: Standards Track category: Interface -status: Draft created: 2019-04-01 -requires: 234, 1474 +requires: 234 --- -## Simple Summary +## Abstract For JSON-RPC methods which currently accept a default block parameter, additionally allow the parameter to be a block hash. -## Abstract - This EIP can be considered a generalization of [EIP-234](./eip-234.md). It would enable clients to unambiguously specify the block they want to query for certain JSON-RPC methods, even if the block is not in the canonical chain. This allows clients to maintain a coherent picture of blockchain state that they are interested in, even in the presence of reorgs, without requiring that the node maintain a persistent connection with the client or store any client-specific state. ## Specification The following JSON-RPC methods are affected: + - `eth_getBalance` - `eth_getStorageAt` - `eth_getTransactionCount` @@ -27,13 +28,17 @@ The following JSON-RPC methods are affected: - `eth_call` - `eth_getProof` -The following options, quoted from the [JSON-RPC spec](https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter), are currently possible for the defaultBlock parameter: +The following options, quoted from the Ethereum JSON-RPC spec, are currently possible for the defaultBlock parameter: + > - HEX String - an integer block number > - String "earliest" for the earliest/genesis block -> - String "latest" - for the latest mined block +> - String "latest" - for the latest canonical block > - String "pending" - for the pending state/transactions +> - String "safe" - for the most recent safe block +> - String "finalized" - for the most recent finalized block Since there is no way to clearly distinguish between a DATA parameter and a QUANTITY parameter, this EIP proposes a new scheme for the block parameter. The following option is additionally allowed: + - OBJECT - `blockNumber`: QUANTITY - a block number - `blockHash`: DATA - a block hash @@ -43,6 +48,7 @@ If the block is not found, the callee SHOULD raise a JSON-RPC error (the recomme If the tag is `blockHash`, an additional boolean field may be supplied to the block parameter, `requireCanonical`, which defaults to `false` and defines whether the block must be a canonical block according to the callee. If `requireCanonical` is `false`, the callee should raise a JSON-RPC error only if the block is not found (as described above). If `requireCanonical` is `true`, the callee SHOULD additionally raise a JSON-RPC error if the block is not in the canonical chain (the recommended error code is `-32000: Invalid input` and in any case should be different than the error code for the block not found case so that the caller can distinguish the cases). The block-not-found check SHOULD take precedence over the block-is-canonical check, so that if the block is not found the callee raises block-not-found rather than block-not-canonical. To maintain backwards compatibility, the block number MAY be specified either as a hex string or using the new block parameter scheme. In other words, the following are equivalent for the default block parameter: + - `"earliest"` - `"0x0"` - `{ "blockNumber": "0x0" }` @@ -55,6 +61,7 @@ To maintain backwards compatibility, the block number MAY be specified either as Currently, the state-querying JSON-RPC methods specified above have no option to unambiguously specify which block to query the state for. This can cause issues for applications which need to make multiple calls to the RPC. For instance, a wallet which just executed a transfer may want to display the balances of both the sender and recipient. If there is a re-org in between when the balance of the sender is queried via `eth_getBalance` and when the balance of the recipient is queried, the balances may not reconcile. As a slightly more complicated example, the UI for a decentralized exchange (which hosts orders on-chain) may walk a list of orders by calling `eth_call` for each of them to get the order data. Another type of use case is where an application needs to make a decision based on multiple pieces of state, e.g. a payout predicated on simultaneous ownership of two NFTs. In order to ensure that the state is coherent (i.e., `eth_call` was called with exactly the same block for every call), the application may currently use one of several strategies: + - Decide on a block number to use (e.g., the latest block number known to the application). After each `eth_call` using that block number, call `eth_getBlockByNumber`, also with that block number. If the block hash does not match the known hash for that block number, rollback the current activity and retry from the beginning. This adds `O(n)` invocations as baseline overhead and another `O(n)` invocations for every retry needed. Moreover, there is no way to detect the (unlikely but possible) case that the relevant block was reorged out before `eth_call`, and then reorged back in before `eth_getBlockByNumber`. - Rely on logs, which *can* be queried unambiguously thanks to the `blockHash` parameter. However, this requires semantic support from the smart contract; if the smart contract does not emit appropriate events, the client will not be able to reconstruct the specific state it is interested in. - Rely on non-standard extensions like `parity_subscribe`. This requires a persistent connection between the client and node (via IPC or websockets), increases coupling between the client and the node, and cannot handle use cases where there are dependencies between invocations of `eth_call`, for example, walking a linked list. @@ -78,10 +85,11 @@ Backwards compatible. - `eth_getStorageAt [ "0x
", { "blockHash": "0x", "requireCanonical": false }` -> return storage at given address in specified block - `eth_getStorageAt [ "0x
", { "blockHash": "0x", "requireCanonical": true }` -> raise block-not-canonical error -## Implementation +## Security Considerations + +None -It is supported by Geth 1.9.6 ([PR](https://github.com/ethereum/go-ethereum/pull/19491)). ## 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-1900.md b/EIPS/eip-1900.md index decd47e06d7c7f..a11b794dbc3a40 100644 --- a/EIPS/eip-1900.md +++ b/EIPS/eip-1900.md @@ -3,7 +3,7 @@ eip: 1900 title: dType - Decentralized Type System for EVM author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) discussions-to: https://github.com/ethereum/EIPs/issues/1882 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-03-28 @@ -273,4 +273,4 @@ A video demo of the current implementation (a more extended version of this prop ## 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-1901.md b/EIPS/eip-1901.md index cb0397f9448424..ad873a408ec2ee 100644 --- a/EIPS/eip-1901.md +++ b/EIPS/eip-1901.md @@ -3,7 +3,7 @@ eip: 1901 title: Add OpenRPC Service Discovery To JSON-RPC Services author: Shane Jonas (@shanejonas), Zachary Belford (@belfordz) discussions-to: https://github.com/ethereum/EIPs/issues/1902 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2019-02-25 @@ -102,4 +102,4 @@ The [OpenRPC mock server](https://github.com/open-rpc/mock-server) provides a mo ## Copyright - Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file + Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-191.md b/EIPS/eip-191.md index 910c30ee85993b..9a166d62af91f7 100644 --- a/EIPS/eip-191.md +++ b/EIPS/eip-191.md @@ -2,11 +2,11 @@ eip: 191 title: Signed Data Standard author: Martin Holst Swende (@holiman), Nick Johnson -status: Last Call +discussions-to: https://github.com/ethereum/EIPs/issues/191 +status: Final type: Standards Track category: ERC created: 2016-01-20 -review-period-end: 2021-05-01 --- # Abstract @@ -21,7 +21,7 @@ Several multisignature wallet implementations have been created which accepts `p * Multisignature wallets have also had the problem that a `presigned` transaction has not been tied to a particular `validator`, i.e a specific wallet. Example: 1. Users `A`, `B` and `C` have the `2/3`-wallet `X` 2. Users `A`, `B` and `D` have the `2/3`-wallet `Y` - 3. User `A` and `B` submites `presigned` transaction to `X`. + 3. User `A` and `B` submit `presigned` transactions to `X`. 4. Attacker can now reuse their presigned transactions to `X`, and submit to `Y`. ## Specification @@ -31,13 +31,12 @@ We propose the following format for `signed_data` ``` 0x19 <1 byte version> . ``` -Version `0` has `<20 byte address>` for the version specific data, and the `address` is the intended validator. In the case of a Multisig wallet, that is the wallet's own address . -The initial `0x19` byte is intended to ensure that the `signed_data` is not valid [RLP](https://github.com/ethereum/wiki/wiki/RLP) +The initial `0x19` byte is intended to ensure that the `signed_data` is not valid RLP. > For a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding. -That means that any `signed_data` cannot be one RLP-structure, but a 1-byte `RLP` payload followed by something else. Thus, any ERC-191 `signed_data` can never be an Ethereum transaction. +That means that any `signed_data` cannot be one RLP-structure, but a 1-byte `RLP` payload followed by something else. Thus, any EIP-191 `signed_data` can never be an Ethereum transaction. Additionally, `0x19` has been chosen because since ethereum/go-ethereum#2940 , the following is prepended before hashing in personal_sign: @@ -55,29 +54,55 @@ Using `0x19` thus makes it possible to extend the scheme by defining a version ` | `0x01` | [712][eip-712] | Structured data | `0x45` | [191][eip-191] | `personal_sign` messages +#### Version `0x00` + +``` +0x19 <0x00> +``` + +The version `0x00` has `` for the version specific data. In the case of a Multisig wallet that perform an execution based on a passed signature, the validator address is the address of the Multisig itself. The data to sign could be any arbitrary data. + +#### Version `0x01` + +The version `0x01` is for structured data as defined in [EIP-712] + +#### Version `0x45` (E) + +``` +0x19 <0x45 (E)> +``` + +The version `0x45` (E) has `` for the version-specific data. The data to sign can be any arbitrary data. + +> NB: The `E` in `Ethereum Signed Message` refers to the version byte 0x45. The character `E` is `0x45` in hexadecimal which makes the remainder, `thereum Signed Message:\n + len(message)`, the version-specific data. + [EIP-191]: ./eip-191.md [EIP-712]: ./eip-712.md ### Example -The following snippet has been written in Solidity 0.5.0. +The following snippets has been written in Solidity 0.8.0. + +#### Version `0x00` ```solidity -function submitTransactionPreSigned(address destination, uint value, bytes data, uint nonce, uint8 v, bytes32 r, bytes32 s) - public - returns (bytes32 transactionHash) -{ +function signatureBasedExecution(address target, uint256 nonce, bytes memory payload, uint8 v, bytes32 r, bytes32 s) public payable { + // Arguments when calculating hash to validate // 1: byte(0x19) - the initial 0x19 byte // 2: byte(0) - the version byte - // 3: this - the validator address - // 4-7 : Application specific data - transactionHash = keccak256(abi.encodePacked(byte(0x19),byte(0),address(this),destination, value, data, nonce)); - sender = ecrecover(transactionHash, v, r, s); - // ... + // 3: address(this) - the validator address + // 4-6 : Application specific data + + bytes32 hash = keccak256(abi.encodePacked(byte(0x19), byte(0), address(this), msg.value, nonce, payload)); + + // recovering the signer from the hash and the signature + addressRecovered = ecrecover(hash, v, r, s); + + // logic of the wallet + // if (addressRecovered == owner) executeOnTarget(target, payload); } ``` - ## 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-1921.md b/EIPS/eip-1921.md index 8c530a056e3d10..9a671313f46a92 100644 --- a/EIPS/eip-1921.md +++ b/EIPS/eip-1921.md @@ -3,7 +3,7 @@ eip: 1921 title: dType Functions Extension author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) discussions-to: https://github.com/ethereum/EIPs/issues/1921 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-04-06 @@ -138,4 +138,4 @@ In-work implementation examples can be found at https://github.com/pipeos-one/dT This proposal will be updated with an appropriate implementation when consensus is reached on the specifications. ## 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-1922.md b/EIPS/eip-1922.md index aa6d0810699b24..2ab2ed7c8a05b1 100644 --- a/EIPS/eip-1922.md +++ b/EIPS/eip-1922.md @@ -5,7 +5,7 @@ author: Michael Connor , Chaitanya Konda , Chaitanya Konda , pinkiebell (@pinkiebell) discussions-to: https://ethereum-magicians.org/t/erc-non-fungible-data-token/3139 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-04-18 @@ -156,4 +156,4 @@ contract ERC1948 is IERC1948, ERC721 { ``` ## 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-1959.md b/EIPS/eip-1959.md index 36c9624734e1ae..bf6d5b29100789 100644 --- a/EIPS/eip-1959.md +++ b/EIPS/eip-1959.md @@ -5,7 +5,7 @@ author: Ronan Sandford (@wighawag) category: Core type: Standards Track discussions-to: https://ethereum-magicians.org/t/eip-1959-valid-chainid-opcode/3170 -status: Draft +status: Stagnant created: 2019-04-20 requires: 155 --- @@ -76,4 +76,4 @@ Similarly to EIP-1344, it might be beneficial to update EIP-712 (still in Draft) This was previously suggested as part of [EIP-1344 discussion](https://ethereum-magicians.org/t/eip-1344-add-chain-id-opcode/1131/39). ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-196.md b/EIPS/eip-196.md index f3be641133d900..6538d198b1db10 100644 --- a/EIPS/eip-196.md +++ b/EIPS/eip-196.md @@ -102,4 +102,4 @@ In both codebases, a specific group on the curve alt_bn128 is used and is called ## 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-1962.md b/EIPS/eip-1962.md index d880f2be506094..5a69e321a551f9 100644 --- a/EIPS/eip-1962.md +++ b/EIPS/eip-1962.md @@ -5,7 +5,7 @@ author: Alex Vlasov (@shamatar) discussions-to: https://ethereum-magicians.org/t/generalised-precompile-for-elliptic-curve-arithmetics-and-pairings-working-group/3208/2 type: Standards Track category: Core -status: Draft +status: Stagnant created: 2019-04-22 requires: 1109 --- @@ -134,4 +134,4 @@ test tests::bench_wnaf_multiexp_bn254 ... bench: 9,161,281 n ``` ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-1965.md b/EIPS/eip-1965.md index 89357acbd80f07..6dd8dc0e662033 100644 --- a/EIPS/eip-1965.md +++ b/EIPS/eip-1965.md @@ -5,7 +5,7 @@ author: Ronan Sandford (@wighawag) category: Core type: Standards Track discussions-to: https://ethereum-magicians.org/t/eip-1965-valid-chainid-for-specific-blocknumber-protect-all-forks/3181 -status: Draft +status: Stagnant created: 2019-04-20 requires: 155 --- @@ -60,4 +60,4 @@ While the pair could be optional for contract that do not care about replays or This was previously suggested as part of [EIP1959 discussion](https://ethereum-magicians.org/t/eip-1959-valid-chainid-opcode/3170). ## 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-1967.md b/EIPS/eip-1967.md index ecd84c76603ff8..98165407df80b8 100644 --- a/EIPS/eip-1967.md +++ b/EIPS/eip-1967.md @@ -1,48 +1,42 @@ --- eip: 1967 -title: Standard Proxy Storage Slots -author: Santiago Palladino (@spalladino) +title: Proxy Storage Slots +description: A consistent location where proxies store the address of the logic contract they delegate to, as well as other proxy-specific information. +author: Santiago Palladino (@spalladino), Francisco Giordano (@frangio), Hadrien Croubois (@Amxx) discussions-to: https://ethereum-magicians.org/t/eip-1967-standard-proxy-storage-slots/3185 -status: Draft +status: Final type: Standards Track category: ERC created: 2019-04-24 --- -## Simple Summary -Standardise how proxies store the address of the logic contract they delegate to, and other proxy specific information. - ## Abstract Delegating **proxy contracts** are widely used for both upgradeability and gas savings. These proxies rely on a **logic contract** (also known as implementation contract or master copy) that is called using `delegatecall`. This allows proxies to keep a persistent state (storage and balance) while the code is delegated to the logic contract. -To avoid clashes in storage usage between the proxy and logic contract, the address of the logic contract is typically saved in a [specific storage slot](https://blog.zeppelinos.org/upgradeability-using-unstructured-storage/) guaranteed to be never allocated by a compiler. This EIP proposes a set of standard slots to store proxy information. This allows clients like block explorers to properly extract and show this information to end users, and logic contracts to optionally act upon it. +To avoid clashes in storage usage between the proxy and logic contract, the address of the logic contract is typically saved in a specific storage slot (for example `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` in OpenZeppelin contracts) guaranteed to be never allocated by a compiler. This EIP proposes a set of standard slots to store proxy information. This allows clients like block explorers to properly extract and show this information to end users, and logic contracts to optionally act upon it. ## Motivation -Delegating proxies are widely in use, as a means to both support upgrades and reduce gas costs of deployments. Examples of these proxies are found in [ZeppelinOS](https://blog.zeppelinos.org/the-transparent-proxy-pattern/), [Terminal](https://medium.com/terminaldotco/escape-hatch-proxy-efb681de108d), [Gnosis](https://blog.gnosis.pm/solidity-delegateproxy-contracts-e09957d0f201), [AragonOS](https://github.com/aragon/aragonOS/blob/dev/contracts/common/DelegateProxy.sol), [Melonport](https://github.com/melonproject/melon-mail/blob/782aeff9418ac8cdd80875fd6c400bf96f3b03b3/solidity/contracts/DelegateProxy.sol), [Limechain](https://github.com/LimeChain/UpgradeableSolidityContract/blob/14bcabc338130fb2aba2ce8bd27b885305566fce/contracts/Upgradeability/Forwardable.sol), [WindingTree](https://github.com/windingtree/upgradeable-token-labs/blob/af3b66096091d8282d5c9c55c33365315d85f3e1/contracts/upgradable/DelegateProxy.sol), [Decentraland](https://github.com/decentraland/land/blob/5154046844f6f94a5074e82abe01381e6fd7c39d/contracts/upgradable/DelegateProxy.sol), and many others. +Delegating proxies are widely in use, as a means to both support upgrades and reduce gas costs of deployments. Examples of these proxies are found in OpenZeppelin Contracts, Gnosis, AragonOS, Melonport, Limechain, WindingTree, Decentraland, and many others. However, the lack of a common interface for obtaining the logic address for a proxy makes it impossible to build common tools that act upon this information. -A classic example of this is a block explorer. Here, the end user wants to interact with the underlying logic contract and not the proxy itself. Having a common way to retrieve the logic contract address from a proxy would allow a block explorer, among other things, to show the ABI of the logic contract and not that of the proxy (see [this proxy](https://etherscan.io/token/0x00fdae9174357424a78afaad98da36fd66dd9e03#readContract) for an example). The explorer should check the storage of the contract at the distinguished slots to determine if it is indeed a proxy, in which case it should show information on both the proxy and the logic contract. - -Another example are logic contracts that explicitly act upon the fact that they are being proxied. This allows them to potentially trigger a code update as part of their logic, as is the case of [Universal Upgradeable Proxy Standard (EIP1822)](./eip-1822.md). A common storage slot allows these use cases independently of the specific proxy implementation being used. - -## Specification -The main requirement for the storage slots chosen is that they must never be picked by the compiler to store any contract state variable. Otherwise, a logic contract could inadvertently overwrite this information on the proxy when writing to a variable of its own. - -[Solidity](https://solidity.readthedocs.io/en/v0.4.21/miscellaneous.html#layout-of-state-variables-in-storage) maps variables to storage based on the order in which they were declared, after the contract inheritance chain is linearized: the first variable is assigned the first slot, and so on. The exception are values in dynamic arrays and mappings, which are stored in the hash of the concatenation of the key and the storage slot. The Solidity development team has [confirmed](https://twitter.com/ethchris/status/1073692785176444928) that the storage layout is to be preserved among new versions. Vyper seems to [follow the same strategy as Solidity](https://github.com/ethereum/vyper/issues/769). Note that contracts written in other languages, or directly in assembly, may incur in clashes. +A classic example of this is a block explorer. Here, the end user wants to interact with the underlying logic contract and not the proxy itself. Having a common way to retrieve the logic contract address from a proxy allows a block explorer to show the ABI of the logic contract and not that of the proxy. The explorer checks the storage of the contract at the distinguished slots to determine if it is indeed a proxy, in which case it shows information on both the proxy and the logic contract. As an example, this is how `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` is shown on Etherscan: -As such, the proposed storage slots for proxy-specific information are the following. They are chosen in such a way so they are guaranteed to not clash with state variables allocated by the compiler, since they depend on the hash of a string that does not start with a storage index. Furthermore, a `-1` offset is added so the preimage of the hash cannot be known, further reducing the chances of a possible attack. +![Sample proxy on Etherscan](../assets/eip-1967/Sample-proxy-on-etherscan.png) -More slots for additional information can be added in subsequent ERCs as needed. +Another example is logic contracts that explicitly act upon the fact that they are being proxied. This allows them to potentially trigger a code update as part of their logic. A common storage slot allows these use cases independently of the specific proxy implementation being used. +## Specification Monitoring of proxies is essential to the security of many applications. It is thus essential to have the ability to track changes to the implementation and admin slots. Unfortunately, tracking changes to storage slots is not easy. Consequently, it is recommended that any function that changes any of these slots SHOULD also emit the corresponding event. This includes initialization, from `0x0` to the first non-zero value. +The proposed storage slots for proxy-specific information are the following. More slots for additional information can be added in subsequent ERCs as needed. + ### Logic contract address Storage slot `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` (obtained as `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`). -Holds the address of the logic contract that this proxy delegates to. SHOULD be empty if a beacon is used instead. Changes to this slot should be notified by the event: +Holds the address of the logic contract that this proxy delegates to. SHOULD be empty if a beacon is used instead. Changes to this slot SHOULD be notified by the event: ```solidity event Upgraded(address indexed implementation); @@ -52,18 +46,32 @@ event Upgraded(address indexed implementation); Storage slot `0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50` (obtained as `bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)`). -Holds the address of the beacon contract this proxy relies on (fallback). SHOULD be empty if a logic address is used directly instead, and should only be considered if the logic contract slot is empty. See the Beacons section below. Changes to this slot should be notified by the event: +Holds the address of the beacon contract this proxy relies on (fallback). SHOULD be empty if a logic address is used directly instead, and should only be considered if the logic contract slot is empty. Changes to this slot SHOULD be notified by the event: ```solidity event BeaconUpgraded(address indexed beacon); ``` +Beacons are used for keeping the logic address for multiple proxies in a single location, allowing the upgrade of multiple proxies by modifying a single storage slot. A beacon contract MUST implement the function: + +``` +function implementation() returns (address) +``` + +Beacon based proxy contracts do not use the logic contract slot. Instead, they use the beacon contract slot to store the address of the beacon they are attached to. In order to know the logic contract used by a beacon proxy, a client SHOULD: + +- Read the address of the beacon for the beacon logic storage slot; +- Call the `implementation()` function on the beacon contract. + +The result of the `implementation()` function on the beacon contract SHOULD NOT depend on the caller (`msg.sender`). + + ### Admin address Storage slot `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` (obtained as `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`). -Holds the address that is allowed to upgrade the logic contract address for this proxy (optional). Changes to this slot should be notified by the event: +Holds the address that is allowed to upgrade the logic contract address for this proxy (optional). Changes to this slot SHOULD be notified by the event: ```solidity event AdminChanged(address previousAdmin, address newAdmin); @@ -71,39 +79,390 @@ event AdminChanged(address previousAdmin, address newAdmin); ## Rationale -This EIP standardises the **storage slot** for the logic contract address, instead of a public method on the proxy contract as [DelegateProxy (EIP897)](./eip-897.md) does. The rationale for this is that proxies should never expose functions to end users that could potentially clash with those of the logic contract. +This EIP standardises the **storage slot** for the logic contract address, instead of a public method on the proxy contract. The rationale for this is that proxies should never expose functions to end users that could potentially clash with those of the logic contract. Note that a clash may occur even among functions with different names, since the ABI relies on just four bytes for the function selector. This can lead to unexpected errors, or even exploits, where a call to a proxied contract returns a different value than expected, since the proxy intercepts the call and answers with a value of its own. -From [_Malicious backdoors in Ethereum proxies_](https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357) by Nomic Labs: +From _Malicious backdoors in Ethereum proxies_ by Nomic Labs: > Any function in the Proxy contract whose selector matches with one in the implementation contract will be called directly, completely skipping the implementation code. > > Because the function selectors use a fixed amount of bytes, there will always be the possibility of a clash. This isn’t an issue for day to day development, given that the Solidity compiler will detect a selector clash within a contract, but this becomes exploitable when selectors are used for cross-contract interaction. Clashes can be abused to create a seemingly well-behaved contract that’s actually concealing a backdoor. -The fact that proxy public functions are potentially exploitable makes it necessary to standardise the logic contract address in a different way. This approach is also used as part of [Universal Upgradeable Proxy Standard (EIP1822)](./eip-1822.md), which could become a specialization of this EIP. +The fact that proxy public functions are potentially exploitable makes it necessary to standardise the logic contract address in a different way. -## Beacons +The main requirement for the storage slots chosen is that they must never be picked by the compiler to store any contract state variable. Otherwise, a logic contract could inadvertently overwrite this information on the proxy when writing to a variable of its own. -Some use-cases rely on multiple proxy contracts delegating their calls to the same logic contract. If each contract was to store the address of the logic contract using the logic contract storage slot, upgrading them would require writing to the corresponding storage slot of every instance, which could be very expensive in terms of gas. +Solidity maps variables to storage based on the order in which they were declared, after the contract inheritance chain is linearized: the first variable is assigned the first slot, and so on. The exception is values in dynamic arrays and mappings, which are stored in the hash of the concatenation of the key and the storage slot. The Solidity development team has confirmed that the storage layout is to be preserved among new versions: -Another approach is to have each proxy retrieve the logic contract's address from a dedicated "beacon". Using this pattern, the address of the logic contract can be modified in the beacon and take immediate effect for all the corresponding proxy contracts. +> The layout of state variables in storage is considered to be part of the external interface of Solidity due to the fact that storage pointers can be passed to libraries. This means that any change to the rules outlined in this section is considered a breaking change of the language and due to its critical nature should be considered very carefully before being executed. In the event of such a breaking change, we would want to release a compatibility mode in which the compiler would generate bytecode supporting the old layout. -A beacon MUST implement the function: +Vyper seems to follow the same strategy as Solidity. Note that contracts written in other languages, or directly in assembly, may incur in clashes. -``` -function implementation() returns (address) -``` +They are chosen in such a way so they are guaranteed to not clash with state variables allocated by the compiler, since they depend on the hash of a string that does not start with a storage index. Furthermore, a `-1` offset is added so the preimage of the hash cannot be known, further reducing the chances of a possible attack. -Beacon based proxy contracts do not use the logic contract slot. Instead, they use the beacon contract slot to store the address of the beacon they are attached to. In order to know the logic contract used by a beacon proxy, one should: +## Reference Implementation -- Read the address of the beacon for the beacon logic storage slot; -- Call the `implementation()` function on the beacon contract. +```solidity +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + */ +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializing the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { + assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); + _upgradeToAndCall(_logic, _data, false); + } + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + return ERC1967Upgrade._getImplementation(); + } +} + +/** + * @dev This abstract contract provides getters and event emitting update functions for + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. + */ +abstract contract ERC1967Upgrade { + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Emitted when the implementation is upgraded. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Returns the current implementation address. + */ + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 implementation slot. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + /** + * @dev Perform implementation upgrade + * + * Emits an {Upgraded} event. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Perform implementation upgrade with additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCall( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + _upgradeTo(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + } + + /** + * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCallSecure( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + address oldImplementation = _getImplementation(); + + // Initial upgrade and setup call + _setImplementation(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + + // Perform rollback test if not already in progress + StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); + if (!rollbackTesting.value) { + // Trigger rollback using upgradeTo from the new implementation + rollbackTesting.value = true; + Address.functionDelegateCall( + newImplementation, + abi.encodeWithSignature("upgradeTo(address)", oldImplementation) + ); + rollbackTesting.value = false; + // Check rollback was effective + require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); + // Finally reset to the new implementation and log the upgrade + _upgradeTo(newImplementation); + } + } + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Returns the current admin. + */ + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + */ + function _changeAdmin(address newAdmin) internal { + emit AdminChanged(_getAdmin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Emitted when the beacon is upgraded. + */ + event BeaconUpgraded(address indexed beacon); + + /** + * @dev Returns the current beacon. + */ + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + /** + * @dev Stores a new beacon in the EIP1967 beacon slot. + */ + function _setBeacon(address newBeacon) private { + require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } + + /** + * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does + * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). + * + * Emits a {BeaconUpgraded} event. + */ + function _upgradeBeaconToAndCall( + address newBeacon, + bytes memory data, + bool forceCall + ) internal { + _setBeacon(newBeacon); + emit BeaconUpgraded(newBeacon); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); + } + } +} + +/** + * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM + * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to + * be specified by overriding the virtual {_implementation} function. + * + * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a + * different contract through the {_delegate} function. + * + * The success and return data of the delegated call will be returned back to the caller of the proxy. + */ +abstract contract Proxy { + /** + * @dev Delegates the current call to `implementation`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _delegate(address implementation) internal virtual { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function + * and {_fallback} should delegate. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates the current call to the address returned by `_implementation()`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable virtual { + _fallback(); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data + * is empty. + */ + receive() external payable virtual { + _fallback(); + } + + /** + * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` + * call, or as part of the Solidity `fallback` or `receive` functions. + * + * If overridden should call `super._beforeFallback()`. + */ + function _beforeFallback() internal virtual {} +} + +/** + * @dev Library for reading and writing primitive types to specific storage slots. + * + * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. + * This library helps with reading and writing to such slots without the need for inline assembly. + * + * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. + */ +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /** + * @dev Returns an `AddressSlot` with member `value` located at `slot`. + */ + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `BooleanSlot` with member `value` located at `slot`. + */ + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + */ + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Uint256Slot` with member `value` located at `slot`. + */ + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + assembly { + r.slot := slot + } + } +} +``` -The result of the `implementation()` function on the beacon contract SHOULD NOT depend on the caller (`msg.sender`). +## Security Considerations + +This ERC relies on the fact that the chosen storage slots are **not** to be allocated by the solidity compiler. This guarantees that an implementation contract will not accidentally overwrite any of the information required for the proxy to operate. As such, locations with a high slot number were chosen to avoid clashes with the slots allocated by the compiler. Also, locations with no known preimage were picked, to ensure that a write to mapping with a maliciously crafted key could not overwrite it. -## Implementation -Sample proxy implementations that follow this standard can be found in the [ZeppelinOS repository](https://github.com/zeppelinos/zos/blob/dc9e4ed/packages/lib/contracts/upgradeability/BaseUpgradeabilityProxy.sol), albeit with a different set of slots. +Logic contracts that intend to modify proxy-specific information must do so deliberately (as is the case with UUPS) by writing to the specific storage slot. ## 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-197.md b/EIPS/eip-197.md index 795d721ca6fdc8..9f9b3d33440e7c 100644 --- a/EIPS/eip-197.md +++ b/EIPS/eip-197.md @@ -137,4 +137,4 @@ Implementations are available here: ## 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-1973.md b/EIPS/eip-1973.md index 1f64ae0f35888e..d3c94b9bf0850b 100644 --- a/EIPS/eip-1973.md +++ b/EIPS/eip-1973.md @@ -4,7 +4,7 @@ title: Scalable Rewards author: Lee Raj (@lerajk), Qin Jian (@qinjian) type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2019-04-01 --- diff --git a/EIPS/eip-198.md b/EIPS/eip-198.md index a57c4cf194b2c8..5c8f066acb210c 100644 --- a/EIPS/eip-198.md +++ b/EIPS/eip-198.md @@ -1,7 +1,7 @@ --- eip: 198 title: Big integer modular exponentiation -author: Vitalik Buterin +author: Vitalik Buterin (@vbuterin) status: Final type: Standards Track category: Core @@ -101,4 +101,4 @@ The bit-based exponent calculation is done specifically to fairly charge for the # 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-1985.md b/EIPS/eip-1985.md index f3f713ff81c7c5..9bb02f1635f7b6 100644 --- a/EIPS/eip-1985.md +++ b/EIPS/eip-1985.md @@ -3,7 +3,7 @@ eip: 1985 title: Sane limits for certain EVM parameters author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast) discussions-to: https://ethereum-magicians.org/t/eip-1985-sane-limits-for-certain-evm-parameters/3224 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2018-08-01 @@ -127,7 +127,7 @@ TBA 1. Does the gas limit apply to the gas argument for call instructions? ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [EVMC]: https://github.com/ethereum/evmc [Aleth]: https://github.com/ethereum/aleth diff --git a/EIPS/eip-1996.md b/EIPS/eip-1996.md index 8043fa5c816e3d..fffb24a77cb62c 100644 --- a/EIPS/eip-1996.md +++ b/EIPS/eip-1996.md @@ -3,7 +3,7 @@ eip: 1996 title: Holdable Token author: Julio Faura , Fernando Paris , Daniel Lehrner discussions-to: https://github.com/ethereum/EIPs/issues/2103 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-04-10 @@ -291,4 +291,4 @@ The GitHub repository [IoBuilders/holdable-token](https://github.com/IoBuilders/ This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). ## 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-2.md b/EIPS/eip-2.md index 14bc9e606f93ae..fd1da487b992ff 100644 --- a/EIPS/eip-2.md +++ b/EIPS/eip-2.md @@ -1,7 +1,7 @@ --- eip: 2 title: Homestead Hard-fork Changes -author: Vitalik Buterin +author: Vitalik Buterin (@vbuterin) status: Final type: Standards Track category: Core diff --git a/EIPS/eip-20-token-standard.md b/EIPS/eip-20-token-standard.md deleted file mode 100644 index 0a18b5683ad3b8..00000000000000 --- a/EIPS/eip-20-token-standard.md +++ /dev/null @@ -1 +0,0 @@ -Moved to [EIP-20](./eip-20.md). diff --git a/EIPS/eip-20.md b/EIPS/eip-20.md index a84396df6856f1..98600bad836171 100644 --- a/EIPS/eip-20.md +++ b/EIPS/eip-20.md @@ -1,6 +1,6 @@ --- eip: 20 -title: ERC-20 Token Standard +title: Token Standard author: Fabian Vogelsteller , Vitalik Buterin type: Standards Track category: ERC @@ -190,4 +190,4 @@ Historical links related to this standard: ## 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-2003.md b/EIPS/eip-2003.md index d8b81488ee0ac5..c96aac070908c8 100644 --- a/EIPS/eip-2003.md +++ b/EIPS/eip-2003.md @@ -3,7 +3,7 @@ eip: 2003 title: EVMC modules for implementations of precompiled contracts author: Paweł Bylica (@chfast), Alex Beregszaszi (@axic) discussions-to: https://github.com/ethereum/evmc/issues/259 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2019-05-09 @@ -104,7 +104,7 @@ EVMC provides the [evmc-vmtester] tool for checking compatibility with the EVMC ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [EIP-1352]: ./eip-1352.md diff --git a/EIPS/eip-2009.md b/EIPS/eip-2009.md index 31ddc0a2fdd021..c628e6b2343e06 100644 --- a/EIPS/eip-2009.md +++ b/EIPS/eip-2009.md @@ -3,7 +3,7 @@ eip: 2009 title: Compliance Service author: Daniel Lehrner discussions-to: https://github.com/ethereum/EIPs/issues/2022 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-05-09 @@ -290,7 +290,7 @@ The GitHub repository [IoBuilders/compliance-service](https://github.com/IoBuild This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [KYC-Wikipedia]: https://en.wikipedia.org/wiki/Know_your_customer [AML-Wikipedia]: https://en.wikipedia.org/wiki/Money_laundering#Anti-money_laundering diff --git a/EIPS/eip-2014.md b/EIPS/eip-2014.md index ee8df44a72a141..3eacb6bdfe27f5 100644 --- a/EIPS/eip-2014.md +++ b/EIPS/eip-2014.md @@ -3,7 +3,7 @@ eip: 2014 title: Extended State Oracle author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2014-extended-state-oracle/3301 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-10 @@ -87,7 +87,7 @@ TBA 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). [Contract ABI Encoding]: https://solidity.readthedocs.io/en/latest/abi-spec.html [EIP-140]: ./eip-140.md diff --git a/EIPS/eip-2015.md b/EIPS/eip-2015.md index e57fc49ffbf749..6b359a5e19525d 100644 --- a/EIPS/eip-2015.md +++ b/EIPS/eip-2015.md @@ -1,108 +1,74 @@ --- eip: 2015 -title: Wallet Update Ethereum Chain RPC Method (`wallet_updateEthereumChain`) -author: Pedro Gomes (@pedrouid), Erik Marks (@rekmarks) +title: wallet_updateEthereumChain RPC Method +description: Adds an RPC method to switch betweeen EVM-compatible chains +author: Pedro Gomes (@pedrouid), Erik Marks (@rekmarks), Pandapip1 (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/eip-2015-wallet-update-chain-json-rpc-method-wallet-updatechain/3274 -status: Draft +status: Review type: Standards Track category: Interface created: 2019-05-12 -requires: 155, 1474 +requires: 155 --- -## Simple Summary -Wallets can update the active chain when connected to a Dapp but not vice-versa, with `wallet_updateEthereumChain` the Dapp will be able to request this change from the Wallet. - ## Abstract -Dapp can request the Wallet to switch chains by providing the minimal parameters of `chainId`, `chainName`, `rpcUrl`, `nativeCurrency` and `blockExplorerUrl`. The Wallet will display a UI element to inform the user of this change. -## Motivation -Wallet and Dapp communication rely on the present provider that acts as middleware between the two. Using JSON-RPC methods, the Dapp is able to access not only the active accounts but also the active chain. With [EIP-1102](./eip-1102.md) we introduced the ability for Dapps to request access to the active accounts and the Wallet is able to provide a simple UI to inform the user of this action however the same is not currently possible for switching chains. The current pattern is to display some UI to request the user to switch chains within the Dapp, however this could be easily improved by triggering a UI from the Wallet side that can be approved or rejected by the user instead. +This EIP adds a wallet-namespaced RPC endpoint, `wallet_updateEthereumChain`, providing a standard interface for switching chains. The method takes the minimal parameters of `chainId`, `chainName`, `rpcUrl`, `nativeCurrency` and `blockExplorerUrl`. ## Specification -The JSON RPC method will be part of `wallet_` namespaced methods which aim to improve the UX and interoperability between Dapps and Wallets. - -### Required Parameters -- chainId (string): the id of the chain compliant with EIP-155 -- chainName (string): the name of the chain to update -- rpcUrl (string): the url endpoint for RPC requests for this chain -- nativeCurrency (Object): includes three fields for `name` (string), `symbol` (string) and `decimals` (number) -- blockExplorerUrl (string): the url endpoint for a block explorer web site for the chain. - -### Best Practices -- The Wallet should display a UI view similar to a [EIP-1102](./eip-1102.md) informing the user that the currently connected Dapp wants to switch to the specified chain. -- the Wallet should default the rpcUrl to any existing endpoints matching a chainId known previously to the wallet, otherwise it will use the provided rpcUrl as a fallback. -- the Wallet should call the rpcUrl with `net_version` and `eth_chainId` to verify the provided chainId and networkId match the responses from the rpcUrl -- the Wallet should change all nativeCurrency symbols to the provided parameter - -### Example 1 -A JSON-RPC request from a Dapp to switch the Ethereum Goerli chain would be as follows: -```json -{ - "id":1, - "jsonrpc": "2.0", - "method": "wallet_updateChain", - "params": [ - { - "chainId": 0x5, - "chainName": "Goerli", - "rpcUrl": "https://goerli.infura.io/v3/406405f9c65348f99d0d5c27104b2213", - "nativeCurrency": { - "name": "Goerli ETH", - "symbol": "gorETH" - }, - "blockExplorerUrl": "https://goerli.etherscan.io" - } - ] + +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. + +This proposal adds a method to a wallet's web3 provider API: `wallet_updateEthereumChain`. + +### `wallet_updateEthereumChain` + +The `wallet_updateEthereumChain` method is used to switch to a network, and registering it with the wallet if it isn't already recognized. + +The `wallet_updateEthereumChain` method takes one parameter, an `EthereumChainSwitchRequest` object, defined below: + +```typescript +interface NativeCurrencyData { + name: string; + symbol: string; + decimals: number; } -``` -### Example 2 -A JSON-RPC request from a Dapp to switch the POA Network's xDAI chain would be as follows: -```json -{ - "id":1, - "jsonrpc": "2.0", - "method": "wallet_updateChain", - "params": [ - { - "chainId": "0x5", - "chainName": "Goerli", - "rpcUrl": "https://goerli.infura.io/v3/406405f9c65348f99d0d5c27104b2213", - "nativeCurrency": { - "name": "Goerli ETH", - "symbol": "gorETH" - }, - "blockExplorerUrl": "https://goerli.etherscan.io" - } - ] +interface EthereumChainSwitchRequest { + chainId: string; + chainName?: string; + rpcUrls?: string[]; + nativeCurrency?: NativeCurrencyData; + blockExplorerUrl?: string; } ``` -### Responses +The `chainId` is the `0x`-prefixed [EIP-155](./eip-155.md)-compliant chain ID. The `chainName` is a suggested human-readable name of the chain, to be displayed to the user. The `rpcUrls` array is a list of RPC endpoints for the given `chainId`. The `nativeCurrency` object suggests how the native currency should be displayed. Its parameters, `name`, `symbol`, and `decimals`, should be interpreted like in [ERC-20](./eip-20.md). Finally, the `blockExplorerUrl` should link to a block explorer compatible with the given `chainId`. -A success response: +All keys other than the `chainId` are optional. All keys other than `chainId` are suggestions to the wallet. Wallets can choose to ignore or display other data to users. Wallets should prompt the user before switching or adding chains. Wallets should also store a default list of data for commonly-used chains, in order to avoid phishing attacks. Wallets MUST sanitize each RPC url before using it to send other requests, including ensuring that it responds correctly to the `net_version` and `eth_chainId` methods. -```json -{ - "id": 1, - "jsonrpc": "2.0", - "result": true -} -``` +The `wallet_updateEthereumChain` method returns `true` if the active chain matches the requested chain, regardless of whether the chain was already active or was added to the wallet previously. If the user rejects the request, it must return an error with code `4001`. -A failure response: +## Rationale -```json -{ - "id": 1, - "jsonrpc": "2.0", - "error": { - "code": 4001, - "message": "The user rejected the request." - } -} -``` +The `wallet_updateEthereumChain` method is designed to be as simple as possible, while still providing the necessary information for a wallet to switch to a new chain. The `chainId` is the only required parameter, as it is the only parameter that is guaranteed to be unique. The `chainName` is included to provide a human-readable name for the chain, and the `rpcUrls` array is included to provide a list of RPC endpoints for the chain. The `nativeCurrency` object is included to provide a suggestion for how the native currency should be displayed. Finally, the `blockExplorerUrl` is included to provide a link to a block explorer for the chain. + +The `wallet_updateEthereumChain` method is namespaced under `wallet_` to avoid conflicts with other methods. The `wallet_` prefix is used by other methods that are wallet-specific, such as `wallet_addEthereumChain` and `wallet_switchEthereumChain`. + +## Backwards Compatibility + +This EIP is fully backwards compatible. + +## Security Considerations + +### Server-Side Request Forgery (SSRF) + +The `rpcUrls` parameter is a list of RPC endpoints for the chain. Wallets should sanitize each RPC url before using it to send other requests, including ensuring that it responds correctly to the `net_version` and `eth_chainId` methods. + +### Phishing + +Wallets should store a default list of data for commonly-used chains, in order to avoid phishing attacks. ## 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-2018.md b/EIPS/eip-2018.md index ec98a676098488..9959a341a2a9b6 100644 --- a/EIPS/eip-2018.md +++ b/EIPS/eip-2018.md @@ -3,7 +3,7 @@ eip: 2018 title: Clearable Token author: Julio Faura , Fernando Paris , Daniel Lehrner discussions-to: https://github.com/ethereum/EIPs/issues/2104 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-04-30 @@ -251,7 +251,7 @@ The GitHub repository [IoBuilders/clearable-token](https://github.com/IoBuilders This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [1] https://en.wikipedia.org/wiki/Clearing_(finance) diff --git a/EIPS/eip-2019.md b/EIPS/eip-2019.md index 152bec120528e7..a59cc4fbc5fe25 100644 --- a/EIPS/eip-2019.md +++ b/EIPS/eip-2019.md @@ -3,7 +3,7 @@ eip: 2019 title: Fundable Token author: Fernando Paris , Julio Faura , Daniel Lehrner discussions-to: https://github.com/ethereum/EIPs/issues/2105 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-05-10 @@ -250,6 +250,6 @@ The GitHub repository [IoBuilders/fundable-token](https://github.com/IoBuilders/ This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [ERC-20]: ./eip-20.md diff --git a/EIPS/eip-2020.md b/EIPS/eip-2020.md index 24dbf6bcf5961d..ddf3419f59ab49 100644 --- a/EIPS/eip-2020.md +++ b/EIPS/eip-2020.md @@ -3,7 +3,7 @@ eip: 2020 title: E-Money Standard Token author: Julio Faura , Fernando Paris , Daniel Lehrner discussions-to: https://github.com/ethereum/EIPs/issues/2407 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-05-10 @@ -222,7 +222,7 @@ The GitHub repository [IoBuilders/em-token](https://github.com/IoBuilders/em-tok This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [ERC-20]: ./eip-20.md [ERC-1066]: ./eip-1066.md diff --git a/EIPS/eip-2021.md b/EIPS/eip-2021.md index f8e43fcc424f41..8464686dd789ce 100644 --- a/EIPS/eip-2021.md +++ b/EIPS/eip-2021.md @@ -3,7 +3,7 @@ eip: 2021 title: Payoutable Token author: Fernando Paris , Julio Faura , Daniel Lehrner discussions-to: https://github.com/ethereum/EIPs/issues/2106 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-05-10 @@ -283,7 +283,7 @@ The GitHub repository [IoBuilders/payoutable-token](https://github.com/IoBuilder This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [ERC-20]: ./eip-20.md [EIP-1066]: ./eip-1066.md diff --git a/EIPS/eip-2025.md b/EIPS/eip-2025.md index 44115af814e00b..ae9baa017c31db 100644 --- a/EIPS/eip-2025.md +++ b/EIPS/eip-2025.md @@ -65,7 +65,7 @@ FOR BENEFICIARY in BENEFICIARY_ADDRESSES: *With a price of Etheruem at $150.00 this will raise approx USD $2,325,000.00 for developing Eth1.X over the next 18 months.* -![Block Rewards Distribution](/assets/eip-2025/block_rewards_distribution.png) *Specific Addresses to be determined +![Block Rewards Distribution](../assets/eip-2025/block_rewards_distribution.png) *Specific Addresses to be determined * [FAQ - Why hardcoded values?]( #why-hardcoded-values ) @@ -80,7 +80,7 @@ The Eth1x initiative needs funding now, not in 18 months. A loan is necessary to ### Loan Repayment -![Loan State Diagram](/assets/eip-2025/loan_state.png) +![Loan State Diagram](../assets/eip-2025/loan_state.png) There is a risk that the investors lose part of their contribution in the case that this EIP is rejected by the community between the time the funds have been collected and the beginning of the payout schedule. In this case all remaining funds will be returned to the contributors. The interest on the loan is an incentive for investors to participate in spite of this risk. Their downside is limited to the amount of funds spent before this EIP is accepted or rejected, which should be no more than about 5%, while their upside consists of the 10% simple interest paid over the period. @@ -206,4 +206,4 @@ The loan is for the specific use of supporting Eth1.X research and development. #### ## 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-2026.md b/EIPS/eip-2026.md index cc26677dc46dca..4523184bd6c481 100644 --- a/EIPS/eip-2026.md +++ b/EIPS/eip-2026.md @@ -3,7 +3,7 @@ eip: 2026 title: State Rent H - Fixed Prepayment for accounts author: Alexey Akhunov (@AlexeyAkhunov) discussions-to: https://ethereum-magicians.org/t/eip-2026-fixed-rent-prepayment-for-all-accounts-change-h-from-state-rent-v3-proposal/3273 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-14 @@ -58,4 +58,4 @@ Tests cases will be generated out of a reference implementation. There will be proof of concept implementation to refine and clarify the specification. ## 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-2027.md b/EIPS/eip-2027.md index e09f93cf7b0add..9faf2ab6a2307b 100644 --- a/EIPS/eip-2027.md +++ b/EIPS/eip-2027.md @@ -3,7 +3,7 @@ eip: 2027 title: State Rent C - Net contract size accounting author: Alexey Akhunov (@AlexeyAkhunov) discussions-to: https://ethereum-magicians.org/t/eip-2027-net-contract-size-accounting-change-c-from-state-rent-v3-proposal/3275 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-14 @@ -61,4 +61,4 @@ Tests cases will be generated out of a reference implementation. There will be proof of concept implementation to refine and clarify the specification. ## 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-2028.md b/EIPS/eip-2028.md index 6a7d1bd8d2a618..707e63ce5bd00a 100644 --- a/EIPS/eip-2028.md +++ b/EIPS/eip-2028.md @@ -76,4 +76,4 @@ To suggest the gas cost of calldata we shall conduct two types of tests: [4] Vitalik Buterin: [Uncle Rate and Transaction Fee Analysis](https://blog.ethereum.org/2016/10/31/uncle-rate-transaction-fee-analysis/) ## 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-2029.md b/EIPS/eip-2029.md index b6da59d61756d5..d3cacaa692ef3e 100644 --- a/EIPS/eip-2029.md +++ b/EIPS/eip-2029.md @@ -3,7 +3,7 @@ eip: 2029 title: State Rent A - State counters contract author: Alexey Akhunov (@AlexeyAkhunov) discussions-to: https://ethereum-magicians.org/t/eip-2029-state-counters-contract-change-a-from-state-rent-v3-proposal/3279 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-15 @@ -48,4 +48,4 @@ Tests cases will be created to ensure that the state counter contract returns it Implementation is envisaged as a transaction that can be posted from any Ethereum address and will cause the deployment of the state counter contract. ## 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-2031.md b/EIPS/eip-2031.md index 40de135a8f550d..ceb2723f79ddf4 100644 --- a/EIPS/eip-2031.md +++ b/EIPS/eip-2031.md @@ -3,7 +3,7 @@ eip: 2031 title: State Rent B - Net transaction counter author: Alexey Akhunov (@AlexeyAkhunov) discussions-to: https://ethereum-magicians.org/t/eip-2031-net-transaction-counter-change-b-from-state-rent-v3-proposal/3283 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-15 @@ -47,5 +47,5 @@ Tests cases will be generated out of a reference implementation. There will be proof of concept implementation to refine and clarify the specification. ## 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-2035.md b/EIPS/eip-2035.md index 81e469ad61f69c..11342e67c6f3c4 100644 --- a/EIPS/eip-2035.md +++ b/EIPS/eip-2035.md @@ -3,7 +3,7 @@ eip: 2035 title: Stateless Clients - Repricing SLOAD and SSTORE to pay for block proofs author: Alexey Akhunov (@AlexeyAkhunov) discussions-to: https://ethereum-magicians.org/t/eip-2035-stateless-clients-repricing-sload-and-sstore-to-pay-for-block-proofs/3284 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-16 @@ -67,5 +67,5 @@ Tests cases will be generated out of a reference implementation. There will be proof of concept implementation to refine and clarify the specification. ## 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-2045.md b/EIPS/eip-2045.md index 1b907af472bf63..e5a8b602ad6a5b 100644 --- a/EIPS/eip-2045.md +++ b/EIPS/eip-2045.md @@ -3,7 +3,7 @@ eip: 2045 title: Particle gas costs for EVM opcodes author: Casey Detrio (@cdetrio), Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2045-fractional-gas-costs/3311 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-17 @@ -118,4 +118,4 @@ TODO 3. The term "particle" was inspired by a proposal for [Ewasm gas costs](https://github.com/ewasm/design/blob/e77d8e3de42784f40a803a23f58ef06881142d9f/determining_wasm_gas_costs.md). ## 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-2046.md b/EIPS/eip-2046.md index 00d3644ca58b40..91171d02593dd5 100644 --- a/EIPS/eip-2046.md +++ b/EIPS/eip-2046.md @@ -3,7 +3,7 @@ eip: 2046 title: Reduced gas cost for static calls made to precompiles author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2046-reduced-gas-cost-for-static-calls-made-to-precompiles/3291 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-05-17 @@ -63,7 +63,7 @@ Jordi Baylina (@jbaylina) and Matthew Di Ferrante (@mattdf) who have proposed th ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [Yellow Paper]: https://github.com/ethereum/yellowpaper [EIP-1352]: ./eip-1352.md diff --git a/EIPS/eip-205.md b/EIPS/eip-205.md index e4753a3ef65047..451cc835e4f67a 100644 --- a/EIPS/eip-205.md +++ b/EIPS/eip-205.md @@ -4,7 +4,7 @@ title: ENS support for contract ABIs author: Nick Johnson type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2017-02-06 requires: 137, 181 --- @@ -66,4 +66,4 @@ Storing ABIs onchain avoids the need to introduce additional dependencies for ap The two-step resolution process permits different names to provide different ABIs for the same contract, such as in the case where it's useful to provide a minimal ABI to some callers, as well as specifying ABIs for contracts that did not specify one of their own. The fallback to looking up an ABI on the reverse record permits contracts to specify their own canonical ABI, and prevents the need for duplication when multiple names reference the same contract without the need for different ABIs. ## 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-2069.md b/EIPS/eip-2069.md index c8e53d48cb5bce..14d8d52f587860 100644 --- a/EIPS/eip-2069.md +++ b/EIPS/eip-2069.md @@ -3,7 +3,7 @@ eip: 2069 title: Recommendation for using YAML ABI in ERCs/EIPs author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2069-recommendation-for-using-yaml-abi-in-specifications/3347 -status: Draft +status: Stagnant type: Informational created: 2017-02-11 --- @@ -99,7 +99,7 @@ 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). [Standard Contract ABI]: https://solidity.readthedocs.io/en/latest/abi-spec.html [yamabi]: https://github.com/axic/yamabi/ diff --git a/EIPS/eip-2070.md b/EIPS/eip-2070.md index 66bcbb7da95fd0..6ed8df9ad820e9 100644 --- a/EIPS/eip-2070.md +++ b/EIPS/eip-2070.md @@ -4,7 +4,7 @@ title: "Hardfork Meta: Berlin" author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/hardfork-meta-eip-2070-berlin-discussion/3561 type: Meta -status: Draft +status: Stagnant created: 2019-05-20 requires: 1679 --- @@ -22,4 +22,4 @@ For an accurate status please refer to the [`berlin.md`](https://github.com/ethe ## 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-2098.md b/EIPS/eip-2098.md index 2a98c2a676a3bd..f440b4057e7655 100644 --- a/EIPS/eip-2098.md +++ b/EIPS/eip-2098.md @@ -1,17 +1,16 @@ --- eip: 2098 title: Compact Signature Representation -status: Draft -type: Informational +description: A compact representation of an Ethereum Signature. +status: Final +type: Standards Track +category: ERC author: Richard Moore (@ricmoo), Nick Johnson discussions-to: https://github.com/ethereum/EIPs/issues/2440 created: 2019-03-14 +requires: 2 --- -## Simple Summary - -This proposal describes a compact representation of an Ethereum Signature. - ## Abstract @@ -22,10 +21,11 @@ as well as on-chain in EVM contracts for example, in meta-transactions and multi-sig contracts. Currently signatures require 65 bytes to represent, which when aligned -to 256-bit words, requires 96 bytes (with 31 zero bytes injected). With -compact signatures, this can be reduced to 64 bytes, which remains 64 -bytes when word-aligned. - +to 256-bit words, requires 96 bytes (with 31 zero bytes injected). The +yParity in RLP-encoded transactions also require (on average) 1.5 bytes. +With compact signatures, this can be reduced to 64 bytes, which remains 64 +bytes when word-aligned, and in the case of RLP-encoded transactions +saves the 1.5 bytes required for the yParity. ## Motivation @@ -35,44 +35,46 @@ transactions in client code, reduce gas costs and reduce transaction sizes. ## Specification -A secp256k1 signature is made up of 3 parameters, `r`, `s` and `v`. The `r` -represents the `x` component on the curve (from which the `y` can be +A secp256k1 signature is made up of 3 parameters, `r`, `s` and `yParity`. +The `r` represents the `x` component on the curve (from which the `y` can be computed), and the `s` represents the challenge solution for signing by a -private key. Due to the symmetric nature of an elliptic curve, a `v` is -required, which indicates which of the 2 possible solutions was intended, +private key. Due to the symmetric nature of an elliptic curve, a `yParity` +is required, which indicates which of the 2 possible solutions was intended, by indicating its parity (odd-ness). Two key observations are required to create a compact representation. -First, the `v` parameter is always either 0 or 1 (canonically the values used -have been 27 and 28, as these values didn't collide with other binary prefixes -used in Bitcoin.) +First, the `yParity` parameter is always either 0 or 1 (canonically the values +used have historically been 27 and 28, as these values didn't collide with other +binary prefixes used in Bitcoin). Second, the top bit of the `s` parameters is **always** 0, due to the use of canonical signatures which flip the solution parity to prevent negative values, which was introduced as [a constraint in Homestead](./eip-2.md). -So, we can hijack the top bit in the `s` parameter to store the value of `v`, resulting in: +So, we can hijack the top bit in the `s` parameter to store the value of +`yParity`, resulting in: ``` -[256-bit r value][1-bit v value][255-bit s value] +[256-bit r value][1-bit yParity value][255-bit s value] ``` ### Example Implementation In Python ```python -def to_compact(r, s, v): +# Assume yParity is 0 or 1, normalized from the canonical 27 or 28 +def to_compact(r, s, yParity): return { "r": r, - "vs": ((v - 27) << 255) | s + "yParityAndS": (yParity << 255) | s } -def to_canonical(r, vs): +def to_canonical(r, yParityAndS): return { "r": r, - "s": vs & ((1 << 255) - 1), - "v": (27 + (vs >> 255)) + "s": yParityAndS & ((1 << 255) - 1), + "yParity": (yParityAndS >> 255) } ``` @@ -87,11 +89,11 @@ while reducing transaction sizes and gas costs. ## Backwards Compatibility The Compact Representation does not collide with canonical signature as -it uses 2 parameters (r, vs) while canonical signatures involve 3 -separate parameters (r, s, v). +it uses 2 parameters (r, yParityAndS) and is 64 bytes long while canonical +signatures involve 3 separate parameters (r, s, yParity) and are 65 bytes long. -## Test Vectors +## Test Cases ``` Private Key: 0x1234567890123456789012345678901234567890123456789012345678901234 @@ -101,8 +103,8 @@ Signature: s: 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064 v: 27 Compact Signature: - r: 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90 - vs: 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064 + r: 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90 + yParityAndS: 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064 ``` ``` @@ -113,94 +115,12 @@ Signature: s: 0x139c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793 v: 28 Compact Signature: - r: 0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76 - vs: 0x939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793 -``` - - -## Gas Analysis - -**Solidity** - -``` -// See: https://ropsten.etherscan.io/address/0xce826fbc499e3723df5668ea90a4bc51aeca13b8 - -pragma solidity 0.5.6; - -contract TestCompact { - address _lastAddr; - - function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public returns (address) { - _lastAddr = ecrecover(hash, v, r, s); - } - - function recoverCompact(bytes32 hash, bytes32 r, bytes32 vs) public returns (address) { - bytes32 s = vs & 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint8 v = 27 + uint8(uint256(vs) >> 255); - _lastAddr = ecrecover(hash, v, r, s); - } - - function lastAddr() public view returns (address) { - return _lastAddr; - } -} -``` - -**JavaScript** - -``` -let address = "0xcE826fbC499e3723DF5668Ea90a4BC51AeCa13b8"; -let abi = [ - 'function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public returns (address)', - 'function recoverCompact(bytes32 hash, bytes32 r, bytes32 vs) public returns (address)', - 'function lastAddr() public view returns (address)' -]; -let wallet = Wallet.fromMnemonic("relief cousin sorry cabin burst frog slush flavor pitch tragic hip disagree").connect(provider); - -let hash = "0x6ac9e083f46825a57816e023ae641ee45ac9b82cc2c370c10ec1ef4c835fa182"; -let sig = wallet.signingKey.signDigest(hash) - -let contract = new Contract(address, abi, wallet); -(async function() { - let tx, receipt; - - console.log("Canonical:"); - tx = await contract.recover(hash, sig.v, sig.r, sig.s); - console.log(" Hash: ", tx.hash); - console.log(" Data: ", tx.data); - console.log(" Length: ", utils.hexDataLength(tx.data)); - receipt = await tx.wait(); - console.log(" Gas Used:", receipt.gasUsed.toString()); - - console.log("Compact:"); - tx = await contract.recoverCompact(hash, sig.r, sig._vs); - console.log(" Hash: ", tx.hash); - console.log(" Data: ", tx.data); - console.log(" Length: ", utils.hexDataLength(tx.data)); - receipt = await tx.wait(); - console.log(" Gas Used:", receipt.gasUsed.toString()); -})(); -``` - - -**Result** - -``` -/home/ricmoo> ethers --network ropsten run test.js -Canonical: - Hash: 0x3280985011d2f25e76141681e865216e796cf065880eb9dd1f8221f3243028d2 - Data: 0xc2bf17b06ac9e083f46825a57816e023ae641ee45ac9b82cc2c370c10ec1ef4c835fa182000000000000000000000000000000000000000000000000000000000000001be95b1a8633ee7ff851f68cf4303030b1e2596d686cafd9803cef74919a9139291c492d05696da754cf342bec961d00d9f7dfac3ab90b1e9352177c7b9bfa2a5d - Length: 132 - Gas Used: 37541 -Compact: - Hash: 0x513bd8bd8710a84903235ca60591fc60f2f3db524d14cc1b6874edc628512176 - Data: 0x1230abb66ac9e083f46825a57816e023ae641ee45ac9b82cc2c370c10ec1ef4c835fa182e95b1a8633ee7ff851f68cf4303030b1e2596d686cafd9803cef74919a9139291c492d05696da754cf342bec961d00d9f7dfac3ab90b1e9352177c7b9bfa2a5d - Length: 100 - Gas Used: 37329 + r: 0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76 + yParityAndS: 0x939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793 ``` -## Implementations +## Reference Implementation The ethers.js library [supports this in v5](https://github.com/ethers-io/ethers.js/blob/ethers-v5-beta/packages/bytes/src.ts/index.ts#L323) as an unofficial property of split signatures (i.e. `sig._vs`), but should be @@ -208,12 +128,11 @@ considered an internal property that may change at discretion of the community and any changes to this EIP. -## Acknowledgments +## Security Considerations -- [Original Tweet from Nick and conversation](https://twitter.com/nicksdjohnson/status/1030830279487709185) -- [Original Solidity Inspiration](https://github.com/HarryR/solcrypto/blob/01a3c5d91053f3b8bffde328146d5f18015ebfed/contracts/ECDSA.sol#L6) +There are no additional security concerns introduced by this EIP. ## 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-210.md b/EIPS/eip-210.md index abba50af202e53..838c23021cc850 100644 --- a/EIPS/eip-210.md +++ b/EIPS/eip-210.md @@ -4,7 +4,7 @@ title: Blockhash refactoring author: Vitalik Buterin (@vbuterin) type: Standards Track category: Core -status: Draft +status: Stagnant created: 2017-02-10 --- @@ -29,11 +29,11 @@ If `block.number >= CONSTANTINOPLE_FORK_BLKNUM`, then when processing a block, b * `GAS`: 1000000 * `TO`: BLOCKHASH_CONTRACT_ADDR * `VALUE`: 0 -* `DATA`: <32 bytes corresponding to the block's prevhash> +* `DATA`: <32 bytes corresponding to the block's prevhash> If `block.number >= CONSTANTINOPLE_FORK_BLKNUM + 256`, then the BLOCKHASH opcode instead returns the result of executing a call (NOT a transaction) with the parameters: -* `SENDER`: +* `SENDER`: <account from which the opcode was called> * `GAS`: 1000000 * `TO`: BLOCKHASH_CONTRACT_ADDR * `VALUE`: 0 diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index 6e05501759231e..29f8c19219d016 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -6,7 +6,6 @@ type: Standards Track category: Core status: Final created: 2017-02-13 -replaces: 5 --- ## Simple Summary @@ -61,4 +60,4 @@ This proposal introduces two new opcodes and stays fully backwards compatible ap ## Implementation ## 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-2124.md b/EIPS/eip-2124.md index 3f8ee13b91fbee..32479f557f4dc5 100644 --- a/EIPS/eip-2124.md +++ b/EIPS/eip-2124.md @@ -3,8 +3,7 @@ eip: 2124 title: Fork identifier for chain compatibility checks author: Péter Szilágyi , Felix Lange discussions-to: https://github.com/ethereum/EIPs/issues/2125 -status: Last Call -review-period-end: 2020-09-18 +status: Final type: Standards Track category: Networking created: 2019-05-03 @@ -309,4 +308,4 @@ Geth: https://github.com/ethereum/go-ethereum/tree/master/core/forkid ## 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-2135.md b/EIPS/eip-2135.md index 970cae1b198aed..5d600773f27803 100644 --- a/EIPS/eip-2135.md +++ b/EIPS/eip-2135.md @@ -1,79 +1,167 @@ --- eip: 2135 -title: Consumable Interface +title: Consumable Interface (Tickets, etc) +description: An interface extending ERC-721 and ERC-1155 for consumability, supporting use case such as an event ticket. author: Zainan Victor Zhou (@xinbenlv) -discussions-to: https://github.com/ethereum/EIPs/issues/2135 -status: Draft +discussions-to: https://ethereum-magicians.org/t/eip-2135-erc-consumable-interface/3439 +status: Final type: Standards Track category: ERC created: 2019-06-23 +requires: 165, 721, 1155 --- -## Simple Summary -An interface marking certain digital assets being consumable. - ## Abstract -The interface identifies functions and events needed for creating a contract to be able to mark a digital asset as "consumable", and react to the request of "consumption". + +This EIP defines an interface to mark a digital asset as "consumable" and to react to its "consumption." ## Motivation -Being a digital assets sometimes means a consumable power. One most common seen examples would be a concert ticket. It will be "consumed" at the moment the ticket-holder uses the ticket to get access to enter a concert. -By having a standard ERC interface, the Ethereum ecosystem can interoperate to provide services, clients, UI, and inter-contract functionalities on top of this very general use-case. +Digital assets sometimes need to be consumaed. One of the most common examples is a concert ticket. +It is "consumed" when the ticket-holder enters the concert hall. -## Specification -The standard will mainly contain the following interface. +Having a standard interface enables interoperability for services, clients, UI, and inter-contract functionalities on top of this use-case. -### The required interface - -```solidity -pragma solidity ^0.5.8; +## Specification -contract EIP2135 { - // The main consume function - function consume(uint256 assetId) public returns(bool success); +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 interface to check whether an asset is consumable. - function isConsumable(uint256 assetId) public view returns (bool consumable); +1. Any compliant contract **MUST** implement the following interface: - // The interface to check whether an asset is consumable. - event OnConsumption(uint256 indexed assetId); +```solidity +pragma solidity >=0.7.0 <0.9.0; + +/// The ERC-165 identifier of this interface is 0xdd691946 +interface IERC2135 { + /// @notice The consume function consumes a token every time it succeeds. + /// @param _consumer the address of consumer of this token. It doesn't have + /// to be the EOA or contract Account that initiates the TX. + /// @param _assetId the NFT asset being consumed + /// @param _data extra data passed in for consume for extra message + /// or future extension. + function consume( + address _consumer, + uint256 _assetId, + uint256 _amount, + bytes calldata _data + ) external returns (bool _success); + + /// @notice The interface to check whether an asset is consumable. + /// @param _consumer the address of consumer of this token. It doesn't have + /// to be the EOA or contract Account that initiates the TX. + /// @param _assetId the NFT asset being consumed. + /// @param _amount the amount of the asset being consumed. + function isConsumableBy( + address _consumer, + uint256 _assetId, + uint256 _amount + ) external view returns (bool _consumable); + + /// @notice The event emitted when there is a successful consumption. + /// @param consumer the address of consumer of this token. It doesn't have + /// to be the EOA or contract Account that initiates the TX. + /// @param assetId the NFT asset being consumed + /// @param amount the amount of the asset being consumed. + /// @param data extra data passed in for consume for extra message + /// or future extension. + event OnConsumption( + address indexed consumer, + uint256 indexed assetId, + uint256 amount, + bytes data + ); } ``` +2. If the compliant contract is an [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md) token, in addition to `OnConsumption`, it **MUST** also emit the `Transfer` / `TransferSingle` event (as applicable) as if a token has been transferred from the current holder to the zero address if the call to `consume` method succeeds. + +3. `supportsInterface(0xdd691946)` **MUST** return `true` for any compliant contract, as per [ERC-165](./eip-165.md). + ## Rationale -The function `consume` performs the consume action. Being an interface standard, -this EIP does not impose any assumption of +1. The function `consume` performs the consume action. This EIP does not assume: - - who has the power to perform such activity. - - under what condition such consumption can occur. - -It does, however, assume the asset can be identified in a `uint256` assetId as in th parameter. A design convention and compatibility consideration is put in place to follow the ERC-721 +- who has the power to perform consumption +- under what condition consumption can occur -The event notifies subscribers whoever are interested to learn an asset is being consumed. The boolean function of `isConsumable` can be used to check whether an asset is still consumable. +It does, however, assume the asset can be identified in a `uint256` asset id as in the parameter. A design convention and compatibility consideration is put in place to follow the ERC-721 pattern. -To keep it simple, this standard *intentionally* contains no functions or events related to creation of a consumable asset. This is because the creation of a consumable asset will need to make assumption of the nature of an actual use-case. If we see some common use-case of creation, we can have another follow up standard. +2. The event notifies subscribers whoever are interested to learn an asset is being consumed. -We also left out metadata associated to the consumables from the standard. If necessary, related metadata can be created with a separate metadata extension interface like [`ERC-721 Metadata`](./eip-721.md) +3. To keep it simple, this standard *intentionally* contains no functions or events related to the creation of a consumable asset. This is because the creation of a consumable asset will need to make assumptions about the nature of an actual use-case. If there are common use-cases for creation, another follow up standard can be created. -## Backwards Compatibility +4. Metadata associated to the consumables is not included the standard. If necessary, related metadata can be created with a separate metadata extension interface like `ERC721Metadata` from [ERC-721](./eip-721.md) -This interface is designed to be compatible with ERC-721. +5. We choose to include an `address consumer` for `consume` function and `isConsumableBy` so that an NFT MAY be consumed for someone other than the transaction initiator. -## Implementation +6. We choose to include an extra `_data` field for future extension, such as +adding crypto endorsements. -A reference implementation accompany with tests of **ERC-721 based ticket** contract is built and you can found it [here](https://github.com/xinbenlv/eip-2135/blob/master/impl/contracts/Ticket721.sol). +7. We explicitly stay opinion-less about whether ERC-721 or ERC-1155 shall be required because +while we design this EIP with ERC-721 and ERC-1155 in mind mostly, we don't want to rule out +the potential future case someone use a different token standard or use it in different use cases. -See [GitHub/xinbenlv/eip-2135/impl](https://github.com/xinbenlv/eip-2135/tree/master/impl) +8. The boolean view function of `isConsumableBy` can be used to check whether an asset is +consumable by the `_consumer`. + +## Backwards Compatibility + +This interface is designed to be compatible with ERC-721 and NFT of ERC-1155. It can be tweaked to used for [ERC-20](./eip-20.md), [ERC-777](./eip-777.md) and Fungible Token of ERC-1155. ## Test Cases -See [GitHub/xinbenlv/eip-2135/impl/test](https://github.com/xinbenlv/eip-2135/tree/master/impl/test) +```ts + + describe("Consumption", function () { + it("Should consume when minted", async function () { + const fakeTokenId = "0x1234"; + const { contract, addr1 } = await loadFixture(deployFixture); + await contract.safeMint(addr1.address, fakeTokenId); + expect(await contract.balanceOf(addr1.address)).to.equal(1); + expect(await contract.ownerOf(fakeTokenId)).to.equal(addr1.address); + expect(await contract.isConsumableBy(addr1.address, fakeTokenId, 1)).to.be.true; + const tx = await contract.consume(addr1.address, fakeTokenId, 1, []); + const receipt = await tx.wait(); + const events = receipt.events.filter((x: any) => { return x.event == "OnConsumption" }); + expect(events.length).to.equal(1); + expect(events[0].args.consumer).to.equal(addr1.address); + expect(events[0].args.assetId).to.equal(fakeTokenId); + expect(events[0].args.amount).to.equal(1); + expect(await contract.balanceOf(addr1.address)).to.equal(0); + await expect(contract.ownerOf(fakeTokenId)) + .to.be.rejectedWith('ERC721: invalid token ID'); + await expect(contract.isConsumableBy(addr1.address, fakeTokenId, 1)) + .to.be.rejectedWith('ERC721: invalid token ID'); + }); + }); + + describe("EIP-165 Identifier", function () { + it("Should match", async function () { + const { contract } = await loadFixture(deployFixture); + expect(await contract.get165()).to.equal("0xdd691946"); + expect(await contract.supportsInterface("0xdd691946")).to.be.true; + }); + }); +``` + +## Reference Implementation -## Reference +A deployment of version 0x1002 has been deployed onto `goerli` testnet at address `0x3682bcD67b8A5c0257Ab163a226fBe07BF46379B`. -### Standards -- [ERC-721](./eip-721.md) +Find the reference contract verified source code on Etherscan's +`goerli` site for the address above. + +## Security Considerations + +Compliant contracts should pay attention to the balance change when a token is consumed. +When the contract is being paused, or the user is being restricted from transferring a token, +the consumeability should be consistent with the transferral restriction. + +Compliant contracts should also carefully define access control, particularlly whether any EOA or contract account may or may not initiate a `consume` method in their own use case. + +Security audits and tests should be used to verify that the access control to the `consume` +function behaves as expected. ## 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-214.md b/EIPS/eip-214.md index 3490f01553ff47..009d0e1ce76b2c 100644 --- a/EIPS/eip-214.md +++ b/EIPS/eip-214.md @@ -48,4 +48,4 @@ To be written. ## 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-2157.md b/EIPS/eip-2157.md index 699c34ba956172..47481224e37605 100644 --- a/EIPS/eip-2157.md +++ b/EIPS/eip-2157.md @@ -3,7 +3,7 @@ eip: 2157 title: dType Storage Extension - Decentralized Type System for EVM author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) discussions-to: https://github.com/ethereum/EIPs/issues/2157 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-06-28 @@ -133,4 +133,4 @@ This proposal will be updated with an appropriate implementation when consensus ## 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-2159.md b/EIPS/eip-2159.md index 1bced7eceef93b..ca57907ded2b17 100644 --- a/EIPS/eip-2159.md +++ b/EIPS/eip-2159.md @@ -56,4 +56,4 @@ Pantheon switched to using these standard metric names in its 1.2 release: https 2. Beacon chain metrics specification. https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md ## 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-2193.md b/EIPS/eip-2193.md index 1d65946b606702..34152793a68686 100644 --- a/EIPS/eip-2193.md +++ b/EIPS/eip-2193.md @@ -3,7 +3,7 @@ eip: 2193 title: dType Alias Extension - Decentralized Type System author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) discussions-to: https://github.com/ethereum/EIPs/issues/2192 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-07-16 @@ -89,4 +89,4 @@ An in-work implementation can be found at https://github.com/pipeos-one/dType/bl This proposal will be updated with an appropriate implementation when consensus is reached on the specifications. ## 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-2200.md b/EIPS/eip-2200.md index a5182f9fa829bb..9da3dc1360fc1f 100644 --- a/EIPS/eip-2200.md +++ b/EIPS/eip-2200.md @@ -177,7 +177,7 @@ This EIP requires a hard fork to implement. No gas cost increase is anticipated, and many contracts will see gas reduction. Performing `SSTORE` has never been possible with less than 5000 gas, so -it does not introduce incompatibility to the Ethereum mainnet. Gas +it does not introduce incompatibility to the Ethereum Mainnet. Gas estimation should account for this requirement. ## Test Cases @@ -312,7 +312,7 @@ We always start at state X. The first `SSTORE` can: ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [EIP-1283]: ./eip-1283.md [EIP-1706]: ./eip-1706.md diff --git a/EIPS/eip-2228.md b/EIPS/eip-2228.md index 6f29ddf52aca7d..c8ac1a4cf1972f 100644 --- a/EIPS/eip-2228.md +++ b/EIPS/eip-2228.md @@ -3,7 +3,7 @@ eip: 2228 title: Canonicalize the name of network ID 1 and chain ID 1 author: William Entriken (@fulldecent) discussions-to: https://github.com/ethereum/EIPs/issues/2228 -status: Review +status: Final type: Informational created: 2019-08-04 --- @@ -35,9 +35,7 @@ Any name or word styling (i.e. capitalization of the letters) of the network whi ### Trademark note -Even though the name of the network is Ethereum Mainnet, it may be necessary for your application to show this as "Ethereum® Mainnet" and include a note like "Ethereum is a registered trademark of Ethereum Foundation" elsewhere. - -For more information on your obligations when mentioning the Ethereum trademark, see: +"Ethereum" is trademarked by the Ethereum Foundation. For more information on your obligations when mentioning "Ethereum", and possibly "Ethereum Mainnet", see: * USPTO registration number 5110579 by Ethereum Foundation * The note "you must not use [this mark] without the prior written permission of the Foundation" on the Ethereum Foundation website, Terms of Use page @@ -52,7 +50,7 @@ Anybody that has travelled to certain countries and seen an "IPhone [sic]" repai - MetaMask previously used "Main Ethereum Network" in the account network chooser. MetaMask has been updated consistent with this EIP. -- References to Mainnet that are inconsistent with this specification are made in: [EIP-2](./eip-2.md), [EIP-779](./eip-779.md), [EIP-150](./eip-150.md), [EIP-155](./eip-155.md), [EIP-161](./eip-161.md), [EIP-170](./eip-170.md), [EIP-190](./eip-190.md), [EIP-225](./eip-225.md), [EIP-1013](./eip-1013.md), [EIP-1679](./eip-1679.md), [EIP-1716](./eip-1716.md), [EIP-2028](./eip-2028.md), [EIP-2200](./eip-2200.md), and [EIP-2387](./eip-2387.md). For consistency, we recommend the editor will update EIPs to consistently use the name as specified in this EIP. +- References to Mainnet that are inconsistent with this specification are made in: [EIP-2](./eip-2.md), [EIP-779](./eip-779.md), [EIP-150](./eip-150.md), [EIP-155](./eip-155.md), [EIP-190](./eip-190.md), [EIP-225](./eip-225.md), [EIP-1013](./eip-1013.md), [EIP-2028](./eip-2028.md), and [EIP-2387](./eip-2387.md). For consistency, we recommend the editor will update EIPs to consistently use the name as specified in this EIP. ## Test Cases @@ -94,4 +92,4 @@ These words literally mean nothing. The lowercase, not-proper-noun word "mainnet ## 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-223.md b/EIPS/eip-223.md new file mode 100644 index 00000000000000..d11b87b959b38a --- /dev/null +++ b/EIPS/eip-223.md @@ -0,0 +1,391 @@ +--- +eip: 223 +title: Token with transaction handling model +description: Token with transaction handling model designed to behave identical to native currency (ether) +author: Dexaran (@Dexaran) +discussions-to: https://ethereum-magicians.org/t/erc-223-token-standard/12894 +status: Final +type: Standards Track +category: ERC +created: 2017-05-03 +--- + +## Abstract + +The following describes an interface and logic for fungible tokens that supports a `tokenReceived` callback to notify contract recipients when tokens are received. This makes tokens behave identical to ether. + +## Motivation + +This token introduces a communication model for contracts that can be utilized to straighten the behavior of contracts that interact with such tokens. Specifically, this proposal: + +1. Informs receiving contracts of incoming token transfers, as opposed to [ERC-20](./eip-20.md) where the recipient of a token transfer gets no notification. +2. Is more gas-efficient when depositing tokens to contracts. +3. Allows for `_data` recording for financial transfers. + +## Specification + +Contracts intending to receive these tokens MUST implement `tokenReceived`. + +Token transfers to contracts not implementing `tokenReceived` as described below MUST revert. + +### Token contract + +#### Token Methods + +##### `totalSupply` + +```solidity +function totalSupply() view returns (uint256) +``` + +Returns the total supply of the token. The functionality of this method is identical to that of ERC-20. + +##### `name` + +```solidity +function name() view returns (string memory) +``` + +Returns the name of the token. The functionality of this method is identical to that of ERC-20. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `symbol` + +```solidity +function symbol() view returns (string memory) +``` + +Returns the symbol of the token. The functionality of this method is identical to that of ERC-20. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `decimals` + +```solidity +function decimals() view returns (uint8) +``` + +Returns the number of decimals of the token. The functionality of this method is identical to that of ERC-20. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `standard` + +```solidity +function standard() view returns (string memory) +``` + +Returns the identifier of the standard. If the token is [ERC-223](./eip-20.md) then it must return `"223"`. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `balanceOf` + +```solidity +function balanceOf(address _owner) view returns (uint256) +``` + +Returns the account balance of another account with address `_owner`. The functionality of this method is identical to that of ERC-20. + +##### `transfer(address, uint)` + +```solidity +function transfer(address _to, uint _value) returns (bool) +``` + +This function must transfer tokens, and if `_to` is a contract, it must call the `tokenReceived(address, uint256, bytes calldata)` function of `_to`. If the `tokenReceived` function is not implemented in `_to` (recipient contract), then the transaction must fail and the transfer of tokens must be reverted. +If `_to` is an externally owned address, then the transaction must be sent without executing `tokenReceived` in `_to`. + `_data` can be attached to this token transaction, but it requires more gas. `_data` can be empty. + +The `tokenReceived` function of `_to` MUST be called after all other operations to avoid re-entrancy attacks. + +NOTE: If `transfer` function is `payable` and ether was deposited then the amount of deposited ether MUST be delivered to `_to` address alongside tokens. If ether was sent alongside tokens in this way then ether MUST be delivered first, then token balances must be updated, then `tokenReceived` function MUST be called in `_to` if it is a contract. + +##### `transfer(address, uint, bytes)` + +```solidity +function transfer(address _to, uint _value, bytes calldata _data) returns (bool) +``` + +This function must transfer tokens and invoke the function `tokenReceived (address, uint256, bytes)` in `_to`, if `_to` is a contract. If the `tokenReceived` function is not implemented in `_to` (recipient contract), then the transaction must fail and the transfer of tokens must not occur. +If `_to` is an externally owned address (determined by the code size being zero), then the transaction must be sent without executing `tokenReceived` in `_to`. + `_data` can be attached to this token transaction, but it requires more gas. `_data` can be empty. + +NOTE: A possible way to check whether the `_to` is a contract or an address is to assemble the code of `_to`. If there is no code in `_to`, then this is an externally owned address, otherwise it's a contract. If `transfer` function is `payable` and ether was deposited then the amount of deposited ether MUST be delivered to `_to` address alongside tokens. + +The `tokenReceived` function of `_to` MUST be called after all other operations to avoid re-entrancy attacks. + +#### Events + +##### `Transfer` + +```solidity +event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes _data) +``` + +Triggered when tokens are transferred. Compatible with and similar to the ERC-20 `Transfer` event. + +### [ERC-223](./eip-223.md) Token Receiver + +#### Receiver Methods + +```solidity +function tokenReceived(address _from, uint _value, bytes calldata _data) returns (bytes4) +``` + +A function for handling token transfers, which is called from the token contract, when a token holder sends tokens. `_from` is the address of the sender of the token, `_value` is the amount of incoming tokens, and `_data` is attached data similar to `msg.data` of ether transactions. It works by analogy with the fallback function of Ether transactions and returns nothing. + +NOTE: `msg.sender` will be a token-contract inside the `tokenReceived` function. It may be important to filter which tokens were sent (by token-contract address). The token sender (the person who initiated the token transaction) will be `_from` inside the `tokenReceived` function. The `tokenReceived` function must return `0x8943ec02` after handling an incoming token transfer. The `tokenReceived` function call can be handled by the fallback function of the recipient contact (and in this case it may not return the magic value 0x8943ec02). + +IMPORTANT: This function must be named `tokenReceived` and take parameters `address`, `uint256`, `bytes` to match the function signature `0x8943ec02`. This function can be manually called by a EOA. + +## Rationale + +This standard introduces a communication model by enforcing the `transfer` to execute a handler function in the destination address. This is an important security consideration as it is required that the receiver explicitly implements the token handling function. In cases where the receiver does not implements such function the transfer MUST be reverted. + +This standard sticks to the push transaction model where the transfer of assets is initiated on the senders side and handled on the receivers side. As the result, ERC-223 transfers are more gas-efficient while dealing with depositing to contracts as ERC-223 tokens can be deposited with just one transaction while ERC-20 tokens require at least two calls (one for `approve` and the second that will invoke `transferFrom`). + +- [ERC-20](./eip-20.md) deposit: `approve` ~46 gas, `transferFrom` ~75K gas + +- ERC-223 deposit: `transfer` and handling on the receivers side ~54K gas + +This standard introduces the ability to correct user errors by allowing to handle ANY transactions on the recipients side and reject incorrect or improper transfers. This tokens utilize ONE transferring method for both types of interactions with contracts and externally owned addresses which can simplify the user experience and allow to avoid possible user mistakes. + +One downside of the commonly used [ERC-20](./eip-20.md) standard that ERC-223 is intended to solve is that [ERC-20](./eip-20.md) implements two methods of token transferring: (1) `transfer` function and (2) `approve + transferFrom` pattern. Transfer function of [ERC-20](./eip-20.md) standard does not notify the receiver and therefore if any tokens are sent to a contract with the `transfer` function then the receiver will not recognize this transfer and the tokens can become stuck in the receivers address without any possibility of recovering them. [ERC-20](./eip-20.md) standard places the burden of determining the transferring method on the user and if the incorrect method is chosen the user can lose the transferred tokens. ERC-223 automatically determines the transferring method, preventing the user from losing tokens due to chosing wrong method. + +ERC-223 is intended to simplify the interaction with contracts that are intended to work with tokens. ERC-223 utilizes a "deposit" pattern, similar to that of plain Ether. An ERC-223 deposit to a contract is a simple call of the `transfer` function. This is one transaction as opposed to two step process of `approve + transferFrom` depositing. + +This standard allows payloads to be attached to transactions using the `bytes calldata _data` parameter, which can encode a second function call in the destination address, similar to how `msg.data` does in an ether transaction, or allow for public logging on chain should it be necessary for financial transactions. + +## Backwards Compatibility + +The interface of this token is similar to that of ERC-20 and most functions serve the same purpose as their analogues in ERC-20. +`transfer(address, uint256, bytes calldata)` function is not backwards compatible with ERC-20 interface. + +ERC-20 tokens can be delivered to a non-contract address with `transfer` function. ERC-20 tokens can be deposited to a contract address with `approve` + `transferFrom` pattern. Depositing ERC-20 tokens to the contract address with `transfer` function will always result in token deposit not being recognized by the recipient contract. + +Here is an example of the contract code that handles ERC-20 token deposit. The following contract can accepts `tokenA` deposits. It is impossible to prevent deposits of non-tokenA to this contract. If tokenA is deposited with `transfer` function then it will result in a loss of tokens for the depositor because the balance of the user will be decreased in the contract of tokenA but the value of `deposits` variable in the `ERC20Receiver` will not be increased i.e. the deposit will not be credited. As of 5/9/2023 **$201M worth of 50 examined ERC-20 tokens are already lost** in this way on Ethereum mainnet. + +```solidity +contract ERC20Receiver +{ + address tokenA; + mapping (address => uint256) deposits; + function deposit(uint _value, address _token) public + { + require(_token == tokenA); + IERC20(_token).transferFrom(msg.sender, address(this), _value); + deposits[msg.sender] += _value; + } +} +``` + +ERC-223 tokens must be delivered to non-contract address or contract address in the same way with `transfer` function. + +Here is an example of the contract code that handles ERC-223 token deposit. The following contract can filter tokens and only accepts `tokenA`. Other ERC-223 tokens would be rejected. + +```solidity +contract ERC223Receiver +{ + address tokenA; + mapping (address => uint256) deposits; + function tokenReceived(address _from, uint _value, bytes memory _data) public returns (bytes4) + { + require(msg.sender == tokenA); + deposits[_from] += _value; + return 0x8943ec02; + } +} +``` + +## Security Considerations + +This token utilizes the model similar to plain ether behavior. Therefore replay issues must be taken into account. + +### Reference Implementation + +```solidity +pragma solidity ^0.8.19; + +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * This test is non-exhaustive, and there may be false-negatives: during the + * execution of a contract's constructor, its address will be reported as + * not containing a contract. + * + * > It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + */ + function isContract(address account) internal view returns (bool) { + // This method relies in extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } +} + +abstract contract IERC223Recipient { +/** + * @dev Standard ERC-223 receiving function that will handle incoming token transfers. + * + * @param _from Token sender address. + * @param _value Amount of tokens. + * @param _data Transaction metadata. + */ + function tokenReceived(address _from, uint _value, bytes memory _data) public virtual returns (bytes4); +} + +/** + * @title Reference implementation of the ERC223 standard token. + */ +contract ERC223Token { + + /** + * @dev Event that is fired on successful transfer. + */ + event Transfer(address indexed from, address indexed to, uint value, bytes data); + + string private _name; + string private _symbol; + uint8 private _decimals; + uint256 private _totalSupply; + + mapping(address => uint256) private balances; // List of user balances. + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + + constructor(string memory new_name, string memory new_symbol, uint8 new_decimals) + { + _name = new_name; + _symbol = new_symbol; + _decimals = new_decimals; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) + { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) + { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC223} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC223-balanceOf} and {IERC223-transfer}. + */ + function decimals() public view returns (uint8) + { + return _decimals; + } + + /** + * @dev See {IERC223-totalSupply}. + */ + function totalSupply() public view returns (uint256) + { + return _totalSupply; + } + + /** + * @dev See {IERC223-standard}. + */ + function standard() public view returns (string memory) + { + return "223"; + } + + + /** + * @dev Returns balance of the `_owner`. + * + * @param _owner The address whose balance will be returned. + * @return balance Balance of the `_owner`. + */ + function balanceOf(address _owner) public view returns (uint256) + { + return balances[_owner]; + } + + /** + * @dev Transfer the specified amount of tokens to the specified address. + * Invokes the `tokenFallback` function if the recipient is a contract. + * The token transfer fails if the recipient is a contract + * but does not implement the `tokenFallback` function + * or the fallback function to receive funds. + * + * @param _to Receiver address. + * @param _value Amount of tokens that will be transferred. + * @param _data Transaction metadata. + */ + function transfer(address _to, uint _value, bytes calldata _data) public returns (bool success) + { + // Standard function transfer similar to ERC20 transfer with no _data . + // Added due to backwards compatibility reasons . + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + if(Address.isContract(_to)) { + IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data); + } + emit Transfer(msg.sender, _to, _value, _data); + return true; + } + + /** + * @dev Transfer the specified amount of tokens to the specified address. + * This function works the same with the previous one + * but doesn't contain `_data` param. + * Added due to backwards compatibility reasons. + * + * @param _to Receiver address. + * @param _value Amount of tokens that will be transferred. + */ + function transfer(address _to, uint _value) public returns (bool success) + { + bytes memory _empty = hex"00000000"; + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + if(Address.isContract(_to)) { + IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty); + } + emit Transfer(msg.sender, _to, _value, _empty); + return true; + } +} +``` + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2242.md b/EIPS/eip-2242.md index 4be7be2946e0e1..7dc308cef0e5c7 100644 --- a/EIPS/eip-2242.md +++ b/EIPS/eip-2242.md @@ -3,7 +3,7 @@ eip: 2242 title: Transaction Postdata author: John Adler (@adlerjohn) discussions-to: https://ethereum-magicians.org/t/eip-2242-transaction-postdata/3557 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-08-16 @@ -57,4 +57,4 @@ TODO TODO ## 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-225.md b/EIPS/eip-225.md index a1630f29d574d8..ce084a71779698 100644 --- a/EIPS/eip-225.md +++ b/EIPS/eip-225.md @@ -98,6 +98,7 @@ We define the following constants: * Suggested `32 bytes` to retain the current extra-data allowance and/or use. * **`EXTRA_SEAL`**: Fixed number of extra-data suffix bytes reserved for signer seal. * `65 bytes` fixed as signatures are based on the standard `secp256k1` curve. + * Filled with zeros on genesis block. * **`NONCE_AUTH`**: Magic nonce number `0xffffffffffffffff` to vote on adding a new signer. * **`NONCE_DROP`**: Magic nonce number `0x0000000000000000` to vote on removing a signer. * **`UNCLE_HASH`**: Always `Keccak256(RLP([]))` as uncles are meaningless outside of PoW. @@ -110,19 +111,19 @@ We also define the following per-block constants: * **`BLOCK_NUMBER`**: Block height in the chain, where the height of the genesis is block `0`. * **`SIGNER_COUNT`**: Number of authorized signers valid at a particular instance in the chain. - * **`SIGNER_INDEX`**: Index of the block signer in the sorted list of current authorized signers. + * **`SIGNER_INDEX`**: Zero-based index of the block signer in the sorted list of current authorized signers. * **`SIGNER_LIMIT`**: Number of consecutive blocks out of which a signer may only sign one. * Must be `floor(SIGNER_COUNT / 2) + 1` to enforce majority consensus on a chain. We repurpose the `ethash` header fields as follows: - * **`beneficiary`**: Address to propose modifying the list of authorized signers with. + * **`beneficiary`** / **`miner`**: Address to propose modifying the list of authorized signers with. * Should be filled with zeroes normally, modified only while voting. * Arbitrary values are permitted nonetheless (even meaningless ones such as voting out non signers) to avoid extra complexity in implementations around voting mechanics. * **Must** be filled with zeroes on checkpoint (i.e. epoch transition) blocks. - * Transaction execution **must** use the actual block signer (see `extraData`) for the `COINBASE` opcode. + * Transaction execution **must** use the actual block signer (see `extraData`) for the `COINBASE` opcode and transaction fees **must** be attributed to the signer account. * **`nonce`**: Signer proposal regarding the account defined by the `beneficiary` field. - * Should be **`NONCE_DROP`** to propose deauthorizing `beneficiary` as a existing signer. + * Should be **`NONCE_DROP`** to propose deauthorizing `beneficiary` as an existing signer. * Should be **`NONCE_AUTH`** to propose authorizing `beneficiary` as a new signer. * **Must** be filled with zeroes on checkpoint (i.e. epoch transition) blocks. * **Must** not take up any other value apart from the two above (for now). @@ -130,7 +131,7 @@ We repurpose the `ethash` header fields as follows: * First **`EXTRA_VANITY`** bytes (fixed) may contain arbitrary signer vanity data. * Last **`EXTRA_SEAL`** bytes (fixed) is the signer's signature sealing the header. * Checkpoint blocks **must** contain a list of signers (`N*20 bytes`) in between, **omitted** otherwise. - * The list of signers in checkpoint block extra-data sections **must** be sorted in ascending order. + * The list of signers in checkpoint block extra-data sections **must** be sorted in ascending byte order. * **`mixHash`**: Reserved for fork protection logic, similar to the extra-data during the DAO. * **Must** be filled with zeroes during normal operation. * **`ommersHash`**: **Must** be **`UNCLE_HASH`** as uncles are meaningless outside of PoW. @@ -141,9 +142,9 @@ We repurpose the `ethash` header fields as follows: ### Authorizing a block -To authorize a block for the network, the signer needs to sign the block's hash containing **everything except the signature itself**. The means that the hash contains every field of the header (`nonce` and `mixDigest` included), and also the `extraData` with the exception of the 65 byte signature suffix. The fields are hashed in the order of their definition in the yellow paper. +To authorize a block for the network, the signer needs to sign the block's sighash containing **everything except the signature itself**. This means that this hash contains every field of the header (`nonce` and `mixDigest` included), and also the `extraData` with the exception of the 65 byte signature suffix. The fields are hashed in the order of their definition in the yellow paper. Note that this sighash differs from the final block hash which also includes the signature. -This hash is signed using the standard `secp256k1` curve, and the resulting 65 byte signature (`R`, `S`, `V`, where `V` is `0` or `1`) is embedded into the `extraData` as the trailing 65 byte suffix. +The sighash is signed using the standard `secp256k1` curve, and the resulting 65 byte signature (`R`, `S`, `V`, where `V` is `0` or `1`) is embedded into the `extraData` as the trailing 65 byte suffix. To ensure malicious signers (loss of signing key) cannot wreck havoc in the network, each singer is allowed to sign **maximum one** out of **`SIGNER_LIMIT`** consecutive blocks. The order is not fixed, but in-turn signing weighs more (**`DIFF_INTURN`**) than out of turn one (**`DIFF_NOTURN`**). @@ -448,7 +449,7 @@ tests := []struct { }, failure: errUnauthorizedSigner, }, { - // An authorized signer that signed recenty should not be able to sign again + // An authorized signer that signed recently should not be able to sign again signers: []string{"A", "B"}, blocks []block{ {signer: "A"}, @@ -474,4 +475,4 @@ tests := []struct { A reference implementation is part of [go-ethereum](https://github.com/ethereum/go-ethereum/tree/master/consensus/clique) and has been functioning as the consensus engine behind the [Rinkeby](https://www.rinkeby.io) testnet since April, 2017. ## 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-2255.md b/EIPS/eip-2255.md index 0174c2a36cb920..14d31a8f67727d 100644 --- a/EIPS/eip-2255.md +++ b/EIPS/eip-2255.md @@ -1,42 +1,29 @@ --- eip: 2255 title: Wallet Permissions System -author: Dan Finlay (@danfinlay), Erik Marks (@rekmarks) +description: An interface to restrict access to sensitive methods +author: Dan Finlay (@danfinlay), Erik Marks (@rekmarks), Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/web3-login-permissions/3583 -status: Draft +status: Final type: Standards Track category: Interface created: 2019-08-22 -requires: 1474 +requires: 1193 --- -![Sample prompt screenshot](../assets/eip-2255/permissions.png) - -## Simple Summary - -A proposed standard interface for restricting and permitting access to security-sensitive methods within a restricted web3 context like a website or "dapp". - ## Abstract -Web3 JavaScript wallet browsers may implement `wallet_getPermissions` and `wallet_requestPermissions`. This provides a standard interface for requesting permissions and checking a domain's current permissions status. +This EIP adds two new wallet-namespaced RPC endpoints, `wallet_getPermissions` and `wallet_requestPermissions`, providing a standard interface for requesting and checking permissions. ## Motivation -Web3 Wallets are built around the responsibility of mediating the interactions between untrusted applications and a user's keys on their computer, getting appropriate consent from the user. - -Today web3 browsers like MetaMask always prompt on a per-action basis. This provides security at the cost of substantial user friction. We believe that a single permissions request can achieve the same level of security with vastly improved UX. +Wallets are responsible for mediating interactions between untrusted applications and users' keys through appropriate user consent. Today, wallets always prompt the user for every action. This provides security at the cost of substantial user friction. We believe that a single permissions request can achieve the same level of security with vastly improved UX. -The pattern of permissions requests is common around the web, from login with Facebook, Twitter, GitHub, and even Apple, making it a very familiar pattern. +The pattern of permissions requests (typically using Oauth2) is common around the web, making it a very familiar pattern: -
- Facebook Permissions - Facebook Permissions -
+![Facebook Permissions](../assets/eip-2255/facebook_permissions.png) -
- Log in With Apple - Log in With Apple -
+![Log in With Apple](../assets/eip-2255/log_in_with_apple.jpeg) Many web3 applications today begin their sessions with a series of repetitive requests: @@ -46,80 +33,87 @@ Many web3 applications today begin their sessions with a series of repetitive re - Grant a token allowance to our contract. - Send a transaction to our contract. -Many of these (and possibly all), and many more (like decryption), could be generalized into a set of human-readable permissions prompts on the original sign-in screen, and additional permissions could be requested only as needed. +Many of these (and possibly all), and many more (like decryption), could be generalized into a set of human-readable permissions prompts on the original sign-in screen, and additional permissions could be requested only as needed: + +![Sample prompt screenshot](../assets/eip-2255/permissions.png) -On the user's end, each of these permissions could be individually rejected (unchecked), or even _attenuated_, or adjusted to meet the user's terms (for example, a sign-in request could have a user-added expiration date, and a token allowance could be adjusted by the user when it is requested), making the web3 login a sort of user-revisable terms of use. +Each of these permissions could be individually rejected, or even _attenuated_--adjusted to meet the user's terms (for example, a sign-in request could have a user-added expiration date, and a token allowance could be adjusted by the user when it is requested). ## Specification -This proposal adds two new methods to a wallet's web3 provider API: +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. -- `wallet_getPermissions` -- `wallet_requestPermissions` +This proposal adds two new methods to a wallet's web3 provider API: `wallet_getPermissions` and `wallet_requestPermissions`. -The `wallet_getPermissions` method is used for getting an array of current permissions (empty by default), while the `wallet_requestPermissions` method is used for an application to request additional permissions. +### `wallet_getPermissions` -These two methods are used to restrict a few hypothetical "restricted methods". The first such method we would suggest should be included as part of the standard is `eth_accounts`. +The `wallet_getPermissions` method is used for getting an array of current permissions (empty by default). It takes no parameters and returns an array of `Permission` objects. -In this framework, the permission for a user to reveal their accounts would look like this: +#### `wallet_getPermissions` Returns -```javascript -const response = await provider.request({ - method: 'wallet_requestPermissions', - params: [{ - 'eth_accounts': {}, - }] -}) +The format of the returned permissions MUST be an array of `Permission` objects, which are defined as follows: + +```typescript +interface Caveat { + type: string; + value: any; +} + +interface Permission { + invoker: string; + parentCapability: string; + caveats: Caveat[]; +} ``` -If this request was rejected, it would throw an error with a `code` value equal to `4001`, per [EIP-1193 errors](./eip-1193.md), which the MetaMask team has canonized in a module [eth-rpc-errors](https://github.com/metamask/eth-rpc-errors). +The `invoker` is a URI used to identify the source of the current dapp (e.g. `https://your-site.com/`). The term `parentCapability` refers to the method that is being permitted (e.g. `eth_accounts`). The `caveats` array represents the specific restrictions applied to the permitted method. The `type` of a `Caveat` is a string, and the `value` is an arbitrary JSON value. The `value` of a `Caveat` is only meaningful in the context of the `type` of the `Caveat`. -If the request is accepted by the user, then subsequent requests to `eth_accounts` will succeed, and return an accounts array as usual. +### `wallet_requestPermissions` -A call to `wallet_getPermissions` will then return a permissions schema object that describes the current permission. +The `wallet_requestPermissions` method is used for an application to request additional permissions. It MUST take a single parameter, a `PermissionRequest` object, and MUST return an array of `RequestedPermission` objects. -```javascript -const response = await provider.request({ - method: 'wallet_getPermissions' -}) -``` +#### `wallet_requestPermissions` Parameters -Would return a value something like this: - -```json -[ - { - invoker: 'ens://your-site.eth', - parentCapability: 'eth_accounts', - caveats: [ - { - type: 'filterResponse', - value: ["0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb"] - } - ] - } -] +The `wallet_requestPermissions` method takes a single parameter, a `PermissionRequest` object, which is defined as follows: + +```typescript +interface PermissionRequest { + [methodName: string]: { + [caveatName: string]: any; + }; +} ``` -Where `invoker` is a unique domain string used to identify the source of the current dapp. To start, this may include only `https` prefixes, but `ens`, `swarm`, `ipfs`, and others may all be valid sources in the future. +The `methodName` is the name of the method for which the permission is being requested (e.g. `eth_accounts`). The `caveatName` is the name of the caveat being applied to the permission (e.g. `requiredMethods`). The caveat value is the value of the caveat (e.g. `["signTypedData_v3"]`). + +Attempted requests to a restricted method must fail with an error, until a `wallet_requestPermissions` request is made and accepted by the user. -The term `parentCapability` comes from the [zcap-ld spec](https://w3c-ccg.github.io/zcap-ld/), which these permissions objects are based on, and refers to the method that is being permitted. +If a `wallet_requestPermissions` request is rejected, it should throw an error with a `code` value equal to `4001` as per [EIP-1193](./eip-1193.md). -The `caveats` array represents the specific restrictions applied to the permitted method. +#### `wallet_requestPermissions` Returns + +The `wallet_requestPermissions` method returns an array of `RequestedPermission` objects, which are defined as follows: + +```typescript +interface RequestedPermission { + parentCapability: string; + date?: number; +} +``` -You can see above how internally the user-selected account is transformed into a [`caveat`](https://github.com/MetaMask/json-rpc-capabilities-middleware/blob/master/src/%40types/ocap-ld.d.ts#L28-L33), which is a restriction on the response values, in this case ensuring the page can only be notified of approved accounts. This also means this permissions system is forward-extensible to support logging into a page with multiple accounts. +The `parentCapability` is the name of the method for which the permission is being requested (e.g. `eth_accounts`). The `date` is the timestamp of the request, in Unix time, and is optional. ## Rationale While the current model of getting user consent on a per-action basis has high security, there are huge usability gains to be had bo getting more general user consent which can cover broad categories of usage, which can be expressed in a more human-readable way. This pattern has a variety of benefits to offer different functions within a web3 wallet. -The `eth_sendTransaction` method itself could be a restricted method (requested by default with the `provider.enable()` method), and the user could at sign-in time decide whether they wanted to require confirmations, approve all transactions, or only approve transactions to a certain contract, or up to a certain token limit, for example. By restricting this method by default, wallets could prevent sites from spamming the user with popups. +The `requestPermissions` method can be expanded to include other options related to the requested permissions, for example, sites could request accounts with specific abilities. For example, a website like an exchange that requires `signTypedData_v3` (which is not supported by some hardware wallets), might want to specify that requirement. This would allow wallets to display only compatible accounts, while preserving the user's privacy and choice regarding how they are storing their keys. -If `eth_call` were a restricted method, then random websites would not be able to drain a user's subscription to a hosted provider, making it easier to protect services like Infura against DDoS attacks. +## Test Cases -On-chain actions could be represented as a permission under this model, for example, the permission to send an allowance-setting transaction to a specific token address is virtually equivalent to the approval of that transaction, except the site could choose to only invoke the transaction when it was needed. This could allow a standard interface for applications to request permissions which may require different actions depending on different types of accounts (hot wallets, hardware wallets, cold wallets, contract accounts). +### Requesting permissions -The `requestPermissions` method could be expanded to include other options related to the requested permissions, for example, sites could request accounts with specific abilities. For example, a website like an exchange that requires `signTypedData_v3` (which is not supported by some hardware wallets), might want to specify that requirement, maybe like this: +The following example should prompt the user to approve the `eth_accounts` permission, and return the permission object if approved. ```javascript provider.request({ @@ -131,19 +125,27 @@ provider.request({ } } ] -}) +}); ``` -This would allow the wallet to limit the user's options to valid ones, and allows dapps to ensure selected accounts are compatible with their service, while preserving the user's privacy regarding how they are storing their keys. +### Getting permissions + +The following example should return the current permissions object. + +```javascript +provider.request({ + method: 'getPermissions' +}); +``` -## Implementation +## Security Considerations -MetaMask uses EIP-2255 to restrict the `eth_accounts` RPC method such that it will return an empty array to any caller that has not been granted the corresponding permission. +### Server-Side Request Forgery (SSRF) -You can get more detailed API and type information [on the RpcCap repository's readme](https://github.com/MetaMask/rpc-cap#rpc-methods). +This consideration is applicable if the favicon of a website is to be displayed. -New hypothetical and proposed permissions can be easily added to [the `restrictedMethods` in the MetaMask permissions controller](https://github.com/MetaMask/metamask-extension/blob/76a2a9b/app/scripts/controllers/permissions/restrictedMethods.js). +Wallets should be careful about making arbitrary requests to URLs. As such, it is recommended for wallets to sanitize the URI by whitelisting specific schemes and ports. A vulnerable wallet could be tricked into, for example, modifying data on a locally-hosted redis database. ## 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-2256.md b/EIPS/eip-2256.md index fcdeb3da52ad7a..eb556fff2523d8 100644 --- a/EIPS/eip-2256.md +++ b/EIPS/eip-2256.md @@ -3,7 +3,7 @@ eip: 2256 title: wallet_getOwnedAssets JSON-RPC Method author: Loredana Cirstea (@loredanacirstea) discussions-to: https://ethereum-magicians.org/t/eip-2256-add-wallet-getownedassets-json-rpc-method/3600 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2019-08-29 @@ -194,4 +194,4 @@ To be done. ## 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-2266.md b/EIPS/eip-2266.md index 0371bfbab5c3e1..2489d320f18ec3 100644 --- a/EIPS/eip-2266.md +++ b/EIPS/eip-2266.md @@ -7,7 +7,7 @@ status: Last Call type: Standards Track category: ERC created: 2019-08-17 -review-period-end: 2020-12-31 +last-call-deadline: 2020-12-31 --- ## Simple Summary @@ -240,7 +240,8 @@ This proposal is fully backward compatible. Functionalities of existing standard Please visit [here](../assets/eip-2266/Example.sol) to find our example implementation. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +Copyright and related rights waived via [CC0](../LICENSE.md). ## References diff --git a/EIPS/eip-2294.md b/EIPS/eip-2294.md new file mode 100644 index 00000000000000..9837e6ad5dc67c --- /dev/null +++ b/EIPS/eip-2294.md @@ -0,0 +1,59 @@ +--- +eip: 2294 +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: Review +type: Standards Track +category: Core +created: 2019-09-19 +requires: 155 +--- + +## Abstract + +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. + +## 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. 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 + +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 + +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, 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-2304.md b/EIPS/eip-2304.md index 53b98763d4e719..16a550edad50c9 100644 --- a/EIPS/eip-2304.md +++ b/EIPS/eip-2304.md @@ -4,7 +4,7 @@ title: Multichain address resolution for ENS author: Nick Johnson type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2019-09-09 discussions-to: https://discuss.ens.domains/t/new-standard-proposal-ens-multicoin-support/1148 requires: 137 @@ -216,4 +216,4 @@ The table below specifies test vectors for valid address encodings for each cryp | Binance | 714 | `bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2` | `40c2979694bbc961023d1d27be6fc4d21a9febe6` | ## 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-2309.md b/EIPS/eip-2309.md index 8cccb4af9447a3..a98b8e0daae20e 100644 --- a/EIPS/eip-2309.md +++ b/EIPS/eip-2309.md @@ -20,7 +20,7 @@ The optional ERC-721 Consecutive Transfer Extension provides a standardized even ## Motivation -This extension provides even more scalibility of the [ERC-721 specification](./eip-721.md). It is possible to create, transfer, and burn 2^255 non-fungible tokens in one transaction. However, it is not possible to emit that many `Transfer` events in one transaction. The `Transfer` event is part of the original specification which states: +This extension provides even more scalibility of the [ERC-721 specification](./eip-721.md). It is possible to create, transfer, and burn 2^256 non-fungible tokens in one transaction. However, it is not possible to emit that many `Transfer` events in one transaction. The `Transfer` event is part of the original specification which states: > This emits when ownership of any NFT changes by any mechanism. > This event emits when NFTs are created (`from` == 0) and destroyed @@ -84,7 +84,7 @@ The `ConsecutiveTransfer` event can be used for a single token as well as many t ## Rationale -Standardizing the `ConsecutiveTransfer` event gives decentralized platforms a standard way of determining ownership of large quantities of non-fungible tokens without the need to support a new token standard. There are many ways in which the batch creation and transfer of NFTs can be implemented. The Consecutive Transfer Extension allows contract creators to implement batch creation, transfer, and burn methods however they see fit, but provides a standardized event in which all implementations can use. By specifying a range of consecutive token identifiers we can easily cover the transfer, or creation of 2^(255) tokens and decentralized platforms can react accordingly. +Standardizing the `ConsecutiveTransfer` event gives decentralized platforms a standard way of determining ownership of large quantities of non-fungible tokens without the need to support a new token standard. There are many ways in which the batch creation and transfer of NFTs can be implemented. The Consecutive Transfer Extension allows contract creators to implement batch creation, transfer, and burn methods however they see fit, but provides a standardized event in which all implementations can use. By specifying a range of consecutive token identifiers we can easily cover the transfer, or creation of 2^(256) tokens and decentralized platforms can react accordingly. Take this example. I sell magical fruit and have a farm with 10,000 magical fruit trees each with different fruit and 1,000 new trees every few years. I want to turn each tree into a non-fungible token that people can own. Each person that owns one of my non-fungible tree tokens will receive a quarterly percentage of each harvest from that tree. The problem is that I would need to create and transfer each of these tokens individually - which will cost me a lot of time and money and frankly would keep me from doing this. @@ -102,11 +102,11 @@ Events in Solidity can have up to three indexed parameters which will make it po This can lead to bugs and unnecessary complex logic for platforms using these events to track token ownership. When transferring a single token it is acceptable to emit the original `Transfer` event, but the `ConsecutiveTransfer` event should not be emitted during the same transaction and vice-versa. -**Comparing 2039 and 1155** +**Comparing 2309 and 1155** As the NFT market continues to grow so does the need for the ability to scale the smart contracts. Users need to be able to do things like mint a massive amount of tokens at one time, transfer a massive amount of tokens, and be able to track ownership of all these assets. We need to do this in a way that is cost effective and doesn’t fail under the confines of the Ethereum blockchain. As millions of tokens are minted we need contracts with the ability to scale. -[ERC-1155](./eip-1155.md) was created and added as a standard in 2019 to try to solve these problems, but it falls short when it comes to minting massive amounts of unique tokens in a cost-effective way. With ERC-1155 it’s either going to cost hundreds (or thousands) of dollars or it’s going to run out of gas. ERC-1155 works well when minting many semi-fungible tokens but falls short when minting many unique tokens. Using the 2039 standard you could mint millions of blank NFTs upfront and update the metadata for each one in a cost effective way. +[ERC-1155](./eip-1155.md) was created and added as a standard in 2019 to try to solve these problems, but it falls short when it comes to minting massive amounts of unique tokens in a cost-effective way. With ERC-1155 it’s either going to cost hundreds (or thousands) of dollars or it’s going to run out of gas. ERC-1155 works well when minting many semi-fungible tokens but falls short when minting many unique tokens. Using the 2309 standard you could mint millions of blank NFTs upfront and update the metadata for each one in a cost effective way. ## Backwards Compatibility @@ -118,4 +118,4 @@ There are no security considerations related directly to the implementation of t ## 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-2315.md b/EIPS/eip-2315.md index 5c7fc8c5a80fd6..276034506f6c2d 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -1,308 +1,316 @@ --- eip: 2315 title: Simple Subroutines for the EVM -status: Draft +description: Two opcodes for efficient, safe, and static subroutines. +author: Greg Colvin (@gcolvin), Martin Holst Swende (@holiman), Brooklyn Zelenka (@expede), John Max Skaller +discussions-to: https://ethereum-magicians.org/t/eip-2315-simple-subroutines-for-the-evm/3941 +status: Withdrawn type: Standards Track category: Core -author: Greg Colvin , Martin Holst Swende (@holiman) -discussions-to: https://ethereum-magicians.org/t/eip-2315-simple-subroutines-for-the-evm/3941 created: 2019-10-17 +requires: 3540, 3670, 4200 +withdrawal-reason: This proposal has been superceded by the EOF proposals. --- -## Simple Summary +## Abstract -(Almost) the smallest possible change that provides native subroutines without breaking backwards compatibility. +This proposal provides a _complete_, _efficient_, _safe_ and _static_ control-flow facility. -## Abstract +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 + +It deprecates `JUMP` and `JUMPI`, allowing valid code to support streaming, one-pass, and other near-linear compilers. -This proposal introduces three opcodes to support subroutines: `BEGINSUB`, `JUMPSUB` and `RETURNSUB`. (The smallest possible change would do without `BEGINSUB`). +In concert with [EIP-3540](./eip-3540.md) and [EIP-3670](./eip-3670.md) it ensures, at initialization time, that valid code will not execute invalid instructions or jump to invalid locations, will not underflow stack, will maintain consistent numbers of inputs and outputs for subroutines, and will have bounded stack height in the absence of recursion. + +This is among the simplest possible proposals that meets these requirements. ## Motivation -The EVM does not provide subroutines as a primitive. Instead, calls can be synthesized by fetching and pushing the current program counter on the data stack and jumping to the subroutine address; returns can be synthesized by getting the return address to the top of the stack and jumping back to it. In the EVM the return +### A complete control-flow facility. -Facilities to directly support subroutines are provided in some form by most physical and virtual machines going back at least fifty years. In whatever form, these operations provide for capturing the current context of execution, transferring control to a new context, and returning to the original context. +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. -We propose a simple _return-stack_ mechanism, known to work well for stack machines, which we specify here. Note that this specification is entirely semantic. It constrains only stack usage and control flow and imposes no syntax on code beyond being a sequence of bytes to be executed. +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. -In the future, amenability to static analysis equivalent to [EIP-615](https://eips.ethereum.org/EIPS/eip-615) could be ensured by enforcing a few simple rules, and validated with the provided algorithm, still without imposing syntactic constraints. +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. -## Specification +### 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. + +### 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 propose to statically validate important safety constraints on code at initialization time. + +### Static control-flow. -We introduce one more stack into the EVM in addition to the existing `data stack` which we call the `return stack`. The `return stack` is limited to `1024` items. +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. -#### `BEGINSUB` +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. -Marks the entry point to a subroutine. Execution of a `BEGINSUB` is a no-op. +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. -#### `JUMPSUB` +## Specification + +### Opcodes + +#### `RJUMPSUB (0x5f) relative_offset` Transfers control to a subroutine. -1. Pop the `location` off the `data stack`. -2. If the opcode at `location` is not a `BEGINSUB` _`abort`_. -3. If the `return stack` already has `1024` items _`abort`_. -4. Push the current `pc + 1` to the `return stack`. -5. Set `pc` to `location + 1`. +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`. + +The `relative_offset` is relative to the current `PC`. The offset is encoded as a two-byte, twos-complement signed integer, stored MSB-first. -* _pops one item off the `data stack`_ -* _pushes one item on the `return stack`_ +The gas cost is _low_. -#### `RETURNSUB` +#### `RETURNSUB (0x5e)` Returns control to the caller of a subroutine. -1. If the `return stack` is empty _`abort`_. -2. Pop `pc` off the `return stack`. +1. Pop the `return stack` to `PC`. -* _pops one item off the `return stack`_ +The gas cost is _verylow_. -_Note 1: If a resulting `pc` to be executed is beyond the last instruction then the opcode is implicitly a `STOP`, which is not an error._ +_Notes:_ -_Note 2: Values popped off the `return stack` do not need to be validated, since they are alterable only by `JUMPSUB` 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.)_ -_Note 3: The description above lays out the semantics of this feature 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 implementor may code `JUMPSUB` 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 + 1` location.)_ +### Validity -### Indirect Jumps +_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. -If [EIP-2327: BEGINDATA](https://eips.ethereum.org/EIPS/eip-2327) or similar is implemented then the indirect jumps from [EIP-615](https://eips.ethereum.org/EIPS/eip-615) -- `JUMPV` and `JUMPSUBV` -- can be implemented. These could take two arguments on the stack: a constant offset relative to `BEGINDATA` to a jump table, and a variable index into that table. +1. Insufficient gas +2. More than 1024 stack items +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 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 + +* Every instruction MUST be valid: + * The `JUMP` and `JUMPI` instructions ARE NOT valid. +* Every jump MUST be valid: + * The `RJUMP`, `RJUMPI`, or `RJUMPSUB` instructions MUST NOT address immediate data or addresses outside of their code section. +* The stacks MUST be valid: + * The number of items on the `data stack` MUST always be positive. + * The number of items on the `return stack `MUST always be positive. +* 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 + * MUST NOT exceed 1024. ## Rationale -We modeled this design on the simple, proven, archetypal Forth virtual machine of 1970. It is a two-stack design -- the data stack is supplemented with a return stack to support jumping into and returning from subroutines, as specified above. The separate return stack ensures that the return address cannot be overwritten or mislaid, and obviates any need to swap the return address past the arguments on the stack. Importantly, a dynamic jump is not needed to implement subroutine returns, allowing for deprecation of dynamic uses of JUMP and JUMPI. Eventually deprecating dynamic jumps is key to practical static analysis of code. +This is a purely semantic specification, placing no constraints on the syntax of code sections beyond being a sequence of opcodes and immediate data – a subroutine is not a contiguous sequence of bytecode, it is a subgraph of the bytecode's control-flow graph. The EVM is a simple state machine. We only promise that valid code will not, as it were, jam up the gears of the machine. + +By avoiding syntactic constraints we allow for optimizations like tail call elimination, multiple-entry subroutines, moving "cold" code out of line, and other ways of reducing redundancy and keeping "hot" code in cache. Since we wish to support one-pass compilation of EVM code to machine code it is crucial that the EVM code be as well optimized as possible up front. + +### Validation + +Rather than enforce constraints via syntax, we enforce them via validation. + +The constraints on valid code cover all of the exceptional halting states that we can validate and allow code to be validated and compiled in time and space quasi-linear in the size of the code. + +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. -(JUMPSUB and RETURNSUB were also defined in terms of a `return stack` in [EIP-615](https://eips.ethereum.org/EIPS/eip-615)) -. -## Backwards and Forwards Compatibility +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. -These changes do not affect the semantics of existing EVM code. +_Note: The JVM and Wasm enforce similar constraints for similar reasons._ -These changes are compatible with using [EIP-3337](https://eips.ethereum.org/EIPS/eip-3337) to provide stack frames, by associating a frame with each subroutine. +### Alternative Designs -## Implementations +There are a few major designs for a subroutine facility, two of which are considered here. The others are mostly not appropriate for the EVM, such as the Wheeler Jump -- self-modifying code that writes return addresses into called subroutines. -Three clients have implemented this (or an earlier version of this) proposal: +*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. -- [geth](https://github.com/ethereum/go-ethereum/pull/20619) . -- [besu](https://github.com/hyperledger/besu/pull/717), and -- [openethereum](https://github.com/openethereum/openethereum/pull/11629). +*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. -### Costs and Codes +#### We prefer the dedicated return stack. -We suggest that the cost of +* It maintains a clear separation between calculation and flow of control: + * the data stack is free of vulnerable return addresses and + * it's impossible to overwrite the return stack. +* It improves efficiency: + * it uses native arithmetic rather than 256-bit EVM instructions for the return address, + * doesn't use up a `data stack` slot for the return address and + * needs less motion of 256-bit data on the stack. -- `BEGINSUB` be _jumpdest_ (`1`) -- `JUMPSUB` be _high_ (`10`) - - This is the same as `JUMPI`, and `2` more than `JUMP`. -- `RETURNSUB` be _low_ (`5`). +### Efficiency -Benchmarking might be needed to tell if the costs are well-balanced. +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 suggest the following opcodes: +#### **Simple Subroutine Call** +Consider these examples of a fairly minimal subroutine, including the code to call it. + +Subroutine call, using `RJUMPSUB`: ``` -0x5c BEGINSUB -0x5d RETURNSUB -0x5e JUMPSUB +SQUARE: + dup1 ; 3 gas + mul ; 5 gas + returnsub ; 3 gas + +CALL_SQUARE: + push 0x02 ; 3 gas + rjumpsub SQUARE ; 5 gas ``` +_Total gas: 19_ -## Security Considerations +Subroutine call, using `JUMP`: +``` +SQUARE: + jumpdest ; 1 gas + swap1 ; 3 gas + dup1 ; 3 gas + mul ; 5 gas + swap1 ; 3 gas + jump ; 8 gas + +CALL_SQUARE: + jumpdest ; 1 gas + push 0x02 ; 3 gas + push RTN_CALL: ; 3 gas + push SQUARE ; 3 gas + jump ; 8 gas +RTN_CALL: + jumpdest ; 1 gas +``` +_Total: 41 gas_. -These changes do introduce new flow control instructions, so any software which does static/dynamic analysis of evm-code needs to be modified accordingly. The `JUMPSUB` semantics are similar to `JUMP` (but jumping to a `BEGINSUB`), whereas the `RETURNSUB` instruction is different, since it can 'land' on any opcode (but the possible destinations can be statically inferred). +Using `RJUMPSUB` versus `JUMP` saves _41 - 19 = 22 gas_ — a _54%_ improvement. -The safety and amenability to static analysis of valid programs can be made comparable to [EIP-615](https://eips.ethereum.org/EIPS/eip-615), but without imposing syntactic constraints, and thus with minimal impact on low-level optimizations. Validity can ensured by following the rules given in the next section, and programs can be validated with the provided algorithm. The validation algorithm is simple and bounded by the size of the code, allowing for validation at deploy time or at load time. +#### **Tail Call Optimization** -While it is crucial going forward that it be possible to validate programs, this EIP does propose that validity be enforced. Note that much value for people doing static analysis (e.g. for proofs that bytecode meets formal specifications of a contract) can be had without enforcement. Code can be scanned in linear time to ensure that the rules are or are not followed before analysis begins. And compilers can easily follow the rules up front. +Of course in cases like this one we can optimize the tail call, so that the return from `SQUARE` actually returns from `TEST_SQUARE`. -### Validity +Tail call optimization, using `RJUMPSUB` and `RETURNSUB`: +```SQUARE: + dup1 ; 3 gas + mul ; 5 gas + returnsub ; 3 gas -#### Exceptional Halting States +CALL_SQUARE: + push 0x02 ; 3 gas + rjump SQUARE ; 3 gas +``` +_Total: 17 gas_ -_Execution_ is as defined in the [Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)—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 five such states. -1. Insufficient gas -2. More than 1024 stack items -3. Insufficient stack items -4. Invalid jump destination -5. Invalid instruction -We would like to consider EVM code valid iff no execution of the program can lead to an exceptional halting state, but we must be able to validate code in linear time to avoid denial of service attacks. So in practice, we can only partially meet these requirements. Our validation algorithm does not consider the code’s data and computations, only its control flow and stack use. This means we will reject programs with any invalid code paths, even if those paths are not reachable at runtime. Further, conditions 1 and 2 —Insufficient gas and stack overflow—must in general be checked at runtime. Conditions 3, 4, and 5 cannot occur if the code conforms to the following rules. +Tail call optimization, using `JUMP`: +``` +SQUARE: + jumpdest ; 1 gas + swap1 ; 3 gas + dup1 ; 3 gas + mul ; 5 gas + swap2 ; 3 gas + jump ; 8 gas + +CALL_SQUARE: + jumpdest ; 1 gas + push 0x02 ; 3 gas + push SQUARE ; 3 gas + jump ; 8 gas +``` +_Total: 38 gas_ -#### The Rules +Using `RJUMPSUB` versus `JUMP` saves _38 - 17 = 21 gas_ — a _55%_ improvement. -1. `JUMP` and `JUMPI` address only valid `JUMPDEST` instructions. -2. `JUMPSUB` addresses only valid `BEGINSUB` instructions. -3. `JUMP`, `JUMPI` and `JUMPSUB` are always preceded by one of the `PUSH` instructions. -4. For each instruction in the code the `stack depth` is always the same. -5. The `stack depth` is always positive and at most 1024. +#### Efficiency Caveats -Rules 1 and 2 are currently enforced at runtime. _Note: Valid instructions are not part of PUSH data._ +We can see that these instructions provide a simpler and more gas-efficient subroutine mechanism than using `JUMP` — in our examples they cut gas use by about half. -Rule 3, requiring a `PUSH` before each `JUMP*` would forbid dynamic jumps. Absent dynamic jumps another mechanism is needed for subroutine returns, as provided here. +Clearly, the benefits of this efficiency are greater for programs that have been factored into smaller subroutines. How small? Wrapping code in a subroutine costs only _8 gas_ using `RJUMPSUB` and `RETURNSUB` versus _30 gas_ using `JUMP`, `PUSH` and `SWAP` as above. -For rules 4 and 5 we need to define `stack depth`. The Yellow Paper has the `stack pointer` or `SP` pointing just past the top item on the `data stack`. We define the `stack base` as where the `SP` pointed at the most recent `JUMPSUB`, or `0` on program entry. So we can define the `stack depth` as the number of stack elements between the current `SP` and the current `stack base`. +### Costs -Given our definition of `stack depth` Rule 4 ensures that control flows which return to the same place with a different `stack depth` are invalid. These can be caused by irreducible paths like jumping into loops and subroutines, and calling subroutines with different numbers of arguments. Taken together, these rules allow for code to be validated by following the control-flow graph, traversing each edge only once. +The _low_ cost of `RJUMPSUB` versus the _mid_ cost of `JUMP` is justified by needing only to decode the immediate two byte destination to the `PC` and push the return address on the `return stack`, all using native arithmetic, versus using the data stack with emulated 256-bit instructions. -Finally, Rule 5 precludes all stack underflows (and some stack overflows.) +The _verylow_ cost of `RETURNSUB` is justified by needing only to pop the `return stack` into the `PC`. Benchmarking will be needed to tell if the costs are well-balanced. -### Validation +## Backwards Compatibility + +These changes affect the semantics of existing EVM code: bytes that would have been interpreted as valid jump destinations may now be interpreted as immediate data. Since this proposal depends on the Ethereum Object Format to signal the change this is not a practical issue. -The following is a pseudo-Go specification of an algorithm for enforcing program validity. It recursively traverses the bytecode, following its control flow and stack use and checking for violations of the rules above. (For simplicity we ignore the issue of JUMPDEST or BEGINSUB bytes in PUSH data.) It runs in time == O(vertices + edges) in the program's control-flow graph, where vertices represent control-flow instructions and the edges represent basic blocks. -``` - var bytecode []byte - var stack_depth []int - var SP := 0 - - func validate(PC :=0) boolean { - // traverse code sequentially, recurse for subroutines and conditional jumps - while true { - instruction = bytecode[PC] - if is_invalid(instruction) { - return false; - } - - // if stack depth non-zero we have been here before - // check for constant depth and return to break cycle - if stack_depth[PC] != 0 { - if SP != stack_depth[PC] { - return false - } - return true - } - stack_depth[PC] = SP - - // effect of instruction on stack - SP -= removed_items(instruction) - SP += added_items(instruction) - if SP < 0 || 1024 < SP { - return false - } - - // successful validation of path - if instruction == STOP, RETURN, or SUICIDE { - return true - } - - if instruction == JUMP { - - // check for constant and correct destination - if (bytecode[PC - 33] != PUSH32) { - return false - } - PC = stack[PC-32] - if byte_code[PC] != JUMPDEST { - return false - } - - // reset PC to destination of jump - PC = stack[PC-32] - continue - } - if instruction == JUMPI { - - // check for constant and correct destination - if (bytecode[PC - 33] != PUSH32) { - return false - } - PC = stack[PC-32] - if byte_code[PC] != JUMPDEST { - return false - } - // recurse to jump to code to validate - if !validate(stack[SP])) { - return false - } - continue - } - if instruction == JUMPSUB { - - // check for constant and correct destination - if (bytecode[PC - 33] != PUSH32) - return false - prevPC = PC - PC = stack[PC-32] - if byte_code[PC] != BEGINSUB { - return false - } - - // recurse to jump to code to validate - prevSP = SP - depth = SP - prevSP - SP = depth - if !validate(stack[SP]+1)) { - return false - } - SP = prevSP - depth + SP - PC = prevPC - continue - } - if instruction == RETURNSUB { - PC = prevPC - return true - } - - // advance PC according to instruction - PC = advance_pc(PC, instruction) - } - } -``` ## Test Cases ### Simple routine This should jump into a subroutine, back out and stop. -Bytecode: `0x60045e005c5d` (`PUSH1 0x04, JUMPSUB, STOP, BEGINSUB, RETURNSUB`) +Bytecode: `0x5f0003005e` (`RJUMPSUB 3, RETURNSUB, STOP`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | PUSH1 | 3 | [] | [] | -| 2 | JUMPSUB | 10 | [4] | [] | -| 5 | RETURNSUB | 5 | [] | [ 2] | -| 3 | STOP | 0 | [] | [] | +| 0 | RJUMPSUB | 5 | [] | [] | +| 2 | STOP | 0 | [] | [] | +| 3 | RETURNSUB | 3 | [] | [] | Output: 0x -Consumed gas: `18` +Consumed gas: `10` ### Two levels of subroutines This should execute fine, going into one two depths of subroutines -Bytecode: `0x6800000000000000000c5e005c60115e5d5c5d` (`PUSH9 0x00000000000000000c, JUMPSUB, STOP, BEGINSUB, PUSH1 0x11, JUMPSUB, RETURNSUB, BEGINSUB, RETURNSUB`) +Bytecode: `0x5f00045F00025200` (`RJUMPSUB 4, RJUMPSUB 2, RETURNSUB, RETURNSUB, STOP`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | PUSH9 | 3 | [] | [] | -| 10 | JUMPSUB | 10 | [12] | [] | -| 13 | PUSH1 | 3 | [] | [10] | -| 15 | JUMPSUB | 10 | [17] | [10] | -| 18 | RETURNSUB | 5 | [] | [10,15] | -| 16 | RETURNSUB | 5 | [] | [10] | -| 11 | STOP | 0 | [] | [] | +| 0 | RJUMPSUB | 5 | [] | [] | +| 3 | RJUMPSUB | 5 | [] | [] | +| 4 | RETURNSUB | 5 | [] | [] | +| 5 | RETURNSUB | 5 | [] | [] | +| 6 | STOP | 0 | [] | [] | -Consumed gas: `36` +Consumed gas: `20` ### Failure 1: invalid jump -This should fail, since the given location is outside of the code-range. The code is the same as previous example, -except that the pushed location is `0x01000000000000000c` instead of `0x0c`. +This should fail, since the given location is outside of the code-range. -Bytecode: `0x6801000000000000000c5e005c60115e5d5c5d` (`PUSH9 0x01000000000000000c, JUMPSUB, STOP, BEGINSUB, PUSH1 0x11, JUMPSUB, RETURNSUB, BEGINSUB, RETURNSUB`) +Bytecode: `0X5fff`(`RJUMPSUB -1`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | PUSH9 | 3 | [] | [] | -| 10 | JUMPSUB | 10 |[18446744073709551628] | [] | +| 0 | RJUMPSUB | 10 | [] | [] | ``` -Error: at pc=10, op=JUMPSUB: invalid jump destination +Error: at pc=0, op=RJUMPSUB: invalid jump destination ``` ### Failure 2: shallow `return stack` This should fail at first opcode, due to shallow `return_stack` -Bytecode: `0x5d5858` (`RETURNSUB, PC, PC`) +Bytecode: `0x5e` (`RETURNSUB`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| @@ -314,27 +322,124 @@ Error: at pc=0, op=RETURNSUB: invalid retsub ### Subroutine at end of code -In this example. the JUMPSUB 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 +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: `0x6005565c5d5b60035e` (`PUSH1 0x05, JUMP, BEGINSUB, RETURNSUB, JUMPDEST, PUSH1 0x03, JUMPSUB`) +Bytecode: `0x5c00045e5fffff` (`RJUMP 4, RETURNSUB, RJUMPSUB -1`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | PUSH1 | 3 | [] | [] | -| 2 | JUMP | 8 | [5] | [] | -| 5 | JUMPDEST | 1 | [] | [] | -| 6 | PUSH1 | 3 | [] | [] | -| 8 | JUMPSUB | 10 | [3] | [] | -| 4 | RETURNSUB | 5 | [] | [ 8] | -| 9 | STOP | 0 | [] | [] | - -Consumed gas: `30` - -## References -Gavin Wood, [Ethereum: A Secure Decentralized Generalized Transaction Ledger](https://ethereum.github.io/yellowpaper/paper.pdf), 2014-2021 -Greg Colvin, Brooklyn Zelenka, Paweł Bylica, Christian Reitwiessner, [EIP-615: Subroutines and Static Jumps for the EVM](https://eips.ethereum.org/EIPS/eip-615), 2016-2019 -Martin Lundfall, [EIP-2327: BEGINDATA Opcode](https://eips.ethereum.org/EIPS/eip-2327), 2019 -Nick Johnson, [EIP-3337: Frame pointer support for memory load and store operations](https://eips.ethereum.org/EIPS/eip-3337), 2021 +| 0 | RJUMP | 5 | [] | [] | +| 3 | RETURNSUB | 5 | [] | [] | +| 4 | RJUMPSUB | 5 | [] | [] | +| 7 | STOP | 0 | [] | [] | + +Consumed gas: `15` + +## 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. + +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. + +It runs in time equal to `O(vertices + edges)` in the program's control-flow graph, where edges represent control flow and the vertices represent _basic blocks_ — thus the algorithm takes time proportional to the size of the _code_. It maintains a stack of continuations for conditional jumps, the size of which is at most proportional to the size of the _code_. + +### Validation Function + +** Note: this function is known to be incomplete and incorrect. ** + +For simplicity's sake we assume that all jumpdest analysis and prior validation has been done, including EIP-3540, EIP-3670, and EIP-4200, so EOF headers and sections are well-formed, and there are no invalid instructions or jumps. In practice, all passes of validation can be folded into a single loop no recursion. + +We also assume some helper functions. +* `is_valid(opcode)` returns true iff opcode is valid. +* `is_terminator(opcode)` returns true iff opcode is terminator. +* `is_valid_jumpdest(pc)` returns true iff `pc` is a valid jump destination. +* `immediate_data(pc)` returns the immediate data for the instruction at `pc`. +* `immediate_size(opcode)` returns the size of the immediate data for an opcode. +* `removed_items(opcode)` returns the number of items removed from the `data_stack` by the `opcode`. +* `added_items(opcode)` returns the number of items added to the `data_stack` by the `opcode`. + +``` +# returns true iff code is valid +def validate_code(code: bytes, pc: int, sp: int, bp: int) -> boolean: + continuations = [] + do + while pc < len(code): + opcode = code[pc] + if !is_valid(opcode): + return false + if is_terminator(opcode): + return true + + # check stack height and return if we have been here before + stack_height = sp - bp + if stack_height > 1024 + return false + if pos in stack_heights: + if stack_height != stack_heights[pos]: + return false + return true + else: + stack_heights[pos] = stack_height + + if opcode == RJUMP: + + # reset pc to destination of jump + jumpdest = immediate_data(pc) + pc += jumpdest + if !is_valid_jumpdest(pc) + return false + + elif opcode == RJUMPI: + + jumpdest = pc + immediate_data(pc) + if !is_valid_jumpdest(pc) + return false + + # continue true side of conditional later + continations.push((jumpdest, sp, bp)) + + # continue false side of conditional now + + elif opcode == RJUMPSUB: + + # will enter subroutine at destination + bp = sp + + # push return address and reset pc to destination + jumpdest = pc + immediate_data(pc) + if !is_valid_jumpdest(pc) + return false + push(return_stack, pc + 3) + pc = jumpdest + continue + + elif opcode == RETURNSUB: + + # will return to subroutine at destination + bp = sp + + # pop return address and check for preceding call + pc = pop(return_stack) + if code[pc - 3] != RJUMPSUB: + return false + + # apply instructions to stack + sp -= removed_items(opcode) + if sp < 0 + return false + sp += added_items(opcode) + + # Skip opcode and immediate data + pc += 1 + immediate_size(opcode) + + while (pc, sp, bp) = continuations.pop() + + return true +``` + +## Security Considerations + +These changes introduce new flow control instructions. They do not introduce any new security considerations. This EIP is intended to improve security by validating a higher level of safety for EVM code deployed on the blockchain. The validation algorithm must be quasi-linear in time and space to not be a denial of service vulnerability. The algorithm here makes one linear-time pass of the bytecode, and uses a stack of continuations that cannot exceed the number of `RJUMPI` instructions in the code. ## 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-2327.md b/EIPS/eip-2327.md index 3546458110da69..f2e4449bbefd69 100644 --- a/EIPS/eip-2327.md +++ b/EIPS/eip-2327.md @@ -3,7 +3,7 @@ eip: 2327 title: BEGINDATA opcode author: Martin Lundfall (@MrChico) discussions-to: https://ethereum-magicians.org/t/new-opcode-begindata/3727 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-10-28 @@ -46,4 +46,4 @@ Test cases should include: Not yet. ## 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-233.md b/EIPS/eip-233.md index c70c6b49c7ab56..dd23be517dd580 100644 --- a/EIPS/eip-233.md +++ b/EIPS/eip-233.md @@ -4,7 +4,7 @@ title: Formal process of hard forks author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-233-formal-process-of-hard-forks/1387 type: Meta -status: Draft +status: Stagnant created: 2017-03-23 --- @@ -104,7 +104,7 @@ This meta-EIP specifies the changes included in the Ethereum hardfork named Ista ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). {% endraw %} ``` @@ -115,4 +115,4 @@ A meta EIP for coordinating the hard fork should help in visibility and traceabi ## 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-2330.md b/EIPS/eip-2330.md index 58a8342e3a65bd..a648f15e78e970 100644 --- a/EIPS/eip-2330.md +++ b/EIPS/eip-2330.md @@ -1,63 +1,62 @@ --- eip: 2330 title: EXTSLOAD opcode +description: A new EVM opcode to read external contract storage data. author: Dominic Letz (@dominicletz), Santiago Palladino (@spalladino) discussions-to: https://ethereum-magicians.org/t/eip-2330-extsload-and-abi-for-lower-gas-cost-and-off-chain-apps/3733 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-10-29 +requires: 2929 --- -## Simple Summary -A new `EXTSLOAD ` EVM opcode to read external contract storage data and corresponding allowing to build registry and token contracts that use less gas. - ## Abstract -While any off-chain application can read all contract storage data of all contracts, this is not possible for deployed smart contracts themselves. These are bound to use contract calls for any interaction including reading data from other contracts. This EIP adds an EVM opcode to directly read external contract storage. + +This proposal adds a new opcode `EXTSLOAD` at `0x5c` which pops two items from the stack: ` ` and pushes one item: ``. The gas cost is sum of account access cost and storage read based on [EIP-2929](./eip-2929.md) Access Lists. ## Motivation -The gas cost when reading from registry style contract such as ERC-20s, ENS and other data contracts is very high, because they incur cross contract call cost, cost for ABI encoding, decoding and dispatching and finally loading the data. In many cases the underlying storage that is being queried is though just a simple mapping. In these cases a new `EXTSLOAD` call directly accessing the mapping in storage could not only **reduce the gas cost** of the interaction more than 10x, but also it would make the gas cost **predictable** for the reading contract. Furthermore with the use of the existing `EXTCODEHASH` an external contracts implementation can be verified and allows `EXTSLOAD` to make deterministic reads even from third-party smart contracts. + +While any off-chain application can read all contract storage data of all contracts, this is not possible for deployed smart contracts themselves. These are bound to use contract calls for any interaction including reading data from other contracts. This EIP adds an EVM opcode to directly read external contract storage. + +The gas cost when reading from registry style contract such as [EIP-20s](./eip-20.md), ENS and other data contracts is very high, because they incur cross contract call cost, cost for ABI encoding, decoding and dispatching and finally loading the data. In many cases the underlying storage that is being queried is though just a simple mapping. On top of that, the view function may SLOAD many other slots which caller may not be interested in, which further adds to the gas costs. In these cases a new `EXTSLOAD` call directly accessing the mapping in storage could not only **reduce the gas cost** of the interaction more than 10x, but also it would make the gas cost **predictable** for the reading contract. ## Specification -**Proposal** -A new EVM instruction `EXTSLOAD (0x5c)` that works like `SLOAD (0x54)` but an additional parameter representing the contract that is to be read from. The gas cost of `EXTSLOAD` would be the sum of the [fee schedule G](https://ethereum.github.io/yellowpaper/paper.pdf) for G\[EXTCODE\](700) + G\[SLOAD\](800) = 1500 gas +A new EVM instruction `EXTSLOAD (0x5c)` that works like `SLOAD (0x54)` but an additional parameter representing the contract that is to be read from. -``` +```shell EXTSLOAD (0x5c) ``` The `EXTSLOAD` instruction pops 2 values from the stack, first `contract` a contract address and then second `slot` a storage address within `contract`. As result `EXTSLOAD` pushes on the stack the value from the contract storage of `contract` at the storage `slot` address or `0` in case the account `contract` does not exist. -**Example** +### Gas cost pre-verkle -An example assuming [further Solidity changes](https://github.com/ethereum/solidity/issues/7593) for illustration: +Gas to be charged before Verkle Tree change is specified as `ACCOUNT_ACCESS_COST + STORAGE_READ_COST` where: -```solidity -interface MemberList { - public fixed(@5) mapping(address => bool) members; -} -``` +- `ACCOUNT_ACCESS_COST` is `0` if the account address is already in `accessed_addresses` set, otherwise `COLD_ACCOUNT_ACCESS_COST`. +- `STORAGE_READ_COST` is `WARM_STORAGE_READ_COST` if storage key is already in `accessed_storage_keys` set, otherwise `COLD_STORAGE_READ_COST`. -And a corresponding contract function that uses this member list. Similarly tokens or other registries could be implemented. +### Gas cost post-verkle -```solidity -function membersOnly(address list, address member) { - MemberList ml = MemberList(list); - if (ml.members[client] == false) revert("Nonmember!"); -} -``` +It is important to consider that post Verkle tree change, `ACCOUNT_ACCESS_COST` will not be needed since a single account's storage would be spread across the entire global trie. Hence gas to be charged post Verkle Tree change is just `STORAGE_READ_COST`, which is as specified in [Gas cost pre-verkle](#gas-cost-pre-verkle). -The call `ml.members[client]` here could let the Solidity compiler generate the normal map access logic but using the new `EXTSLOAD ` instructions to read from the `ml` contract storage instead of the local contract storage. +## Rationale + +- Without this EIP, a contract can still opt-in to make their entire state public, by having a method that simply SLOADs and returns the values ([example](../assets/eip-2330/Extsload.sol)). The complexity of the gas cost can be seen as `1`x CALL cost + `N`x SLOAD cost. Hence, the gas cost specified for using EXTSLOAD opcode on an account for `N` times, the charge of `1`x `COLD_ACCOUNT_ACCESS_COST` and `N`x `STORAGE_READ_COST` is hereby justified. +- Without this EIP, a contract can still use internal state of other contracts. An external party can supply a value and proof to a contract, which the contract can verify using `BLOCKHASH`. This is only possible for the previous blocks and not the latest state (since current blockhash cannot be determined before execution). +- This opcode can be seen as breaking object-oriented (OO) model because it allows to read storage of other contracts. In usual systems using OO is net positive, because there is no limit on machine code and it hardly adds any cost to add more methods or use single method to get a ton of data while the caller needs to just a small portion of data. However on EVM, there are visible costs, i.e. about $0.2 per SLOAD (20 gwei and ETHUSD 2000). Also, OO has caused misleading assumptions for developers where variables marked as "private" in smart contracts are encrypted in some way/impossible to read which has resulted bad designs. Hence, this EIP can be beneficial in terms of making smart contract systems more efficient as well as preventing misconceptions as well. ## Backwards Compatibility + This change is fully backwards compatible since it adds a new instruction. -## Test Cases -Not started yet. +## Security Considerations -## Implementation -[Aleth Pull Request](https://github.com/ethereum/aleth/pull/5805) +- Since the opcode is similar to SLOAD, it should be easy to implement in various clients. +- This opcode allows the callee `A` to re-enter a caller contract `B` and read state of `B` and `B` cannot stop `A` from doing that. Since this does not change any state, it should not be a security issue. Contracts generally use re-entrancy guards, but that is only added to write methods. So even currently without EXTSLOAD, `A` can re-enter `B` and read their state exposed by any view methods and it has not been an issue. ## 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-2333.md b/EIPS/eip-2333.md index 7e601b0c3c7a39..095c82a874cb6b 100644 --- a/EIPS/eip-2333.md +++ b/EIPS/eip-2333.md @@ -3,7 +3,7 @@ eip: 2333 title: BLS12-381 Key Generation author: Carl Beekhuizen discussions-to: https://github.com/ethereum/EIPs/issues/2337 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-09-30 @@ -21,13 +21,13 @@ In addition to the above, this method of deriving keys provides an emergency bac ## A note on purpose -This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted the BLS12-381 signature standard. It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. +This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted [BLS signatures over BLS12-381](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. ## Motivation ### Deficiencies of the existing mechanism -The new curve (BLS12-381) used for signatures within Ethereum 2.0 (alongside many other projects) mandates a new key derivation scheme. The most commonly used scheme for key derivation within Ethereum 1.x is [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) (also known as HD derivation) which deems keys greater than the curve order invalid. Based on the order of the private key subgroup of BLS12-381 and the size of the entropy utilised, more than 54% of keys generated by BIP32 would be invalid. (secp256k1 keys derived by BIP32 are invalid with probability less than 1 in 2-127.) +The curve BLS12-381 used for BLS signatures within Ethereum 2.0 (alongside many other projects) mandates a new key derivation scheme. The most commonly used scheme for key derivation within Ethereum 1.x is [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) (also known as HD derivation) which deems keys greater than the curve order invalid. Based on the order of the private key subgroup of BLS12-381 and the size of the entropy utilised, more than 54% of keys generated by BIP32 would be invalid. (secp256k1 keys derived by BIP32 are invalid with probability less than 1 in 2-127.) ### Establishing a multi-chain standard early on @@ -41,7 +41,7 @@ This key derivation scheme has a Lamport key pair which is generated as a interm ### Version -Due to the evolving BLS standard (currently v4), the `KeyGen` function was updated, meaning that `hkdf_mod_r` no longer reflected what appeared in the BLS standard. This EIP was updated on the 17th of September 2020 to reflect this new method for deriving keys, **if you are implementing this EIP, please make sure your version is up to date.** +Due to the evolving BLS signatures CFRG draft (currently v4), the `KeyGen` function was updated, meaning that `hkdf_mod_r` no longer reflected what appeared in the BLS standard. This EIP was updated on the 17th of September 2020 to reflect this new method for deriving keys, **if you are implementing this EIP, please make sure your version is up to date.** ### Specification @@ -152,7 +152,7 @@ Every key generated via the key derivation process derives a child key via a set * `"BLS-SIG-KEYGEN-SALT-"` is an ASCII string comprising 20 octets. * `OS2IP` is as defined in [RFC3447](https://ietf.org/rfc/rfc3447.txt) (Big endian encoding) * `I2OSP` is as defined in [RFC3447](https://ietf.org/rfc/rfc3447.txt) (Big endian decoding) -* `r` is the order of the BLS 12-381 curve defined in [the draft IETF BLS signature scheme standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04) `r=52435875175126190479447740508185965837690552500527637822603658699938581184513` +* `r` is the order of the BLS 12-381 curve defined in [the v4 draft IETF BLS signature scheme standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04) `r=52435875175126190479447740508185965837690552500527637822603658699938581184513` ##### Procedure @@ -219,11 +219,11 @@ The Lamport signatures used within this scheme have 255 bits worth of security, ### SHA256 -SHA256 is used as the hash function throughout this standard as it is the hash function chosen by the for the [IETF BLS12-381 standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04). Using a single hash function for everything decreases the number of cryptographic primitives required to implement the entire BLS standardised key-stack while reducing the surface for flaws in the overall system. +SHA256 is used as the hash function throughout this standard as it is the hash function chosen by the [IETF BLS signature proposed standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). Using a single hash function for everything decreases the number of cryptographic primitives required to implement the entire BLS standardised key-stack while reducing the surface for flaws in the overall system. ### `hkdf_mod_r()` -The function `hkdf_mod_r()` in this standard is the same as the `KeyGen` function described in the [draft IETF BLS standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04) and therefore the private key obtained from `KeyGen` is equal to that obtained from `hkdf_mod_r` for the same seed bytes. This means that common engineering can be done when implementing this function. Additionally because of its inclusion in an IETF standard, it has had much scrutiny by many cryptographers and cryptanalysts, thereby lending credence to its safety as a key derivation mechanism. +The function `hkdf_mod_r()` in this standard is the same as the `KeyGen` function described in the [proposed standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/) and therefore the private key obtained from `KeyGen` is equal to that obtained from `hkdf_mod_r` for the same seed bytes. This means that common engineering can be done when implementing this function. Additionally because of its inclusion in an IETF standard, it has had much scrutiny by many cryptographers and cryptanalysts, thereby lending credence to its safety as a key derivation mechanism. While `hkdf_mod_r()` has modulo bias, the magnitude of this bias is minuscule (the output size of HKDF is set to 48 bytes which is greater 2128 time larger than the curve order). This bias is deemed acceptable in light of the simplicity of the constant time scheme. @@ -813,4 +813,4 @@ child_SK = 203977898597366509423174122624725581078753921724440767926710919752109 ## 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-2334.md b/EIPS/eip-2334.md index 300a1ac794eeb2..76a9b198d3941d 100644 --- a/EIPS/eip-2334.md +++ b/EIPS/eip-2334.md @@ -3,7 +3,7 @@ eip: 2334 title: BLS12-381 Deterministic Account Hierarchy author: Carl Beekhuizen discussions-to: https://github.com/ethereum/EIPs/issues/2338 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-09-30 @@ -20,11 +20,11 @@ A standard for allocating keys generated by [EIP-2333](./eip-2333.md) to a speci ## A note on purpose -This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted the BLS12-381 signature standard. It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. +This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted [BLS signatures over BLS12-381](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. ## Motivation -Ethereum 2.0 alongside many other projects will use BLS12-381 signatures. This new scheme requires a new key derivation mechanism, which is established within [EIP-2333](./eip-2333.md). This new scheme is incompatible with the current form of this specification ([BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) due to the: exclusive use of hardened keys, the increased number of keys per level, not using [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) for key derivation. It is therefore necessary to establish a new *path* for traversing the [EIP-2333](./eip-2333.md) key-tree. +Ethereum 2.0 alongside many other projects will use BLS signatures over BLS12-381, an [IETF proposed standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). This new scheme requires a new key derivation mechanism, which is established within [EIP-2333](./eip-2333.md). This new scheme is incompatible with the current form of this specification ([BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) due to the: exclusive use of hardened keys, the increased number of keys per level, not using [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) for key derivation. It is therefore necessary to establish a new *path* for traversing the [EIP-2333](./eip-2333.md) key-tree. The path structure specified in this EIP aims to be more general than [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) by not having UTXO-centric features [which gave rise to the 4 different types of wallet paths being used within Ethereum 1.0](https://github.com/ethereum/EIPs/issues/84#issuecomment-292324521) and gave rise to (draft) [EIP-600](./eip-600.md) & [EIP-601](./eip-601.md) @@ -101,4 +101,4 @@ The primary reason validators have separate signing and withdrawal keys is to al ## 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-2335.md b/EIPS/eip-2335.md index 4bad77e287bf12..43ca6240e06a81 100644 --- a/EIPS/eip-2335.md +++ b/EIPS/eip-2335.md @@ -3,7 +3,7 @@ eip: 2335 title: BLS12-381 Keystore author: Carl Beekhuizen discussions-to: https://github.com/ethereum/EIPs/issues/2339 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-09-30 @@ -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 @@ -298,4 +298,4 @@ Implementations exist in the following languages: ## 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-234.md b/EIPS/eip-234.md index 27b111a9104af7..5cd5a0ff3ab73b 100644 --- a/EIPS/eip-234.md +++ b/EIPS/eip-234.md @@ -2,10 +2,10 @@ eip: 234 title: Add `blockHash` to JSON-RPC filter options. author: Micah Zoltu (@MicahZoltu) +discussions-to: https://github.com/ethereum/EIPs/issues/234 type: Standards Track category: Interface -status: Last Call -review-period-end: 2020-08-08 +status: Final created: 2017-03-24 requires: 1474 --- @@ -46,4 +46,4 @@ The only potential issue here is the `fromBlock` and `toBlock` fields. It would ## 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-2364.md b/EIPS/eip-2364.md index dde5598e4eb9cd..981f56d0253b87 100644 --- a/EIPS/eip-2364.md +++ b/EIPS/eip-2364.md @@ -1,9 +1,10 @@ --- eip: 2364 title: "eth/64: forkid-extended protocol handshake" -author: Péter Szilágyi +description: Introduces validation of the `forkid` when handshaking with peers. +author: Péter Szilágyi , Péter Szilágyi (@karalabe), Tim Beiko (@timbeiko) discussions-to: https://github.com/ethereum/EIPs/issues/2365 -status: Draft +status: Final type: Standards Track category: Networking created: 2019-11-08 @@ -12,22 +13,22 @@ requires: 2124 ## Abstract +This EIP specifies the inclusion of the `forkid`, originally defined in [(EIP-2124)](./eip-2124.md), as a new field in the Ethereum wire protocol (`eth`) handshake. This change is implemented as a new version of the wire protocol, `eth/64`. + +## Motivation + The [`forkid` (EIP-2124)](./eip-2124.md) was designed to permit two Ethereum nodes to quickly and cheaply decide if they are compatible or not, not only at a genesis/networking level, but also from the perspective of the currently passed network updates (i.e. forks). [EIP-2124](./eip-2124.md) only defines how the `forkid` is calculated and validated, but does not specify how the `forkid` should be exchanged between peers. This EIP specifies the inclusion of the `forkid` as a new field in the Ethereum wire protocol (`eth`) handshake (releasing a new version, `eth/64`). By cross-validating `forkid` during the handshake, incompatible nodes can disconnect before expensive block exchanges and validations take place (PoW check, EVM execution, state reconstruction). This further prevents peer slots from being taken up by nodes that are incompatible, but have not yet been detected as such. -## Motivation - From a micro perspective, cutting off incompatible nodes from one another ensures that a node only spends its resources on tasks that are genuinely useful to it. The sooner we can decide the remote peer is useless, the less time and processing we expend in vain. From a macro perspective, keeping incompatible nodes partitioned from one another ensures that disjoint clusters retain more resources for maintaining their own chain, thus raising the quality of service for all networks globally. ## Specification -The specification is tiny since most parts are already specified in EIP-2124. `eth/63` is not specified as an EIP, but is maintained [here](https://github.com/ethereum/devp2p/blob/master/caps/eth.md). - - Implement `forkid` generation and validation per [EIP-2124](./eip-2124.md). - Advertise a new `eth` protocol capability (version) at `eth/64`. - The old `eth/63` protocol should still be kept alive side-by-side, until `eth/64` is sufficiently adopted by implementors. @@ -40,15 +41,17 @@ Whenever two peers connect using the `eth/64` protocol, the updated `Status` mes ## Rationale -##### EIP-2124 mentions advertising the `forkid` in the discovery protocol too. How does that compare to advertising in the `eth` protocol? Why is the redundancy needed? +The specification is tiny since most parts are already specified in EIP-2124. `eth/63` is not specified as an EIP, but is maintained in the [ethereum/devp2p](https://github.com/ethereum/devp2p) Github repository. + +### EIP-2124 mentions advertising the `forkid` in the discovery protocol too. How does that compare to advertising in the `eth` protocol? Why is the redundancy needed? Advertising and validating the `forkid` in the discovery protocol is a more optimal solution, as it can help avoid the cost of setting up the TCP connection and cryptographic RLPx stream, only to be torn down if `eth/64` rejects it. Compared to the `eth` protocol however, discovery is a bit fuzzy. The goal there is to suggest potential peers, not to be fool-proof. Information may be outdated, nodes may have changed or disappeared. Discovery can do a rough filtering, but more precision is still needed afterwards. -Additionally, `forkid` validation via the discovery protocol requires ENR implementation ([EIP-778](./eip-778.md)) and ENR extension support ([EIP-868](./eip-868.md)), which is not mandated by the Ethereum network currently. Lastly, the discovery protocol is just one way to find peers, but systems that cannot use UDP or that rely on other mechanism (e.g. DNS discovery ([EIP-1459](./eip-1459.md))) still need a way to filter connections. +Additionally, `forkid` validation via the discovery protocol requires ENR implementation ([EIP-778](./eip-778.md)) and ENR extension support ([EIP-868](./eip-868.md)), which is not mandated by the Ethereum network currently. Lastly, the discovery protocol is just one way to find peers, but systems that cannot use UDP or that rely on other mechanism (e.g. DNS discovery)) still need a way to filter connections. -##### The `forkid` implicitly contains the genesis hash checksummed into the `FORK_HASH` field. Why doesn't this proposal remove the `genesisHash` field from the `eth` handshake? +### The `forkid` implicitly contains the genesis hash checksummed into the `FORK_HASH` field. Why doesn't this proposal remove the `genesisHash` field from the `eth` handshake? Originally this EIP did remove it as redundant data, since filtering based on the `forkid` is a superset of filtering based on genesis hash. The reason for backing out of that decision was that the genesis hash may be useful for other things too, not just connection filtering (network crawlers use it currently to split nodes across networks). @@ -64,12 +67,10 @@ This EIP does not change the consensus engine, thus does _not_ require a hard fo For calculating and validating fork IDs, see test cases in [EIP-2124](./eip-2124.md). -Testing proper advertising and validation at the networking level will require a [hive](https://github.com/ethereum/hive) test. - -## Implementation +## Security Considerations -Geth: https://github.com/ethereum/go-ethereum/pull/20140 +None. ## 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-2378.md b/EIPS/eip-2378.md index 6740a74f809132..7db43ca52872d1 100644 --- a/EIPS/eip-2378.md +++ b/EIPS/eip-2378.md @@ -3,7 +3,7 @@ eip: 2378 title: EIPs Eligible for Inclusion author: James Hancock (@MadeofTin) discussions-to: https://gitter.im/ethereum/EIPs -status: Draft +status: Stagnant type: Meta created: 2019-11-13 --- @@ -67,4 +67,4 @@ Development of clear specifications and pull requests to existing Ethereum Clien ## 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-2384.md b/EIPS/eip-2384.md index 2c7c39dd8accfe..e8079af021c592 100644 --- a/EIPS/eip-2384.md +++ b/EIPS/eip-2384.md @@ -37,4 +37,4 @@ Test cases shall be created once the specification is to be accepted by the deve The implementation in it's logic does not differ from [EIP-649](./eip-649.md) or [EIP-1234](./eip-1234.md); an implementation for Parity-Ethereum is available in [parity-ethereum#9187](https://github.com/paritytech/parity-ethereum/pull/9187). ## 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-2386.md b/EIPS/eip-2386.md index 0d3381e65bae47..f09e375c76fe54 100644 --- a/EIPS/eip-2386.md +++ b/EIPS/eip-2386.md @@ -3,7 +3,7 @@ eip: 2386 title: Ethereum 2 Hierarchical Deterministic Walletstore author: Jim McDonald discussions-to: https://ethereum-magicians.org/t/eip-2386-walletstore/3792 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-11-21 @@ -198,4 +198,4 @@ An example high-security configuration may involve the walletstore existing on a ## 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-2387.md b/EIPS/eip-2387.md index 94e810c43825ec..27a441e7a8686a 100644 --- a/EIPS/eip-2387.md +++ b/EIPS/eip-2387.md @@ -72,4 +72,4 @@ Previous Hardforks to address the Ice Age have also included reductions in the b ## 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-2390.md b/EIPS/eip-2390.md index 832efb82c8a5f9..4d87272c0810ad 100644 --- a/EIPS/eip-2390.md +++ b/EIPS/eip-2390.md @@ -3,7 +3,7 @@ eip: 2390 title: Geo-ENS author: James Choncholas (@james-choncholas) discussions-to: https://github.com/ethereum/EIPs/issues/2959 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2019-11-15 @@ -313,4 +313,4 @@ of coarse granularity user location. ## 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-2400.md b/EIPS/eip-2400.md index 9cc754e2a48b17..1978fd56b03e0b 100644 --- a/EIPS/eip-2400.md +++ b/EIPS/eip-2400.md @@ -1,25 +1,24 @@ --- eip: 2400 -title: URL Format for Transaction Receipts +title: Transaction Receipt URI +description: URI format for submitted transactions with complete information for transaction decoding author: Ricardo Guilherme Schmidt (@3esmit), Eric Dvorsak (@yenda) discussions-to: https://ethereum-magicians.org/t/eip-2400-transaction-receipt-uri/ +status: Stagnant type: Standards Track category: ERC -status: Draft created: 2019-11-05 -requires: 155, 831 +requires: 155, 681 --- +## Abstract -## Simple Summary - -A standardized URI for transaction receipt with complete information about transaction. +A transaction hash is not very meaningful on its own, because it looks just like any other hash, and it might lack important information for reading a transaction. -A standard way of representing a submitted transaction. +This standard includes all needed information for displaying a transaction and its details, such as `chainId`, `method` signature called, and `events` signatures emitted. -## Abstract +## Motivation -A transaction hash is not very meaningful on its own, because it looks just like any other hash, and it might lack important information for reading a transaction. -This standard includes all needed information for displaying a transaction and it's details, such as `chainId`, `method` signature called and `events` signatures emitted. +Interoperability between ethereum clients, allowing different systems to agree on a standard way of representing submitted transactions hashes, optionally with necessary information for decoding transaction details. ### Use-cases @@ -27,22 +26,18 @@ Transaction Receipt URIs embedded in QR-codes, hyperlinks in web-pages, emails o - In web3 (dapps, mining pools, exchanges), links would automatically open user's preferred transaction explorer; - In wallets, for users sharing transaction receipts easier; -- In chat applications, as a reply to an [ERC-681] transaction request; +- In chat applications, as a reply to an [EIP-681] transaction request; - In crypto vending machines, a QRCode can be displayed when transactions are submitted; - Anywhere transaction receipts are presented to users. -## Motivation - -Interoperability for web3: wallets, browsers and messengers. - ## Specification ### Syntax Transaction receipt URLs contain "ethereum" in their schema (protocol) part and are constructed as follows: - receipt = erc831_part transaction_hash [ "@" chain_id ] [ "?" parameters ] - erc831_part = "ethereum:tx-" + receipt = schema_part transaction_hash [ "@" chain_id ] [ "?" parameters ] + schema_part = "ethereum:tx-" transaction_hash = "0x" 64*HEXDIG chain_id = 1*DIGIT parameters = parameter *( "&" parameter ) @@ -57,7 +52,7 @@ Transaction receipt URLs contain "ethereum" in their schema (protocol) part and event_type = ["!"] TYPE -Where `TYPE` is a standard ABI type name, as defined in [Ethereum Contract ABI specification](https://solidity.readthedocs.io/en/develop/abi-spec.html). `STRING` is a URL-encoded unicode string of arbitrary length. +Where `TYPE` is a standard ABI type name, as defined in Ethereum Contract ABI specification. `STRING` is a URL-encoded unicode string of arbitrary length. The exclamation symbol (`!`), in `event_type`, is used to identify indexed event parameters. @@ -65,18 +60,24 @@ The exclamation symbol (`!`), in `event_type`, is used to identify indexed event `transaction_hash` is mandatory. The hash must be looked up in the corresponding `chain_id` transaction history, if not found it should be looked into the pending transaction queue and rechecked until is found. If not found anequivalent error as "transaction not found error" should be shown instead of the transaction. When the transaction is pending, it should keep checking until the transaction is included in a block and becomes "unrevertable" (usually 12 blocks after transaction is included). -`chain_id` is optional and contains the decimal chain ID, such that transactions on various test and private networks can be represented as well. If no `chain_id` is present, the $ETH/mainnet (`1`) is considered. + +`chain_id` is specified by [EIP-155] optional and contains the decimal chain ID, such that transactions on various test and private networks can be represented as well. If no `chain_id` is present, the $ETH/mainnet (`1`) is considered. If `method` is not present, this means that the transaction receipt URI does not specify details, or that it was a transaction with no calldata. When present it needs to be validated by comparing the first 4 bytes of transaction calldata with the first 4 bytes of the keccak256 hash of `method`, if invalid, an equivalent error as "method validation error" must be shown instead of the transaction. -If `events` is not present, this means that the transaction receipt URI does not specify details, or that the transaction did not raised any events. Pending and failed transactions don't validate events, however, when transaction is successful (or changes from pending to success) and events are present in URI, each event in the `event_list` must occur at least once in the transaction receipt event logs, otherwise an equivalent error as "event validation error: {event(s) [$event_signature, ...] not found}" should be shown instead of the transaction. +If `events` is not present, this means that the transaction receipt URI does not specify details, or that the transaction did not raised any events. Pending and failed transactions don't validate events, however, when transaction is successful (or changes from pending to success) and events are present in URI, each event in the `event_list` must occur at least once in the transaction receipt event logs, otherwise an equivalent error as "event validation error: {event(s) [$event_signature, ...] not found}" should be shown instead of the transaction. A URI might contain the event signature for all, some or none of the raised events. #### Examples -Simple ETH transfer: +##### Simple ETH transfer: `ethereum:tx-0x1143b5e38fe3cf585fb026fb9b5ce35c85a691786397dc8a23a07a62796d8172@1` -[Complex contract transaction](https://etherscan.io/tx/0x4465e7cce3c784f264301bfe26fc17609855305213ec74c716c7561154b76fec#eventlog): +##### Standard Token transfer: + +`ethereum:tx-0x5375e805b0c6afa20daab8d37352bf09a533efb03129ba56dee869e2ce4f2f92@1?method="transfer(address,uint256)"&events="Transfer(!address,!address,uint256)"` + +##### Complex contract transaction: + `ethereum:tx-0x4465e7cce3c784f264301bfe26fc17609855305213ec74c716c7561154b76fec@1?method="issueAndActivateBounty(address,uint256,string,uint256,address,bool,address,uint256)"&events="Transfer(!address,!address,uint256);BountyIssued(uint256);ContributionAdded(uint256,!address,uint256);BountyActivated(uint256,address)"` ## Rationale @@ -89,16 +90,11 @@ In order to decode transaction parameters and events, a part of the ABI is requi ## Backwards Compatibility -Future upgrades that are partially or fully incompatible with this proposal must use a prefix other than `tx-` that is separated by a dash (-) character from whatever follows it, as specified by [ERC-831]. - -## References - -* [ERC-831] -* [ERC-681] +Future upgrades that are partially or fully incompatible with this proposal must use a prefix other than `tx-` that is separated by a dash (-) character from whatever follows it. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). -[ERC-831]: ./eip-831.md -[ERC-681]: ./eip-681.md +[EIP-155]: ./eip-155.md +[EIP-681]: ./eip-681.md diff --git a/EIPS/eip-2458.md b/EIPS/eip-2458.md index 21ea4bc1ef8249..d4ed2d39754ccb 100644 --- a/EIPS/eip-2458.md +++ b/EIPS/eip-2458.md @@ -70,6 +70,6 @@ An implementation is adding a header option. This standard is informational and does not introduce technical security issues. ## 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-2464.md b/EIPS/eip-2464.md index dea697f81a8880..2cdc86df7f4d18 100644 --- a/EIPS/eip-2464.md +++ b/EIPS/eip-2464.md @@ -1,9 +1,10 @@ --- eip: 2464 title: "eth/65: transaction announcements and retrievals" -author: Péter Szilágyi , Gary Rong +description: Introduces `NewPooledTransactionHashes`, `GetPooledTransactions`, and `PooledTransactions`. +author: Péter Szilágyi , Péter Szilágyi (@karalabe), Gary Rong , Tim Beiko (@timbeiko) discussions-to: https://github.com/ethereum/EIPs/issues/2465 -status: Draft +status: Final type: Standards Track category: Networking created: 2020-01-13 @@ -12,7 +13,11 @@ requires: 2364 ## Abstract -The `eth` network protocol has two ways to propagate a newly mined block: it can be broadcast to a peer in its entirety (via `NewBlock (0x07)` [in `eth/64` and prior](https://github.com/ethereum/devp2p/blob/master/caps/eth.md)) or it can be announced only (via `NewBlockHashes (0x01)`). This duality allows nodes to do the high-bandwidth broadcasting (10s-100s KB) for a square root number of peers; and the low-bandwidth announcing (10s-100s B) for the remaining linear number of peers. The square root broadcast is enough to reach all well connected nodes, but the linear announce is needed to get across degenerate topologies. This works well. +This EIP introduces three additional message types into the `eth` protocol (releasing a new version, `eth/65`): `NewPooledTransactionHashes (0x08)` to announce a set of transactions without their content; `GetPooledTransactions (0x09)` to request a batch of transactions by their announced hash; and `PooledTransactions (0x0a)` to reply to a transaction request. This permits reducing the bandwidth used for transaction propagation from linear complexity in the number of peers to square root; and also reducing the initial transaction exchange from 10s-100s MB to `len(pool) * 32B ~= 128KB`. + +## Motivation + +The `eth` network protocol has two ways to propagate a newly mined block: it can be broadcast to a peer in its entirety (via `NewBlock (0x07)` in `eth/64` and prior or it can be announced only (via `NewBlockHashes (0x01)`). This duality allows nodes to do the high-bandwidth broadcasting (10s-100s KB) for a square root number of peers; and the low-bandwidth announcing (10s-100s B) for the remaining linear number of peers. The square root broadcast is enough to reach all well connected nodes, but the linear announce is needed to get across degenerate topologies. This works well. The `eth` protocol, however, does not have a similar dual mechanism for propagating transactions, so nodes need to rely on broadcasting (via `Transactions (0x02)`). To cater for degenerate topologies, transactions cannot be broadcast square rooted, rather they need to be transferred linearly to all peers. With N peers, each node will transfer the same transaction N times (counting both directions), whereas 1 would be enough in a perfect world. This is a significant waste. @@ -20,8 +25,6 @@ A similar issue arises when a new network connection is made between two nodes, This EIP introduces three additional message types into the `eth` protocol (releasing a new version, `eth/65`): `NewPooledTransactionHashes (0x08)` to announce a set of transactions without their content; `GetPooledTransactions (0x09)` to request a batch of transactions by their announced hash; and `PooledTransactions (0x0a)` to reply to a transaction request. This permits reducing the bandwidth used for transaction propagation from linear complexity in the number of peers to square root; and also reducing the initial transaction exchange from 10s-100s MB to `len(pool) * 32B ~= 128KB`. -## Motivation - With transaction throughput (and size) picking up in Ethereum, transaction propagation is the current dominant component of the used network resources. Most of these resources are however wasted, as the `eth` protocol does not have a mechanism to deduplicate transactions remotely, so the same data is transferred over and over again across all network connections. This EIP proposes a tiny extension to the `eth` protocol, which permits nodes to agree on the set of transactions that need to be transferred across a network connection, before doing the costly exchange. This should help reduce the global (operational) bandwidth usage of the Ethereum network by at least an order of magnitude. @@ -69,14 +72,10 @@ This EIP extends the `eth` protocol in a backwards incompatible way and requires This EIP does not change the consensus engine, thus does _not_ require a hard fork. -## Test Cases - -TODO - -## Implementation +## Security Considerations -Geth: https://github.com/ethereum/go-ethereum/pull/20234 +None. ## 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-2470.md b/EIPS/eip-2470.md index 811385f030b4c9..406c749f587cc7 100644 --- a/EIPS/eip-2470.md +++ b/EIPS/eip-2470.md @@ -3,7 +3,7 @@ eip: 2470 title: Singleton Factory author: Ricardo Guilherme Schmidt (@3esmit) discussions-to: https://ethereum-magicians.org/t/erc-2470-singleton-factory/3933 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-01-15 @@ -103,7 +103,7 @@ This contract is going to be deployed using the keyless deployment method---also 5. Broadcast the deployment transaction. - > Note: 247000 is the double of gas needed to deploy the smart contract, this ensures that future changes in OPCODE pricing are unlikely to cause this deploy transction to fail out of gas. A left over will sit in the address of about 0.01 ETH will be forever locked in the single use address. + > Note: 247000 is the double of gas needed to deploy the smart contract, this ensures that future changes in OPCODE pricing are unlikely to cause this deploy transaction to fail out of gas. A left over will sit in the address of about 0.01 ETH will be forever locked in the single use address. The resulting transaction hash is `0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c`. @@ -185,7 +185,7 @@ Account contracts are singletons in the point of view of each user, when wallets Contracts deployed on factory must not use `msg.sender` in constructor, instead use constructor parameters, otherwise the factory would end up being the controller/only owner of those. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). [EIP-155]: ./eip-155.md [EIP-1191]: ./eip-1191.md diff --git a/EIPS/eip-2474.md b/EIPS/eip-2474.md index df5983cda63859..d3ae13e3e0948a 100644 --- a/EIPS/eip-2474.md +++ b/EIPS/eip-2474.md @@ -3,7 +3,7 @@ eip: 2474 title: Coinbase calls author: Ricardo Guilherme Schmidt (@3esmit) discussions-to: https://ethresear.ch/t/gas-abstraction-non-signed-block-validator-only-procedures/4388/2 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-01-19 @@ -59,4 +59,4 @@ TBD ## 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-2477.md b/EIPS/eip-2477.md index 7ce473676740dd..6a76dc903eb23b 100644 --- a/EIPS/eip-2477.md +++ b/EIPS/eip-2477.md @@ -5,7 +5,7 @@ author: Kristijan Sedlak (@xpepermint), William Entriken , discussions-to: https://github.com/ethereum/EIPs/issues/2483 type: Standards Track category: ERC -status: Draft +status: Stagnant created: 2020-01-02 requires: 165, 721, 1155 --- @@ -103,26 +103,24 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL **Smart contracts implementing the ERC-2477 standard MUST implement the `ERC2477` interface.** ```solidity -pragma solidity ^0.6.0; +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.7; /// @title ERC-2477 Token Metadata Integrity /// @dev See https://eips.ethereum.org/EIPS/eip-2477 -/// @dev The ERC-165 identifier for this interface is 0x#######. //TODO: FIX THIS +/// @dev The ERC-165 identifier for this interface is 0x832a7e0e interface ERC2477 /* is ERC165 */ { - /** - * @notice Get the cryptographic hash of the specified tokenID's metadata - * @param tokenId Identifier for a specific token - * @return digest Bytes returned from the hash algorithm, or "" if not available - * @return hashAlgorithm The name of the cryptographic hash algorithm, or "" if not available - */ + /// @notice Get the cryptographic hash of the specified tokenID's metadata + /// @param tokenId Identifier for a specific token + /// @return digest Bytes returned from the hash algorithm, or "" if not available + /// @return hashAlgorithm The name of the cryptographic hash algorithm, or "" if not available function tokenURIIntegrity(uint256 tokenId) external view returns(bytes memory digest, string memory hashAlgorithm); - - /** - * @notice Get the cryptographic hash for the specified tokenID's metadata schema - * @param tokenId Id of the Xcert. - * @return digest Bytes returned from the hash algorithm, or "" if not available - * @return hashAlgorithm The name of the cryptographic hash algorithm, or "" if not available - */ + + /// @notice Get the cryptographic hash for the specified tokenID's metadata schema + /// @param tokenId Identifier for a specific token + /// @return digest Bytes returned from the hash algorithm, or "" if not available + /// @return hashAlgorithm The name of the cryptographic hash algorithm, or "" if not available function tokenURISchemaIntegrity(uint256 tokenId) external view returns(bytes memory digest, string memory hashAlgorithm); } ``` @@ -141,37 +139,31 @@ Smart contracts implementing the ERC-2477 standard MAY use any mechanism to prov ### Metadata -A metadata document MAY use this schema to provide referential integrity to its schema document. +A metadata document MAY conform to this schema to provide referential integrity to its schema document. ```json { - "title": "EIP-2477 JSON Object With Refererential Integrity to Schema", - "type": "object", - "properties": { - "$schema": { - "type": "string", - "format": "uri" + "title": "EIP-2477 JSON Object With Refererential Integrity to Schema", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "format": "uri" + }, + "$schemaIntegrity": { + "type": "object", + "properties": { + "digest": { + "type": "string" }, - "$schemaIntegrity": { - "type": "object", - "properties": { - "digest": { - "type": "string" - }, - "hashAlgorithm": { - "type": "string" - } - }, - "required": [ - "digest", - "hashAlgorithm" - ] + "hashAlgorithm": { + "type": "string" } - }, - "required": [ - "$schema", - "$schemaIntegrity" - ] + }, + "required": ["digest", "hashAlgorithm"] + } + }, + "required": ["$schema", "$schemaIntegrity"] } ``` @@ -201,8 +193,6 @@ The digest return value is first, this is an optimization because we expect on-c The digest is a byte array and supports various hash lengths. This is consistent with SRI. Whereas SRI uses base64 encoding to target an HTML document, we use a byte array because Ethereum already allows this encoding. -:warning: TODO: WE NEED TO SPECIFY ENDIANNESS ABOVE AND PROVIDE TEST CASES BELOW. AND JUSTIFY THAT HERE. - The hash function name is a string. Currently there is no universal taxonomy of hash function names. SRI recognizes the names `sha256`, `sha384` and `sha512` with case-insensitive matching. We are aware of two authorities which provide taxonomies and canonical names for hash functions: ETSI Object Identifiers and NIST Computer Security Objects Register. However, SRI's approach is easier to follow and we have adopted this here. **Function return type — hash length** @@ -223,9 +213,9 @@ One possible way to achieve strong content integrity with the existing token sta Other supplementary reasons are: -* For on-chain consumers of data, it is easier to parse a direct hash field than to perform string operations +* For on-chain consumers of data, it is easier to parse a direct hash field than to perform string operations. -* Maybe there are some URIs which are not amenable to being modified in that way, therefore limiting the generalizability of that approach +* Maybe there are some URIs which are not amenable to being modified in that way, therefore limiting the generalizability of that approach. This design justification also applies to `tokenURISchemaIntegrity`. The current JSON-LD specification allows a JSON document to link to a schema document. But it does not provide integrity. Rather than changing how JSON-LD works, or changing JSON Schemas, we have the `tokenURISchemaIntegrity` property to just provide the integrity. @@ -245,10 +235,10 @@ Following is a token metadata document which is simultaneously compatible with E ```json { - "$schema": "https://URL_TO_SCHEMA_DOCUMENT", - "name": "Asset Name", - "description": "Lorem ipsum...", - "image": "https:\/\/s3.amazonaws.com\/your-bucket\/images\/{id}.png", + "$schema": "https://URL_TO_SCHEMA_DOCUMENT", + "name": "Asset Name", + "description": "Lorem ipsum...", + "image": "https://s3.amazonaws.com/your-bucket/images/{id}.png" } ``` @@ -258,21 +248,21 @@ Following is a corresponding schema document which is accessible using the URI ` ```json { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Identifies the asset to which this NFT represents" - }, - "description": { - "type": "string", - "description": "Describes the asset to which this NFT represents" - }, - "image": { - "type": "string", - "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." - } + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." } + } } ``` @@ -289,7 +279,7 @@ To avoid doubt: the previous paragraph specifies "MAY" have that output because ## Implementation -TODO: ADD IMPLEMENTATIONS WITH 0XCERT ENJIN, NIKE, AZURE/MICROSOFT +0xcert Framework supports ERC-2477. ## Reference @@ -319,15 +309,17 @@ Discussion Other -1. [Shattered] The first collision for full SHA-1. https://shattered.io/static/shattered.pdf -2. [320 byte file] The second SHA Collision. https://privacylog.blogspot.com/2019/12/the-second-sha-collision.html -3. [Chosen prefix] https://sha-mbles.github.io -4. Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths. (Rev. 1. Superseded.) https://csrc.nist.gov/publications/detail/sp/800-131a/rev-1/archive/2015-11-06 -5. Commercial National Security Algorithm (CNSA) Suite Factsheet. https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/commercial-national-security-algorithm-suite-factsheet.cfm -6. ETSI Assigned ASN.1 Object Identifiers. https://portal.etsi.org/pnns/oidlist -7. Computer Security Objects Register. https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration -8. The Sandbox implementation. https://github.com/pixowl/sandbox-smart-contracts/blob/7022ce38f81363b8b75a64e6457f6923d91960d6/src/Asset/ERC1155ERC721.sol +1. [0xcert Framework supports ERC-2477]. https://github.com/0xcert/framework/pull/717 +2. [Shattered] The first collision for full SHA-1. https://shattered.io/static/shattered.pdf +3. [320 byte file] The second SHA Collision. https://privacylog.blogspot.com/2019/12/the-second-sha-collision.html +4. [Chosen prefix] https://sha-mbles.github.io +5. Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths. (Rev. 1. Superseded.) https://csrc.nist.gov/publications/detail/sp/800-131a/rev-1/archive/2015-11-06 +6. Commercial National Security Algorithm (CNSA) Suite Factsheet. https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/commercial-national-security-algorithm-suite-factsheet.cfm +7. ETSI Assigned ASN.1 Object Identifiers. https://portal.etsi.org/pnns/oidlist +8. Computer Security Objects Register. https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration +9. The Sandbox implementation. https://github.com/pixowl/sandbox-smart-contracts/blob/7022ce38f81363b8b75a64e6457f6923d91960d6/src/Asset/ERC1155ERC721.sol ## 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-2481.md b/EIPS/eip-2481.md index 584c2e6a3e9dc5..c8bc379b8d6dfc 100644 --- a/EIPS/eip-2481.md +++ b/EIPS/eip-2481.md @@ -1,24 +1,21 @@ --- eip: 2481 -title: "eth/66: request identifier" -author: Christoph Burgdorf -discussions-to: https://github.com/ethereum/EIPs/issues/2482 -status: Review +title: eth/66 request identifier +description: Introduces a request id for all requests of the eth protocol +author: Christoph Burgdorf (@cburgdorf) +discussions-to: https://ethereum-magicians.org/t/eip-2481-eth-66-request-identifiers/12132 +status: Final type: Standards Track category: Networking created: 2020-01-17 requires: 2464 --- -## Simple Summary - -This document proposes a way to increase the efficiency of the `eth` networking protocol while at the same time reducing complexity in Ethereum node implementations. It does so by introducing a request id to all requests which their corresponding responses must include. - ## Abstract -The `eth` protocol defines various request and response commands that are used to exchange data between Ethereum nodes. For example, to ask a peer node for a specific set of headers, a node sends it the [`GetBlockHeaders`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getblockheaders-0x03) command. +The `eth` protocol defines various request and response commands that are used to exchange data between Ethereum nodes. For example, to ask a peer node for a specific set of headers, a node sends it the [`GetBlockHeaders`](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#getblockheaders-0x03) command. -*Citing from the [`GetBlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getblockheaders-0x03):* +*Citing from the [`GetBlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#getblockheaders-0x03):* >`[block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]` @@ -27,9 +24,9 @@ headers, of rising number when `reverse` is `0`, falling when `1`, `skip` blocks beginning at block `block` (denoted by either number or hash) in the canonical chain, and with at most `maxHeaders` items. -The node that receives the `GetBlockHeaders` command should answer it with the [`BlockHeaders`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#blockheaders-0x04) response command accordingly. +The node that receives the `GetBlockHeaders` command should answer it with the [`BlockHeaders`](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#blockheaders-0x04) response command accordingly. -*Citing from the [`BlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#blockheaders-0x04):* +*Citing from the [`BlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#blockheaders-0x04):* >`[blockHeader_0, blockHeader_1, ...]` @@ -139,16 +136,7 @@ This EIP extends the `eth` protocol in a backwards incompatible way and requires This EIP does not change the consensus engine, thus does *not* require a hard fork. -## Implementation - -Trinity has a [draft PR](https://github.com/ethereum/trinity/pull/1672) with an implementation. -Geth [PR](https://github.com/ethereum/go-ethereum/pull/22241). - -## Security Considerations - -None - -## Test cases +## Test Cases These testcases cover RLP-encoding of all the redefined messages types, where the `rlp` portion is the rlp-encoding of the message defined in the `data` portion. @@ -169,6 +157,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "GetBlockHeadersPacket66", @@ -185,6 +174,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "BlockHeadersPacket66", @@ -214,6 +204,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "GetBlockBodiesPacket66", @@ -227,6 +218,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "BlockBodiesPacket66", @@ -286,6 +278,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "GetNodeDataPacket66", @@ -299,6 +292,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "NodeDataPacket66", @@ -306,12 +300,13 @@ These testcases cover RLP-encoding of all the redefined messages types, where th "data": { "RequestId": 1111, "NodeDataPacket": [ - "0xdeadcode", + "0xdeadc0de", "0xfeedbeef" ] } } ``` + ```json { "type": "GetReceiptsPacket66", @@ -325,6 +320,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "ReceiptsPacket66", @@ -379,6 +375,7 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } } ``` + ```json { "type": "PooledTransactionsPacket66", @@ -415,6 +412,9 @@ These testcases cover RLP-encoding of all the redefined messages types, where th } ``` +## Security Considerations + +None ## 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-2488.md b/EIPS/eip-2488.md index 35e548a31bc149..5af41eb8bf0775 100644 --- a/EIPS/eip-2488.md +++ b/EIPS/eip-2488.md @@ -3,7 +3,7 @@ eip: 2488 title: Deprecate the CALLCODE opcode author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2488-deprecate-the-callcode-opcode/3957 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2019-12-20 @@ -55,4 +55,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-2494.md b/EIPS/eip-2494.md index b8672e79bbcd72..0fa1a28e990a16 100644 --- a/EIPS/eip-2494.md +++ b/EIPS/eip-2494.md @@ -3,7 +3,7 @@ eip: 2494 title: Baby Jubjub Elliptic Curve author: Barry WhiteHat (@barryWhiteHat), Marta Bellés (@bellesmarta), Jordi Baylina (@jbaylina) discussions-to: https://ethereum-magicians.org/t/eip-2494-baby-jubjub-elliptic-curve/3968 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-01-29 @@ -127,7 +127,7 @@ Baby Jubjub was generated by running the algorithm with the prime `r = 21888242871839275222246405745257275088548364400416034343698204186575808495617`, -which is the order of alt_bn128, the curve used to verfiy zk-SNARK proofs in Ethereum. The output of the algorithm was `A=168698`. Afterwards, the corresponding Montgomery curve was transformed into twisted Edwards form. Using SAGE libraries for curves, the order `n` of the curve and its factorization `n = 8*l` was calculated. +which is the order of alt_bn128, the curve used to verify zk-SNARK proofs in Ethereum. The output of the algorithm was `A=168698`. Afterwards, the corresponding Montgomery curve was transformed into twisted Edwards form. Using SAGE libraries for curves, the order `n` of the curve and its factorization `n = 8*l` was calculated. - **Choice of generator** : the generator point `G` is the point of order `n` with smallest positive `x`-coordinate in `F_r`. - **Choice of base point**: the base point `B` is chosen to be `B = 8*G`, which has order `l`. @@ -379,4 +379,4 @@ Arithmetic of Baby Jubjub and some cryptographic primitives using the curve have - Go: https://github.com/iden3/go-iden3-crypto/tree/master/babyjub ## 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-2515.md b/EIPS/eip-2515.md index 1c53b19beceb66..7f60b3fa45808c 100644 --- a/EIPS/eip-2515.md +++ b/EIPS/eip-2515.md @@ -3,7 +3,7 @@ eip: 2515 title: Implement Difficulty Freeze author: James Hancock (@madeoftin) discussions-to: https://ethereum-magicians.org/t/eip-2515-replace-the-difficulty-bomb-with-a-difficulty-freeze/3995 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-02-10 @@ -85,4 +85,4 @@ Under the current Difficult, Bomb issuance of ETH is reduced as the Ice Age take It is also easy to predict when this change would happen, and stakeholders who are affected (Eth Holders) can keep client developers accountable by observing when the Difficulty Freeze is approaching and yell at them on twitter. ## 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-2520.md b/EIPS/eip-2520.md index 5546ee247ec7d9..d72c1e920bf93e 100644 --- a/EIPS/eip-2520.md +++ b/EIPS/eip-2520.md @@ -3,7 +3,7 @@ eip: 2520 title: Multiple contenthash records for ENS author: Filip Štamcar (@filips123) discussions-to: https://github.com/ethereum/EIPs/issues/2393 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-02-18 @@ -72,4 +72,4 @@ contract ContentHashResolver { TBD ## 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-2525.md b/EIPS/eip-2525.md index 4153668eb3cd37..71a285511154ec 100644 --- a/EIPS/eip-2525.md +++ b/EIPS/eip-2525.md @@ -3,7 +3,7 @@ eip: 2525 title: ENSLogin author: Hadrien Croubois (@amxx) discussions-to: https://ethereum-magicians.org/t/discussion-ens-login/3569 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-02-19 diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index 9e58284377fed3..de056305d6fe88 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -1,27 +1,20 @@ --- eip: 2535 -title: Diamonds +title: Diamonds, Multi-Facet Proxy +description: Create modular smart contract systems that can be extended after deployment. author: Nick Mudge (@mudgen) -discussions-to: https://github.com/ethereum/EIPs/issues/2535 -status: Draft +discussions-to: https://ethereum-magicians.org/t/discussion-for-eip2535-diamonds/10459/ +status: Final type: Standards Track category: ERC created: 2020-02-22 --- - +## Abstract -## Simple Summary +Diamonds contract structure -Enables people to write smart contracts with virtually no size limit in a modular and gas-efficient way. - -Diamonds can be upgraded on the fly without having to redeploy existing functionality. - -Standardizes contract interfaces and implementation details of diamonds, enabling software integration and interoperability. - -A diamond is a contract that implements the Specification in this standard. - -Terminology from the diamond industry. +This proposal standardizes diamonds, which are modular smart contract systems that can be upgraded/extended after deployment, and have virtually no size limit. More technically, a **diamond** is a contract with external functions that are supplied by contracts called **facets**. Facets are separate, independent contracts that can share internal functions, libraries, and state variables. ## Motivation @@ -30,237 +23,43 @@ There are a number of different reasons to use diamonds. Here are some of them: 1. **A single address for unlimited contract functionality.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. 1. **Your contract exceeds the 24KB maximum contract size.** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. 1. **A diamond provides a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. -1. **A diamond provides a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. - -### Diamonds Support Transparency - -To find out what functions a regular smart contract has it is only necessary to look at its verified source code. - -The verified source code of a diamond does not include what functions it has so a different mechanism is needed. - -A diamond has four standard functions called the loupe functions that are used to show what functions a diamond has. - -The loupe functions can be used for many things including: -1. To show all functions used by a diamond. -1. To query services like Etherscan or files to retrieve and show all source code used by a diamond. -1. To query services like Etherscan or files to retrieve ABI information for a diamond. -1. To test or verify that a transaction that adds/replaces/removes functions on a diamond succeeded. -1. To find out what functions a diamond has before calling functions on it. -1. To be used by tools and programming libraries to deploy and upgrade diamonds. -1. To be used by user interfaces to show information about diamonds. -1. To be used by user interfaces to enable users to call functions on diamonds. - -Diamonds support another form of transparency which is a historical record of all upgrades on a diamond. This is done with the DiamondCut event which is used to record all functions that are added, replaced or removed on a diamond. - -This standard is an improvement of [EIP-1538 Transparent Contract Standard](https://eips.ethereum.org/EIPS/eip-1538). The same motivations of that standard apply to this standard. - -See the [Learning & References section](https://eips.ethereum.org/EIPS/eip-2535#learning--references) for additional information and uses of diamonds. - -## What is a Diamond? - -A diamond is a contract with external functions that are supplied by contracts called **facets**. - -Facets are separate, independent contracts that can share internal functions, libraries and state variables. - -## How a Diamond Works - -A diamond stores within it a mapping of function selector to facet address, for example `selectorToFacet`. - -When an external function is called on a diamond its fallback function is executed. The fallback function finds in the `selectorToFacet` mapping which facet has the function that has been called and then executes that function from the facet using `delegatecall`. - -A diamond's fallback function and `delegatecall` enable a diamond to execute a facet's external function as its own external function. The `msg.sender` and `msg.value` values do not change and only the diamond's contract storage is read and written to. - -Here is a simple example of a diamond's fallback function: - -```Solidity -// Find facet for function that is called and execute the -// function if a facet is found and return any value. -fallback() external payable { - address facet = selectorTofacet[msg.sig]; - require(facet != address(0)); - // Execute external function from facet using delegatecall and return any value. - assembly { - calldatacopy(0, 0, calldatasize()) - let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) - returndatacopy(0, 0, returndatasize()) - switch result - case 0 {revert(0, returndatasize())} - default {return (0, returndatasize())} - } -} -``` - -A diamond can use a `diamondCut` function to add/replace/remove any number of functions from any number of facets in a single transaction. `diamondCut` updates the mapping of function selector to facet address. Other such functions can be used. - -An event is emitted any time external functions are added, replaced or removed to report what changed. - -A diamond has four standard external functions that can be called to show what facets and functions it currently has. +1. **A diamond provides a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgraded without having to redeploy existing functionality. Parts of a diamond can be added/replaced/removed while leaving other parts alone. +1. **A diamond can be immutable.** It is possible to deploy an immutable diamond or make an upgradeable diamond immutable at a later time. +1. **A diamond can reuse deployed contracts.** Instead of deploying contracts to a blockchain, existing already deployed, onchain contracts can be used to create diamonds. Custom diamonds can be created from existing deployed contracts. This enables the creation of on-chain smart contract platforms and libraries. -## Upgrades and Immutability - -A diamond can be immutable from inception by not adding any external functions that can add/replace/remove functions. [There are number of reasons to do this.](#different-kinds-of-diamonds) - -A diamond that is mutable can be made immutable by removing such functions. - -A diamond can be upgraded if it has a diamondCut external function or other function(s) that can add/replace/remove functions. - -## Organizing State Variables in Diamonds - -A state variable or storage layout organizational pattern is needed because Solidity's builtin storage layout system doesn't support proxy contracts or diamonds. - -Described below are diamond storage and AppStorage which are two state variable organizational patterns that have been successfully used in diamonds. - -Another successful pattern has been to write a storage contract that contains all state variables and is inherited by all facets. - -## Facets, State Variables and Diamond Storage - -A facet defines external functions and can define or use internal functions, libraries, and state variables. - -A facet can declare state variables in structs. Each struct is given a specific position in contract storage. This technique is called **Diamond Storage**. - -Here is a simple example that shows diamond storage and its use in a facet: - -```Solidity -// A contract that implements diamond storage. -library LibA { - - // This struct contains state variables we care about. - struct DiamondStorage { - address owner; - bytes32 dataA; - } - - // Returns the struct from a specified position in contract storage - // ds is short for DiamondStorage - function diamondStorage() internal pure returns(DiamondStorage storage ds) { - // Specifies a random position from a hash of a string - bytes32 storagePosition = keccak256("diamond.storage.LibA") - // Set the position of our struct in contract storage - assembly {ds.slot := storagePosition} - } -} - -// Our facet uses the diamond storage defined above. -contract FacetA { - - function setDataA(bytes32 _dataA) external { - LibA.DiamondStorage storage ds = LibA.diamondStorage(); - require(ds.owner == msg.sender, "Must be owner."); - ds.dataA = _dataA - } - - function getDataA() external view returns (bytes32) { - return LibA.diamondStorage().dataA - } -} -``` - -Any number of structs, each with a different storage position, can be used by a facet. - -By using diamond storage facets can declare their own state variables that do not conflict with the storage locations of state variables declared in other facets. - -By using diamond storage facets can be developed independently, without connection or concern for other facets. - -## AppStorage - -A specialized version of diamond storage is AppStorage. This pattern is used to more conveniently and easily share state variables between facets. - -The AppStorage pattern is implemented by defining a struct called AppStorage that contains the state variables of your application. It can contain any number of state variables of any type, including arrays and mappings, and more can be added in upgrades. - -The AppStorage struct is defined or imported and declared as the first and only state variable directly (or via inheritance) in any facet that uses it. This means that AppStorage is always located at position 0 in contract storage. - -The AppStorage state variable is often named `s` to provide easy access to it and to distinguish state variables from function arguments, function names, and local variables. - -Here is a simple example of a contract that uses AppStorage: - -```Solidity -import "./AppStorage.sol" - -contract StakingFacet { - AppStorage internal s; - - function myFacetFunction(uint256 _nextVar) external { - s.total = s.firstVar + _nextVar; - } - -``` - -The above example accesses the `s.firstVar` state variable and stores a computation in the `s.total` state variable. - -Read this article to learn more about AppStorage: [AppStorage Pattern for State Variables in Solidity](https://dev.to/mudgen/appstorage-pattern-for-state-variables-in-solidity-3lki). - - -## Diamonds Can Use Other Contract Storage Strategies - -Diamonds and facets don't have to use diamond storage or AppStorage. They can use or mix with other contract storage strategies such as contract inheritance. - -Diamonds only need to implement the [Specification section](#specification) of this standard. - -## Facets Can Share State and Functionality - -Facets can share state variables by using the same structs at the same storage positions. - -Facets can share internal functions and libraries by inheriting the same contracts or using the same libraries. - -In these ways facets are separate, independent units but can share state and functionality. - -## Facets are Reusable and Composable +This standard is an improvement of [EIP-1538](./eip-1538.md). The same motivations of that standard apply to this standard. A deployed facet can be used by any number of diamonds. -Different combinations of facets can be used with different diamonds. - -It is possible to create and deploy a set of facets that are reused by different diamonds over time. - -The ability to use the same deployed facets for many diamonds reduces deployment costs. - -A limitation is that two external functions with the same function signature can't be added to a diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. - -## Diagrams - -### Diamond Structure - -This diagram shows the structure of a diamond: - - - -### Diamond Storage - -The diagram below shows facets with their own data and data shared between them. - -Notice that all data is stored in the diamond. But different facets have different access to data. - -In this diagram - -- Only FacetA can access DataA -- Only FacetB can access DataB -- Only the diamond's own code can access DataD. -- FacetA and FacetB share access to DataAB. -- The diamond's own code, FacetA and FacetB share access to DataABD. - - +The diagram below shows two diamonds using the same two facets. -### Deployed Facets Can Be Reused +- `FacetA` is used by `Diamond1` +- `FacetA` is used by `Diamond2` +- `FacetB` is used by `Diamond1` +- `FacetB` is used by `Diamond2` -A deployed facet can be used by any number of diamonds. +Facet reuse -The diagram below shows two diamonds using the same two facets. +### Upgradeable Diamond vs. Centralized Private Database -- FacetA is used by Diamond1 -- FacetA is used by Diamond2 -- FacetB is used by Diamond1 -- FacetB is used by Diamond2 +Why have an upgradeable diamond instead of a centralized, private, mutable database? - +1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. +1. Wide interaction and integration with the Ethereum ecosystem. +1. With open storage data and verified source code it is possible to show a provable history of trustworthiness. +1. With openness bad behavior can be spotted and reported when it happens. +1. Independent security and domain experts can review the change history of contracts and vouch for their history of trustworthiness. +1. It is possible for an upgradeable diamond to become immutable and trustless. -## Some Diamond Benefits +### Some Diamond Benefits 1. A stable contract address that provides needed functionality. 1. A single address with the functionality of multiple contracts (facets) that are independent from each other but can share internal functions, libraries and state variables. +1. Emitting events from a single address can simplify event handling. 1. A way to add, replace and remove multiple external functions atomically (in the same transaction). 1. Fine-grained upgrades, so you can change just the parts of a diamond that need to be changed. 1. Have greater control over when and what functions exist. -1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. +1. Decentralized Autonomous Organizations (DAOs), multisig contracts and other governance systems can be used to upgrade diamonds. 1. An event that shows what functions are added, replaced and removed. 1. The ability to show all changes made to a diamond. 1. Increase trust over time by showing all changes made to a diamond. @@ -268,7 +67,8 @@ The diagram below shows two diamonds using the same two facets. 1. Have an immutable, trustless diamond. 1. Solves the 24KB maximum contract size limitation. Diamonds can be any size. 1. Separate functionality can be implemented in separate facets and used together in a diamond. -1. Larger contracts have to reduce their size by removing error messages and other things. You can keep your error messages and the full functionality that you need by implementing a diamond. +1. Diamonds can be created from already deployed, existing onchain contracts. +1. Larger contracts have to reduce their size by removing error messages and other things. You can keep your full functionality that you need by implementing a diamond. 1. Enables zero, partial or full diamond immutability as desired, and when desired. 1. The ability to develop and improve an application over time with an upgradeable diamond and then make it immutable and trustless if desired. 1. Develop incrementally and let your diamond grow with your application. @@ -276,84 +76,117 @@ The diagram below shows two diamonds using the same two facets. 1. Organize your code with a diamond and facets. 1. Diamonds can be large (have many functions) but still be modular because they are compartmented with facets. 1. Contract architectures that call multiple contracts in a single transaction can save gas by condensing those contracts into a single diamond and accessing state variables directly. -1. Save gas by creating external functions for specific use cases, such as bulk transfers. +1. Save gas by converting external functions to internal functions. This done by sharing internal functions between facets. +1. Save gas by creating external functions for gas-optimized specific use cases, such as bulk transfers. 1. Diamonds are designed for tooling and user-interface software. -### New User-Interface Software & Libraries -User-interface software can be written to show all documentation, functions and source code used by a diamond. +## Specification -Diamond events can be filtered from the Ethereum blockchain to show all changes to a diamond. +### Terms -Existing and new programming libraries and software can be used to deploy, show, upgrade and use diamonds. +1. A **diamond** is a facade smart contract that `delegatecall`s into its facets to execute function calls. A diamond is stateful. Data is stored in the contract storage of a diamond. +1. A **facet** is a stateless smart contract or Solidity library with external functions. A facet is deployed and one or more of its functions are added to one or more diamonds. A facet does not store data within its own contract storage but it can define state and read and write to the storage of one or more diamonds. The term facet comes from the diamond industry. It is a side, or flat surface of a diamond. +1. A **loupe facet** is a facet that provides introspection functions. In the diamond industry, a loupe is a magnifying glass that is used to look at diamonds. +1. An **immutable function** is an external function that cannot be replaced or removed (because it is defined directly in the diamond, or because the diamond's logic does not allow it to be modified). +1. A **mapping** for the purposes of this EIP is an association between two things and does not refer to a specific implementation. -### Upgradeable Diamond vs. Centralized Private Database +The term **contract** is used loosely to mean a smart contract or deployed Solidity library. -Why have an upgradeable diamond instead of a centralized, private, mutable database? +When this EIP uses **function** without specifying internal or external, it means external function. -1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. -1. Wide interaction and integration with the Ethereum ecosystem. -1. With open storage data and verified source code it is possible to show a provable history of trustworthiness. -1. With openness bad behavior can be spotted and reported when it happens. -1. Independent security and domain experts can review the change history of contracts and vouch for their history of trustworthiness. -1. It is possible for an upgradeable diamond to become immutable and trustless. +In this EIP the information that applies to external functions also applies to public functions. -### Different Kinds of Diamonds +### Overview -Many designs of diamonds are possible. Here are a few kinds of diamonds and their uses. +A diamond calls functions from its facets using `delegatecall`. -**Upgradeable Diamond** -An upgradeable diamond has the `diamondCut` function and/or possibly other functions to add/replace/remove functions. It is useful for iterative development or improving an application over time. +In the diamond industry diamonds are created and shaped by being cut, creating facets. In this standard diamonds are cut by adding, replacing or removing functions from facets. -**Finished Diamond** -A finished diamond was an upgradeable diamond and had a number of upgrades. Then its `diamondCut` function and/or other upgrade functions were removed and upgrades are no longer possible. It is no longer possible to add/replace/remove functions. It has become an immutable diamond. +### A Note on Implementing Interfaces -**Single Cut Diamond** -A single cut diamond adds all functions to itself in its constructor function, but it does not add the `diamondCut` function or any other function that can add/replace/remove functions. This means that a single cut diamond is fully created in its constructor and once created can never be upgraded. It has the same immutability and trustless guarantees as a regular vanilla contract. Why would someone do this? There may be a number of reasons. The two use cases below are good reasons. +Because of the nature of diamonds, a diamond can implement an interface in one of two ways: directly (`contract Contract is Interface`), or by adding functions to it from one or more facets. For the purposes of this proposal, when a diamond is said to implement an interface, either method of implementation is permitted. -1. Your contract hits the max contract size limit. Make it into a single cut diamond. You still break your big contract into smaller facets, modularizing your code. -2. You start with an upgradeable diamond in your development and testing and upgrade it to your heart's delight. Reap the advantages of easy upgrading and a stable address as you work out new features, bugs and kinks. Release the upgradeable diamond on a test network with your application for beta testing and upgrade it when needed. This is iterative development. When it is solid then deploy it as a single cut diamond on the main network. +### Fallback Function -## Specification +When an external function is called on a diamond its fallback function is executed. The fallback function determines which facet to call based on the first four bytes of the call data (known as the function selector) and executes that function from the facet using `delegatecall`. -> **Note:** -> The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract, not the contract where the functions are defined. +A diamond's fallback function and `delegatecall` enable a diamond to execute a facet's function as if it was implemented by the diamond itself. The `msg.sender` and `msg.value` values do not change and only the diamond's storage is read and written to. -> **Note:** This specification specifies what needs to be implemented for a contract to be a diamond. +Here is an illustrative example of how a diamond's fallback function might be implemented: -### Terms +```solidity +// Find facet for function that is called and execute the +// function if a facet is found and return any value. +fallback() external payable { + // get facet from function selector + address facet = selectorTofacet[msg.sig]; + require(facet != address(0)); + // Execute external function from facet using delegatecall and return any value. + assembly { + // copy function selector and any arguments + calldatacopy(0, 0, calldatasize()) + // execute function call using the facet + let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) + // get any return value + returndatacopy(0, 0, returndatasize()) + // return any return value or error back to the caller + switch result + case 0 {revert(0, returndatasize())} + default {return (0, returndatasize())} + } +} +``` -1. A **diamond** is a contract that uses functions from its facets to execute function calls. A diamond can have one or more facets. -2. The word **facet** comes from the diamond industry. It is a side, or flat surface of a diamond. A diamond can have many facets. In this standard a facet is a contract with one or more functions that executes functionality of a diamond. -3. A **loupe** is a magnifying glass that is used to look at diamonds. In this standard a loupe is a facet that provides functions to look at a diamond and its facets. -4. An **immutable function** is a function that is defined directly in a diamond and so cannot be replaced or removed. Or it is a function that is defined in a facet that cannot be replaced or removed because all upgrade functions have been removed from a diamond. +This diagram shows the structure of a diamond: -### General Summary +Mapping facets and storage -A diamond calls functions from its facets using `delegatecall`. +### Storage + +A state variable or storage layout organizational pattern is needed because Solidity's builtin storage layout system doesn't support proxy contracts or diamonds. The particular layout of storage is not defined in this EIP, but may be defined by later proposals. Examples of storage layout patterns that work with diamonds are [Diamond Storage](../assets/eip-2535/storage-examples/DiamondStorage.sol) and [AppStorage](../assets/eip-2535/storage-examples/AppStorage.sol). + +Facets can share state variables by using the same structs at the same storage positions. Facets can share internal functions and libraries by inheriting the same contracts or using the same libraries. In these ways facets are separate, independent units but can share state and functionality. -In the diamond industry diamonds are created and shaped by being cut, creating facets. In this standard diamonds are cut by adding, replacing or removing facets and their functions. +The diagram below shows facets with their own data and data shared between them. + +Notice that all data is stored in the diamond's storage, but different facets have different access to data. + +In this diagram -#### The diamondCut Function +- Only `FacetA` can access `DataA` +- Only `FacetB` can access `DataB` +- Only the diamond's own code can access `DataD`. +- `FacetA` and `FacetB` share access to `DataAB`. +- The diamond's own code, `FacetA` and `FacetB` share access to `DataABD`. -The standard `diamondCut` function specified below can be used to add/replace/remove any number of functions from/to a diamond in a single transaction. +Mapping code, data, and facets -The standard `diamondCut` function below is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. Diamonds that might work with diamond specific tooling to add/replace/remove functions should implement the standard `diamondCut` function. +### Solidity Libraries as Facets -The `diamondCut` function is optional. It does not have to be implemented in a diamond. For example an immutable diamond wouldn't have this function. +Smart contracts or deployed Solidity libraries can be facets of diamonds. -You can implement your own custom functions that add or replace or remove functions. You can also implement your own non-standard versions of `diamondCut` that have different parameters. +Only Solidity libraries that have one or more external functions can be deployed to a blockchain and be a facet. -If you want to create your own custom function(s) for adding/replacing/removing functions you might also want to implement the standard `diamondCut` function for interoperability with tools. +Solidity libraries that contain internal functions only cannot be deployed and cannot be a facet. Internal functions from Solidity libraries are included in the bytecode of facets and contracts that use them. Solidity libraries with internal functions only are useful for sharing internal functions between facets. -**In ALL cases any function or code that adds or replaces or removes one or more functions MUST emit the standard DiamondCut event specified below.** +Solidity library facets have a few properties that match their use as facets: +* They cannot be deleted. +* They are stateless. They do not have contract storage. +* Their syntax prevents declaring state variables outside Diamond Storage. -The `DiamondCut` event records all changes to a diamond. +### Adding/Replacing/Removing Functions -### Diamond Interface +#### `IDiamond` Interface -```Solidity -interface IDiamondCut { +All diamonds must implement the `IDiamond` interface. + +During the deployment of a diamond any immutable functions and any external functions added to the diamond must be emitted in the `DiamondCut` event. + +**A `DiamondCut` event must be emitted any time external functions are added, replaced, or removed.** This applies to all upgrades, all functions changes, at any time, whether through `diamondCut` or not. + +```solidity +interface IDiamond { enum FacetCutAction {Add, Replace, Remove} // Add=0, Replace=1, Remove=2 @@ -363,6 +196,24 @@ interface IDiamondCut { bytes4[] functionSelectors; } + event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); +} +``` + +The `DiamondCut` event records all function changes to a diamond. + +#### `IDiamondCut` Interface + +A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. + +Diamonds should implement the `IDiamondCut` interface if after their deployment they allow modifications to their function selector mapping. + +The `diamondCut` function updates any number of functions from any number of facets in a single transaction. Executing all changes within a single transaction prevents data corruption which could occur in upgrades done over multiple transactions. + +`diamondCut` is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. + +```solidity +interface IDiamondCut is IDiamond { /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors @@ -374,65 +225,41 @@ interface IDiamondCut { address _init, bytes calldata _calldata ) external; - - event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); } ``` -#### Adding/Replacing/Removing Functions +The `_diamondCut` argument is an array of `FacetCut` structs. -A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. +Each `FacetCut` struct contains a facet address and array of function selectors that are updated in a diamond. -The `_diamondCut` argument is an array of FacetCut structs. +For each `FacetCut` struct: -Each FacetCut struct contains a facet address and array of function selectors that are updated in a diamond. + * If the `action` is `Add`, update the function selector mapping for each `functionSelectors` item to the `facetAddress`. If any of the `functionSelectors` had a mapped facet, revert instead. + * If the `action` is `Replace`, update the function selector mapping for each `functionSelectors` item to the `facetAddress`. If any of the `functionSelectors` had a value equal to `facetAddress` or the selector was unset, revert instead. + * If the `action` is `Remove`, remove the function selector mapping for each `functionSelectors` item. If any of the `functionSelectors` were previously unset, revert instead. -To add new functions create a FacetCut struct with `facetAddress` set to the facet that has the new functions and `functionSelectors` set with the function selectors to add. Set the `action` enum to `Add`. +Any attempt to replace or remove an immutable function must revert. -To replace functions create a FacetCut struct with `facetAddress` set to the facet that has the replacement functions and `functionSelectors` set with the function selectors to replace. Set the `action` enum to `Replace`. +Being intentional and explicit about adding/replacing/removing functions helps catch and prevent upgrade mistakes. -To remove functions create a FacetCut struct with `facetAddress` set to `address(0)` and `functionSelectors` set with the function selectors to remove. Set the `action` enum to `Remove`. - -It is the design of the `diamondCut` function that cuts are explicit and intentional. - -The `diamondCut` function reverts when attempting to add a function that already exists. - -The `diamondCut` function reverts when attempting to replace a function with a facet it is already using. - -The `diamondCut` function reverts when attempting to remove a function that already does not exist. - -#### Executing \_calldata +##### Executing `_calldata` After adding/replacing/removing functions the `_calldata` argument is executed with `delegatecall` on `_init`. This execution is done to initialize data or setup or remove anything needed or no longer needed after adding, replacing and/or removing functions. -If the `_init` value is `address(0)` then `_calldata` must contain 0 bytes or the transaction reverts. - -If the `_init` value is not `address(0)` then `_calldata` must contain more than 0 bytes or the transaction reverts. - -If `_init` is `address(0)` and `_calldata` contains 0 bytes then `_calldata` execution is skipped. `_calldata` is not executed and the `diamondCut` call can complete successfully. - -#### DiamondCut Event +If the `_init` value is `address(0)` then `_calldata` execution is skipped. In this case `_calldata` can contain 0 bytes or custom information. -The `_diamondCut`, `_init`, and `_calldata` arguments are passed directly to the `DiamondCut` event. - -Any time one or more functions are added, replaced or removed the `DiamondCut` event MUST be emitted to record changes. - -### Diamond Loupe +### Inspecting Facets & Functions > A loupe is a small magnifying glass used to look at diamonds. -> These functions look at diamonds. -The function selectors used by a diamond are queried to get what functions the diamond has and what facets are used. +Diamonds must support inspecting facets and functions by implementing the `IDiamondLoupe` interface. -A diamond loupe is a facet that implements this interface: +#### `IDiamondLoupe` Interface -```Solidity +```solidity // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds interface IDiamondLoupe { - /// These functions are expected to be called frequently - /// by tools. - struct Facet { address facetAddress; bytes4[] functionSelectors; @@ -459,31 +286,28 @@ interface IDiamondLoupe { } ``` -See an [example implementation](https://github.com/mudgen/Diamond) to see how this can be implemented. +See a [reference implementation](#reference-implementation) to see how this can be implemented. The loupe functions can be used in user-interface software. A user interface calls these functions to provide information about and visualize diamonds. The loupe functions can be used in deployment functionality, upgrade functionality, testing and other software. -Some loupe implementations are not gas efficient and should not be called in on-chain transactions. Some loupe implementations may be gas efficient and can be called in on-chain transactions. Read the documentation of the loupe implementation you use. - ### Implementation Points -A diamond implements the following implementation points: +A diamond must implement the following: 1. A diamond contains a fallback function and zero or more immutable functions that are defined within it. 1. A diamond associates function selectors with facets. -1. When a function is called on a diamond it executes immediately if it is an "immutable function" defined in the diamond. Otherwise the diamond's fallback function is executed. The fallback function finds the facet associated with the function and executes the function using `delegatecall`. If there is no facet for the function and no other mechanism to handle it then execution reverts. +1. When a function is called on a diamond it executes immediately if it is an "immutable function" defined directly in the diamond. Otherwise the diamond's fallback function is executed. The fallback function finds the facet associated with the function and executes the function using `delegatecall`. If there is no facet for the function then optionally a default function may be executed. If there is no facet for the function and no default function and no other mechanism to handle it then execution reverts. 1. Each time functions are added, replaced or removed a `DiamondCut` event is emitted to record it. 1. A diamond implements the DiamondLoupe interface. -1. All immutable functions must be emitted in the `DiamondCut` event as new functions added. And the loupe functions must return information about immutable functions if they exist. The facet address for an immutable function is the diamond's address. -1. Optionally a diamond implements ERC165. If a diamond has the `diamondCut` function then the interface ID used for it is `IDiamondCut.diamondCut.selector`. The interface ID used for the diamond loupe interface is `IDiamondLoupe.facets.selector ^ IDiamondLoupe.facetFunctionSelectors.selector ^ IDiamondLoupe.facetAddresses.selector ^ IDiamondLoupe.facetAddress.selector`. +1. All immutable functions must be emitted in the `DiamondCut` event as new functions added. And the loupe functions must return information about immutable functions if they exist. The facet address for an immutable function is the diamond's address. Any attempt to delete or replace an immutable function must revert. -The diamond address is the address that users interact with. The diamond address does not change. Only facet addresses can change by using the `diamondCut` function, or other function. +A diamond may implement the following: -## Implementation +1. [EIP-165](./eip-165.md)'s `supportsInterface`. If a diamond has the `diamondCut` function then the interface ID used for it is `IDiamondCut.diamondCut.selector`. The interface ID used for the diamond loupe interface is `IDiamondLoupe.facets.selector ^ IDiamondLoupe.facetFunctionSelectors.selector ^ IDiamondLoupe.facetAddresses.selector ^ IDiamondLoupe.facetAddress.selector`. -Example diamond implementations exist in the [Diamond repository](https://github.com/mudgen/Diamond). +The diamond address is the address that users interact with. The diamond address does not change. Only facet addresses can change by using the `diamondCut` function, or other function. ## Rationale @@ -497,163 +321,115 @@ This standard is designed to make diamonds work well with user-interface softwar Delegating function calls does have some gas overhead. This is mitigated in several ways: -1. Because diamonds do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a diamond to implement the ERC721 standard and implement batch transfer functions from the [ERC1412 standard](https://github.com/ethereum/EIPs/issues/1412) to reduce gas (and make batch transfers more convenient). -1. Some contract architectures require calling many contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. -1. Facets can be small, reducing gas costs. Because it costs more gas to call a function in a contract with many functions than a contract with few functions. +1. Because diamonds do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a diamond to implement the [EIP-721](./eip-721.md) standard and implement batch transfer functions to reduce gas (and make batch transfers more convenient). +1. Some contract architectures require calling multiple contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. +1. Facets can contain few external functions, reducing gas costs. Because it costs more gas to call a function in a contract with many functions than a contract with few functions. 1. The Solidity optimizer can be set to a high setting causing more bytecode to be generated but the facets will use less gas when executed. -### Diamond Storage +### Versions of Functions -Since Solidity 0.6.4 it is possible to create pointers to structs in arbitrary places in contract storage. This enables diamonds and their facets to create their own storage layouts that are separate from each other and do not conflict with each other, but can still be shared between them. See this blog post for more information: [New Storage Layout For Proxy Contracts and Diamonds](https://medium.com/1milliondevs/new-storage-layout-for-proxy-contracts-and-diamonds-98d01d0eadb). The example implementation for EIP-2535 uses diamond storage. +Software or a user can verify what version of a function is called by getting the facet address of the function. This can be done by calling the `facetAddress` function from the `IDiamondLoupe` interface. This function takes a function selector as an argument and returns the facet address where it is implemented. -Diamond storage is not the same thing as unstructured storage. Unstructured storage reads and writes specific values like unsigned integers and addresses at specified locations in contract storage. Diamond storage uses structs at specified locations in contract storage for reading and writing. Structs can hold any number of state variables of any type. +### Default Function -### Versions of Functions +Solidity provides the `fallback` function so that specific functionality can be executed when a function is called on a contract that does not exist in the contract. This same behavior can optionally be implemented in a diamond by implementing and using a default function, which is a function that is executed when a function is called on a diamond that does not exist in the diamond. -Software or a user can verify what version of a function is called by getting the facet address of the function. This can be done by calling the `facetAddress` function from the DiamondLoupe interface. This function takes a function selector as an argument and returns the facet address where it is implemented. +A default function can be implemented a number of ways and this standard does not specify how it must be implemented. + +### Loupe Functions & `DiamondCut` Event + +To find out what functions a regular contract has it is only necessary to look at its verified source code. + +The verified source code of a diamond does not include what functions it has so a different mechanism is needed. + +A diamond has four standard functions called the loupe functions that are used to show what functions a diamond has. + +The loupe functions can be used for many things including: +1. To show all functions used by a diamond. +1. To query services like Etherscan or files to retrieve and show all source code used by a diamond. +1. To query services like Etherscan or files to retrieve ABI information for a diamond. +1. To test or verify that a transaction that adds/replaces/removes functions on a diamond succeeded. +1. To find out what functions a diamond has before calling functions on it. +1. To be used by tools and programming libraries to deploy and upgrade diamonds. +1. To be used by user interfaces to show information about diamonds. +1. To be used by user interfaces to enable users to call functions on diamonds. + +Diamonds support another form of transparency which is a historical record of all upgrades on a diamond. This is done with the `DiamondCut` event which is used to record all functions that are added, replaced or removed on a diamond. ### Sharing Functions Between Facets -In some cases it might be necessary to call a function defined in a different facet. -Here are some solutions to this: +In some cases it might be necessary to call a function defined in a different facet. Here are ways to do this: 1. Copy internal function code in one facet to the other facet. 1. Put common internal functions in a contract that is inherited by multiple facets. 1. Put common internal functions in a Solidity library and use the library in facets. -1. A type safe way to call an external function defined in another facet is to do this: MyOtherFacet(address(this)).myFunction(arg1, arg2) -1. A more gas-efficient way to call an external function defined in another facet is to use `delegatecall`. Here is an example of doing that: -```Solidity +1. A type safe way to call an external function defined in another facet is to do this: `MyOtherFacet(address(this)).myFunction(arg1, arg2)` +1. A more gas-efficient way to call an external function defined in another facet is to use delegatecall. Here is an example of doing that: +```solidity DiamondStorage storage ds = diamondStorage(); 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. -## Security Considerations - -### Ownership and Authentication - -> **Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. +### Facets can be Reusable and Composable -It is possible to create many different authentication or ownership schemes with EIP-2535. Authentication schemes can be very simple or complex, fine grained or coarse. EIP-2535 does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. - -Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples. - -The development of standards and implementations of ownership, control and authentication of diamonds is encouraged. - -### Security of Diamond Storage - -If a person can add/replace functions then that person can alter storage willy-nilly. This is very powerful and very dangerous. However the capability can be used while eliminating or reducing the danger. The danger is eliminated or reduced by limiting **who** can add/replace/remove functions, limiting **when** functions can be added/replaced/removed and by **transparency**. - -**Who** -Here are some ways **who** can be limited: - -1. Only allow a trusted individual or organization to make diamond upgrades. -1. Only allow a distributed autonomous organization to make diamond upgrades. -1. Only allow multi-signature upgrades. -1. Only allow the end user who owns his own diamond to make upgrades. This enables users to opt-in to upgrades. -1. Don't allow anybody to make upgrades by making a single cut diamond. - -**When** -Here are some ways **when** can be limited: - -1. Only allow upgrades during development and testing. Make a single cut diamond for main network release. -1. Use an upgradeable diamond until it is certain that no new features are needed and then make it a finished diamond by removing the ability to add/replace/remove functions. -1. Program into the `diamondCut` function certain periods of time that the diamond can be upgraded. For example the `diamondCut` function could be programmed so that a diamond could only be upgraded during a specific five hour period each year. Attention and transparency could be applied to that five hour period to ensure upgrades are done right. - -**Transparency** -Transparency provides certainty and proof that upgrades are done correctly and honestly. +A deployed facet can be used by any number of diamonds. -1. Publish and make available verified source code used by diamonds and facets. -1. Provide documentation for diamonds, facets, upgrade plans, and results of upgrades. -1. Provide tools and/or user interfaces that make your diamonds more visible and understandable. +Different combinations of facets can be used with different diamonds. -### Function Selector Clash +It is possible to create and deploy a set of facets that are reused by different diamonds over time. -A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. This scenario is not possible with the `diamondCut` function because it prevents adding function selectors that already exist. +The ability to use the same deployed facets for many diamonds reduces deployment costs. -### Transparency +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. -Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. +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. -Security and domain experts can review the history of change of a diamond to detect any history of foul play. +All the functions of a facet do not have to be added to a diamond. Some functions in a facet can be added to a diamond while other functions in the facet are not added to the diamond. ## Backwards Compatibility This standard makes upgradeable diamonds compatible with future standards and functionality because new functions can be added and existing functions can be replaced or removed. -## Learning & References - -### Diamond Articles - -[Why Ethereum Diamonds Need A Standard](https://dev.to/mudgen/why-diamonds-need-a-standard-1i1h) - -[Ethereum's Maximum Contract Size Limit is Solved with EIP-2535 Diamonds](https://dev.to/mudgen/ethereum-s-maximum-contract-size-limit-is-solved-with-the-diamond-standard-2189) - -[Understanding Diamonds](https://dev.to/mudgen/understanding-diamonds-on-ethereum-1fb) - -[Why Ethereum Diamonds Need A Standard](https://dev.to/mudgen/why-diamonds-need-a-standard-1i1h) - -[Diamond Loupe Functions?](https://dev.to/mudgen/why-loupe-functions-for-diamonds-1kc3) - -[EIP-2535 Diamonds: A new paradigm for upgradeability](https://medium.com/derivadex/the-diamond-standard-a-new-paradigm-for-upgradeability-569121a08954) - -[Upgradeable smart contracts using EIP-2535 Diamonds](https://hiddentao.com/archives/2020/05/28/upgradeable-smart-contracts-using-diamond-standard) - -[Ethereum Diamonds for On-Chain Decentralized Governance](https://dev.to/mudgen/ethereum-diamonds-for-on-chain-decentralized-governance-39op) - -[How to Share Functions Between Facets of a Diamond](https://dev.to/mudgen/how-to-share-functions-between-facets-of-a-diamond-1njb) +## Reference Implementation -### Diamond Storage Articles +All the Solidity code for a complete reference implementation has been put in a single file here: [Diamond.sol](../assets/eip-2535/reference/Diamond.sol) -[How Diamond Storage Works](https://dev.to/mudgen/how-diamond-storage-works-90e) +The same reference implementation has been organized into multiple files and directories and also includes a deployment script and tests. Download it as a zip file: [`EIP2535-Diamonds-Reference-Implementation.zip`](../assets/eip-2535/reference/EIP2535-Diamonds-Reference-Implementation.zip) -[AppStorage Pattern for State Variables in Solidity](https://dev.to/mudgen/appstorage-pattern-for-state-variables-in-solidity-3lki) - -[New Storage Layout For Proxy Contracts and Diamonds](https://medium.com/1milliondevs/new-storage-layout-for-proxy-contracts-and-diamonds-98d01d0eadb) - -[Solidity Libraries Can't Have State Variables -- Oh Yes They Can!](https://dev.to/mudgen/solidity-libraries-can-t-have-state-variables-oh-yes-they-can-3ke9) - -[ Smart Contracts Sharing Common Data](https://medium.com/coinmonks/smart-contracts-sharing-common-data-777310263ac0?source=friends_link&sk=b462ff3559ae9c8da243ba31a557e4f4) - -[Sharing Common Data Using Libraries](https://medium.com/coinmonks/sharing-common-data-using-libraries-6573857d328c?source=friends_link&sk=1da1ef153b8d15f3f7bb9f4ce429890a) - -### Diamond Tools - -[Louper](https://louper.dev/) A user interface for all diamonds. - -[buidler-deploy](https://github.com/wighawag/buidler-deploy#diamonds-and-facets) +## Security Considerations -### Implementations +### Ownership and Authentication -[Diamond example implementations](https://github.com/mudgen/Diamond) +> **Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. -[GHST Staking](https://github.com/aavegotchi/ghst-staking) +It is possible to create many different authentication or ownership schemes with this proposal. Authentication schemes can be very simple or complex, fine grained or coarse. This proposal does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. -[pie-dao / ExperiPie](https://github.com/pie-dao/ExperiPie) +Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples. -[Nayms Contracts](https://github.com/nayms/contracts) +The development of standards and implementations of ownership, control and authentication of diamonds is encouraged. -[Aavegotchi Diamond](https://github.com/aavegotchi/aavegotchi-contracts) +### Arbitrary Execution with `diamondCut` -### Help +The `diamondCut` function allows arbitrary execution with access to the diamond's storage (through `delegatecall`). Access to this function must be restricted carefully. -[EIP-2535 Diamonds Discord](https://discord.gg/kQewPw2) +### Do Not Self Destruct +Use of `selfdestruct` in a facet is heavily discouraged. Misuse of it can delete a diamond or a facet. -## Inspiration & Development +### Function Selector Clash -EIP-2535 is an improved design over [EIP-1538](./eip-1538.md) using ABIEncoderV2 and function selectors. +A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. This scenario is not possible with a properly implemented `diamondCut` function because it prevents adding function selectors that already exist. -EIP-2535 replaces EIP-1538. +### Transparency -This standard was inspired by [EIP-1538](./eip-1538.md) and ZeppelinOS's implementation of [Upgradeability with vtables](https://github.com/zeppelinos/labs/tree/master/upgradeability_with_vtable). +Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. -This standard was also inspired by the design and implementation of the [Mokens contract](https://etherscan.io/address/0xc1eab49cf9d2e23e43bcf23b36b2be14fc2f8838#code). +Security and domain experts can review the history of change of a diamond to detect any history of foul play. ## 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-2537.md b/EIPS/eip-2537.md index df7d5bdbdc2898..14b9858c8eb210 100644 --- a/EIPS/eip-2537.md +++ b/EIPS/eip-2537.md @@ -1,9 +1,9 @@ --- eip: 2537 title: Precompile for BLS12-381 curve operations -author: Alex Vlasov (@shamatar), Kelly Olson (@ineffectualproperty) +author: Alex Vlasov (@shamatar), Kelly Olson (@ineffectualproperty), Alex Stokes (@ralexstokes) discussions-to: https://ethereum-magicians.org/t/eip2537-bls12-precompile-discussion-thread/4187 -status: Review +status: Stagnant type: Standards Track category: Core created: 2020-02-21 @@ -35,15 +35,15 @@ Multiexponentiation operation is included to efficiently aggregate public keys o |Precompile |Address | |---|---| -|BLS12_G1ADD | 0x0a | -|BLS12_G1MUL | 0x0b | -|BLS12_G1MULTIEXP | 0x0c | -|BLS12_G2ADD | 0x0d | -|BLS12_G2MUL | 0x0e | -|BLS12_G2MULTIEXP | 0x0f | -|BLS12_PAIRING | 0x10 | -|BLS12_MAP_FP_TO_G1 | 0x11 | -|BLS12_MAP_FP2_TO_G2 | 0x12 | +|BLS12_G1ADD | 0x0c | +|BLS12_G1MUL | 0x0d | +|BLS12_G1MULTIEXP | 0x0e | +|BLS12_G2ADD | 0x0f | +|BLS12_G2MUL | 0x10 | +|BLS12_G2MULTIEXP | 0x11 | +|BLS12_PAIRING | 0x12 | +|BLS12_MAP_FP_TO_G1 | 0x13 | +|BLS12_MAP_FP2_TO_G2 | 0x14 | ## Motivation @@ -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: @@ -122,6 +122,7 @@ Certain operations have variable length input, such as multiexponentiations (tak G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). Output is an encoding of addition operation result - single G1 point (`128` bytes). Error cases: + - Either of points being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length @@ -131,6 +132,7 @@ Error cases: G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiplication operation result - single G1 point (`128` bytes). Error cases: + - Point being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length @@ -140,6 +142,7 @@ Error cases: G1 multiexponentiation call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). Error cases: + - Any of G1 points being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length @@ -150,6 +153,7 @@ Error cases: G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). Output is an encoding of addition operation result - single G2 point (`256` bytes). Error cases: + - Either of points being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length @@ -159,6 +163,7 @@ Error cases: G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiplication operation result - single G2 point (`256` bytes). Error cases: + - Point being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length @@ -168,6 +173,7 @@ Error cases: G2 multiexponentiation call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). Error cases: + - Any of G2 points being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length @@ -176,12 +182,14 @@ Error cases: #### ABI for pairing Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: + - `128` bytes of G1 point encoding - `256` bytes of G2 point encoding Output is a `32` bytes where first `31` bytes are equal to `0x00` and the last byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise. Error cases: + - Any of G1 or G2 points being not on the curve must result in error - Any of G1 or G2 points are not in the correct subgroup - Field elements encoding rules apply (obviously) @@ -193,6 +201,7 @@ Error cases: Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field. Output of this call is `128` bytes and is G1 point following respective encoding rules. Error cases: + - Input has invalid length - Input is not a valid field element @@ -201,6 +210,7 @@ Error cases: Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field. Output of this call is `256` bytes and is G2 point following respective encoding rules. Error cases: + - Input has invalid length - Input is not a valid field element @@ -250,11 +260,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 @@ -267,6 +277,7 @@ For multiexponentiation and pairing functions gas cost depends on the input leng Define a constant `LEN_PER_PAIR` that is equal to `160` for G1 operation and to `288` for G2 operation. Define a function `discount(k)` following the rules in the corresponding section, where `k` is number of pairs. The following pseudofunction reflects how gas should be calculated: + ``` k = floor(len(input) / LEN_PER_PAIR); if k == 0 { @@ -286,6 +297,7 @@ We use floor division to get number of pairs. If length of the input is not divi Define a constant `LEN_PER_PAIR = 384`; The following pseudofunction reflects how gas should be calculated: + ``` k = floor(len(input) / LEN_PER_PAIR); @@ -317,13 +329,13 @@ Subgroup check **is mandatory** during the pairing call. Implementations *should ### Field to curve mapping -Algorithms and set of parameters for SWU mapping method is provided by a separate [document](../assets/eip-2537/field_to_curve.md) +Algorithms and set of parameters for SWU mapping method is provided by a separate [document](../assets/eip-2537/field_to_curve.md) ## Test Cases -Due to the large test parameters space we first provide properties that various operations must satisfy. We use additive notation for point operations, capital letters (`P`, `Q`) for points, small letters (`a`, `b`) for scalars. Generator for G1 is labeled as `G`, generator for G2 is labeled as `H`, otherwise we assume random point on a curve in a correct subgroup. `0` means either scalar zero or point of infinity. `1` means either scalar one or multiplicative identity. `group_order` is a main subgroup order. `e(P, Q)` means pairing operation where `P` is in G1, `Q` is in G2. +Due to the large test parameters space we first provide properties that various operations must satisfy. We use additive notation for point operations, capital letters (`P`, `Q`) for points, small letters (`a`, `b`) for scalars. Generator for G1 is labeled as `G`, generator for G2 is labeled as `H`, otherwise we assume random point on a curve in a correct subgroup. `0` means either scalar zero or point of infinity. `1` means either scalar one or multiplicative identity. `group_order` is a main subgroup order. `e(P, Q)` means pairing operation where `P` is in G1, `Q` is in G2. -Requeired properties for basic ops (add/multiply): +Required properties for basic ops (add/multiply): - Commutativity: `P + Q = Q + P` - Additive negation: `P + (-P) = 0` @@ -334,7 +346,8 @@ Requeired properties for basic ops (add/multiply): - Multiplication by the unnormalized scalar `(scalar + group_order) * P = scalar * P` Required properties for pairing operation: -- Degeneracy `e(P, 0*Q) = e(0*P, Q) = 1` + +- Degeneracy `e(P, 0*Q) = e(0*P, Q) = 1` - Bilinearity `e(a*P, b*Q) = e(a*b*P, Q) = e(P, a*b*Q)` (internal test, not visible through ABI) ### Benchmarking test cases @@ -344,6 +357,7 @@ A set of test vectors for quick benchmarking on new implementations is located i ## Reference Implementation There are two fully spec compatible implementations on the day of writing: + - One in Rust language that is based on the EIP1962 code and integrated with OpenEthereum for this library - One implemented specifically for Geth as a part of the current codebase @@ -354,4 +368,5 @@ Strictly following the spec will eliminate security implications or consensus im Important topic is a "constant time" property for performed operations. We explicitly state that this precompile **IS NOT REQUIRED** to perform all the operations using constant time algorithms. ## 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-2539.md b/EIPS/eip-2539.md index 8977521b907d8c..d84fa690191bb1 100644 --- a/EIPS/eip-2539.md +++ b/EIPS/eip-2539.md @@ -3,7 +3,7 @@ eip: 2539 title: BLS12-377 curve operations author: Alex Vlasov (@shamatar) discussions-to: https://ethereum-magicians.org/t/eip-2539-bls12-377-precompile-discussion-thread/4659 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-02-26 @@ -263,4 +263,4 @@ Strictly following the spec will eliminate security implications or consensus im Important topic is a "constant time" property for performed operations. We explicitly state that this precompile **IS NOT REQUIRED** to perform all the operations using constant time algorithms. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-2542.md b/EIPS/eip-2542.md index 833546ab8d54e6..5580dca0503dba 100644 --- a/EIPS/eip-2542.md +++ b/EIPS/eip-2542.md @@ -3,7 +3,7 @@ eip: 2542 title: New opcodes TXGASLIMIT and CALLGASLIMIT author: Alex Forshtat discussions-to: https://ethereum-magicians.org/t/eip-2542-add-txgaslimit-callgaslimit-txgasrefund-opcodes -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-02-29 @@ -84,4 +84,4 @@ Smart contracts that will use proposed opcodes must not use them for the core of The implementations must be completed before any EIP is given status "Final", but it need not be completed before the EIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details. ## 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-2544.md b/EIPS/eip-2544.md index 46d971fc6f3c0f..4df16b5dfb930f 100644 --- a/EIPS/eip-2544.md +++ b/EIPS/eip-2544.md @@ -1,24 +1,23 @@ --- eip: 2544 title: ENS Wildcard Resolution +description: Adds support for "wildcard" resolution of subdomains in ENS. author: Nick Johnson (@arachnid), 0age (@0age) discussions-to: https://ethereum-magicians.org/t/eip-2544-ens-wildcard-resolution -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-02-28 requires: 137 --- -## Simple Summary - -EIP-2544 extends ENS client behavior to support "wildcard" resolution of subdomains. This is accomplished by using a parent domain's resolver if none is set on a given subdomain. - ## Abstract -The Ethereum Name Service Specification (EIP-137) establishes a two-step name resolution process. First, an ENS client takes a provided name, performs the namehash algorithm to determine the associated "node", and supplies that node to the ENS Registry contract to determine the resolver. Then, if a resolver has been set on the Registry, the client supplies that same node to the resolver contract, which will return the associated address or other record. +The Ethereum Name Service Specification (EIP-137) establishes a two-step name resolution process. First, an ENS client performs the namehash algorithm on the name to determine the associated "node", and supplies that node to the ENS Registry contract to determine the resolver. Then, if a resolver has been set on the Registry, the client supplies that same node to the resolver contract, which will return the associated address or other record. -As currently specified, this process terminates if a resolver is not set on the ENS Registry for a given node. This EIP extends the existing name resolution process by adding an additional step if a resolver is not set for subdomain. This step strips out the leftmost label from the name, derives the node of the new fragment, and supplies that node to the ENS Registry. If a resolver is located for that node, the client supplies the original, complete node to that resolver contract to derive the relevant records. +As currently specified, this process terminates if a resolver is not set on the ENS Registry for a given node. This EIP changes the name resolution process by adding an additional step if a resolver is not set for a domain. This step strips out the leftmost label from the name, derives the node of the new fragment, and supplies that node to the ENS Registry. If a resolver is located for that node, the client supplies the original, complete node to that resolver contract to derive the relevant records. This step is repeated until a node with a resolver is found. + +Further, this specification defines a new way for resolvers to resolve names, using a unified `resolve()` method that permits more flexible handling of name resolution. ## Motivation @@ -28,61 +27,76 @@ Furthermore, users cannot immediately utilize these subdomains upon account crea Enabling wildcard support allows for the design of more advanced resolvers that deterministically generate addresses and other records for unassigned subdomains. The generated addresses could map to counterfactual contract deployment addresses (i.e. `CREATE2` addresses), to designated "fallback" addresses, or other schemes. Additionally, individual resolvers would still be assignable to any given subdomain, which would supersede the wildcard resolution using the parent resolver. -Another critical motivation with EIP-2544 is to enable wildcard resolution in a backwards-compatible fashion. It does not require modifying the current ENS Registry contract or any assigned resolvers, and continues to support existing ENS records — legacy ENS clients would simply fail to resolve wildcard records. +Another critical motivation with EIP-2544 is to enable wildcard resolution in a backwards-compatible fashion. It does not require modifying the current ENS Registry contract or any existing resolvers, and continues to support existing ENS records — legacy ENS clients would simply fail to resolve wildcard records. ## 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-2544-compliant ENS clients MUST perform the following sequence when determining the resolver for a given name: +Let: + - `namehash` be the algorithm defined in EIP 137. + - `dnsencode` be the process for encoding DNS names specified in section 3.1 of RFC1035, with the exception that there is no limit on the total length of the encoded name. The empty string is encoded identically to the name '.', as a single 0-octet. + - `parent` be a function that removes the first label from a name (eg, `parent('foo.eth') = 'eth'`). `parent('tld')` is defined as the empty string ''. + - `ens` is the ENS registry contract for the current network. + +EIP-2544-compliant ENS resolvers MAY implement the following function interface: + +``` +interface ExtendedResolver { + function resolve(bytes calldata name, bytes calldata data) external view returns(bytes); +} +``` + +If a resolver implements this function, it MUST return true when `supportsInterface()` is called on it with the interface's ID, 0xTBD. -1. Apply the namehash algorithm to the supplied name as specified in EIP-137 to derive the `node`. -2. Call the ENS Registry contract, supplying the node as the argument to `function resolver(bytes32 node) constant returns (address)`. -3. If an address other than the null address is returned, the client MUST use the returned address as the resolver. -4. If the null address is returned, the client MUST strip the leftmost label from the name to derive a new name. -5. If the only remaining label is a top-level domain, or if no labels remain, the client MUST refuse to interact with the resolver. -6. Apply the namehash algorithm to the new name as specified in EIP-137 to derive the `parentNode`. -7. Call the ENS Registry contract, supplying the parent node as the argument to `function resolver(bytes32 node) constant returns (address)` to determine the resolver. -8. If the null address is returned from this second request, the client MUST refuse to interact with the resolver. +ENS clients will call `resolve` with the DNS-encoded name to resolve and the encoded calldata for a resolver function (as specified in EIP-137 and elsewhere); the function MUST either return valid return data for that function, or revert if it is not supported. -In the event that a non-null resolver is located via this process, the client MUST supply the full, original `node` to the resolver to derive the address or other records. As with EIP-137, clients attempting to resolve an address via `function addr(bytes32 node) constant returns (address)` MUST refuse to interact with the returned address when the null address is returned. +EIP-2544-compliant ENS clients MUST perform the following procedure when determining the resolver for a given name: + +1. Set `currentname = name` +2. Set `resolver = ens.resolver(namehash(currentname))` +3. If `resolver` is not the zero address, halt and return `resolver`. +4. If `name` is the empty name ('' or '.'), halt and return null. +5. Otherwise, set `currentname = parent(currentname)` and go to 2. + +If the procedure above returns null, name resolution MUST terminate unsuccessfully. Otherwise, EIP-2544-compliant ENS clients MUST perform the following procedure when resolving a record: + +1. Set `calldata` to the ABI-encoded call data for the resolution function required - for example, the ABI encoding of `addr(namehash(name))` when resolving the `addr` record. +2. Set `supports2544 = resolver.supportsInterface(0xTBD)`. +3. If `supports2544` is true, set `result = resolver.resolve(dnsencode(name), calldata)` +4. Otherwise, set `result` to the result of calling `resolver` with `calldata`. +5. Return `result` after decoding it using the return data ABI of the corresponding resolution function (eg, for `addr()`, ABI-decode the result of `resolver.resolve()` as an `address`). + +Note that in all cases the resolution function (`addr()` etc) and the `resolve` function are supplied the original `name`, *not* the `currentname` found in the first stage of resolution. ### Pseudocode ``` -function getNodeAndResolver(name) { - // 1. Apply the namehash algorithm to supplied name to determine the node. - const node = namehash(name); - - // 2. Attempt to retrieve a resolver from the ENS Registry using the node. - let resolver = ENS_REGISTRY.methods.resolver(node).call(); - - // 3. Use the resolver if a non-null result is returned from the registry. - if (resolver != "0x0000000000000000000000000000000000000000") { - return (node, resolver); +function getResolver(name) { + for(let currentname = name; currentname !== ''; currentname = parent(currentname)) { + const node = namehash(currentname); + const resolver = ens.resolver(node); + if(resolver != '0x0000000000000000000000000000000000000000') { + return resolver; + } } - - // 4. Remove the leftmost label from the name. - const labelsWithoutLeftmost = name.split(".").slice(1); - - // 5. Do not continue if only the top-level domain (or no domain) remains. - if (labelsWithoutLeftmost.length < 2) { - throw new Error("ENS resolver not found."); + return null; +} + +function resolve(name, func, ...args) { + const resolver = getResolver(name); + if(resolver === null) { + return null; } - - // 6. Apply the namehash algorithm to new name to determine the parent node. - const parentNode = namehash(labelsWithoutLeftmost.join(".")); - - // 7. Attempt to retrieve a resolver from registry using the parent node. - resolver = ENS_REGISTRY.methods.resolver(parentNode).call(); - - // 8. Do not continue if a null result is returned from the registry. - if (resolver == "0x0000000000000000000000000000000000000000") { - throw new Error("ENS resolver not found."); + const supports2544 = resolver.supportsInterface('0xTBD'); + let result; + if(supports2544) { + const calldata = resolver[func].encodeFunctionCall(namehash(name), ...args); + result = resolver.resolve(dnsencode(name), calldata); + return resolver[func].decodeReturnData(result); + } else { + return resolver[func](...args); } - - // Return the located resolver along with the original node. - return (node, resolver); -}; +} ``` ## Rationale @@ -91,11 +105,13 @@ The proposed implementation supports wildcard resolution in a manner that minimi It also recognizes an existing consensus concerning the desirability of wildcard resolution for ENS, enabling more widespread adoption of the original specification by solving for a key scalability obstacle. -By not recursively applying the namehash operation after removing successive leftmost subdomains, round-trip requests are reduced and edge cases around overriding ownership of nested subdomains are adequately handled. ENS clients may optimize further for reduced latency by utilizing a helper contract that performs the logic necessary to support wildcard resolution. +While introducing an optional `resolve` function for resolvers, taking the unhashed name and calldata for a resolution function increases implementation complexity, it provides a means for resolvers to obtain plaintext labels and act accordingly, which enables many wildcard-related use-cases that would otherwise not be possible - for example, a wildcard resolver could resolve `id.nifty.eth` to the owner of the NFT with id `id` in some collection. With only namehashes to work with, this is not possible. Resolvers with simpler requirements can continue to simply implement resolution functions directly and omit support for the `resolve` function entirely. + +The DNS wire format is used for encoding names as it permits quick and gas-efficient hashing of names, as well as other common operations such as fetching or removing individual labels; in contrast, dot-separated names require iterating over every character in the name to find the delimiter. ## Backwards Compatibility -There are no backwards compatibility concerns — existing ENS clients that are compliant with EIP-137 will fail to resolve wildcard records and refuse to interact with them, while those compliant with EIP-2544 will continue to correctly resolve, or reject, existing ENS records. +Existing ENS clients that are compliant with EIP-137 will fail to resolve wildcard records and refuse to interact with them, while those compliant with EIP-2544 will continue to correctly resolve, or reject, existing ENS records. Resolvers wishing to implement the new `resolve` function for non-wildcard use-cases (eg, where the resolver is set directly on the name being resolved) should consider what to return to legacy clients that call the individual resolution functions for maximum compatibility. ## Security Considerations @@ -107,4 +123,4 @@ There is also the possibility that some applications might require that no resol ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2565.md b/EIPS/eip-2565.md index d15aef9fda5631..bfd83317efc59a 100644 --- a/EIPS/eip-2565.md +++ b/EIPS/eip-2565.md @@ -101,4 +101,4 @@ The biggest security consideration for this EIP is creating a potential DoS vect [EIP-198](./eip-198.md) ## 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-2566.md b/EIPS/eip-2566.md index 00a1cb6fb3dda9..27ad65af53f3e1 100644 --- a/EIPS/eip-2566.md +++ b/EIPS/eip-2566.md @@ -3,7 +3,7 @@ eip: 2566 title: Human Readable Parameters for Contract Function Execution author: Joseph Stockermans (@jstoxrocky) discussions-to: https://ethereum-magicians.org/t/human-readable-parameters-for-contract-function-execution/4154 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-03-23 @@ -103,4 +103,4 @@ With backwards compatibility in mind, this EIP proposes augmenting the set of Et Displaying the contract address, function name, and argument values can provide additional security to users, but it is not a guarantee that a function will execute as the user expects. A poorly implemented contract can still name its function `transfer` and accept `address` and `uint256` arguments - but there is nothing short of contract examination that will let a user know that this contract is indeed a valid ERC20 contract. This EIP does not intend to solve the larger problem around trust in a contract's code, but instead intends to give users better tools to understand exactly what is contained within the data they are broadcasting to the Ethereum network. ## 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-2569.md b/EIPS/eip-2569.md index 482e769765726a..8ed6b119c29551 100644 --- a/EIPS/eip-2569.md +++ b/EIPS/eip-2569.md @@ -1,17 +1,15 @@ --- eip: 2569 title: Saving and Displaying Image Onchain for Universal Tokens -author: Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous) +description: A set of interfaces to save an SVG image in Ethereum, and to retrieve the image file from Ethereum for universal tokens. +author: Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous), Ran Xing (@lemontreeran) discussions-to: https://ethereum-magicians.org/t/erc-2569-saving-and-displaying-image-onchain-for-universal-tokens/4167 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-03-28 --- -## Simple Summary -A set of interfaces to save an SVG image in Ethereum, and to retrieve the image file from Ethereum for universal tokens. - ## Abstract This set of interfaces allow a smart contract to save an SVG image in Ethereum and to retrieve an SVG image from Ethereum for fungible tokens, non-fungible tokens and tokens based on standards that will be developed in the future. @@ -89,14 +87,7 @@ This solution not only works for tokens based on ERC-721, ERC-1155 and ERC-20 bu ## Backwards Compatibility There are no backward compatibility issues. -## Test Cases -[DHonor](https://dhonor.io/) - -[1155](http://dhonor.io/1155) - -[DeMe](https://www.deme.app/) - -## Implementation +## Reference Implementation `tokenId`: a token index in an ERC-721 token or a token type/index in an ERC-1155 token. It is a uint256 variable. `imageSvg`: an SVG image's file content. It is a string variable. Note: the SVG image should include at least three attributes:"name", "description" and "issuer". @@ -357,10 +348,6 @@ _tokenImageSvg = svgCode ``` -## Articles & Discussions - -[Image Storage Solution Based on Ethereum Sharding](https://naturaldao.io/en/collaboration/blog/65-storage-shading.html) - ## 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-2583.md b/EIPS/eip-2583.md index 6514d9c9b92a3e..12689ee89f8e20 100644 --- a/EIPS/eip-2583.md +++ b/EIPS/eip-2583.md @@ -3,7 +3,7 @@ eip: 2583 title: Penalty for account trie misses author: Martin Holst Swende (@holiman) discussions-to: https://ethereum-magicians.org/t/eip-2583-penalties-for-trie-misses/4190 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-02-21 @@ -291,4 +291,4 @@ Downside: ## 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-2584.md b/EIPS/eip-2584.md index 1e7b6e9978e8bf..e2a6293027ddd5 100644 --- a/EIPS/eip-2584.md +++ b/EIPS/eip-2584.md @@ -3,7 +3,7 @@ eip: 2584 title: Trie format transition with overlay trees author: Guillaume Ballet (@gballet) discussions-to: https://ethresear.ch/t/overlay-method-for-hex-bin-tree-conversion/7104 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-04-03 @@ -111,4 +111,4 @@ There are three attack vectors that I can foresee: * The initial version of this EIP expected miners to vote on the value of the binary base root. This has been removed because of the complexity of this process, and because this functionality is already guaranteed by the "longest chain" rule. ## 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-2593.md b/EIPS/eip-2593.md index 4fb7f0da7a96d1..c5e1cc6169da6a 100644 --- a/EIPS/eip-2593.md +++ b/EIPS/eip-2593.md @@ -3,7 +3,7 @@ eip: 2593 title: Escalator fee market change for ETH 1.0 chain author: Dan Finlay discussions-to: https://ethresear.ch/t/another-simple-gas-fee-model-the-escalator-algorithm-from-the-agoric-papers/6399 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-03-13 @@ -128,4 +128,4 @@ The security considerations for this EIP are: - None currently known. ## 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-2612.md b/EIPS/eip-2612.md index d23fa0931778af..90e47d67bf12ee 100644 --- a/EIPS/eip-2612.md +++ b/EIPS/eip-2612.md @@ -1,69 +1,67 @@ --- eip: 2612 -title: permit – 712-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: Draft +status: Final type: Standards Track category: ERC created: 2020-04-13 requires: 20, 712 --- -## Simple Summary -A function `permit` extending [ERC-20](./eip-20.md) which allows for approvals to be made via `secp256k1` signatures. This kind of "account abstraction for ERC-20" brings about two main benefits: - -- transactions involving ERC-20 operations can be paid using the token itself rather than ETH, -- approve and pull operations can happen in a single transaction instead of two consecutive transactions, - -while adding as little as possible over the existing ERC-20 standard. - ## Abstract -Arguably one of the main reasons for the success of ERC-20 tokens lies in the interplay between `approve` and `transferFrom`, -which allows for tokens to not only be transferred between externally owned accounts (EOA), but to be used in other contracts under application specific conditions by abstracting away `msg.sender` as the defining mechanism for token access control. -However, a limiting factor in this design stems from the fact that the ERC-20 `approve` function itself is defined in terms of `msg.sender`. This means that user's _initial action_ involving ERC-20 tokens must be performed by an EOA [1]. If the user needs to interact with a smart contract, then they need to make 2 transactions (`approve` and the smart contract call which will internally call `transferFrom`). Even in the simple use case of paying another person, they need to hold ETH to pay for transaction gas costs. +Arguably one of the main reasons for the success of [EIP-20](./eip-20.md) tokens lies in the interplay between `approve` and `transferFrom`, which allows for tokens to not only be transferred between externally owned accounts (EOA), but to be used in other contracts under application specific conditions by abstracting away `msg.sender` as the defining mechanism for token access control. -This ERC extends the ERC-20 standard with a new function `permit`, which allows users to modify the `allowance` mapping using a signed message, instead of through `msg.sender`. +However, a limiting factor in this design stems from the fact that the EIP-20 `approve` function itself is defined in terms of `msg.sender`. This means that user's _initial action_ involving EIP-20 tokens must be performed by an EOA (_but see Note below_). If the user needs to interact with a smart contract, then they need to make 2 transactions (`approve` and the smart contract call which will internally call `transferFrom`). Even in the simple use case of paying another person, they need to hold ETH to pay for transaction gas costs. -For an improved user experience, the signed data is structured following [ERC-712](./eip-712.md), which already has wide spread adoption in major RPC providers. +This ERC extends the EIP-20 standard with a new function `permit`, which allows users to modify the `allowance` mapping using a signed message, instead of through `msg.sender`. +For an improved user experience, the signed data is structured following [EIP-712](./eip-712.md), which already has wide spread adoption in major RPC providers. + +**_Note:_** EIP-20 must be performed by an EOA unless the address owning the token is actually a contract wallet. Although contract wallets solves many of the same problems that motivates this EIP, they are currently only scarcely adopted in the ecosystem. Contract wallets suffer from a UX problem -- since they separate the EOA `owner` of the contract wallet from the contract wallet itself (which is meant to carry out actions on the `owner`s behalf and holds all of their funds), user interfaces need to be specifically designed to support them. The `permit` pattern reaps many of the same benefits while requiring little to no change in user interfaces. ## Motivation -While ERC-20 tokens have become ubiquotous in the Ethereum ecosystem, their status remains that of second class tokens from the perspective of the protocol. The ability for users to interact with Ethereum without holding any ETH has been a [long outstanding goal](https://github.com/ethereum/EIPs/blob/ed621645c8f3bc5756492f327cda015f35d9f8da/EIPS/eip-101.md) and the [subject](./eip-1077.md) [of](./eip-777.md) [many](https://github.com/ethereum/EIPs/issues/1776#) [EIPs](https://eips.ethereum.org/EIPS/eip-1271). -So far, many of these proposals have seen very little adoption, and the ones that have been adopted (such as [ERC-777](./eip-777.md)), introduce a lot of additional functionality, causing [unexpected behavior in mainstream contracts](https://medium.com/consensys-diligence/uniswap-audit-b90335ac007). +While EIP-20 tokens have become ubiquitous in the Ethereum ecosystem, their status remains that of second class tokens from the perspective of the protocol. The ability for users to interact with Ethereum without holding any ETH has been a long outstanding goal and the subject of many EIPs. -This ERC proposes an alternative solution which is designed to be as minimal as possible and to only address _one problem_: the lack of abstraction in the ERC-20 `approve` method. +So far, many of these proposals have seen very little adoption, and the ones that have been adopted (such as [EIP-777](./eip-777.md)), introduce a lot of additional functionality, causing unexpected behavior in mainstream contracts. -While it may be tempting to introduce `*_by_signature` counterparts for every ERC-20 function, they are intentionally left out of this ERC-20 for two reasons: +This ERC proposes an alternative solution which is designed to be as minimal as possible and to only address _one problem_: the lack of abstraction in the EIP-20 `approve` method. - - the desired specifics of such functions, such as decision regarding fees for `transfer_by_signature`, possible batching algorithms, varies depending on the use case, and, - - they can be implemented using a combination of `permit` and additional helper contracts without loss of generality. +While it may be tempting to introduce `*_by_signature` counterparts for every EIP-20 function, they are intentionally left out of this EIP-20 for two reasons: +- the desired specifics of such functions, such as decision regarding fees for `transfer_by_signature`, possible batching algorithms, varies depending on the use case, and, +- 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 ERC20 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) function DOMAIN_SEPARATOR() external view returns (bytes32) ``` + The semantics of which are as follows: -For all addresses `owner`, `spender`, uint256s `value`, `deadline` and `nonce`, uint8 `v`, bytes32 `r` and `s`, -a call to `permit(owner, spender, value, deadline, v, r, s)` will set +For all addresses `owner`, `spender`, uint256s `value`, `deadline` and `nonce`, uint8 `v`, bytes32 `r` and `s`, +a call to `permit(owner, spender, value, deadline, v, r, s)` will set `approval[owner][spender]` to `value`, increment `nonces[owner]` by 1, -and emit a corresponding `Approval` event, +and emit a corresponding `Approval` event, if and only if the following conditions are met: - - The current blocktime is less than or equal to `deadline`. - `owner` is not the zero address. - `nonces[owner]` (before the state update) is equal to `nonce`. - `r`, `s` and `v` is a valid `secp256k1` signature from `owner` of the message: +If any of these conditions are not met, the `permit` call must revert. + ```sol keccak256(abi.encodePacked( hex"1901", @@ -77,9 +75,11 @@ keccak256(abi.encodePacked( deadline)) )) ``` + where `DOMAIN_SEPARATOR` is defined according to EIP-712. The `DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, and satisfy the requirements of EIP-712, but is otherwise unconstrained. A common choice for `DOMAIN_SEPARATOR` is: + ```solidity DOMAIN_SEPARATOR = keccak256( abi.encode( @@ -91,8 +91,8 @@ DOMAIN_SEPARATOR = keccak256( )); ``` -In other words, the message is the ERC-712 typed structure: - +In other words, the message is the EIP-712 typed structure: + ```js { "types": { @@ -114,9 +114,10 @@ In other words, the message is the ERC-712 typed structure: "type": "address" } ], - "Permit": [{ - "name": "owner", - "type": "address" + "Permit": [ + { + "name": "owner", + "type": "address" }, { "name": "spender", @@ -135,12 +136,13 @@ In other words, the message is the ERC-712 typed structure: "type": "uint256" } ], - "primaryType": "Permit", - "domain": { - "name": erc20name, - "version": version, - "chainId": chainid, - "verifyingContract": tokenAddress + }, + "primaryType": "Permit", + "domain": { + "name": erc20name, + "version": version, + "chainId": chainid, + "verifyingContract": tokenAddress }, "message": { "owner": owner, @@ -149,42 +151,35 @@ In other words, the message is the ERC-712 typed structure: "nonce": nonce, "deadline": deadline } -}} +} ``` Note that nowhere in this definition we refer to `msg.sender`. The caller of the `permit` function can be any address. - ## Rationale -The `permit` function is sufficient for enabling any operation involving ERC-20 tokens to be paid for using the token itself, rather than using ETH. -An example of a contract which enables gasless token transactions can be found [here](https://github.com/dapphub/ds-dach). -It avoids any calls to unknown code. +The `permit` function is sufficient for enabling any operation involving EIP-20 tokens to be paid for using the token itself, rather than using ETH. The `nonces` mapping is given for replay protection. A common use case of `permit` has a relayer submit a `Permit` on behalf of the `owner`. In this scenario, the relaying party is essentially given a free option to submit or withhold the `Permit`. If this is a cause of concern, the `owner` can limit the time a `Permit` is valid for by setting `deadline` to a value in the near future. The `deadline` argument can be set to `uint(-1)` to create `Permit`s that effectively never expire. -ERC-712 typed messages are included because of its wide spread adoption in many wallet providers. - +EIP-712 typed messages are included because of its wide spread adoption in many wallet providers. ## Backwards Compatibility + There are already a couple of `permit` functions in token contracts implemented in contracts in the wild, most notably the one introduced in the `dai.sol`. Its implementation differs slightly from the presentation here in that: + - instead of taking a `value` argument, it takes a bool `allowed`, setting approval to 0 or `uint(-1)`. - the `deadline` argument is instead called `expiry`. This is not just a syntactic change, as it effects the contents of the signed message. -There is also an implementation in the token [`Stake`](https://etherscan.io/address/0x0Ae055097C6d159879521C384F1D2123D1f195e6#code) with the same ABI as `dai` but with different semantics: it lets users issue "expiring approvals", that only allow `transferFrom` to occur while `expiry >= block.timestamp`. - -The specification presented here is in line with the implementation in [Uniswap-v2](https://github.com/uniswap/uniswap-v2-core). - -## Test Cases +There is also an implementation in the token `Stake` (Ethereum address `0x0Ae055097C6d159879521C384F1D2123D1f195e6`) with the same ABI as `dai` but with different semantics: it lets users issue "expiring approvals", that only allow `transferFrom` to occur while `expiry >= block.timestamp`. -Some basic tests can be found here https://github.com/Uniswap/uniswap-v2-core/blob/master/test/UniswapV2ERC20.spec.ts. +The specification presented here is in line with the implementation in Uniswap V2. -## Implementation -[UniswapV2ERC20.sol](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) +The requirement to revert if the permit is invalid was added when the EIP was already widely deployed, but at the moment it was consistent with all found implementations. ## Security Considerations @@ -194,11 +189,10 @@ Since the ecrecover precompile fails silently and just returns the zero address Signed `Permit` messages are censorable. The relaying party can always choose to not submit the `Permit` after having received it, withholding the option to submit it. The `deadline` parameter is one mitigation to this. If the signing party holds ETH they can also just submit the `Permit` themselves, which can render previously signed `Permit`s invalid. -The standard [ERC-20 race condition for approvals](https://swcregistry.io/docs/SWC-114) applies to `permit` as well. +The standard EIP-20 race condition for approvals (SWC-114) applies to `permit` as well. -If the `DOMAIN_SEPARATOR` contains the `chainId` and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a fututre chain split. +If the `DOMAIN_SEPARATOR` contains the `chainId` and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a future chain split. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). -[1] - Unless the address owning the token is actually a contract wallet. Although contract wallets solves many of the same problems that motivates this EIP, they are currently only scarcely adopted in the ecosystem. Contract wallets suffer from a UX problem -- since they separate the EOA `owner` of the contract wallet from the contract wallet itself (which is meant to carry out actions on the `owner`s behalf and holds all of their funds), user interfaces need to be specifically designed to support them. The `permit` pattern reaps many of the same benefits while requiring little to no change in user interfaces. +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2615.md b/EIPS/eip-2615.md index 8f24badb673107..9191c801c64bc3 100644 --- a/EIPS/eip-2615.md +++ b/EIPS/eip-2615.md @@ -3,7 +3,7 @@ eip: 2615 title: Non-Fungible Token with mortgage and rental functions author: Kohshi Shiba discussions-to: https://github.com/ethereum/EIPs/issues/2616 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-04-25 @@ -237,5 +237,5 @@ Since the external contract will control lien or tenant rights, flaws within the ## 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-2645.md b/EIPS/eip-2645.md index bdbe0349a6e929..9cba91992bc1c2 100644 --- a/EIPS/eip-2645.md +++ b/EIPS/eip-2645.md @@ -3,7 +3,7 @@ eip: 2645 title: Hierarchical Deterministic Wallet for Layer-2 author: Tom Brand , Louis Guthmann discussions-to: https://ethereum-magicians.org/t/hierarchical-deterministic-wallet-for-computation-integrity-proof-cip-layer-2/4286 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-05-13 @@ -19,7 +19,7 @@ m / purpose' / layer' / application' / eth_address_1' / eth_address_2' / index ``` ## Motivation -In the context of Computation Integrity Proof (CIP) Layer-2 solutions such as ZK-Rollups, users are required to sign messages on new elliptic curves optimized for those environnements. Extensive work has been done to make it secure on Bitcoin via [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). Those protosals have become the standard for wallets in the entire industry, independently on the associated blockchain. As Layer-2 solutions are taking off, it is a necessary requirement to maintain the same standard and security in this new space. +In the context of Computation Integrity Proof (CIP) Layer-2 solutions such as ZK-Rollups, users are required to sign messages on new elliptic curves optimized for those environnements. Extensive work has been done to make it secure on Bitcoin via [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). These protocols are the standard for wallets in the entire industry, independent of the underlying blockchain. As Layer-2 solutions are taking off, it is a necessary requirement to maintain the same standard and security in this new space. ## Specification Starkware keys are derived with the following [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)-compatible derivation path, with direct inspiration from [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki): @@ -39,7 +39,7 @@ As example, the expected path for address 0x0000....0000 assuming seed `m` and i The key derivation should follow the following algorithm ``` N = 2**256 -n = Layer2 curve order +n = Layer2 curve order path = stark derivation path BIP32() = Official BIP-0032 derivation function on secp256k1 hash = SHA256 @@ -51,7 +51,7 @@ while True: return key % n i++ ``` -This algorithm has been defined to maintain efficiency on existing restricted devices. +This algorithm has been defined to maintain efficiency on existing restricted devices. Nota Bene: At each round, the probability for a key to be greater than (N - (N % n)) is < 2^(-5). @@ -68,4 +68,4 @@ This standard complies with BIP43. This EIP has been defined to maintain separation of keys while providing foolproof logic on key derivation. ## 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-2657.md b/EIPS/eip-2657.md index 182380482ce110..83b1c215c78bca 100644 --- a/EIPS/eip-2657.md +++ b/EIPS/eip-2657.md @@ -3,7 +3,7 @@ eip: 2657 title: Ephemeral Testnet Yolo author: James Hancock (@madeoftin) discussions-to: https://gitter.im/ethereum/AllCoreDevs -status: Draft +status: Stagnant type: Meta created: 2020-04-19 --- @@ -63,4 +63,4 @@ Client syncs with the network - Faucet is unauthenticated, you can reach it from the dashboard ## 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-2666.md b/EIPS/eip-2666.md index 1c2de46647404e..3bd458a40aec09 100644 --- a/EIPS/eip-2666.md +++ b/EIPS/eip-2666.md @@ -3,7 +3,7 @@ eip: 2666 title: Repricing of precompiles and Keccak256 function author: Alex Vlasov (@shamatar) discussions-to: https://ethereum-magicians.org/t/eip2666-global-precompiles-repricing-and-many-more-discussion-thread/4332 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-05-22 @@ -130,4 +130,4 @@ There is no reference implementation at the time of writing as it requires just As described in backward compatibility section in some cases reduction of cost may allow e.g. re-entrancy that was not expected before, but we think that re-entrancy protection based on fixed gas costs is anyway flawed design decision. ## 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-2677.md b/EIPS/eip-2677.md index 749464a9ec7938..7bf2473d7766ab 100644 --- a/EIPS/eip-2677.md +++ b/EIPS/eip-2677.md @@ -1,12 +1,13 @@ --- eip: 2677 title: Limit size of `initcode` -author: Martin Holst Swende (@holiman), Pawel Bylica (@chfast), Alex Beregszazi (@axic) +author: Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2677-limit-size-of-initcode/4550 -status: Draft +status: Withdrawn type: Standards Track category: Core created: 2020-05-18 +withdrawal-reason: Replaced by EIP-3860. --- ## Simple Summary @@ -73,4 +74,4 @@ Test cases should include the following cases, 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-2678.md b/EIPS/eip-2678.md index 9d183b0fd115e2..5daf0ac93a10fc 100644 --- a/EIPS/eip-2678.md +++ b/EIPS/eip-2678.md @@ -4,7 +4,6 @@ title: Revised Ethereum Smart Contract Packaging Standard (EthPM v3) author: g. nicholas d’andrea (@gnidan), Piper Merriam (@pipermerriam), Nick Gheorghita (@njgheorghita), Christian Reitwiessner (@chriseth), Ben Hauser (@iamdefinitelyahuman), Bryant Eisenbach (@fubuloubu) discussions-to: https://ethereum-magicians.org/t/ethpm-v3-specification-working-group/4086 status: Final -review-period-end: 2021-04-14 type: Standards Track category: ERC created: 2020-05-26 @@ -1040,4 +1039,4 @@ released by individuals or organizations that they trust to include non-maliciou ## 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-2680.md b/EIPS/eip-2680.md index 50e9d9b59466b4..497cac5080d0c9 100644 --- a/EIPS/eip-2680.md +++ b/EIPS/eip-2680.md @@ -3,7 +3,7 @@ eip: 2680 title: Ethereum 2 wallet layout author: Jim McDonald discussions-to: https://ethereum-magicians.org/t/eip-2680-ethereum-2-wallet-layout/4323 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-05-29 @@ -133,4 +133,4 @@ The names for both wallet and key stores are UUIDs, ensuring that no data is lea ## 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-2681.md b/EIPS/eip-2681.md index 9e5972f49be38b..db9e1ffc38de76 100644 --- a/EIPS/eip-2681.md +++ b/EIPS/eip-2681.md @@ -3,7 +3,7 @@ eip: 2681 title: Limit account nonce to 2^64-1 author: Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2681-limit-account-nonce-to-2-64-1/4324 -status: Review +status: Final type: Standards Track category: Core created: 2020-04-25 @@ -23,10 +23,10 @@ Lastly, this facilitates a minor optimisation in clients, because the nonce no l ## Specification -If `block.number >= FORK_BLOCK` introduce two new restrictions: +Introduce two new restrictions retroactively from genesis: -1. Consider any transaction invalid, where the nonce exceeds `2^64-1`. -2. The `CREATE` instruction to abort with an exceptional halt, where the account nonce is `2^64-1`. +1. Consider any transaction invalid, where the nonce exceeds or equals to `2^64-1`. +2. The `CREATE` and `CREATE2` instructions' execution ends with the result `0` pushed on stack, where the account nonce is `2^64-1`. Gas for initcode execution is not deducted in this case. ## Rationale @@ -37,13 +37,15 @@ This mode of replay protection is out of fashion since [EIP-155](./eip-155.md) i 3. Most clients already consider the nonce field to be 64-bit, such as go-ethereum. +4. The reason a transaction with nonce `2^64-1` is invalid, because otherwise after inclusion the sender account's nonce would exceed `2^64-1`. + ## Backwards Compatibility While this is a breaking change, no actual effect should be visible: 1. There is no account in the state currently which would have a nonce exceeding that value. As of November 2020, the account `0xea674fdde714fd979de3edf0f56aa9716b898ec8` is responsible for the highest account nonce at approximately 29 million. -2. go-ethereum already has this restriction in place (`state.Account.Nonce` and `types.txdata.AccountNonce` it as a 64-bit number). +2. go-ethereum already has this restriction partially in place (`state.Account.Nonce` and `types.txdata.AccountNonce` it as a 64-bit number). ## Security Considerations @@ -51,4 +53,4 @@ None. ## 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-2696.md b/EIPS/eip-2696.md index 9b50efb0fcfa23..555d5178967d85 100644 --- a/EIPS/eip-2696.md +++ b/EIPS/eip-2696.md @@ -97,4 +97,4 @@ While this standard is perhaps not the greatest mechanism for communicating betw The relationship between Ethereum Provider and client is a trusted one, where it is assumed that the user implicitly trusts the Ethereum Provider which is how it managed to get injected into the client, or the client expressly pulled in a connection to it. ## 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-2700.md b/EIPS/eip-2700.md index 31e9f408f5c176..bd7c590b259a77 100644 --- a/EIPS/eip-2700.md +++ b/EIPS/eip-2700.md @@ -44,4 +44,4 @@ This EIP is mostly a retrospective EIP meaning it codifies an already existing s The relationship between Ethereum Provider and client is a trusted one, where it is assumed that the user implicitly trusts the Ethereum Provider which is how it managed to get injected into the client, or the client expressly pulled in a connection to it. ## 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-2711.md b/EIPS/eip-2711.md index 5d7e16a4014c1b..1f4d5139ce1255 100644 --- a/EIPS/eip-2711.md +++ b/EIPS/eip-2711.md @@ -3,7 +3,7 @@ eip: 2711 title: Sponsored, expiring and batch transactions. author: Micah Zoltu (@MicahZoltu) discussions-to: https://ethereum-magicians.org/t/eip-2711-separate-gas-payer-from-msg-sender/4353 -status: Draft +status: Withdrawn type: Standards Track category: Core created: 2020-06-11 @@ -133,4 +133,4 @@ No known issues. ## Security Considerations ## 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-2718.md b/EIPS/eip-2718.md index 1e7154eadeda9d..83a19b0fa865dc 100644 --- a/EIPS/eip-2718.md +++ b/EIPS/eip-2718.md @@ -1,6 +1,7 @@ --- eip: 2718 title: Typed Transaction Envelope +description: Defines a new transaction type that is an envelope for future transaction types. author: Micah Zoltu (@MicahZoltu) discussions-to: https://ethereum-magicians.org/t/eip-2718-typed-transaction-envelope/4355 status: Final @@ -9,9 +10,6 @@ category: Core created: 2020-06-13 --- -## Simple Summary -Defines a new transaction type that is an envelope for future transaction types. - ## Abstract `TransactionType || TransactionPayload` is a valid transaction and `TransactionType || ReceiptPayload` is a valid transaction receipt where `TransactionType` identifies the format of the transaction and `*Payload` is the transaction/receipt contents, which are defined in future EIPs. @@ -21,7 +19,7 @@ This was seen in [EIP-155](./eip-155.md) where the new value was bit-packed into There are multiple proposals in discussion that define new transaction types such as one that allows EOA accounts to execute code directly within their context, one that enables someone besides `msg.sender` to pay for gas, and proposals related to layer 1 multi-sig transactions. These all need to be defined in a way that is mutually compatible, which quickly becomes burdensome to EIP authors and to clients who now have to follow complex rules for differentiating transaction type. -By introducing an envolope transaction type, we only need to ensure backward compatibility with existing transactions and from then on we just need to solve the much simpler problem of ensuring there is no numbering conflict between `TransactionType`s. +By introducing an envelope transaction type, we only need to ensure backward compatibility with existing transactions and from then on we just need to solve the much simpler problem of ensuring there is no numbering conflict between `TransactionType`s. ## Specification ### Definitions @@ -31,7 +29,7 @@ By introducing an envolope transaction type, we only need to ensure backward com As of `FORK_BLOCK_NUMBER`, the transaction root in the block header **MUST** be the root hash of `patriciaTrie(rlp(Index) => Transaction)` where: * `Index` is the index in the block of this transaction * `Transaction` is either `TransactionType || TransactionPayload` or `LegacyTransaction` -* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transcation +* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transaction * `TransactionPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs * `LegacyTransaction` is `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` @@ -42,7 +40,7 @@ This makes it so we do not have to worry about signatures for one transaction ty As of `FORK_BLOCK_NUMBER`, the receipt root in the block header **MUST** be the root hash of `patriciaTrie(rlp(Index) => Receipt)` where: * `Index` is the index in the block of the transaction this receipt is for * `Receipt` is either `TransactionType || ReceiptPayload` or `LegacyReceipt` -* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transcation +* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transaction * `ReceiptPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs * `LegacyReceipt` is `rlp([status, cumulativeGasUsed, logsBloom, logs])` @@ -78,4 +76,4 @@ If it starts with a value in the range `[0, 0x7f]` then it is a new transaction When designing a new 2718 transaction type, it is **STRONGLY** recommended to include the transaction type as the first byte of the signed payload. If you fail to do this, it is possible that your transaction may be signature compatible with transactions of another type which can introduce security vulnerabilities for users. ## 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-2733.md b/EIPS/eip-2733.md index 57d6d0a749a224..04b276efdb09b7 100644 --- a/EIPS/eip-2733.md +++ b/EIPS/eip-2733.md @@ -3,11 +3,12 @@ eip: 2733 title: Transaction Package author: Matt Garnett (@lightclient) discussions-to: https://ethereum-magicians.org/t/eip-transaction-package/4365 -status: Draft +status: Withdrawn type: Standards Track category: Core created: 2020-06-16 requires: 2718 +withdrawal-reason: I have decided to pursue EIP-3074 as the preferred solution to transaction packages. --- ## Simple Summary @@ -288,4 +289,4 @@ package. This way, as soon as one part becomes invalid, it can request the parent to invalidate all outstanding parts of the package. ## 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-2746.md b/EIPS/eip-2746.md index fc006be63b0731..22aa20efbabfb9 100644 --- a/EIPS/eip-2746.md +++ b/EIPS/eip-2746.md @@ -3,7 +3,7 @@ eip: 2746 title: Rules Engine Standard author: Aaron Kendall (@jaerith), Juan Blanco (@juanfranblanco) discussions-to: https://ethereum-magicians.org/t/eip-2746-rules-engine-interface/4435 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-06-20 @@ -217,4 +217,4 @@ The deployer of the contract should be the owner and administrator, allowing for - [EIP-2535 Diamond Standard](./eip-2535.md) ## 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-2767.md b/EIPS/eip-2767.md index bec3ccc440b141..87ac14f0f4c6d7 100644 --- a/EIPS/eip-2767.md +++ b/EIPS/eip-2767.md @@ -3,7 +3,7 @@ eip: 2767 title: Contract Ownership Governance author: Soham Zemse (@zemse), Nick Mudge (@mudgen) discussions-to: https://github.com/ethereum/EIPs/issues/2766 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-07-04 @@ -120,4 +120,4 @@ In such implementations, community can create transaction proposals and vote on ## 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-2770.md b/EIPS/eip-2770.md index c8ba49abf93df8..db0b5d726f7a40 100644 --- a/EIPS/eip-2770.md +++ b/EIPS/eip-2770.md @@ -3,7 +3,7 @@ eip: 2770 title: Meta-Transactions Forwarder Contract author: Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh) discussions-to: https://ethereum-magicians.org/t/erc-2770-meta-transactions-forwarder-contract/5391 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-07-01 @@ -204,4 +204,4 @@ All contracts introducing support for the Forwarded requests thereby authorize t It is critical that this contract has no vulnerabilities or centralization issues. ## 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-2771.md b/EIPS/eip-2771.md index 0ea7c33f9fe18f..8fadf2340bfc89 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), Gavin John (@Pandapip1) discussions-to: https://ethereum-magicians.org/t/erc-2771-secure-protocol-for-native-meta-transactions/4488 -status: Draft +status: Final 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,54 @@ 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. + +`isTrustedForwarder` function MAY be called on-chain, and as such gas restrictions MUST be put in place. It SHOULD NOT consume more than 50,000 gas -Internally, the **Recipient** MUST then accept a request from forwarder +## Rationale -That function can be called on-chain and as such gas restriction needs to be -put in place. +* 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**. -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. +## 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,47 +129,12 @@ 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 -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-2780.md b/EIPS/eip-2780.md index e1b2c65ba0c348..3c75c91340940d 100644 --- a/EIPS/eip-2780.md +++ b/EIPS/eip-2780.md @@ -113,4 +113,4 @@ The table below captures the effect of this proposal on the savings multiplier i ## 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-2786.md b/EIPS/eip-2786.md index f192d335bef354..2c8d944931ee6c 100644 --- a/EIPS/eip-2786.md +++ b/EIPS/eip-2786.md @@ -4,7 +4,6 @@ title: Ethereum Provider Connect/Disconnect Events author: Micah Zoltu (@MicahZoltu), Erik Marks (@rekmarks) discussions-to: https://github.com/ethereum/EIPs/issues/2787 status: Withdrawn -review-period-end: 2020-07-31 type: Standards Track category: Interface created: 2020-07-15 @@ -59,7 +58,7 @@ The relationship between Ethereum Provider and client is a trusted one, where it ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). ## Appendix I: Examples diff --git a/EIPS/eip-2803.md b/EIPS/eip-2803.md index 0f1dc7ccbd1704..4804c8d5aefb0a 100644 --- a/EIPS/eip-2803.md +++ b/EIPS/eip-2803.md @@ -1,26 +1,24 @@ --- eip: 2803 title: Rich Transactions +description: Support 'rich transactions' by allowing transactions from externally owned accounts to execute bytecode directly. author: Micah Zoltu (@MicahZoltu) discussions-to: https://ethereum-magicians.org/t/rich-transactions-via-evm-bytecode-execution-from-externally-owned-accounts/4025 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-07-18 --- -## Simple Summary -Support 'rich transactions' by allowing transactions from externally owned accounts to execute bytecode directly. - ## Abstract -If a transaction has a `to` of address `x`, then the `data` of the transaction will be treated as EVM bytecode and executed from the context of the `CALLER` of the transaction (aka: the transaction signer at the moment). +If a transaction has a `to` of address `x`, then the `data` of the transaction will be treated as EVM bytecode and it will be executed from the context of the `CALLER` of the transaction (aka: the transaction signer). ## Motivation Many Ethereum DApps presently require users to approve multiple transactions in order to produce one effect - for example, the common pattern of first approving a contract to spend a token, then calling that contract. This results in a poor user-experience, and complicates the experience of interacting with DApps. Making it possible for externally owned accounts to execute EVM bytecode directly allows a single transaction to execute multiple contract calls, allowing DApps to provide a streamlined experience, where every interaction results in at most one transaction. -While this is in principle possible today using contract wallets, other UX issues, such as the need to fund a sending account with gas money, lack of support for contract wallets in browser integrations, and lack of a consistent API for contract wallets has led to poor adoption of these. We propose this EIP as a way of enhancing the utility of existing EOAs, in the spirit of "don't let the perfect be the enemy of the good". +While this is in principle possible today using contract wallets, other UX issues, such as the need to fund a sending account with gas money, lack of support for contract wallets in browser integrations, and lack of a consistent API for contract wallets has led to poor adoption of these.This EIP is a way of enhancing the utility of existing EOAs, in the spirit of "don't let the perfect be the enemy of the good". ## Specification A new reserved address is specified at `x`, in the range used for precompiles. When a transaction is sent to this address from an externally owned account, the payload of the transaction is treated as EVM bytecode, and executed with the signer of the transaction as the current account. For clarity: @@ -30,10 +28,10 @@ A new reserved address is specified at `x`, in the range used for precompiles. W - `CALL` will set the `CALLER` to the EOA (not `x`). - `DELEGATECALL` preserves the EOA as the owning account. - The `CALLER` and `ORIGIN` opcodes both return the address of the EOA that signed the transaction. - - There is no code associated with the precompile address. `CODE*` and `EXTCODE*` opcodes do not return the transaction payload. + - There is no code associated with the precompile address. `CODE*` and `EXTCODE*` opcodes behave the same as they do for any empty address. - `CALLDATA*` opcodes operate on the transaction payload as expected. - `SLOAD` and `SSTORE` operate on the storage of the EOA. As a result, an EOA can have data in storage, that persists between transactions. - - The `SELFDESTRUCT` opcode transfers the balance of the EOA to the specified address, and at the end of the transaction zeroes out the account's state storage, but does not zero the account's nonce. No refund is applied for the `SELFDESTRUCT` at the end of the transaction. + - The `SELFDESTRUCT` opcode does nothing. - All other opcodes behave as expected for a call to a contract address. - The transaction is invalid if there is any value attached. - A call to the precompile address from a contract has no special effect and is equivalent to a call to a nonexistent precompile or an empty address. @@ -48,11 +46,5 @@ This EIP introduces a new feature that will need to be implemented in a future h Contracts or DApps that assume that an EOA cannot atomically perform multiple operations may be affected by this change, as this now makes it possible for EOAs to execute multiple atomic operations together. The authors do not believe this is a significant use-case, as this 'protection' is already trivially defeated by miners. -## Test Cases -TBD. - -## Implementation -None yet. - ## 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-2831.md b/EIPS/eip-2831.md index 04208503a94f81..3a07fba8b28848 100644 --- a/EIPS/eip-2831.md +++ b/EIPS/eip-2831.md @@ -3,7 +3,7 @@ eip: 2831 title: Transaction Replacement Message Type author: Gregory Markou (@GregTheGreek) discussions-to: https://ethereum-magicians.org/t/eip-2831-transaction-replacement-message-type/4448 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-07-26 @@ -126,7 +126,7 @@ None at the current time. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0](../LICENSE.md). ## Appendix I: Examples diff --git a/EIPS/eip-2844.md b/EIPS/eip-2844.md index 9edc9c64eed582..6546ba3f79bc65 100644 --- a/EIPS/eip-2844.md +++ b/EIPS/eip-2844.md @@ -3,7 +3,7 @@ eip: 2844 title: Add DID related methods to the JSON-RPC author: Joel Thorstensson (@oed) discussions-to: https://github.com/ethereum/EIPs/issues/2845 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-08-01 @@ -129,4 +129,4 @@ Both JOSE and DIDs are standards that have gone though a lot of scrutiny. Their The main security consideration of this EIP is the suggested permission system. Here various threat models could be considered. However, this EIP does not go into details about how it should work other than suggesting an approach. In the end it is up to wallet implementations to choose how to ask their users for consent. ## 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-2848.md b/EIPS/eip-2848.md index a71b4fc14116c0..5212f677e59255 100644 --- a/EIPS/eip-2848.md +++ b/EIPS/eip-2848.md @@ -3,7 +3,7 @@ eip: 2848 title: My Own Messages (MOM) author: Giuseppe Bertone (@Neurone) discussions-to: https://github.com/InternetOfPeers/EIPs/issues/1 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-08-02 @@ -17,9 +17,9 @@ My Own Messages (MOM) is a standard to create your very own public, always updat My Own Messages (MOM) use Ethereum as a certification layer for commands and multihash of your messages. It don't use smart contracts but simple self-send transactions with specific payload attached. -## Motivation +To ge more insights, you can test a [live client](http://internetofpeers.org/mom-client/), watch a [full video overview and demo](https://www.youtube.com/watch?v=z1SnoQkQYkU) and read a [brief presentation](../assets/eip-2848/presentation.pdf). -_Note: contents are all here but I'm trying to improve this section to be clearer_ +## Motivation As a _developer_ or _pool's owner_, I'd like to send messages to my users in a decentralized way. They must be able to easily verify my role in the smart contract context (owner, user, and so on) and they must be able to do it without relying on external, insecure and hackable social media sites (Facebook, Twitter, you name it). Also, I'd like to read messages from my userbase, in the same secure and verifiable manner. @@ -30,6 +30,7 @@ As an _explorer service_, I want to give my users an effective way to read infor And in _any role_, I want a method that does not allow scams - transactions without values, no smart contract's address to remember or to fake - and it does not allow spam - it's cheap but not free, and even if you can link/refer other accounts, you cannot send them messages directly, and others must explicitly follow and listen to your transactions if they want to read your messages. Main advantages: + - You can send messages to users of your ÐApp or Smart Contract, and they always know it is a voice reliable as the smart contract is. - Create your Ethereum account dedicated to your personal messages, say something only once and it can be seen on every social platform (no more reply of the same post/opinion on dozens of sites like Reddit, Twitter, Facebook, Medium, Disqus, and so on...) - Small fee to be free: pay just few cents of dollar to notarize your messages, and distribute them with IPFS, Swarm or any other storage you prefer. Because the multihash of the content is notarized, you can always check the integrity of the message you download even from centralized storage services. @@ -56,6 +57,7 @@ Clients **MAY** let users choose to parse messages considering other content typ It's **RECOMMENDED** that clients inform users about the actual setting of the default content type. ### MOM transactions + Clients **MUST** assume that **invalid MOM transactions don't exist**. If a transaction does not strictly follow the MOM standard, clients **MUST** ignore it and they **MUST NOT** consider it a MOM transaction at all. Because there can be security implications parsing data sent by users, clients **SHOULD NOT** try to keep track or interpret transactions as _invalid_ MOM transactions. @@ -124,9 +126,10 @@ The objective is also to avoid in the first place any kind of scam and malicious ### Why not using a smart contract? MOM wants to be useful, easy to implement and read, error proof, fast and cheap, but: + - using a smart contract for messages can leads more easily to errors and misunderstandings: - - address of the contract can be wrong - - smart contract must be deployed on that specific network to send messages + - address of the contract can be wrong + - smart contract must be deployed on that specific network to send messages - executing a smart contract costs much more than sending transactions - executing a smart contract just to store static data is the best example of an anti-pattern (expensive and almost useless) @@ -167,7 +170,8 @@ You can use the latest version of MOM client directly via [GitHub Pages](https:/ ## Implementation You can use an already working MOM JavaScript package on [GitHub Packages](https://github.com/InternetOfPeers/mom-js/packages/323930) or [npmjs](https://www.npmjs.com/package/@internetofpeers/mom-js). The package is already used by the MOM client above, and you can use it in your ÐApps too with: -``` + +```bash npm install @internetofpeers/mom-js ``` @@ -180,6 +184,7 @@ MOM is very simple and it has no real security concerns by itself. The standard The only concerns can come from the payload, but it is more related to the client and not to the standard itself, so here you can find some security suggestions related to clients implementing the standard. ### Parsing commands + MOM standard involves parsing payloads generated by potentially malicious clients, so attention must be made to avoid unwanted code execution. - Strictly follow only the standard codes @@ -187,9 +192,11 @@ MOM standard involves parsing payloads generated by potentially malicious client - Ignore malformed transactions (transactions that don't strictly follow the rules) ### Messages + Default content-type of a message following the MOM standard is Markdown text in UTF8 without BOM. It is highly recommended to disallow the reading of any not-text content-type, unless expressly acknowledged by the user. Because content multihash is always stored into the chain, clients can download that content from Content Addressable Network (like IPFS or Swarm) or from central servers. In the latter case, a client should always check the integrity of the received messages, or it must warn the user if it cannot do that (feature not implemented or in error). ## 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-2876.md b/EIPS/eip-2876.md index 3db042e096ed9a..e2b55e433260fb 100644 --- a/EIPS/eip-2876.md +++ b/EIPS/eip-2876.md @@ -3,7 +3,7 @@ eip: 2876 title: Deposit contract and address standard author: Jonathan Underwood (@junderw) discussions-to: https://github.com/junderw/deposit-contract-poc/issues/1 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-08-13 @@ -181,4 +181,4 @@ In general, contracts that implement the contract interface should forward funds To prevent problems with deposits being sent after the parent application is shut down, a contract SHOULD have a kill switch that will revert all calls to deposit(bytes8) rather than using `selfdestruct(address)` (since users who deposit will still succeed, since an external account will receive value regardless of the calldata, and essentially the self-destructed contract would become a black hole for any new deposits) ## 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-2917.md b/EIPS/eip-2917.md index 84e9b4d33e9f76..cc88c591047469 100644 --- a/EIPS/eip-2917.md +++ b/EIPS/eip-2917.md @@ -3,7 +3,7 @@ eip: 2917 title: Staking Reward Calculation author: Tony Carson , Mehmet Sabir Kiraz , Süleyman Kardaş discussions-to: https://github.com/ethereum/EIPs/issues/2925 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-08-28 @@ -162,4 +162,4 @@ The implementation code is on the github: TBD ## 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-2926.md b/EIPS/eip-2926.md index 70aa5b1e3f4b19..bb1562d5a56cc7 100644 --- a/EIPS/eip-2926.md +++ b/EIPS/eip-2926.md @@ -3,7 +3,7 @@ eip: 2926 title: Chunk-Based Code Merkleization author: Sina Mahmoodi (@s1na), Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2926-chunk-based-code-merkleization/4555 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-08-25 @@ -158,4 +158,4 @@ The implementation of the chunking and merkleization logic in Typescript can be 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-2929.md b/EIPS/eip-2929.md index e2940a9567017e..0e11417265a623 100644 --- a/EIPS/eip-2929.md +++ b/EIPS/eip-2929.md @@ -37,7 +37,7 @@ In the further future, there are similar benefits in the case of SNARK/STARK wit | Constant | Value | | - | - | -| `FORK_BLOCK` | TBD | +| `FORK_BLOCK` | 12244000 | | `COLD_SLOAD_COST` | 2100 | | `COLD_ACCOUNT_ACCESS_COST` | 2600 | | `WARM_STORAGE_READ_COST` | 100 | @@ -169,4 +169,4 @@ But there are ways to further expand the usability of this pattern. One possibil Another option is [EIP-2930](./eip-2930.md), which would have a similar effect to `POKE` but is more general: it also works for the EOA -> contract -> contract case, and generally should work for all known cases of breakage due to gas cost increases. This option is more complex, though it is arguably a stepping stone toward access lists being used for other use cases (regenesis, account abstraction, SSA all demand access lists). ## 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-2930.md b/EIPS/eip-2930.md index bb507969989b0a..c9db53a936c5c9 100644 --- a/EIPS/eip-2930.md +++ b/EIPS/eip-2930.md @@ -16,9 +16,9 @@ Adds a transaction type which contains an access list, a list of addresses and s ## Abstract -We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, access_list, yParity, senderR, senderS])`. +We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS])`. -The `access_list` specifies a list of addresses and storage keys; these addresses and storage keys are added into the `accessed_addresses` and `accessed_storage_keys` global sets (introduced in [EIP-2929](./eip-2929.md)). A gas cost is charged, though at a discount relative to the cost of accessing outside the list. +The `accessList` specifies a list of addresses and storage keys; these addresses and storage keys are added into the `accessed_addresses` and `accessed_storage_keys` global sets (introduced in [EIP-2929](./eip-2929.md)). A gas cost is charged, though at a discount relative to the cost of accessing outside the list. ## Motivation @@ -42,19 +42,19 @@ This EIP serves two functions: | Constant | Value | | - | - | -| `FORK_BLOCK` | TBD | +| `FORK_BLOCK` | 12244000 | | `ACCESS_LIST_STORAGE_KEY_COST` | 1900 | | `ACCESS_LIST_ADDRESS_COST` | 2400 | As of `FORK_BLOCK_NUMBER`, a new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` `1`. -The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, access_list, yParity, senderR, senderS])`. +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS])`. -The `yParity, senderR, senderS` elements of this transaction represent a secp256k1 signature over `keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, access_list]))`. +The `signatureYParity, signatureR, signatureS` elements of this transaction represent a secp256k1 signature over `keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList]))`. The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulativeGasUsed, logsBloom, logs])`. -For the transaction to be valid, `access_list` must be of type `[[{20 bytes}, [{32 bytes}...]]...]`, where `...` means "zero or more of the thing to the left". For example, the following is a valid access list (all hex strings would in reality be in byte representation): +For the transaction to be valid, `accessList` must be of type `[[{20 bytes}, [{32 bytes}...]]...]`, where `...` means "zero or more of the thing to the left". For example, the following is a valid access list (all hex strings would in reality be in byte representation): ``` [ @@ -72,7 +72,7 @@ For the transaction to be valid, `access_list` must be of type `[[{20 bytes}, [{ ] ``` -At the beginning of execution (ie. at the same time as the `21000 + 4 * zeroes + 12 * nonzeroes` start gas is charged), we charge additional gas for the access list: `ACCESS_LIST_ADDRESS_COST` gas per address and `ACCESS_LIST_STORAGE_KEY_COST` gas per storage key. For example, the above example would be charged `ACCESS_LIST_ADDRESS_COST * 2 + ACCESS_LIST_STORAGE_KEY_COST * 2` gas. +At the beginning of execution (ie. at the same time as the `21000 + 4 * zeroes + 16 * nonzeroes` start gas is charged according to [EIP-2028](./eip-2028.md) rules), we charge additional gas for the access list: `ACCESS_LIST_ADDRESS_COST` gas per address and `ACCESS_LIST_STORAGE_KEY_COST` gas per storage key. For example, the above example would be charged `ACCESS_LIST_ADDRESS_COST * 2 + ACCESS_LIST_STORAGE_KEY_COST * 2` gas. Note that non-unique addresses and storage keys are not disallowed, though they will be charged for multiple times, and aside from the higher gas cost there is no other difference in execution flow or outcome from multiple-inclusion of a value as opposed to the recommended single-inclusion. @@ -137,4 +137,4 @@ However, this EIP proposes only a 10% initial discount to access lists, so there Average block size will increase as a result of access lists being used. However, the per-byte cost of access lists is `1900 / 32 = 59.375` for storage keys and `2400 / 20 = 120` for addresses, making it much more expensive than calldata; hence, worst-case block size will not increase. Additionally, increases in average block size will be partially compensated for by the ability to pre-fetch storage at time of receiving a transaction and/or load storage in parallel upon receiving a block. ## 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-2935.md b/EIPS/eip-2935.md index 22383207384d50..bbe12f21b04e06 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -3,7 +3,7 @@ eip: 2935 title: Save historical block hashes in state author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak) discussions-to: https://ethereum-magicians.org/t/eip-2935-save-historical-block-hashes-in-state/4565 -status: Review +status: Stagnant type: Standards Track category: Core created: 2020-09-03 @@ -60,4 +60,4 @@ Adding ~2.5 million storage slots per year bloats the state somewhat (but not mu ## 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-2936.md b/EIPS/eip-2936.md index 92f5e18ccf1170..daad2d0bf28d4b 100644 --- a/EIPS/eip-2936.md +++ b/EIPS/eip-2936.md @@ -3,7 +3,7 @@ eip: 2936 title: EXTCLEAR Opcode For SELFDESTRUCTed contracts author: William Morriss (@wjmelements) discussions-to: https://ethereum-magicians.org/t/eip-2936-extclear-for-selfdestruct/4569 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-09-03 @@ -57,4 +57,4 @@ Implementation is required on all major clients to add the opcode. A reincarnated contract that does not expect its state to be cleared by malicious actors SHOULD reinitialize itself to avoid antagonistic `EXTCLEAR`. ## 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-2937.md b/EIPS/eip-2937.md index 17e79f2f540a4a..4122d7c7d02fc7 100644 --- a/EIPS/eip-2937.md +++ b/EIPS/eip-2937.md @@ -3,7 +3,7 @@ eip: 2937 title: SET_INDESTRUCTIBLE opcode author: Vitalik Buterin (@vbuterin) discussions-to: https://ethereum-magicians.org/t/eip-2937-set-indestructible/4571 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-09-04 @@ -45,4 +45,4 @@ This breaks forward compatibility with _some_ forms of state rent, which would s If `SELFDESTRUCT` is ever removed in the future, this EIP would simply become a no-op. ## 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-2938.md b/EIPS/eip-2938.md index 41227957856c1a..2d208f4a2c87b5 100644 --- a/EIPS/eip-2938.md +++ b/EIPS/eip-2938.md @@ -3,7 +3,7 @@ eip: 2938 title: Account Abstraction author: Vitalik Buterin (@vbuterin), Ansgar Dietrichs (@adietrichs), Matt Garnett (@lightclient), Will Villanueva (@villanuevawill), Sam Wilson (@SamWilsn) discussions-to: https://ethereum-magicians.org/t/eip-2938-account-abstraction/4630 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-09-04 @@ -284,4 +284,4 @@ There are, however, cases where an attacker can publish a transaction that inval Denial-of-Service attacks are difficult to defend against, due to the difficulty in identifying sybils within a peer list. At any moment, one may decide (or be bribed) to initiate an attack. This is not a problem that Account Abstraction introduces. It can be accomplished against existing clients today by inundating a target with transactions whose signatures are invalid. However, due to the increased allotment of validation work allowed by AA, it's important to bound the amount of computation an adversary can force a client to expend with invalid transactions. For this reason, it's best for the miner to follow the recommended mining strategies. ## 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-2942.md b/EIPS/eip-2942.md index 3a3df32b728f44..ea2ec7e3fdb579 100644 --- a/EIPS/eip-2942.md +++ b/EIPS/eip-2942.md @@ -3,7 +3,7 @@ eip: 2942 title: EthPM URI Specification author: Nick Gheorghita (@njgheorghita), Piper Merriam (@pipermerriam), g. nicholas d'andrea (@gnidan), Benjamin Hauser (@iamdefinitelyahuman) discussions-to: https://ethereum-magicians.org/t/ethpm-v3-specification-working-group/4086/7 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-09-04 @@ -63,4 +63,4 @@ The EthPM URI scheme has been implemented in the following libraries: In most cases, an EthPM URI points to an immutable asset, giving full security that the target asset has not been modified. However, in the case where an EthPM URI uses an ENS name as its registry address, it is possible that the ENS name has been redirected to a new registry, in which case the guarantee of immutability no longer exists. ## 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-2970.md b/EIPS/eip-2970.md index 92f2463f791933..0d58fe4d4ffe9a 100644 --- a/EIPS/eip-2970.md +++ b/EIPS/eip-2970.md @@ -3,7 +3,7 @@ eip: 2970 title: IS_STATIC opcode author: Vitalik Buterin (@vbuterin) discussions-to: https://ethereum-magicians.org/t/is-static-opcode-useful-for-aa/4609 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-09-13 @@ -36,4 +36,4 @@ TBD TBD ## 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-2972.md b/EIPS/eip-2972.md index 4fd3ef616145ec..0db0c868d1bac8 100644 --- a/EIPS/eip-2972.md +++ b/EIPS/eip-2972.md @@ -124,4 +124,4 @@ Luckily, [EIP-2718](./eip-2718.md) also excludes transaction types `0xc0` to `0x A signature for these transaction types **does** collide with legacy transactions, but the transactions will be processed the same so it doesn't matter if the transaction ends up included as a legacy transaction or a typed transaction. ## 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-2976.md b/EIPS/eip-2976.md index 04ad6dae9117c6..6f8056f967d75a 100644 --- a/EIPS/eip-2976.md +++ b/EIPS/eip-2976.md @@ -1,18 +1,16 @@ --- eip: 2976 -title: 'Typed Transactions over Gossip' +title: Typed Transactions over Gossip +description: Adds support for transmission of typed transactions over devp2p. author: Micah Zoltu (@MicahZoltu) discussions-to: https://ethereum-magicians.org/t/eip-2976-eth-typed-transactions-over-gossip/4610 -status: Review +status: Final type: Standards Track category: Networking created: 2020-09-13 requires: 2718 --- -## Simple Summary -Adds support for transmission of typed transactions over devp2p. - ## Abstract [Typed Transactions](./eip-2718.md) can be sent over devp2p as `TransactionType || TransactionPayload`. The exact contents of the `TransactionPayload` are defined by the `TransactionType` in future EIPs, and clients may start supporting their gossip without incrementing the devp2p version. @@ -96,4 +94,4 @@ If a client chooses to ignore the **SHOULD** recommendation for disconnecting pe Ignoring this recommendation should be limited to trusted peers only, or other situations where the risk of DoS is extremely low. ## 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-2980.md b/EIPS/eip-2980.md index 459c34858e6f1d..ec74e0e5fcf248 100644 --- a/EIPS/eip-2980.md +++ b/EIPS/eip-2980.md @@ -1,19 +1,16 @@ --- eip: 2980 title: Swiss Compliant Asset Token +description: An interface for asset tokens, compliant with Swiss Law and compatible with [ERC-20](./eip-20.md). author: Gianluca Perletti (@Perlets9), Alan Scarpellini (@alanscarpellini), Roberto Gorini (@robertogorini), Manuel Olivi (@manvel79) discussions-to: https://github.com/ethereum/EIPs/issues/2983 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-09-08 requires: 20 --- -## Simple Summary - -An interface for asset tokens, compliant with Swiss Law and compatible with [ERC-20](./eip-20.md). - ## Abstract This new standard is an [ERC-20](./eip-20.md) compatible token with restrictions that comply with the following Swiss laws: the [Stock Exchange Act](../assets/eip-2980/Swiss-Confederation-SESTA.pdf), the [Banking Act](../assets/eip-2980/Swiss-Confederation-BA.pdf), the [Financial Market Infrastructure Act](../assets/eip-2980/Swiss-Confederation-FMIA.pdf), the [Act on Collective Investment Schemes](../assets/eip-2980/Swiss-Confederation-CISA.pdf) and the [Anti-Money Laundering Act](../assets/eip-2980/Swiss-Confederation-AMLA.pdf). The [Financial Services Act](../assets/eip-2980/Swiss-Confederation-FINSA.pdf) and the [Financial Institutions Act](../assets/eip-2980/Swiss-Confederation-FINIA.pdf) must also be considered. The solution achieved meet also the European jurisdiction. @@ -191,14 +188,10 @@ This EIP does not introduce backward incompatibilities and is backward compatibl This standard allows the implementation of ERC-20 functions transfer, transferFrom, approve and allowance alongside to make a token fully compatible with ERC-20. The token MAY implement decimals() for backward compatibility with ERC-20. If implemented, it MUST always return 0. -## Implementations - -Although an official implementation of the ERC-2980 has not been presented, in the Specifications section some interfaces have been reported which, once implemented correctly and according to the reccomandations, allow to create an asset token compliant with this ERC. - ## Security Considerations The security considerations mainly concern the role played by the Issuers. This figure, in fact, is not generally present in common ERC-20 tokens but has very powerful rights that allow him to move tokens without being in possession and freeze other addresses, preventing them from transferring tokens. It must be the responsibility of the owner to ensure that the addresses that receive this charge remain in possession of it only for the time for which they have been designated to do so, thus preventing any abuse. ## 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-2981.md b/EIPS/eip-2981.md index 5c4ad3ed6b0493..a24821bd229ed5 100644 --- a/EIPS/eip-2981.md +++ b/EIPS/eip-2981.md @@ -1,9 +1,9 @@ --- eip: 2981 -title: ERC-721 Royalty Standard +title: NFT Royalty Standard author: Zach Burks (@vexycats), James Morgan (@jamesmorgan), Blaine Malone (@blmalone), James Seibel (@seibelj) discussions-to: https://github.com/ethereum/EIPs/issues/2907 -status: Draft +status: Final type: Standards Track category: ERC created: 2020-09-15 @@ -12,24 +12,33 @@ requires: 165 ## Simple Summary -A standardized way to retrieve royalty payment information for ERC-721 tokens to enable universal support for royalty payments in all NFT marketplaces and ecosystem participants. +A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal support for royalty payments across all NFT marketplaces and ecosystem participants. ## Abstract -This standard extends the [ERC-721 specification](./eip-721.md) to enable setting a royalty amount paid to the NFT creator or rights holder every time an NFT is sold and re-sold. This is intended for NFT marketplaces that want to support the ongoing funding of artists and other NFT creators. The royalty payment must be voluntary as required by the EIP-721 standard, as `transferFrom()` includes NFT transfers between wallets, and executing `transferFrom()` does not always imply a sale occurred. Marketplaces and individuals implement this standard by retrieving the royalty payment information with `royaltyInfo()`, which specifies how much to pay to which address for a given sale price. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. This ERC should be considered a minimal, gas-efficient building block for further innovation in NFT royalty payments. +This standard allows contracts, such as NFTs that support [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) interfaces, to signal a royalty amount to be paid to the NFT creator or rights holder every time the NFT is sold or re-sold. This is intended for NFT marketplaces that want to support the ongoing funding of artists and other NFT creators. The royalty payment must be voluntary, as transfer mechanisms such as `transferFrom()` include NFT transfers between wallets, and executing them does not always imply a sale occurred. Marketplaces and individuals implement this standard by retrieving the royalty payment information with `royaltyInfo()`, which specifies how much to pay to which address for a given sale price. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. This ERC should be considered a minimal, gas-efficient building block for further innovation in NFT royalty payments. ## Motivation -There are many marketplaces for NFTs with multiple unique royalty payment implementations that are not easily compatible or usable by other marketplaces. Just like the early days of ERC-20 tokens, ERC-721 marketplace smart contracts are varied by ecosystem and not standardized. This EIP enables all marketplaces to retrieve royalty payment information that NFT creators specify and are entitled to, enabling accurate royalty payments regardless of which marketplace the NFT is sold or re-sold at. +There are many marketplaces for NFTs with multiple unique royalty payment implementations that are not easily compatible or usable by other marketplaces. Just like the early days of ERC-20 tokens, NFT marketplace smart contracts are varied by ecosystem and not standardized. This EIP enables all marketplaces to retrieve royalty payment information for a given NFT. This enables accurate royalty payments regardless of which marketplace the NFT is sold or re-sold at. -Many of the largest ERC-721 marketplaces have implemented royalty payments that are incompatible with other platforms and therefore make it much harder to enforce when the NFT is sold on another marketplace, not fulfilling the potential of any implemented royalty system. This standard implements standardized royalty information retrieval that can be accepted across any type of NFT marketplace. This minimalist proposal leaves the actual funds transfer up to the marketplace itself, and only provides a mechanism to fetch the royalty amounts. Future EIPs may build on this ERC to implement payment notifications and other features. +Many of the largest NFT marketplaces have implemented bespoke royalty payment solutions that are incompatible with other marketplaces. This standard implements standardized royalty information retrieval that can be accepted across any type of NFT marketplace. This minimalist proposal only provides a mechanism to fetch the royalty amount and recipient. The actual funds transfer is something which the marketplace should execute. -This standard extends the [ERC-721 specification](./eip-721.md) to enable setting a royalty amount paid to the NFT creator or rights holder every time an NFT is sold and re-sold. If a marketplace chooses *not* to implement this EIP, then obviously no funds are paid for secondary sales. But as most NFT marketplaces have developed some unique royalty system themselves - and all of them are singular and only work within their own contracts - there should be an accepted standard for royalty payment information (if the creator chooses to set royalties on their NFTs). We believe the NFT marketplace ecosystem will voluntarily implement royalty payments to provide ongoing funding for artists and other creators, and NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions. +This standard allows NFTs that support [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) interfaces, to have a standardized way of signalling royalty information. More specifically, these contracts can now calculate a royalty amount to provide to the rightful recipient. -Without an agreed royalty payment standard, the NFT ecosystem will lack an effective means to collect royalties across all marketplaces and artists and other creators will not receive ongoing funding. This will hamper the growth and adoption of NFTs and demotivate artists and other NFT creators from minting new and innovative tokens. +Royalty amounts are always a percentage of the sale price. If a marketplace chooses *not* to implement this EIP, then no funds will be paid for secondary sales. It is believed that the NFT marketplace ecosystem will voluntarily implement this royalty payment standard; in a bid to provide ongoing funding for artists/creators. NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions. -*"Yes we have royalties, but if your NFT is sold on another marketplace, we cannot provide royalties" ... "But can't I sell my NFT anywhere with a click of my wallet?" ... "Yes... but we don't have a standard for royalties so you'll lose out."* +Without an agreed royalty payment standard, the NFT ecosystem will lack an effective means to collect royalties across all marketplaces and artists and other creators will not receive ongoing funding. This will hamper the growth and adoption of NFTs and demotivate NFT creators from minting new and innovative tokens. -This EIP fixes this issue, enabling all NFT marketplaces to unify on a single royalty payment information standard and benefiting the entire NFT ecosystem. +Enabling all NFT marketplaces to unify on a single royalty payment standard will benefit the entire NFT ecosystem. + +While this standard focuses on NFTs and compatibility with the ERC-721 and ERC-1155 standards, EIP-2981 does not require compatibility with ERC-721 and ERC-1155 standards. Any other contract could integrate with EIP-2981 to return royalty payment information. ERC-2981 is, therefore, a universal royalty standard for many asset types. + +At a glance, here's an example conversation summarizing NFT royalty payments today: + +>**Artist**: "Do you support royalty payments on your platform?" +>**Marketplace**: "Yes we have royalty payments, but if your NFT is sold on another marketplace then we cannot enforce this payment." +>**Artist**: "What about other marketplaces that support royalties, don't you share my royalty information to make this work?" +>**Marketplace**: "No, we do not share royalty information." ## Specification @@ -38,52 +47,65 @@ NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. -**ERC-721 compliant contracts MAY implement this ERC for royalties to provide a standard method of specifying royalty payment information.** +**ERC-721 and ERC-1155 compliant contracts MAY implement this ERC for royalties to provide a standard method of specifying royalty payment information.** Marketplaces that support this standard **SHOULD** implement some method of transferring royalties to the royalty recipient. Standards for the actual transfer and notification of funds will be specified in future EIPs. -Implementers of this standard **MUST NOT** use the `_data` parameter and `_royaltyPaymentData` return value. These are intended for future EIPs that extend this ERC with additional features. +Marketplaces **MUST** pay the royalty in the same unit of exchange as that of the `_salePrice` passed to `royaltyInfo()`. This is equivalent to saying that the `_salePrice` parameter and the `royaltyAmount` return value **MUST** be denominated in the same monetary unit. For example, if the sale price is in ETH, then the royalty payment must also be paid in ETH, and if the sale price is in USDC, then the royalty payment must also be paid in USDC. + +Implementers of this standard **MUST** calculate a percentage of the `_salePrice` when calculating the royalty amount. Subsequent invocations of `royaltyInfo()` **MAY** return a different `royaltyAmount`. Though there are some important considerations for implementers if they choose to perform different percentage calculations between `royaltyInfo()` invocations. + +The `royaltyInfo()` function is not aware of the unit of exchange for the sale and royalty payment. With that in mind, implementers **MUST NOT** return a fixed/constant `royaltyAmount`, wherein they're ignoring the `_salePrice`. For the same reason, implementers **MUST NOT** determine the `royaltyAmount` based on comparing the `_salePrice` with constant numbers. In both cases, the `royaltyInfo()` function makes assumptions on the unit of exchange, which **MUST** be avoided. + +The percentage value used must be independent of the sale price for reasons previously mentioned (i.e. if the percentage value 10%, then 10% **MUST** apply whether `_salePrice` is 10, 10000 or 1234567890). If the royalty fee calculation results in a remainder, implementers **MAY** round up or round down to the nearest integer. For example, if the royalty fee is 10% and `_salePrice` is 999, the implementer can return either 99 or 100 for `royaltyAmount`, both are valid. + +The implementer **MAY** choose to change the percentage value based on other predictable variables that do not make assumptions about the unit of exchange. For example, the percentage value may drop linearly over time. An approach like this **SHOULD NOT** be based on variables that are unpredictable like `block.timestamp`, but instead on other more predictable state changes. One more reasonable approach **MAY** use the number of transfers of an NFT to decide which percentage value is used to calculate the `royaltyAmount`. The idea being that the percentage value could decrease after each transfer of the NFT. Another example could be using a different percentage value for each unique `_tokenId`. + +Marketplaces that support this standard **SHOULD NOT** send a zero-value transaction if the `royaltyAmount` returned is `0`. This would waste gas and serves no useful purpose in this EIP. + +Marketplaces that support this standard **MUST** pay royalties no matter where the sale occurred or in what currency, including on-chain sales, over-the-counter (OTC) sales and off-chain sales such as at auction houses. As royalty payments are voluntary, entities that respect this EIP must pay no matter where the sale occurred - a sale conducted outside of the blockchain is still a sale. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. Implementers of this standard **MUST** have all of the following functions: ```solidity pragma solidity ^0.6.0; -import "./ERC165.sol"; +import "./IERC165.sol"; /// -/// @dev Implementation of royalties for 721s +/// @dev Interface for the NFT Royalty Standard /// -interface IERC2981 is ERC165 { +interface IERC2981 is IERC165 { /// ERC165 bytes to add to interface array - set in parent contract /// implementing this standard /// - /// bytes4(keccak256("royaltyInfo(uint256,uint256,bytes)")) == 0x6057361d - /// bytes4 private constant _INTERFACE_ID_ERC721ROYALTIES = 0x6057361d; - /// _registerInterface(_INTERFACE_ID_ERC721ROYALTIES); + /// bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a + /// bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a; + /// _registerInterface(_INTERFACE_ID_ERC2981); /// @notice Called with the sale price to determine how much royalty // is owed and to whom. /// @param _tokenId - the NFT asset queried for royalty information - /// @param _value - the sale price of the NFT asset specified by _tokenId - /// @param _data - information used by extensions of this ERC. - /// Must not to be used by implementers of EIP-2981 - /// alone. - /// @return _receiver - address of who should be sent the royalty payment - /// @return _royaltyAmount - the royalty payment amount for _value sale price - /// @return _royaltyPaymentData - information used by extensions of this ERC. - /// Must not to be used by implementers of - /// EIP-2981 alone. - function royaltyInfo(uint256 _tokenId, uint256 _value, bytes calldata _data) external returns (address _receiver, uint256 _royaltyAmount, bytes memory _royaltyPaymentData); - - /// @notice Informs callers that this ERC721 supports ERC2981 - /// @dev If `_registerInterface(_INTERFACE_ID_ERC721ROYALTIES)` is called - /// in the initializer, this should be automatic + /// @param _salePrice - the sale price of the NFT asset specified by _tokenId + /// @return receiver - address of who should be sent the royalty payment + /// @return royaltyAmount - the royalty payment amount for _salePrice + function royaltyInfo( + uint256 _tokenId, + uint256 _salePrice + ) external view returns ( + address receiver, + uint256 royaltyAmount + ); +} + +interface IERC165 { + /// @notice Query if a contract implements an interface /// @param interfaceID The interface identifier, as specified in ERC-165 - /// @return `true` if the contract implements - /// `_INTERFACE_ID_ERC721ROYALTIES` and `false` otherwise + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceID) external view returns (bool); } - ``` ### Examples @@ -93,7 +115,7 @@ This standard being used on an ERC-721 during deployment: #### Deploying an ERC-721 and signaling support for ERC-2981 ```solidity -constructor (string memory name, string memory symbol, string memory baseURI) public Royalties(royalty_amount, msg.sender) { +constructor (string memory name, string memory symbol, string memory baseURI) { _name = name; _symbol = symbol; _setBaseURI(baseURI); @@ -101,18 +123,18 @@ constructor (string memory name, string memory symbol, string memory baseURI) p _registerInterface(_INTERFACE_ID_ERC721); _registerInterface(_INTERFACE_ID_ERC721_METADATA); _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); - // Royalties interface - _registerInterface(_INTERFACE_ID_ERC721ROYALTIES); + // Royalties interface + _registerInterface(_INTERFACE_ID_ERC2981); } ``` #### Checking if the NFT being sold on your marketplace implemented royalties -Note: using address.call() is completely **OPTIONAL** and is just one method. - ```solidity -function checkRoyalties(address _token) internal returns (bool) { - (bool success) = address(_token).call(abi.encodeWithSignature("royaltyInfo(uint256,uint256,bytes)")); +bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a; + +function checkRoyalties(address _contract) internal returns (bool) { + (bool success) = IERC165(_contract).supportsInterface(_INTERFACE_ID_ERC2981); return success; } ``` @@ -121,25 +143,36 @@ function checkRoyalties(address _token) internal returns (bool) { ### Optional royalty payments -It is impossible to know which NFT transfers are the result of sales, and which are merely wallets moving or consolidating their NFTs. Therefore, we cannot force every `transferFrom()` call to involve a royalty payment, as not every transfer is a sale that would require such payment. We believe the NFT marketplace ecosystem will voluntarily implement this royalty payment standard to provide ongoing funding for artists and other creators, and NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions. +It is impossible to know which NFT transfers are the result of sales, and which are merely wallets moving or consolidating their NFTs. Therefore, we cannot force every transfer function, such as `transferFrom()` in ERC-721, to involve a royalty payment as not every transfer is a sale that would require such payment. We believe the NFT marketplace ecosystem will voluntarily implement this royalty payment standard to provide ongoing funding for artists/creators. NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions. ### Simple royalty payments to a single address -This EIP does not specify the manner of payment to the royalty recipient. Furthermore, it is impossible to fully know and efficiently implement all possible types of royalty payments logic, so it is on the royalty payment receiver to implement all additional complexity and logic for fee splitting, multiple receivers, taxes, accounting, etc. in their own receiving contract or off-chain. If we attempted to do this as part of this standard, it would dramatically increase the implementation complexity, increase gas costs, and could not possibly cover every potential use-case. This ERC should be considered a minimal, gas-efficient building block for further innovation in NFT royalty payments. Future EIPs can specify more details regarding payment transfer and notification. +This EIP does not specify the manner of payment to the royalty recipient. Furthermore, it is impossible to fully know and efficiently implement all possible types of royalty payments logic. With that said, it is on the royalty payment receiver to implement all additional complexity and logic for fee splitting, multiple receivers, taxes, accounting, etc. in their own receiving contract or off-chain processes. Attempting to do this as part of this standard, it would dramatically increase the implementation complexity, increase gas costs, and could not possibly cover every potential use-case. This ERC should be considered a minimal, gas-efficient building block for further innovation in NFT royalty payments. Future EIPs can specify more details regarding payment transfer and notification. + +### Royalty payment percentage calculation + +This EIP mandates a percentage-based royalty fee model. It is likely that the most common case of percentage calculation will be where the `royaltyAmount` is always calculated from the `_salePrice` using a fixed percent i.e. if the royalty fee is 10%, then a 10% royalty fee must apply whether `_salePrice` is 10, 10000 or 1234567890. + +As previously mentioned, implementers can get creative with this percentage-based calculation but there are some important caveats to consider. Mainly, ensuring that the `royaltyInfo()` function is not aware of the unit of exchange and that unpredictable variables are avoided in the percentage calculation. To follow up on the earlier `block.timestamp` example, there is some nuance which can be highlighted if the following events ensued: + +1. Marketplace sells NFT. +2. Marketplace delays `X` days before invoking `royaltyInfo()` and sending payment. +3. Marketplace receives `Y` for `royaltyAmount` which was significantly different from the `royaltyAmount` amount that would've been calculated `X` days prior if no delay had occurred. +4. Royalty recipient is dissatisfied with the delay from the marketplace and for this reason, they raise a dispute. -### Royalty payment amounts over percentages +Rather than returning a percentage and letting the marketplace calculate the royalty amount based on the sale price, a `royaltyAmount` value is returned so there is no dispute with a marketplace over how much is owed for a given sale price. The royalty fee payer must pay the `royaltyAmount` that `royaltyInfo()` stipulates. -This EIP does not specify how the `_royaltyAmount` is calculated from the sale price `_value`. Many NFT contracts may follow a fixed-percentage royalty that is the same regardless of price. However, EIP-2981 implementers may choose whatever logic they want to calculate the `_royaltyAmount` - this EIP does not mandate a fixed-percentage fee model. +### Unit-less royalty payment across all marketplaces, both on-chain and off-chain -Additionally, we return a specific `_royaltyAmount` so there is no dispute with a marketplace over how much is owed for a given sale price. +This EIP does not specify a currency or token used for sales and royalty payments. The same percentage-based royalty fee must be paid regardless of what currency, or token was used in the sale, paid in the same currency or token. This applies to sales in any location including on-chain sales, over-the-counter (OTC) sales, and off-chain sales using fiat currency such as at auction houses. As royalty payments are voluntary, entities that respect this EIP must pay no matter where the sale occurred - a sale outside of the blockchain is still a sale. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. -### Unused data parameters in function signature +### Universal Royalty Payments -This EIP mandates the inclusion of the `_data` parameter and the `_royaltyPaymentData` return value, and also mandates that these are unused. The purpose of this is to have a seamless path to build on this ERC by further EIP standards that specify payment notifications, fee splitting, and other advanced use cases. +Although designed specifically with NFTs in mind, this standard does not require that a contract implementing EIP-2981 is compatible with either ERC-721 or ERC-1155 standards. Any other contract could use this interface to return royalty payment information, provided that it is able to uniquely identify assets within the constraints of the interface. ERC-2981 is, therefore, a universal royalty standard for many other asset types. ## Backwards Compatibility -This standard is completely compatible with current ERC-721 standards - in fact it requires it. +This standard is compatible with current ERC-721 and ERC-1155 standards. ## Security Considerations @@ -147,4 +180,4 @@ There are no security considerations related directly to the implementation of t ## 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-2982.md b/EIPS/eip-2982.md index 3deec8f744e4f9..eba2dd8153ff85 100644 --- a/EIPS/eip-2982.md +++ b/EIPS/eip-2982.md @@ -1,28 +1,25 @@ --- eip: 2982 title: Serenity Phase 0 +description: Phase 0 of the release schedule of Serenity, a series of updates to Ethereum a scalable, proof-of-stake consensus author: Danny Ryan (@djrtwo), Vitalik Buterin (@vbuterin) discussions-to: https://ethereum-magicians.org/t/serenity-phase-0-eip/4621 -status: Review +status: Final type: Informational created: 2020-09-15 --- -## Simple Summary - -Serenity, aka eth2, is the [long-planned](https://blog.ethereum.org/2015/03/03/ethereum-launch-process) upgrade of Ethereum to a scalable, proof-of-stake (PoS) consensus. Specifications, development, and releases are divided into phases to iteratively manage the complexity of the upgrade. This EIP addresses Phase 0 of the release schedule. - -Early phases of eth2 are executed without any breaking consensus changes on current Ethereum mainnet. This EIP serves to document the bootstrapping of this consensus mechanism and note the path for eth2 to supplant Ethereum's current proof-of-work (PoW) consensus. - ## Abstract This EIP specifies Phase 0 of Serenity (eth2), a multi-phased upgrade to the consensus mechanism for Ethereum mainnet. In Phase 0, the existing PoW chain and mechanics are entirely unaffected, while a PoS chain -- the beacon chain -- is built in parallel to serve as the core of the upgraded consensus. In subsequent phases, the beacon chain is enhanced to support and secure the consensus of a number of parallel shard chains, ultimately incorporating current Ethereum mainnet as one of those shards. -At the core of the beacon chain is a proof of stake consensus mechanism called Casper the Friendly Finality Gadget (FFG) and a fork-choice rule called Latest Message Driven Greedy Heaviest Observed Sub-Tree (LMD-GHOST). Phase 0 is centered primarily around the mechanics and incentives of validators executing these algorithms. The detailed specifications for eth2 are contained in an [independent repository](https://github.com/ethereum/eth2.0-specs) from this EIP, and safety and liveness proofs can be found in the [Combining GHOST and Casper](https://arxiv.org/abs/2003.03052) paper. To avoid duplication, this EIP just references relevant spec files and releases. +At the core of the beacon chain is a proof of stake consensus mechanism called Casper the Friendly Finality Gadget (FFG) and a fork-choice rule called Latest Message Driven Greedy Heaviest Observed Sub-Tree (LMD-GHOST). Phase 0 is centered primarily around the mechanics and incentives of validators executing these algorithms. The detailed specifications for eth2 are contained in an independent repository from this EIP, and safety and liveness proofs can be found in the [Combining GHOST and Casper](../assets/eip-2982/arxiv-2003.03052-Combining-GHOST-and-Casper.pdf) paper. To avoid duplication, this EIP just references relevant spec files and releases. + +Early phases of eth2 are executed without any breaking consensus changes on current Ethereum mainnet. This EIP serves to document the bootstrapping of this consensus mechanism and note the path for eth2 to supplant Ethereum's current proof-of-work (PoW) consensus. ## Motivation -Eth2 aims to fulfill the [original vision](https://blog.ethereum.org/2015/03/03/ethereum-launch-process/) of Ethereum to support an efficient, global-scale, general-purpose transactional platform while retaining high cryptoeconomic security and decentralization. +Eth2 aims to fulfill the original vision of Ethereum to support an efficient, global-scale, general-purpose transactional platform while retaining high cryptoeconomic security and decentralization. Today, Ethereum blocks are consistently full due to increasingly high demand for decentralized applications. Ever since the first serious spikes in adoption in 2017 (cryptokitties), the Ethereum community has consistently and vocally demanded scaling solutions. @@ -34,26 +31,23 @@ As the Ethereum network and the applications built upon it have seen increasing To provide more scale to Ethereum, while not inducing a restrictively high burden on both consumer and consensus nodes, eth2 introduces a "sharded" solution in which a number of blockchain shards -- each of similar capacity to Ethereum mainnet today -- run in parallel under a unified consensus mechanism. The core consensus (the beacon chain) and a small number of these shards can be processed via a single consumer machine, while the aggregate of the system provides much higher capacity. -See the [Ethereum wiki sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) for an excellent introduction and discussion of scaling through sharding. - ### Decentralization and economic finality through proof-of-stake -Since the [early days](https://blog.ethereum.org/2015/12/28/understanding-serenity-part-2-casper/) of Ethereum, proof-of-stake has been a long-awaited desideratum for the following: +Since the early days of Ethereum, proof-of-stake has been a long-awaited desideratum for the following: * Increased decentralization of the core consensus by lowering the barrier to entry and technical requirements of participation * Increased cryptoeconomic security via in-protocol penalties for misbehaviour and the addition of economic finality * Elimination of the energy hungry mining of the current PoW consensus mechanism -In addition to the above, PoS has synergies with the sharding scaling solution. Due to the random sampling requirement of sharding, PoS provides a more simple and direct access to the ["active validator set"](https://eth.wiki/sharding/Sharding-FAQs#how-do-you-actually-do-this-sampling-in-proof-of-work-and-in-proof-of-stake) than PoW and thus allows for a more direct sharded protocol construction. +In addition to the above, PoS has synergies with the sharding scaling solution. Due to the random sampling requirement of sharding, PoS provides a more simple and direct access to the "active validator set" than PoW and thus allows for a more direct sharded protocol construction. -See the [Ethereum wiki proof-of-stake FAQ](https://eth.wiki/en/concepts/proof-of-stake-faqs) for an excellent introduction and discussion of proof-of-stake consensus. ## Specification Phase 0 is designed to require _no breaking consensus changes_ to existing Ethereum mainnet. Instead, this is the bootstraping a new PoS consensus that can, once stable, supplant the current PoW consensus. -Phase 0 specifications are maintained in a [repository](https://github.com/ethereum/eth2.0-specs) independent of this EIP. `SPEC_RELEASE_VERSION` release (TBD) of the specs at `SPEC_RELEASE_COMMIT` are considered the canonical Phase 0 specs for this EIP. +Phase 0 specifications are maintained in a repository independent of this EIP. `SPEC_RELEASE_VERSION` release of the specs at `SPEC_RELEASE_COMMIT` are considered the canonical Phase 0 specs for this EIP. -This EIP provides a high level view on the Phase 0 mechanisms, especially those that are relevant to Ethereum mainnet (e.g. the deposit contract) and users (e.g. validator mechanics and eth2 issuance). The extended and low level details remain in the [specs repository](https://github.com/ethereum/eth2.0-specs). +This EIP provides a high level view on the Phase 0 mechanisms, especially those that are relevant to Ethereum mainnet (e.g. the deposit contract) and users (e.g. validator mechanics and eth2 issuance). The extended and low level details remain in the `consensus-specs` repository ### Parameters @@ -68,42 +62,31 @@ This EIP provides a high level view on the Phase 0 mechanisms, especially those | `PROPORTIONAL_SLASHING_MULTIPLIER` | `1` | | `MIN_SLASHING_PENALTY_QUOTIENT` | `2**7` (128) | -_Note:_ Eth2 has many more [Phase 0 configuration parameters](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/configs/mainnet/phase0.yaml) but the majority are left out of this EIP for brevity. +_Note:_ Eth2 has many more Phase 0 configuration parameters but the majority are left out of this EIP for brevity. ### Validator deposit contract -In Phase 0, eth2 uses a contract deployed on Ethereum mainnet -- the [Deposit Contract](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/deposit-contract.md) -- at `DEPOSIT_CONTRACT_ADDRESS` to onboard validators into the PoS consensus of the beacon chain. +In Phase 0, eth2 uses a contract deployed on Ethereum mainnet -- the Deposit Contract -- at `DEPOSIT_CONTRACT_ADDRESS` to onboard validators into the PoS consensus of the beacon chain. To participate in the PoS consensus, users submit validator deposits to the deposit contract. The beacon chain comes to consensus on the state of this contract and processes new validator deposits. This uni-directional deposit mechanism is the only technical link between the two components of the system (Ethereum mainnet and beacon chain) in Phase 0. -More on this mechanism: -* [Deposit Contract](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/deposit-contract.md) -* [Beacon Chain -- `Deposit` processing](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#deposits) -* [Validator -- Becoming a validator](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#becoming-a-validator) - ### Beacon chain and validator mechanics -Users who choose to participate in eth2 consensus deposit ETH collateral into the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/deposit-contract.md) in order to be inducted into the beacon chain validator set. From there, these validators are responsible for constructing the **beacon chain** (note that these consensus participants in PoS are akin to miners in PoW). +Users who choose to participate in eth2 consensus deposit ETH collateral into the deposit contract in order to be inducted into the beacon chain validator set. From there, these validators are responsible for constructing the **beacon chain** (note that these consensus participants in PoS are akin to miners in PoW). The beacon chain is a pure PoS chain that in Phase 0 is primarily concerned with maintaining its own consensus and managing the registry of validators. The consensus rules define _roles_ (e.g. block proposal, block attesting) that validators are expected to participate in; validators who perform their roles well are rewarded, and validators who perform their roles poorly or are offline are penalized. Phase 0 does not yet include any ETH transfer, sharding or smart contract / VM execution capabilities. In subsequent phases, additional mechanisms and validator responsibilities will be added to the beacon chain to manage the consensus of a number of parallel shard chains ("Phase 1"), to integrate the existing Ethereum system ("Phase 1.5") and to add full support for sharded smart contract execution ("Phase 2"). -More on the beacon chain and validator mechanics: -* [Beacon Chain -- State transition function](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function) -* [Beacon chain -- Operations](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#operations) -* [Beacon chain -- Rewards and Penalties](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#rewards-and-penalties-1) -* [Validator -- Beacon chain responsibilities](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/validator.md#beacon-chain-responsibilities) - ### Issuance To incentivize validators to deposit ether collateral and participate in the eth2 consensus, we propose that rewards (in the form of Ethereum's native asset, ether) be regularly issued to consensus participants. Due to the beacon chain operating in parallel to the existing PoW chain in early phases of eth2, this issuance is _in addition to_ any PoW rewards until the existing chain is merged into eth2 as a shard. -The amount of ether issued to validators on the beacon chain is proportional to the square root of the total ether deposited. This issuance curve was chosen as a more stable and sustainable curve to the two obvious alternatives -- fixed total issuance and fixed issuance per ether staked. For a more technical discussion on this choice see [here](https://github.com/ethereum/research/blob/master/papers/discouragement/discouragement.pdf). +The amount of ether issued to validators on the beacon chain is proportional to the square root of the total ether deposited. This issuance curve was chosen as a more stable and sustainable curve to the two obvious alternatives -- fixed total issuance and fixed issuance per ether staked. For a more technical discussion on this choice see [here](../assets/eip-2982/ef-Discouragement-Attacks.pdf). In eth2, this curve is parameterized by `BASE_REWARD_FACTOR` in the context of slot time and epoch length. Below is the issuance curve as a function of ether staked, along with a table of examples for illustration. Note, all figures shown are annualized. -![](https://storage.googleapis.com/ethereum-hackmd/upload_953c502d09928c0b306cc078268945c1.png) +![](../assets/eip-2982/2982-issuance.png) | Active Deposits | Max Annual Validator Reward\* | Max Annual ETH Issued\* | | -------- | -------: | --------: | @@ -119,11 +102,9 @@ In eth2, this curve is parameterized by `BASE_REWARD_FACTOR` in the context of s _\*Assuming validators are online 100% of the time and behaving optimally. Suboptimal validator behavior will lead to reduced rewards and/or penalties that reduce total issuance._ -For more details, see this [eth2 calculator](https://docs.google.com/spreadsheets/d/15tmPOvOgi3wKxJw7KQJKoUe-uonbYR6HF7u83LR5Mj4/edit#gid=842896204) maintained by _ConsenSys Activate_. - ### Initial punitive parameters -For PoS protocols to be crypto-economically secure, in-protocol penalties are required (see discussion of ["Nothing at Stake"](https://blog.ethereum.org/2014/11/25/proof-stake-learned-love-weak-subjectivity/)). Small offline penalties incentivize validator liveness, whereas (potentially) much larger penalties provide protocol security in the event of tail-risk scenarios. +For PoS protocols to be crypto-economically secure, in-protocol penalties are required. Small offline penalties incentivize validator liveness, whereas (potentially) much larger penalties provide protocol security in the event of tail-risk scenarios. Specifically, the following significant penalties exist: * **Inactivity Leak**: an offline penalty that increases each epoch is applied to validators during extended times of no finality (e.g. if one-third or more are offline or not on the canonical chain). This ensures the chain can eventually regain finality even under catastrophic conditions. @@ -137,58 +118,326 @@ _`PROPORTIONAL_SLASHING_MULTIPLIER` is configured initially to one-third of its _`MIN_SLASHING_PENALTY_QUOTIENT` configured initially to four times its final value_. This results in a lower guaranteed minimum penalty for a slashable offense and thus reduces the baseline punitive incentive to keep an individual validator's system secure. As with `PROPORTIONAL_SLASHING_MULTIPLIER`, slashings during the early months of eth2 are far more likely to be due to user mismanagement, or issues with client software, than an organized attack. -___A hard fork of the eth2 beacon chain is planned for 5 months after genesis. This hard fork will adjust `INACTIVITY_PENALTY_QUOTIENT`, `PROPORTIONAL_SLASHING_MULTIPLIER`, and `MIN_SLASHING_PENALTY_QUOTIENT` to their final, more secure values.___ - ## Rationale -For details regarding specification design rationale and decisions, see the following: -* [Serenity Design Rationale](https://notes.ethereum.org/@vbuterin/rkhCgQteN) -* [Ben Edgington's Eth2 Annotated Spec](https://benjaminion.xyz/eth2-annotated-spec/) -* [Vitalik Buterin's Eth2 Annotated Spec](https://github.com/ethereum/annotated-spec/blob/master/phase0/beacon-chain.md) +### Principles -## Backwards Compatibility +* **Simplicity**: especially since cryptoeconomic proof of stake and quadratic sharding are inherently complex, the protocol should strive for maximum simplicity in its decisions as much as possible. This is important because it (i) minimizes development costs, (ii) reduces risk of unforeseen security issues, and (iii) allows protocol designers to more easily convince users that parameter choices are legitimate. When complexity is unavoidable to achieve a given level of functionality, the preference order for where the complexity goes is: layer 2 protocols > client implementations > protocol spec. +* **Long-term stability**: the low levels of the protocol should ideally be built so that there is no need to change them for a decade or longer, and any needed innovation can happen on higher levels (client implementations or layer 2 protocols). +* **Sufficiency**: it should be fundamentally possible to build as many classes of applications as possible on top of the protocol. +* **Defense in depth**: the protocol should continue to work as well as possible under a variety of possible security assumptions (eg. regarding network latency, fault count, the motivations of users) +* **Full light-client verifiability**: given some assumptions (eg. network latency, bounds on attacker budget, 1-of-N or few-of-N honest minority), a client verifying a small fixed amount of data (ideally just the beacon chain) should be able to gain indirect assurance that all of the data in the full system is available and valid, even under a 51% attack (note: this is a form of defense-in-depth but it's important enough to be separate) -Although this EIP does not introduce any immediate changes to the current Ethereum mainnet, this EIP lays the groundwork for future backwards incompatibilities through the introduction of the new eth2 consensus mechanism in which Ethereum will be integrated in subsequent phases. To secure this mechanism, users move ether into the beacon chain and additional ether is issued. This EIP is a commitment to this path being canonical, as well as directly informing the future and roadmap of Ethereum mainnet. +### The Layer 1 vs Layer 2 Tradeoff -## Implementation +The Ethereum roadmap uses a mixed layer 1 / layer 2 approach. We focus on serving a particular type of layer 2 (rollups) because it's the only kind of layer 2 that both inherits the security of layer 1 and provides scaling of general-purpose applications. However, rollups come at a cost: they require some on-chain data _per transaction_, and so a blockchain with really high capacity rollups must be able to handle a still quite high amount of data bandwidth. So make this more feasible, we are implementing on scalable data layer technologies, particularly data availability sampling. -The following implementations of eth2 Phase 0 exist. At the time of writing at least five are near production-ready and in testnet/audit phase: +The reason to not take a pure layer 2 approach is that pure layer 2 scaling can only be done either with trust-based solutions (not desirable), or with channels or plasma (which have inherent limitations and cannot support the full EVM. -* [Cortex](https://github.com/NethermindEth/cortex) (.net) -* [Lighthouse](https://github.com/sigp/lighthouse) (rust) -* [Lodestar](https://github.com/ChainSafe/lodestar) (javascript) -* [Nimbus](https://github.com/status-im/nim-beacon-chain) (nim) -* [Prysm](https://github.com/prysmaticlabs/prysm/) (go) -* [Teku](https://github.com/pegasyseng/teku) (java) -* [Trinity](https://github.com/ethereum/trinity/) (python) +The reason to not take a pure layer 1 approach is to enable more room for experimentation in execution layers, and allow the base protocol to be simpler and have less intensive governance. -In addition to implementations of the spec, the deposit contract is [implemented in Solidity](), along with a [formal verification](https://github.com/runtimeverification/verified-smart-contracts/blob/master/deposit/deposit-formal-verification.pdf) of the bytecode. +### Why proof of stake -## Security Considerations +In short: -Eth2 is a major overhaul of the Ethereum's core consensus from PoW to a sharded PoS. There are inherent risks in this migration but there is extensive research literature analyzing security and trade-offs. _The following only represents a high level selection of the resources available:_ +* **No need to consume large quantities of electricity** in order to secure a blockchain (e.g. it's estimated that both Bitcoin and Ethereum burn over $1 million worth of electricity and hardware costs per day as part of their consensus mechanism). +* Because of the lack of high electricity consumption, there is **not as much need to issue as many new coins** in order to motivate participants to keep participating in the network. It may theoretically even be possible to have negative net issuance, where a portion of transaction fees is "burned" and so the supply goes down over time. +* Proof of stake opens the door to a wider array of techniques that use game-theoretic mechanism design in order to better **discourage centralized cartels** from forming and, if they do form, from acting in ways that are harmful to the network (e.g. like selfish mining in proof of work). +* **Reduced centralization risks**, as economies of scale are much less of an issue. $10 million of coins will get you exactly 10 times higher returns than $1 million of coins, without any additional disproportionate gains because at the higher level you can afford better mass-production equipment, which is an advantage for Proof-of-Work. +* Ability to use economic penalties to **make various forms of 51% attacks vastly more expensive** to carry out than proof of work - to paraphrase Vlad Zamfir, "it's as though your ASIC farm burned down if you participated in a 51% attack". -* [Casper FFG](https://arxiv.org/abs/1710.09437) -* [Combining GHOST and Casper](https://arxiv.org/abs/2003.03052) -* [Sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) -* [PoS FAQ](https://eth.wiki/en/concepts/proof-of-stake-faqs) -* [Eth2 research compendium](https://notes.ethereum.org/@serenity/H1PGqDhpm?type=view) +### Why Casper -In addition to the research supporting this path, a number of audits and formal verification of specs, cryptography, and client implementations have been performed. _Many client and utility library audits are currently in progress and will be appended here upon completion._ +There are currently three major schools of proof of stake consensus algorithm: + +* **Nakamoto-inspired** (Peercoin, NXT, Ouroboros...) +* **PBFT-inspired** (Tendermint, Casper FFG, Hotstuff...) +* **CBC Casper** + +Within the latter two camps, there is also the question of whether and how to use security deposits and slashing (Nakamoto-inspired algorithms are incompatible with non-trivial slashing). All three are superior to proof of work, but we want to defend our own approach. + +#### Slashing + +Ethereum 2.0 uses a **slashing** mechanism where a validator that is detected to have misbehaved can be penalized, in the best case ~1% but in the worst case up to its entire deposit. + +We defend our use of slashing as follows: + +1. **Raising the cost of attack**: We want to be able to make a hard claim that a 51% attack on a proof of stake blockchain forces the attacker to incur a very large amount of expense (think: hundreds of millions of dollars worth of coins) that get burned, and any attack can be recovered from quickly. This makes the attack/defense calculus very unfavorable for attackers, and in fact makes attacks potentially _counterproductive_, because the disruption to service is outweighed by price gains to legitimate coin holders. +2. **Overcoming the validator's dilemma**: the most realistic immediate way for nodes to start to deviate from "honest" behavior is _laziness_ (ie. not validating things that one should be validating, signing everything just in case, etc). See [the validator's dilemma paper](../assets/eip-2982/iacr-2015-702-Demystifying-Incentives-in-the-Consensus-Computer.pdf) (Luu et al., CC BY) for theoretical reasoning and the Bitcoin SPV mining fork for examples of this happening and leading to very harmful consequences in the wild. Having very large penalties for self-contradicting or for signing incorrect things helps to alleviate this. + +A more subtle instance of (2) can be seen as follows. In July 2019 a validator on Cosmos was slashed for signing two conflicting blocks. An investigation revealed that this happened because that validator was running a primary and a backup node (to ensure that one of the two going offline would not prevent them from getting rewards) and the two were accidentally turned on at the same time, leading to them contradicting each other. + +If it became standard practice to have a primary and backup node, then an attacker could partition the network and get the primaries and the backups of all the validators to commit to different blocks, and thereby lead to two conflicting blocks being finalized. Slashing penalties help to heavily disincentivize this practice, reducing the risk of such a scenario taking place. + +#### Choice of consensus algorithm + +Only the BFT-inspired and CBC schools of consensus algorithm have a notion of finality, where a block is confirmed in such a way that a large portion (1/3 in BFT-inspired, 1/4 in CBC) of validators would need to misbehave and get slashed for that block to get reverted in favor of some conflicting block; Nakamoto-inspired (ie. longest-chain-rule) consensus algorithms have no way of achieving finality in this sense. + +Note that finality requires a (super)majority of validators being online, but this is a requirement of the sharding mechanism already, as it requires 2/3 of a randomly sampled committee of validators to sign off on a crosslink for that crosslink to be accepted. + +Our choice of [Casper FFG](../assets/eip-2982/arxiv-1710.09437-Casper-the-Friendly-Finality-Gadget.pdf) was simply a matter of it being the simplest algorithm available at the time that part of the protocol was being finalized. Details are still subject to long-term change; in particular, we are actively exploring solutions to achieve single slot finality. + +### Sharding - or, why do we hate supernodes? + +The main alternative to sharding for layer-1 scaling is the use of supernodes - simply requiring every consensus node to have a powerful server so that it can individually process every transaction. Supernode-based scaling is convenient because it is simple to implement: it works just the same way blockchains do now, except that more software-engineering work is required to build things in a way that is more parallelizable. + +Our main objections to this approach are as follows: + +* **Pool centralization risk**: in a supernode-based system, running a node has a high fixed cost, so far fewer users can participate. This is usually rebutted with "well consensus in most PoW and PoS coins is dominated by 5-20 pools anyway, and the pools will be able to run nodes just fine". However, this response ignores the risk of centralization pressure even between pools that can afford it. If the fixed cost of running a validator is significant relative to the returns, then larger pools will be able to offer smaller fees than smaller ones and this could lead to smaller pools being pushed out or feeling pressure to merge. In a sharded system, on the other hand, validators with more ETH need to verify more transactions, so costs are not fixed. +* **AWS centralization risk**: in a supernode-based system, home staking is infeasible and so it's more likely that most staking will happen inside cloud computing environments, of which there are only a few options to choose from. This creates a single point of failure. +* **Reduced censorship resistance**: making it impossible to participate in consensus without high computation+bandwidth requirements makes detection and censorship of validators easier. +* **Scalability**: as transaction throughput increases, in a supernode-based system the above risks increase, whereas sharded systems can more easily handle the increased load. + +These centralization risks are also why we are NOT attempting to achieve super-low-latency (<1s) of the blockchain, instead opting for (relatively!) conservative numbers. + +Instead, Ethereum is taking an approach where each validator is only assigned to process a small portion of all data. Only validators staking large amounts of ETH (think: tens of thousands or more) are required to process the entire data in the chain. + +Note that there is a possible middle-ground in sharding design where block _production_ is centralized but (i) block _verification_ is decentralized and (ii) there exist "bypass channels" where users can send transactions and block producers are forced to include them, so even a monopoly producer cannot censor. We are actively considering sharding designs that lean somewhat in this direction to increase simplicity so that scaling can be deployed faster, though if desired even within this spec it's possible to run distributed builders and avoid centralization even there. + + +### Security models + +It's commonly assumed that blockchains depend on an "honest majority" assumption for their security: that >=50% of participants will faithfully follow a prescribed protocol, even forgoing opportunities to defect for their own personal interest. In reality, (i) an honest majority model is unrealistic, with participants being "lazy" and signing off on blocks without validating them (see [the validator's dilemma paper](../assets/eip-2982/iacr-2015-702-Demystifying-Incentives-in-the-Consensus-Computer.pdf) and the Bitcoin SPV mining fork) being a very common form of defection, but fortunately (ii) blockchains maintain many or all of their security properties under much harsher models, and it's really important to preserve those extra guarantees. + +A common harsher model is the _uncoordinated rational majority_ model, which states that participants act in their own self-interest, but no more than some percentage (eg. 23.21% in simple PoW chains) are cooperating with each other. An even harsher model is the worst-case model where there is a single actor that controls more than 50% of hashpower or stake, and the question becomes: + +* Can we, even under that scenario, force the attacker to have to pay a very high cost to break the chain's guarantees? +* What guarantees can we unconditionally preserve? + +Slashing in proof of stake chains accomplishes the first objective. In non-sharded chains, every node verifying every block accomplishes the second objective for two specific guarantees: (i) that the longest chain is valid, and (ii) that the longest chain is _available_. + +A major challenge in sharding is getting the same two properties without requiring each node to verify the full chain. Our defense-in-depth approach with sharding accomplishes just that. The core idea is to combine together random committee sampling, proof of custody, fraud proofs, [data availability sampling (DAS)](../assets/eip-2982/arxiv-1809.09044-Fraud-and-Data-Availability-Proofs--Maximising-Light-Client-Security-and-Scaling-Blockchains-with-Dishonest-Majorities.pdf) and eventually ZK-SNARKs, to allow clients to detect and reject invalid or unavailable chains without downloading and verifying all of the data, even if the invalid chains are supported by a majority of all proof of stake validators. + +Censorship of transactions can potentially be detected by clients in a consensus-preserving way, but this research has not yet been incorporated into the ethereum roadmap. + +Here is the current expected security properties expressed in a table: + +|| Network delay <3s |Network delay 3s - 6 min|Network delay > 6 min| +|---|---|---|---|---| +|>2/3 validators honest|Perfect operation|Imperfect but acceptable operation. No rigorous proof of liveness, but liveness expected in practice.|Likely intermittent liveness failures, no safety failures| +|>2/3 validators rational, <1/3 coordinated|Perfect operation|Imperfect but acceptable operation, heightened centralization risk|Likely intermittent liveness failures, no safety failures, very high centralization risk| +|51% attacker|Can revert finality or censor, but at high cost; cannot force through invalid or unavailable chains|Can revert finality or censor, but at high cost; cannot force through invalid or unavailable chains|Can revert finality or censor; cannot force through invalid or unavailable chains| + +### Why are the Casper incentives set the way they are? + +#### Base rewards + +During each epoch, every validator is expected to make an "attestation", a signature that expresses that validator's opinion on what the head of the chain is. There is a reward for having one's attestation included, with four components (called "duties"): + +1. Reward for the attestation getting included at all +2. Reward for the attestation specifying the correct epoch checkpoint +3. Reward for the attestation specifying the correct chain head +4. Reward for correctly participating in sync committee signatures + +Note also that mixed into these duties is a timeliness requirement: your attestation has to be included within a certain time to be eligible for the rewards, and for the "correct head" duty that time limit is 1 slot. + +For each duty, the actual reward is computed as follows. If: + +* $R = B * \frac{nom}{den}$ equals the base reward multiplied by the fraction $\frac{nom}{den}$ that corresponds to that particular duty +* $P$ is the portion of validators that did the desired action + +Then: + +* Any validator that fulfilled the dury gets a reward of $R * P$ +* Any validator that did not fulfill the duty gets a penalty $-R$ + +The purpose of this "collective reward" scheme where "if anyone performs better, everyone performs better" is to bound griefing factors (see [this paper](../assets/eip-2982/ef-Discouragement-Attacks.pdf) for one description of griefing factors and why they are important). + +The base reward $B$ is itself calculated as $k * \frac{D_i}{\sqrt{\sum_{j=1}^{n} D_j}}$ where $D_1 ... D_n$ are deposit sizes and $k$ is a constant; this is a halfway compromise between two common models, (i) fixed reward rate, ie. $k * D_i$, and (ii) fixed total reward, ie. $k * \frac{D_i}{\sum_{j=1}^{n} D_j}$. + +The main argument against (i) is that it imposes too much uncertainty on the network of two kinds: uncertainty of the total level of issuance, and uncertainty of the total level of deposits (as if a fixed reward rate is set too low then almost no one will participate, threatening the network, and if a rate is set too high then very many validators will participate, leading to unexpectedly high issuance). The main argument against (ii) is greater vulnerability to discouragement attacks (again see the [discouragement attacks paper](../assets/eip-2982/ef-Discouragement-Attacks.pdf)). The inverse-square-root approach compromises between the two and avoids the worst consequences of each one. + +When an attestation gets a reward, the proposer gets a fraction of that reward. This is to encourage proposers to listen well for messages and accept as many as possible. + +Note also that the rewards are designed to be forgiving to validators who are offline often: being offline 1% of the time only sacrifices about 1.6% of your reward. This is also to promote decentralization: the goal of a decentralized system is to create a reliable whole out of unreliable parts, so we should not be trying to force each individual node to be extremely reliable. + +#### Inactivity leak + +If the chain fails to finalize for $tsf > 4$ epochs ($tsf$ = "time since finality"), then a penalty is added so that the maximum possible reward is zero (validators performing imperfectly get penalized), and a second penalty component is added, proportional to $tsf$ (that is, the longer the chain has not finalized, the higher the _per-epoch_ penalty for being offline). This ensures that if more than 1/3 of validators drop off, validators that are not online get penalized much more heavily, and the total penalty goes up quadratically over time. + +This has three consequences: + +* Penalizes being offline much more heavily in the case where you being offline is actually preventing blocks from being finalized +* Serves the goals of being an anti-correlation penalty (see section below) +* Ensures that if more than 1/3 do go offline, eventually the portion online goes back up to 2/3 because of the declining deposits of the offline validators + +With the current parametrization, if blocks stop being finalized, validators lose 1% of their deposits after 2.6 days, 10% after 8.4 days, and 50% after 21 days. This means for example that if 50% of validators drop offline, blocks will start finalizing again after 21 days. + +#### Slashing and anti-correlation penalties + +If a validator is caught violating the Casper FFG slashing condition, they get penalized a portion of their deposit equal to three times the portion of validators that were penalized around the same time as them (specifically, between 18 days before they were penalized and roughly the time they withdrew). This is motivated by several goals: + +* A validator misbehaving is only really bad for the network if they misbehave at the same time as many other validators, so it makes sense to punish them more in that case +* It heavily penalizes actual attacks, but applies very light penalties to single isolated failures that are likely to be honest mistakes +* It ensures that smaller validators take on less risk than larger validators (as in the normal case, a large validator would be the only one failing at the same time as themselves) +* It creates a disincentive against everyone joining the largest pool + +### BLS Signatures + +BLS signatures are used because of their aggregation-friendliness: any two signatures $S_1$ and $S_2$ of a message $M$ signed by keys $k_1$ and $k_2$ (corresponding pubkeys $K_1 = G * k_1$ and $K_2 = G * k_2$ where $G$ is the generator of the elliptic curve) can simply be aggregated by elliptic curve point addition: $S_1 + S_2$, which verifies against the public key $K_1 + K_2$. This allows many thousands of signatures to be aggregated, with the marginal cost of one signature being one bit of data (to express that a particular public key is present in the aggregate signature) and one elliptic curve addition for computation. + +Note that BLS signatures of this form are vulnerable to _rogue key attacks_: if you see that other validators have already published public keys $K_1 ... K_n$, then you can generate a private key $r$ and publish a public key $G * r - K_1 - ... - K_n$. The aggregate public key would simply be $G * r$, so you would be able to make a signature that verifies against the combined public key by yourself. The standard way to get around this is to require a _proof of possession_: basically, a signature of a message (that depends on the public key, and that would normally not be signed) that verifies against the public key by itself (ie. $sign(message=H'(K), key=k)$ for privkey $k$ and pubkey $K$, where $H'$ is a hash function). This ensures that you personally control the private key connected to the public key that you publish. + +We use the signature of a deposit message (which specifies the signing key but also other important data such as the withdrawal key) as a proof of possession. + +### Why 32 ETH validator sizes? + +Any BFT consensus algorithm with accountable fault tolerance (ie. if two conflicting blocks get finalized you can identify which 1/3 of nodes were responsible) must have all validators participate, and furthermore for technical reasons you need two rounds of every validator participating to finalize a message. -* [Eth2 Phase 0 Spec audit by Least Authority](https://leastauthority.com/blog/ethereum-2-0-specifications/) -* [Gossipsub v1.1 audit by Least Authority](https://leastauthority.com/blog/audit-of-gossipsub-v1-1-protocol-design-implementation-for-protocol-labs/) -* [Discv5 audit by Least Authority](https://leastauthority.com/blog/audit-of-gossipsub-v1-1-protocol-design-implementation-for-protocol-labs/) -* [Formal Verification of Finality in eth2 by Runtime Verification](https://runtimeverification.com/blog/formally-verifying-finality-in-gasper-the-core-of-the-beacon-chain/) -* [Formal Verification of Deposit Contract by Runtime Verification](https://github.com/runtimeverification/deposit-contract-verification/blob/master/deposit-contract-verification.pdf) -* [Prysm client audit by Quantstamp](https://quantstamp.com/blog/ethereum-2-0-moves-closer-to-launch-with-quantstamp-audit-of-prysm) and [the results](https://medium.com/prysmatic-labs/quantstamp-security-audit-results-for-the-prysm-eth2-client-7f949c6c866f) -* [Lodestar utility libraries audit by Least Authority](https://leastauthority.com/blog/audit-of-chainsafe-utility-libraries/) +This leads to the decentralization / finality time / overhead tradeoff: if $n$ is the number of validators in a network, $f$ is the time to finalize a block, and $\omega$ is the overhead in messages per second, then we have: -Finally, the EF maintains bounty programs for the eth2 spec, attacknets, and clients. +$$\omega \ge \frac{2 * n}{f}$$ -* [Eth2 Phase 0 bug bounty program](https://notes.ethereum.org/@djrtwo/phase0-bounty) -* [Eth2 public attacknets](https://github.com/ethresearch/public-attacknets) -* _Eth2 Client Bounty Program to be released soon_ +For example, if we are ok with an overhead of 10 messages per second, then a 10000-node network could only have a finality time of at least 2000 seconds (~33 minutes). + +In Ethereum's case, if we assume a total ETH supply of $\approx 2^{27}$ ETH, then with 32 ETH deposit sizes, there are at most $2^{22}$ validators (and that's if everyone validates; in general we expect ~10x less ETH validating). With a finality time of 2 epochs (2 * 32 * 12 = 768 seconds), that implies a max overhead of $\frac{2^{22}}{768} \approx 5461$ messages per second. We can tolerate such high overhead due to BLS aggregation reducing the marginal size of each signature to 1 bit and the marginal verification complexity to one elliptic curve addition. + +32 slots is a safe minimum for another reason: if an attacker manipulates the randomness used for proposer selection, this number still provides enough space to ensure that there will be at least one honest proposer in each epoch, which is sufficient to ensure blocks keep finalizing. Our calculations suggest that current levels of overhead are acceptable, but higher levels would make running a node too difficult. Finally, the validator deposit size is ideal for shard crosslinking (see below). + +### Random sampling + +#### Seed selection + +The seed used for randomness is updated every block by "mixing in" (ie. `seed <- xor(seed, new_data)`) a value that must be revealed by the proposer of the block. Just like proof of custody subkeys, a validator's values are all pre-determined once the validator has deposited, third parties cannot compute subkeys but can verify subkeys that are revealed voluntarily (this mechanism is sometimes called RANDAO). + +This ensures that each proposer has one "bit of manipulation" over the seed: they can either make a block or not make a block. Not making a block is expensive in that the proposer misses out on many rewards. Furthermore, because the persistent and beacon committee sizes (see below) are large, manipulation of the randomness almost certainly cannot allow minority attackers to get 2/3 of any committee. + +In the future we plan to use verifiable delay functions (VDFs) to further increase the random seeds' robustness against manipulation. + +#### Shuffling + +We use the swap-or-not shuffle described by Viet Tung Hoang, Ben Morris, and Phillip Rogaway to shuffle the validator set and assign responsibilities every epoch. This algorithm ensures that: + +* As the shuffle is a permutation, each validator is assigned to be a member of exactly one committee during each epoch (keeping their load stable and reducing the chance manipulation of randomness can be profitable) +* As the shuffle is pointwise-evaluable in the forward direction, a validator can determine their own responsibilities in O(1) time +* As the shuffle is pointwise-evaluable in the reverse direction, the members of any specific committee or the proposer of any specific block can be computed in O(1) time + +#### Shuffling by slot + +There are 32 slots in an epoch, and the validators responsible for attesting to each slot are chosen by shuffling. This ensures that attackers with a significant but still small portion of total stake cannot take over specific slots and cause short-range reorgs. + +#### Beacon committees + +The committee for each slot is in turn split up into some number of _beacon committees_. Today (2022 Jan), this design does serve one useful function, allowing different subsets of attestations to get aggregated in separate subnets and thus making the p2p network more efficient. However, it does not serve any other useful consensus-related role. + +In the original sharding design, the intention was that each beacon committee would be responsible for verifying a specific shard. However, this approach is likely deprecated if we switch to a single-proposer model. Hence, the more fine-grained beacon committees may well only be there vestigially and could eventually be removed or re-structured in a different way to focus on facilitating attestation aggregation. + +#### Sync committee + +A sync committee of 512 validators is selected once every ~27 hours to sign a block; this committee's pubkeys are saved in an easily accessible list, allowing signatures to be easily verified by ultra-light clients. + +### LMD GHOST fork choice rule + +The beacon chain uses an LMD GHOST fork choice rule. + +The LMD GHOST fork choice rule incorporates information from all validators, hundreds in each slot, making it extremely unlikely in the normal case that even a single block will be reverted. Because the fork choice is dependent on all validators, this also ensures that blocks cannot be reverted unless an attacker really does control close to 50% of the entire validator set; one cannot achieve a large advantage by manipulating the randomness. + +### The proof of custody game + +For each 9-day period, each validator has the ability to privately generate a "period subkey". The validator's public key uniquely determines their period subkey for every period, so once a validator has deposited they have no further freedom to choose what their subkeys are. No one can compute any given validator's subkey except the validator themselves, but once a validator reveals a subkey voluntarily, anyone can verify its correctness (this is all done via BLS magic, and if quantum-safety is required in the future it can be done with hash-based commitments). + +When signing a block with data $D$ with root $R$ during period $j$, letting $s$ being the period-$j$ secret of that validator, a validator is required to compute a bitfield $M$ as follows: + +* Split $D$ into 512-byte chunks $D[0] ... D[n-1]$ +* Set $M$ to be the bitfield where the i'th bit is $M[i] = mix(s, D[i])$ + +They include `get_chunk_bits_root(M)` (this is called the **custody bit**) as part of what they are signing. `mix` and `get_chunk_bits_root` can both be viewed as hash-like functions that output a single bit (but are designed for multi-party-computation friendliness). + +If a crosslink includes multiple blocks $B_1 ... B_n$ and the validator computes custody bits $c_1 ... c_n$ for these blocks, then the validator signs all $(B_i, c_i, i)$ tuples and aggregates them. This unconventional self-aggregation is used to ensure that there are only $2n$ different messages (n different block/index pairs * 2 different bit values) being signed by different validators, reducing the number of pairings needed to verify the signatures. + +If a validator publishes their period $j$ subkey during or before period $j$, any other validator can publish a proof-of-knowledge of the subkey to the chain, which then penalizes the validator whose subkey was revealed. The proof of knowledge also proves which validator created it; this prevents block proposers from "stealing" the whistleblowing reward (once again all done via BLS magic, if quantum-safety is required in the future this can be done with STARKs). The goal of this is to make it dangerous to outsource computation of $M$. + +After a validator publishes their period $j$ subkey, anyone else can check their work for any block that they signed. If they discover that a validator provided an incorrect custody bit, they can challenge this on-chain, and slash that validator. In general, random guessing (or any procedure that does not involve all of $D$) will only give a correct answer half the time, leading to a 50% risk of slashing per block signed. + +The purpose of this mechanism is to mitigate the [validator's dilemma](../assets/eip-2982/iacr-2015-702-Demystifying-Incentives-in-the-Consensus-Computer.pdf) problem, where validators have an incentive to avoid verifying data out of laziness and piggyback on the assumption that all _other_ validators are honest; if too many validators think in this way, it could lead to a tragedy of the commons leading to the chain accepting invalid blocks. With this scheme, if a validator attempts to commit to data that they did not personally process, then they would not be able to compute $M$, and so would lose the interactive challenge game. + +### SSZ + +The SimpleSerialize suite contains the following algorithms: + +* A serialization algorithm +* A hashing algorithm (called `SSZTreeHash` or `hash_tree_root`) +* A generalized Merkle proof algorithm (called "SSZ partials") that can handle proofs for multiple values and optimally deduplicates Merkle tree sister nodes in such cases. + +The serialization algorithm has the following design goals: + +* Simplicity (eg. no three clauses like RLP has for long / list encoding depending on item length) +* Being equivalent to simple concatenation in the case of entirely fixed-size objects +* Being usable as both a consensus-layer serialization algorithm and an application-layer ABI + +Note that the resulting SSZ serialization spec is very similar to the ethereum 1.0 ABI, with the major differences being (i) different basic data types and (ii) replacing the 32 byte length/position record size with a 4 byte size. + +The hashing algorithm has the following design goals: + +* Efficiency of computing the hash of an updated object, especially in the case where the object is very large and/or complex and the change is relatively small +* Efficiency (in terms of verification complexity _and_ witness size) of proving the value of a specific field even in a large or complex object with a given hash + +These requirements together make a Merkle tree structure the obvious choice. + +#### Generalized indices + +See https://github.com/ethereum/eth2.0-specs/blob/dev/ssz/merkle-proofs.md for a description of generalized indices, and how they allow for very easy verification of Merkle proofs "poking into" arbitrary positions in an object by representing paths as an integer or bitfield. + +### The validator lifecycle + +#### Depositing + +A validator deposits by sending a transaction that calls a function on the deposit contract on the eth1 chain (eventually, a way to deposit from inside eth2 will also be added). A deposit specifies: + +* The public key corresponding to the private key that will be used to sign messages +* The withdrawal credentials (hash of the public key that will be used to withdraw funds once the validator is done validating) +* The deposit amount + +These values are all signed by the signing key. The goal of having separate signing and withdrawal keys is to allow the more dangerous withdrawal key to be kept more securely (offline, not shared with any staking pools, etc) while the signing key is used to actively sign a message every epoch. + +The deposit contract maintains a Merkle root of all deposits made so far. Once a merkle root containing the deposit gets included into the eth2 chains (via the Eth1Data voting mechanism), an eth2 block proposer can submit a Merkle proof of the deposit and start the deposit process. + +#### Activation + +The validator immediately joins the validator registry, but is at first inactive. The validator becomes active after $N \ge 4$ epochs; the minimum of 4 is there to ensure the RANDAO is not manipulable, and $N$ may exceed 4 if too many validators try to join at the same time. If the active validator set has size $|V|$, a maximum of $max(4, \frac{|V|}{65536})$ valdators can join per epoch; if more validators try to join, they are put in a queue and processed as quickly as possible. + +#### Exiting + +When a validator exits (whether by publishing a voluntary exit message or being slashed), they are similarly put into an exit queue with the same maximum throughput. + +The reason for the entrance/exit queue limits is to ensure that the validator set cannot change too quickly between any two points in time, which ensures that finality guarantees still remain between two chains as long as a validator logs on often enough (once every ~1-2 months if $|V| \ge 262144$). See here https://ethresear.ch/t/rate-limiting-entry-exits-not-withdrawals/4942 for rationale (and specifically why to use a rate limit instead of a fixed waiting time) and https://ethresear.ch/t/weak-subjectivity-under-the-exit-queue-model/5187 for how to calculate safety margin for a client that has been offline for some amount of time. + +#### Withdrawal + +Once a validator leaves the exit queue, there is a ~27 hour period until they can withdraw. This period has several functions: + +* It ensures that if a validator misbehaved there is a period of time within which the error can be caught and the validator can be slashed even if the exit queue is nearly empty. +* It provides time for the last period of shard rewards to be included. +* It provides time for proof of custody challenges to be made. + +If a validator is slashed, a further delay of ~36 days is imposed. This further penalizes them (and forces them to hold the ETH; this makes the penalty somewhat larger for malicious validators that are trying to destroy the ethereum blockchain than those validators that want to support the platform but just made a mistake, as the former category would have to risk the exposure or pay for derivatives to cancel it out), but also allows for a period during which the number of other validators that got slashed can be calculated. + +In phase 0, a "withdrawn" validator cannot actually withdraw to anywhere yet; the only distinction is that it is protected from validator penalties and does not have any responsibilities. In later phases, the ability to move funds from a withdrawn validator slot to an execution environment will be made available. + +#### Effective balances + +Most calculations based on a validator's balance use the validator's "effective balance"; the only exception is calculations that increase or decrease the validator's balance. Only when the validators's balance $B$ falls below $EB$ or goes above $EB + 1.5$ is $EB$ adjusted to equal $floor(B)$. This is done to ensure that effective balance changes infrequently, reducing the amount of hashing needed to do to recompute the state every epoch; on average, only the balances need to all be updated, and the effective balances only get updated relatively rarely per validator. Balances are all stored in a dedicated vector, so $2 * \frac{N * 8}{32} = \frac{N}{2}$ hashes are needed to recompute a balance array, whereas effective balances are stored in validator record objects, where at least $5N$ hashes would be needed per validator to adjust the SSZ hash roots. Additionally, $EB$ can be compressed more easily into a CompactValidator object, as it's only one byte. + +### Fork mechanism + +The `Fork` data structure contains (i) the current "fork ID", (ii) the previous fork ID and (iii) the switchover slot. The fork ID at the current height influences the valid signatures of all messages; hence, a message signed with one fork ID is invalid to a verification function using any other fork ID. + +A fork is done by adding a state transition at some "fork slot" $n$ which sets the previous fork ID to the current fork ID, the current fork ID to a new value, and the switchover slot to $n$. Signature verification functions will verify messages using the fork ID at the slot the message is for, which could be the previous fork ID or the current one (eg. consider the case of attestations included after a delay; an attestation from before the fork slot could be included after the fork slot). + +If any users do not want to join the fork, they can simply continue the chain that does not change the fork ID at the fork slot. The two chains would be able to proceed and validators would be free to validate on both without getting slashed. + +## Backwards Compatibility + +Although this EIP does not introduce any immediate changes to the current Ethereum mainnet, this EIP lays the groundwork for future backwards incompatibilities through the introduction of the new eth2 consensus mechanism in which Ethereum will be integrated in subsequent phases. To secure this mechanism, users move ether into the beacon chain and additional ether is issued. This EIP is a commitment to this path being canonical, as well as directly informing the future and roadmap of Ethereum mainnet. + +## Security Considerations + +Eth2 is a major overhaul of the Ethereum's core consensus from PoW to a sharded PoS. There are inherent risks in this migration but there is extensive research literature analyzing security and trade-offs. _The following only represents a high level selection of the resources available:_ + +* [Casper FFG](../assets/eip-2982/arxiv-1710.09437-Casper-the-Friendly-Finality-Gadget.pdf) +* [Combining GHOST and Casper](../assets/eip-2982/arxiv-2003.03052-Combining-GHOST-and-Casper.pdf) + +In addition to the research supporting this path, a number of audits and formal verification of specs, cryptography, and client implementations have been performed. _Many client and utility library audits are currently in progress and will be appended here upon completion._ ## 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-2997.md b/EIPS/eip-2997.md index 13197c2f4e0d90..ee1152b5406c89 100644 --- a/EIPS/eip-2997.md +++ b/EIPS/eip-2997.md @@ -5,7 +5,7 @@ author: Sergio Demian Lerner (@SergioDemianLerner) discussions-to: https://ethresear.ch/t/impersonatecall-opcode/8020 category: Core type: Standards Track -status: Draft +status: Stagnant created: 2020-09-24 --- @@ -100,4 +100,4 @@ The address derivation scheme prevents address collision with another deployed c ## 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-3000.md b/EIPS/eip-3000.md index 9f3f17c5799725..7b36755eda0b64 100644 --- a/EIPS/eip-3000.md +++ b/EIPS/eip-3000.md @@ -3,7 +3,7 @@ eip: 3000 title: Optimistic enactment governance standard author: Jorge Izquierdo (@izqui), Fabien Marino (@bonustrack) discussions-to: https://github.com/ethereum/EIPs/issues/3042 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-09-24 @@ -112,7 +112,7 @@ abstract contract IERC3000 { * @dev OPTIONAL * @notice Apply arbitrator's ruling over a challenge once it has come to a final ruling * @param payloadHash Hash of the payload being vetoed - * @param config A ERC3000Data.Config struct holding the config attached to the payload being vetod + * @param config A ERC3000Data.Config struct holding the config attached to the payload being vetoed */ function veto(bytes32 payloadHash, ERC3000Data.Config memory config, bytes memory reason) virtual public; event Vetoed(bytes32 indexed containerHash, address indexed actor, bytes reason, ERC3000Data.Collateral collateral); @@ -151,4 +151,4 @@ On the other hand, implementing a deterministic resolver is prone to dangerous b - [Implementation (GPL-3.0 license)](https://github.com/aragon/govern/blob/master/packages/govern-core) ## 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-3005.md b/EIPS/eip-3005.md index 58c8b85e9f63ee..c3f1219dfda2b8 100644 --- a/EIPS/eip-3005.md +++ b/EIPS/eip-3005.md @@ -3,7 +3,7 @@ eip: 3005 title: Batched meta transactions author: Matt (@defifuture) discussions-to: https://ethereum-magicians.org/t/eip-3005-the-economic-viability-of-batched-meta-transactions/4673 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-09-25 @@ -413,4 +413,4 @@ This addition could open new security implications, that's why it is left out of ## Copyright -Copyright and related rights are waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights are waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-3009.md b/EIPS/eip-3009.md index 588a4a3bb257ff..1919a5d0efa13b 100644 --- a/EIPS/eip-3009.md +++ b/EIPS/eip-3009.md @@ -3,7 +3,7 @@ eip: 3009 title: Transfer With Authorization author: Peter Jihoon Kim (@petejkim), Kevin Britz (@kbrizzle), David Knott (@DavidLKnott) discussions-to: https://github.com/ethereum/EIPs/issues/3010 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-09-28 @@ -533,4 +533,4 @@ The zero address must be rejected when using `ecrecover` to prevent unauthorized ## 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-3014.md b/EIPS/eip-3014.md index b43e21ad6a5450..408d992c0f5c74 100644 --- a/EIPS/eip-3014.md +++ b/EIPS/eip-3014.md @@ -1,9 +1,9 @@ --- eip: 3014 -title: `eth_symbol` JSON-RPC method +title: eth_symbol JSON-RPC method author: Peter Grassberger (@PeterTheOne) discussions-to: https://github.com/ethereum/EIPs/issues/3012 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-09-30 @@ -45,4 +45,4 @@ This endpoint is similar to [EIP-695](./eip-695.md) but it provides the symbol i It is a read only endpoint. The information is only as trusted as the JSON-RPC node itself, it could supply wrong information and thereby trick the user in believing he/she is dealing with another native coin. ## 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-3026.md b/EIPS/eip-3026.md index 6af4f5d59df72a..b6e74445500029 100644 --- a/EIPS/eip-3026.md +++ b/EIPS/eip-3026.md @@ -1,20 +1,19 @@ --- eip: 3026 title: BW6-761 curve operations -author: Youssef El Housni (@yelhousni), Michael Connor (@iAmMichaelConnor), Aurore Guillevic +description: Precompiles for BW6-761 curve operations +author: Youssef El Housni (@yelhousni), Michael Connor (@iAmMichaelConnor), Aurore Guillevic , hujw77 (@hujw77) discussions-to: https://ethereum-magicians.org/t/eip-3026-bw6-761-curve-operations/4790 status: Draft type: Standards Track category: Core -requires: 2539 created: 2020-10-05 +requires: 2539 --- -## Simple Summary -This precompile adds operations for the BW6-761 curve (from the EY/Inria [research paper](https://eprint.iacr.org/2020/351.pdf)) as a precompile in a set necessary to *efficiently* perform verification of one-layer composed zkSNARKs proofs. - ## Abstract +This precompile adds operations for the BW6-761 curve (from the EY/Inria **Optimized and secure pairing-friendly elliptic curves suitable for one layer proof composition** research paper) as a precompile in a set necessary to *efficiently* perform verification of one-layer composed zkSNARKs proofs. If `block.number >= X` we introduce *seven* separate precompiles to perform the following operations (addresses to be determined): - BW6_G1_ADD - to perform point addition on a curve defined over a prime field @@ -29,20 +28,20 @@ The multiexponentiation operations are a generalization of point multiplication, ## Motivation -This EIP is based on and tends to replace [EIP-2541](https://github.com/matter-labs/EIPs/blob/sw6_wrapping/EIPS/eip-2541.md) for significant performance reasons. In most applications, BW6-761 is used as an outer curve to BLS12-377 considered in [EIP-2539](https://github.com/ethereum/EIPs/pull/2539). +This EIP is based on and tends to replace matter-labs' proposol for significant performance reasons. In most applications, BW6-761 is used as an outer curve to BLS12-377 considered in [EIP-2539](./eip-2539.md). The motivation of this precompile is to allow efficient one-layer composition of SNARK proofs. Currently this is done by Zexe using the BLS12-377/CP6-782 pair of curves. This precompile proposes a replacement of CP6-782 by BW6-761, which allows much faster operations. For example, it was shown that verifying a Groth16 proof with BW6-761 is 30 times faster than with CP6-782. ### Proposed addresses table -|Precompile |Address | -|---|---| -|BW6_G1_ADD | 0x13 | -|BW6_G1_MUL | 0x14 | -|BW6_G1_MULTIEXP | 0x15 | -|BW6_G2_ADD | 0x16 | -|BW6_G2_MUL | 0x17 | -|BW6_G2_MULTIEXP | 0x18 | -|BW6_PAIRING | 0x19 | +| Precompile | Address | +| --------------- | ------- | +| BW6_G1_ADD | 0x1e | +| BW6_G1_MUL | 0x1f | +| BW6_G1_MULTIEXP | 0x20 | +| BW6_G2_ADD | 0x21 | +| BW6_G2_MUL | 0x22 | +| BW6_G2_MULTIEXP | 0x23 | +| BW6_PAIRING | 0x24 | ## Specification @@ -79,9 +78,9 @@ loop_count_1 is negative = false loop_count_2 is negative = false ``` -#### Encoding +### Encoding -##### Field elements encoding: +#### Field elements encoding: To encode points involved in the operation one has to encode elements of only the base field. @@ -89,16 +88,16 @@ The base field element (Fp) is encoded as `96` bytes by performing BigEndian enc If encodings do not follow this spec anywhere during parsing in the precompile, the precompile **MUST** revert with "endoding error". -##### Encoding of uncompressed points: +#### Encoding of uncompressed points: Points in both G1 and G2 can be expressed as `(x, y)` affine coordinates, where `x` and `y` are elements of the base field. Therefore, points in both G1 and G2 are encoded as the byte concatenation of the field element encodings of the `x` and `y` affine coordinates. The total encoding length for a G1/G2 point is thus `192` bytes. -##### Point at infinity encoding: +#### Point at infinity encoding: Also referred as the "zero point". For BW6-761 (`y^2=x^3-1`) and its M-twisted curves (`y^3=x^3+4`), the point with coordinates `(0, 0)` (formal zeros in Fp) is *not* on the curve, and so the encoding of `(0, 0)` is used as a convention to encode the point at infinity. -##### Encoding of scalars for multiplication and multiexponentiation operations: +#### Encoding of scalars for multiplication and multiexponentiation operations: For multiplication and multiexponentiation operations, a scalar is encoded as `64` bytes by performing BigEndian encoding of the corresponding (unsigned) integer. @@ -106,65 +105,74 @@ Note that the main subgroup order for BW6-761 is actually only `377` bits (`48` The corresponding integer **MAY** be greater than the main subgroup order. -#### ABI for operations +### ABI for operations -##### ABI for G1 addition +#### ABI for G1 addition G1 addition call expects `384` bytes as an input that is interpreted as the byte concatenation of two G1 points (point-encoded as `192` bytes each). Output is a point-encoding of the addition operation result. Error cases: + - Either of the points being not on the curve - Input has invalid length - Field element encoding rules apply (obviously) -##### ABI for G1 multiplication +#### ABI for G1 multiplication + G1 multiplication call expects `256` bytes as an input that is interpreted as the byte concatenation of the point-encoding of a G1 point (`192` bytes) and the encoding of a scalar value (`64` bytes). Output is a point-encoding of the multiplication operation result. Error cases: + - Point being not on the curve - Input has invalid length - Field element encoding rules apply (obviously) - Scalar encoding rules apply (obviously) -##### ABI for G1 multiexponentiation +#### ABI for G1 multiexponentiation G1 multiplication call expects `256*k` bytes as an input that is interpreted as the byte concatenation of `k` slices, each of them being a byte concatenation of the point-encoding of a G1 point (`192` bytes) and the encoding of a scalar value (`64` bytes). Output is an encoding of the multiexponentiation operation result. Error cases: + - Any of the G1 points being not on the curve - Input has invalid length - Field element encoding rules apply (obviously) - Scalar encoding rules apply (obviously) -##### ABI for G2 addition +#### ABI for G2 addition G2 addition call expects `384` bytes as an input that is interpreted as the byte concatenation of two G2 points (point-encoded as `192` bytes each). Output is a point-encoding of the addition operation result. Error cases: + - Either of points being not on the curve - Input has invalid length - Field elements encoding rules apply (obviously) -##### ABI for G2 multiplication +#### ABI for G2 multiplication + G2 multiplication call expects `256` bytes as an input that is interpreted as the byte concatenation of the point-encoding of a G2 point (`192` bytes) and the encoding of a scalar value (`64` bytes). Output is an encoding of multiplication operation result. Error cases: + - Point being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length -##### ABI for G2 multiexponentiation +#### ABI for G2 multiexponentiation G2 multiplication call expects `240*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`192` bytes) and encoding of a scalar value (`48` bytes). Output is an encoding of multiexponentiation operation result. Error cases: + - Any of G2 points being not on the curve must result in error - Field elements encoding rules apply (obviously) - Input has invalid length -##### ABI for pairing +#### ABI for pairing Pairing call expects `384*k` bytes as an input, that is interpreted as the byte concatenation of `k` slices. Each slice has the following structure: + - `192` bytes G1 point encoding - `192` bytes G2 point encoding @@ -174,46 +182,55 @@ Output is `32` bytes representing a boolean: - `0x0000000000000000000000000000000000000000000000000000000000000000` otherwise. Error cases: + - Any of the G1 or G2 points being not on the curve - Any of the G1 or G2 points being not in the correct subgroup - Input has invalid length - Field elements encoding rules apply (obviously) -#### Prevention of DDoS on error handling +### Prevention of DDoS on error handling This precompile performs extensive computations and in case of any errors during execution it **MUST** consume all gas from the the gas schedule for the corresponding operation. -#### Gas schedule +### Gas schedule + +#### G1 addition + +`180` gas + +#### G1 multiplication -##### G1 addition -`` gas +`64000` gas -##### G1 multiplication -`` gas +#### G2 addition -##### G2 addition -`` gas +`180` gas -##### G2 multiplication -`` gas +#### G2 multiplication + +`64000` gas + +#### G1/G2 Multiexponentiation -##### G1/G2 Multiexponentiation Discounts table as a vector of pairs `[k, discount]`: ``` - +[[1, 1266], [2, 733], [3, 561], [4, 474], [5, 422], [6, 387], [7, 362], [8, 344], [9, 329], [10, 318], [11, 308], [12, 300], [13, 296], [14, 289], [15, 283], [16, 279], [17, 275], [18, 272], [19, 269], [20, 266], [21, 265], [22, 260], [23, 259], [24, 256], [25, 255], [26, 254], [27, 252], [28, 251], [29, 250], [30, 249], [31, 249], [32, 220], [33, 228], [34, 225], [35, 223], [36, 219], [37, 216], [38, 214], [39, 212], [40, 209], [41, 209], [42, 205], [43, 203], [44, 202], [45, 200], [46, 198], [47, 196], [48, 199], [49, 195], [50, 192], [51, 192], [52, 191], [53, 190], [54, 187], [55, 186], [56, 185], [57, 184], [58, 184], [59, 181], [60, 181], [61, 181], [62, 180], [63, 178], [64, 179], [65, 176], [66, 177], [67, 176], [68, 175], [69, 174], [70, 173], [71, 171], [72, 171], [73, 170], [74, 170], [75, 169], [76, 168], [77, 168], [78, 167], [79, 167], [80, 166], [81, 165], [82, 167], [83, 166], [84, 166], [85, 165], [86, 165], [87, 164], [88, 164], [89, 163], [90, 163], [91, 162], [92, 162], [93, 160], [94, 163], [95, 159], [96, 162], [97, 159], [98, 160], [99, 159], [100, 159], [101, 158], [102, 158], [103, 158], [104, 158], [105, 157], [106, 157], [107, 156], [108, 155], [109, 155], [110, 156], [111, 155], [112, 155], [113, 154], [114, 155], [115, 154], [116, 153], [117, 153], [118, 153], [119, 152], [120, 152], [121, 152], [122, 152], [123, 151], [124, 151], [125, 151], [126, 151], [127, 151], [128, 150]] ``` -`max_discount = ` +`max_discount = 150` -##### Pairing operation -Base cost of the pairing operation is `*k + ` where `k` is a number of pairs. +#### Pairing operation + +Base cost of the pairing operation is `120000*k + 320000` where `k` is a number of pairs. ## Rationale -Gas costs are based on EIP1962 estimation strategy (but do not fully include yet parsing of ABI, decoding and encoding of the result as a byte array). -#### Gas estimation strategy -Gas cost is derived by taking the average timing of the same operations over different implementations and assuming a constant `30 MGas/second`. Since the execution time is machine-specific, this constant is determined based on execution times of [ECRECOVER](https://github.com/matter-labs/eip1962/blob/master/run_bn_pairing_estimate.sh) and [BNPAIR](https://github.com/matter-labs/eip1962/blob/master/run_bn_pairing_estimate.sh) precompiles on my machine and their proposed gas price (`43.5 MGas/s` for ECRECOVER and `16.5 MGas/s` for BNPAIR). Following are the proposed methods to time the precompile operations: +Gas costs are based on [EIP-1962](./eip-1962.md) estimation strategy (but do not fully include yet parsing of ABI, decoding and encoding of the result as a byte array). + +### Gas estimation strategy + +Gas cost is derived by taking the average timing of the same operations over different implementations and assuming a constant `30 MGas/second`. Since the execution time is machine-specific, this constant is determined based on execution times of *ECRECOVER* and *BNPAIR* precompiles on my machine and their proposed gas price (`43.5 MGas/s` for ECRECOVER and `16.5 MGas/s` for BNPAIR). Following are the proposed methods to time the precompile operations: - G1 addition: Average timing of 1000 random samples. - G1 multiplication: Average timing of 1000 samples of random worst-case of double-and-add algorithm (scalar of max bit length and max hamming weight and random base points in G1) @@ -222,13 +239,16 @@ Gas cost is derived by taking the average timing of the same operations over dif - G1 and G2 multiexponentiations: Expected to be performed by the Peppinger algorithm, with a table prepared for discount in case of `k <= 128` points in the multiexponentiation with a discount cup `max_discount` for `k > 128`. To avoid non-integer arithmetic call cost is calculated as `k * multiplication_cost * discount / multiplier` where `multiplier = 1000`, `k` is a number of (scalar, point) pairs for the call, `multiplication_cost` is a corresponding single multiplication call cost for G1/G2. - Pairing: Average timing of 1000 random samples (random points in G1 and G2) for different number of pairs with linear lifting. -#### Multiexponentiation as a separate call +### Multiexponentiation as a separate call + Explicit separate multiexponentiation operation that allows one to save execution time (so gas) by both the algorithm used (namely Peppinger algorithm) and (usually forgotten) by the fact that `CALL` operation in Ethereum is expensive (at the time of writing), so one would have to pay non-negigible overhead if e.g. for multiexponentiation of `100` points would have to call the multipication precompile `100` times and addition for `99` times (roughly `138600` would be saved). -#### Explicit subgroup checks +### Explicit subgroup checks + G2 subgroup check has the same cost as G1 subgroup check. Endomorphisms can be leverages to optimize this operation. ## Backwards Compatibility + There are no backward compatibility questions. ## Test Cases @@ -246,32 +266,39 @@ Requeired properties for basic ops (add/multiply): - Multiplication by the unnormalized scalar `(scalar + group_order) * P = scalar * P` Required properties for pairing operation: + - Degeneracy `e(P, 0*Q) = e(0*P, Q) = 1` - Bilinearity `e(a*P, b*Q) = e(a*b*P, Q) = e(P, a*b*Q)` (internal test, not visible through ABI) -Test vector for all operations are expanded in this [gist](https://gist.github.com/shamatar/506ab3193a7932fe9302a2f3a31a23e8) until it's final. +## Reference Implementation -## Implementation There is a various choice of existing implementations: **Libraries:** -- Rust implementation (EY/Zexe): https://github.com/yelhousni/zexe/tree/youssef/BW6-761-Fq-ABLR-2ML-M -- C++ implementation (EY/libff): https://github.com/EYBlockchain/zk-swap-libff -- Golang implementation (Consensys/gurvy): https://github.com/ConsenSys/gurvy + +- Rust implementation (EY/Zexe): github.com/yelhousni/zexe/tree/youssef/BW6-761-Fq-ABLR-2ML-M +- C++ implementation (EY/libff): github.com/EYBlockchain/zk-swap-libff +- Golang implementation (Consensys/gurvy): github.com/ConsenSys/gurvy **Stand-alone implementation:** -- Golang implementation with Intel assembly (Onur Kilic): https://github.com/kilic/bw6 + +- Golang implementation with Intel assembly (Onur Kilic): github.com/kilic/bw6 **Precompiles:** -- OpenEthereum (EY/Parity): https://github.com/EYBlockchain/solidity-elliptic-curves -**Sripts:** -- SageMath and Magma scripts: https://gitlab.inria.fr/zk-curves/bw6-761/ +- OpenEthereum (EY/Parity): github.com/EYBlockchain/solidity-elliptic-curves +- Frontier (Parity): github.com/paritytech/frontier/pull/1049/files + +**Scripts:** + +- SageMath and Magma scripts: gitlab.inria.fr/zk-curves/bw6-761/ ## Security Considerations + Strictly following the spec will eliminate security implications or consensus implications in a contrast to the previous BN254 precompile. Important topic is a "constant time" property for performed operations. We explicitly state that this precompile **IS NOT REQUIRED** to perform all the operations using constant time algorithms. ## 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-3030.md b/EIPS/eip-3030.md index 25da7195c9be2d..7d3ecba3d8de80 100644 --- a/EIPS/eip-3030.md +++ b/EIPS/eip-3030.md @@ -3,7 +3,7 @@ eip: 3030 title: BLS Remote Signer HTTP API author: Herman Junge (@hermanjunge) discussions-to: https://ethereum-magicians.org/t/eip-3030-bls-remote-signer-http-api-standard/4810 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-09-30 @@ -358,4 +358,4 @@ An attacker can dump the memory in the remote signer to disclose a secret key. | An attacker can DoS the remote signer. | Implement IP filtering.
_or_
Implement Rate limiting. ## 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-3041.md b/EIPS/eip-3041.md index 20fb247014f386..3a21c44d1b91b3 100644 --- a/EIPS/eip-3041.md +++ b/EIPS/eip-3041.md @@ -3,7 +3,7 @@ eip: 3041 title: Adds `baseFee` to `eth_getBlockByHash` author: Abdelhamid Bakhta (@abdelhamidbakhta) discussions-to: https://ethereum-magicians.org/t/eip-3041-add-basefee-in-eth-getblockbyhash-response/4825 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-10-13 @@ -91,4 +91,4 @@ Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) The added field (`baseFee`) is informational and does not introduce technical security issues. ## 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-3044.md b/EIPS/eip-3044.md index 9dcd9870214c6d..73af4b98df9dc7 100644 --- a/EIPS/eip-3044.md +++ b/EIPS/eip-3044.md @@ -3,7 +3,7 @@ eip: 3044 title: Adds `baseFee` to `eth_getBlockByNumber` author: Abdelhamid Bakhta (@abdelhamidbakhta) discussions-to: https://ethereum-magicians.org/t/eip-3044-add-basefee-to-eth-getblockbynumber/4828 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-10-14 @@ -91,4 +91,4 @@ Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) The added field (`baseFee`) is informational and does not introduce technical security issues. ## 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-3045.md b/EIPS/eip-3045.md index 07f0c4ab9945fd..f3a2f2c40603b2 100644 --- a/EIPS/eip-3045.md +++ b/EIPS/eip-3045.md @@ -3,7 +3,7 @@ eip: 3045 title: Adds `baseFee` to `eth_getUncleByBlockHashAndIndex` author: Abdelhamid Bakhta (@abdelhamidbakhta) discussions-to: https://ethereum-magicians.org/t/add-basefee-to-eth-getunclebyblockhashandindex/4829 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-10-14 @@ -91,4 +91,4 @@ Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) The added field (`baseFee`) is informational and does not introduce technical security issues. ## 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-3046.md b/EIPS/eip-3046.md index 0804a25defda1e..d5ca817043c80a 100644 --- a/EIPS/eip-3046.md +++ b/EIPS/eip-3046.md @@ -3,7 +3,7 @@ eip: 3046 title: Adds `baseFee` to `eth_getUncleByBlockNumberAndIndex` author: Abdelhamid Bakhta (@abdelhamidbakhta) discussions-to: https://ethereum-magicians.org/t/add-basefee-to-eth-getunclebyblocknumberandindex/4830 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-10-14 @@ -91,4 +91,4 @@ Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) The added field (`baseFee`) is informational and does not introduce technical security issues. ## 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-3068.md b/EIPS/eip-3068.md index 6a27b8bbf416dc..8283305613fd99 100644 --- a/EIPS/eip-3068.md +++ b/EIPS/eip-3068.md @@ -3,7 +3,7 @@ eip: 3068 title: Precompile for BN256 HashToCurve Algorithms author: Dr. Christopher Gorman (@chgormanMH) discussions-to: https://ethereum-magicians.org/t/pre-compile-for-bls/3973 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-10-23 @@ -293,5 +293,4 @@ related to the elliptic curve pairing; they are independent of this EIP. ## 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-3074.md b/EIPS/eip-3074.md index 96a712c42ed989..0c0480087d97c6 100644 --- a/EIPS/eip-3074.md +++ b/EIPS/eip-3074.md @@ -1,27 +1,25 @@ --- eip: 3074 title: AUTH and AUTHCALL opcodes +description: Allow externally owned accounts to delegate control to a contract. author: Sam Wilson (@SamWilsn), Ansgar Dietrichs (@adietrichs), Matt Garnett (@lightclient), Micah Zoltu (@micahzoltu) discussions-to: https://ethereum-magicians.org/t/eip-3074-sponsored-transaction-precompile/4880 status: Review type: Standards Track category: Core created: 2020-10-15 +requires: 155 --- -## Simple Summary - -Allow externally owned accounts (EOAs) to delegate control of their account to a contract. - ## Abstract -This EIP introduces two EVM instructions `AUTH` and `AUTHCALL`. The first sets a context variable `authorized` based on an ECDSA signature. The second sends a call as the `authorized`. This essentially delegates control of the EOA to smart contract. +This EIP introduces two EVM instructions `AUTH` and `AUTHCALL`. The first sets a context variable `authorized` based on an ECDSA signature. The second sends a call as the `authorized` account. This essentially delegates control of the externally owned account (EOA) to a smart contract. ## Motivation -Adding more functionlity to EOAs has been a long-standing feature request. The requests have spanned from implementing batching capabilities, allowing for gas sponsoring, expirations, scripting, and beyond. These changes often mean increased complexity and rigidity of the protocol. In some cases, it also means increased attack surfaces. +Adding more functionality to EOAs has been a long-standing feature request. The requests have spanned from implementing batching capabilities, allowing for gas sponsoring, expirations, scripting, and beyond. These changes often mean increased complexity and rigidity of the protocol. In some cases, it also means increased attack surfaces. -This EIP takes a different approach. Instead of enshrining these capabilities in the protocol as transaction validity requirements, it allows users to *delegate* control of their EOA to a contract. This gives developers a flexible framework for developing novel transaction schemes for EOAs. A good analogy for the benefit this EIP provides is that it's similar to allowing any EOA to become a smart contract wallet *without* deploying a contract. +This EIP takes a different approach. Instead of enshrining these capabilities in the protocol as transaction validity requirements, it allows users to *delegate* control of their EOA to a contract. This gives developers a flexible framework for developing novel transaction schemes for EOAs. A motivating use case of this EIP is that it allows any EOA to act like a smart contract wallet *without* deploying a contract. Although this EIP provides great benefit to individual users, the leading motivation for this EIP is "sponsored transactions". This is where the fee for a transaction is provided by a different account than the one that originates the call. @@ -41,7 +39,7 @@ With the extraordinary growth of tokens on Ethereum, it has become common for EO | ---------------- | ------ | | `MAGIC` | `0x03` | -`MAGIC` is used for EIP-3074 signatures to prevent signature collisions with other signing formats. +`MAGIC` is used for [EIP-3074](./eip-3074.md) signatures to prevent signature collisions with other signing formats. ### Context Variables @@ -55,38 +53,67 @@ The variable has the same scope as the program counter -- `authorized` persists ### `AUTH` (`0xf6`) -A new opcode `AUTH` shall be created at `0xf6`. It shall take four stack element inputs and returns one stack element. +A new opcode `AUTH` shall be created at `0xf6`. It shall take three stack element inputs (the last two describing a memory range), and it shall return one stack element. #### Input +##### Stack + | Stack | Value | | ---------- | ------------ | -| `top - 0` | `commit` | -| `top - 1` | `yParity` | -| `top - 2` | `r` | -| `top - 3` | `s` | +| `top - 0` | `authority` | +| `top - 1` | `offset` | +| `top - 2` | `length` | + +##### Memory + +The final two stack arguments (`offset` and `length`) describe a range of memory. The format of the contents of that range is: + + - `memory[offset : offset+32 ]` - `yParity` + - `memory[offset+32 : offset+64 ]` - `r` + - `memory[offset+64 : offset+96 ]` - `s` + - `memory[offset+96 : offset+128]` - `commit` #### Output +##### Stack + | Stack | Value | | ---------- | -------------| -| `top - 0` | `authorized` | +| `top - 0` | `success` | + +##### Memory + +Memory is not modified by this instruction. #### Behavior -The arguments (`yParity`, `r`, `s`) are interpreted as an ECDSA signature on the secp256k1 curve over the message `keccak256(MAGIC || paddedInvokerAddress || commit)`, where: +If `length` is greater than 128, the extra bytes are ignored for signature verification (they still incur a gas cost as defined later). Bytes outside the range (in the event `length` is less than 128) are treated as if they had been zeroes. + +`authority` is the address of the account which generated the signature. + +The arguments (`yParity`, `r`, `s`) are interpreted as an ECDSA signature on the secp256k1 curve over the message `keccak256(MAGIC || chainId || paddedInvokerAddress || commit)`, where: + + - `chainId` is the current chain's [EIP-155](./eip-155.md) unique identifier padded to 32 bytes. - `paddedInvokerAddress` is the address of the contract executing `AUTH` (or the active state address in the context of `CALLCODE` or `DELEGATECALL`), left-padded with zeroes to a total of 32 bytes (ex. `0x000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`). - `commit`, one of the arguments passed into `AUTH`, is a 32-byte value that can be used to commit to specific additional validity conditions in the invoker's pre-processing logic (e.g. a nonce for replay protection). -Signature validity and signer recovery is handled analogous to transaction signatures, including the stricter `s` range for preventing ECDSA malleability. Note that `yParity` is expected to be `0` or `1`. +Signature validity and signer recovery is handled analogously to transaction signatures, including the stricter `s` range for preventing ECDSA malleability. Note that `yParity` is expected to be `0` or `1`. -If the signature is valid, the context variable `authorized` is set to the signer address. In particular, this is also true if `signerAddress == tx.origin`, which used to be handled separately in earlier versions of this EIP (see Security Considerations). If the signature is instead invalid, `authorized` is reset to an unset value. +If the signature is valid and the signer address is equal to `authority`, the context variable `authorized` is set to the `authority`. In particular, this is also true if `authority == tx.origin`, which used to be handled separately in earlier versions of this EIP (see Security Considerations). If the signature is instead invalid or the signer address does not equal `authority`, `authorized` is reset to an unset value. -`AUTH` returns the new `authorized` if set, or `0` otherwise. +`AUTH` returns `1` if `authorized` is set, or `0` otherwise. #### Gas Cost -The gas cost for `AUTH` is `3100`. This is equal to the cost for the `ecrecover` precompile, plus a bit extra to cover a keccak256 hash and some additional logic. +The gas cost for `AUTH` is equal to the sum of: + + - fixed fee `3100`. + - memory expansion gas cost (`auth_memory_expansion_fee`) + +The fixed fee is equal to the cost for the `ecrecover` precompile, plus a bit extra to cover a keccak256 hash and some additional logic. + +The memory expansion gas cost (`auth_memory_expansion_fee`) shall be calculated in the same way as `RETURN`, where memory is expanded if the specified range is outside the current allocation. ### `AUTHCALL` (`0xf7`) @@ -113,10 +140,11 @@ A new opcode `AUTHCALL` shall be created at `0xf7`. It shall take eight stack el #### Behavior -`AUTHCALL` is interpreted the same as `CALL`, except for: +`AUTHCALL` is interpreted the same as `CALL`, except for (note: this list is also the order of precedence for the logical checks): - If `authorized` is unset, execution is invalid (as defined above). Otherwise, the caller address for the call is set to `authorized`. - The gas cost, including how much gas is available for the subcall, is specified in the Gas Cost section. + - If the `gas` operand is equal to `0`, the instruction will send all available gas as per [EIP-150](./eip-150). - If the gas available for the subcall would be less than `gas`, execution is invalid. - There is no gas stipend, even for non-zero `value`. - `value` is deducted from the balance of the executing contract. It is not paid by `authorized`. If `value` is higher than the balance of the executing contract, execution is invalid. @@ -167,6 +195,16 @@ As with `CALL`, the full gas cost is charged immediately, independently of actua ## Rationale +### Signature in Memory + +The signature format (`yParity`, `r`, and `s`) is fixed, so it might seem curious that `auth` accepts a dynamic memory range. The signature is placed in memory so that `auth` can be upgraded in the future to work with contract accounts (which might use non-ECDSA signatures) and not just EOAs. + +### Signing Address `auth` Argument + +Including `authority` (the signing address) as an argument to `auth` allows future upgrades to the instruction to work with contract accounts, and not just EOAs. + +If `authority` were not included and multiple signature schemes allowed, it would not be possible to compute the authorizing account's address from just the signature alone. + ### Reserving One Sixty-Fourth of Available Gas `AUTHCALL` will not pass more than 63/64th of the available gas for the reasons enumerated in [EIP-150](./eip-150.md). @@ -179,7 +217,7 @@ A well-behaved contract should never reach an `AUTHCALL` without having successf There are two general approaches to separating the "fee payer" from the "action originator". -The first is introducing a new transaction. This requires significant changes to clients to support and is generally less upgradeable than other solutions (e.g. this EIP). This approach is also not immediately compatible with account abstraction (AA). These proposals require a _signed_ transaction from the sponsor's account, which is not possible from an AA contract, because it has no private key to sign with. The main advantage of new transaction types is that the validity requirements are enforced by the protocol, therefore invalid transactions do not pollute block space. +The first is introducing a new transaction type. This requires significant changes to clients to support and is generally less upgradeable than other solutions (e.g. this EIP). This approach is also not immediately compatible with account abstraction (AA). These proposals require a *signed* transaction from the sponsor's account, which is not possible from an AA contract, because it has no private key to sign with. The main advantage of new transaction types is that the validity requirements are enforced by the protocol, therefore invalid transactions do not pollute block space. The other main approach is to introduce a new mechanism in the EVM to masquerade as other accounts. This EIP introduces `AUTH` and `AUTHCALL` to make calls as EOAs. There are many different permutations of this mechanism. An alternative mechanism would be add an opcode that can make arbitrary calls based on a similar address creation scheme as `CREATE2`. Although this mechanism would not benefit users today, it would immediately allow for those accounts to send and receive ether -- making it feel like a more first-class primitive. @@ -203,7 +241,7 @@ A user will specifically interact with an invoker they trust. Because they trust ![auth message format](../assets/eip-3074/auth-msg.png) -Using `commit` as a hash of values allows for invokers to implement arbitrary constraints. For example, they could allow accounts to have `N` parallel nonces. Or, they could allow a user to commit to multiple calls with a single signature. This would allow mult-tx flows, such as ERC-20 `approve`-`transfer` actions, to be condensed into a single transaction with a single signature verification. A commitment to multiple calls would look something like the diagram below. +Using `commit` as a hash of values allows for invokers to implement arbitrary constraints. For example, they could allow accounts to have `N` parallel nonces. Or, they could allow a user to commit to multiple calls with a single signature. This would allow mult-tx flows, such as [ERC-20](./eip-20.md) `approve`-`transfer` actions, to be condensed into a single transaction with a single signature verification. A commitment to multiple calls would look something like the diagram below. ![multi-call auth message](../assets/eip-3074/auth-msg-multi-call.png) @@ -214,7 +252,7 @@ The invoker contract is a trustless intermediary between the sponsor and sponsee Choosing an invoker is similar to choosing a smart contract wallet implementation. It's important to choose one that has been thoroughly reviewed, tested, and accepted by the community as secure. We expect a few invoker designs to be utilized by most major transaction relay providers, with a few outliers that offer more novel mechanisms. -An important note is that invoker contracts **MUST NOT** be upgradeable. If an invoker can be redeployed to the same address with different code, it would be possible to redeploy the invoker with code that does not properly verify `commit` and any account that signed a message over that invoker would be compromised. Although this sounds scary, it is no different than using a smart contract wallet via `DELEGATECALL`. If the wallet is redeployed with different logic, all wallet using its code could be compromised. +An important note is that invoker contracts **MUST NOT** be upgradeable. If an invoker can be redeployed to the same address with different code, it would be possible to redeploy the invoker with code that does not properly verify `commit` and any account that signed a message over that invoker would be compromised. Although this sounds scary, it is no different than using a smart contract wallet via `DELEGATECALL`. If the wallet is redeployed with different logic, all wallets using its code could be compromised. ### On Call Depth @@ -226,10 +264,10 @@ It is, therefore, sufficient for the invoker to guarantee a minimum amount of ga Any non-zero `value` passed into an `AUTHCALL` is deducted from the invoker's balance. A natural alternative source for `value` would be the `authorized` account. However, deducting value from an EOA mid-execution is problematic, as it breaks important invariants for handling pending transactions. Specifically: -* Transaction pools expect transactions for a given EOA to only turn invalid when other transactions from the same EOA are included into a block, increasing its nonce and (possibly) decreasing its balance. Deducting `value` from the `authorized` account would make transaction invalidation an unpredictable side effect of any smart contract execution. -* Similarly, miners rely on the ability to statically pick a set of valid transactions from their transaction pool to include into a new block. Deducting `value` from the `authorized` account would break this ability, increasing the overhead and thus the time for block creation. +- Transaction pools expect transactions for a given EOA to only turn invalid when other transactions from the same EOA are included into a block, increasing its nonce and (possibly) decreasing its balance. Deducting `value` from the `authorized` account would make transaction invalidation an unpredictable side effect of any smart contract execution. +- Similarly, miners rely on the ability to statically pick a set of valid transactions from their transaction pool to include into a new block. Deducting `value` from the `authorized` account would break this ability, increasing the overhead and thus the time for block creation. -At the same time, the ability to directly take ether out of the `authorized` account is an important piece of functionality and thus a desired future addition via an additional opcode similar to `AUTHCALL`. For this reason, it is included as `valueExt`, an operand of `AUTHCALL`, which may be activated in a future fork. The prerequisite for that would be to find satisfying mitigations to the transaction invalidation concerns outlined above. One potential avenue for that could be the addition of account access lists similar to EIP-2930, used to signal accounts whose balance can be reduced as a side effect of the transaction (without on their own constituting authorization to do so). +At the same time, the ability to directly take ether out of the `authorized` account is an important piece of functionality and thus a desired future addition via an additional opcode similar to `AUTHCALL`. For this reason, it is included as `valueExt`, an operand of `AUTHCALL`, which may be activated in a future fork. The prerequisite for that would be to find satisfying mitigations to the transaction invalidation concerns outlined above. One potential avenue for that could be the addition of account access lists similar to [EIP-2930](./eip-2930.md), used to signal accounts whose balance can be reduced as a side effect of the transaction (without on their own constituting authorization to do so). ### Allowing `tx.origin` as Signer @@ -251,36 +289,33 @@ This distribution of occurrences—many (1), some (2), and no (3)—is exactly w There are other approaches to mitigate this restriction which do not break the invariant: - * Set `tx.origin` to a constant `ENTRY_POINT` address for `AUTHCALL`s. - * Set `tx.origin` to the invoker address for `AUTHCALL`s. - * Set `tx.origin` to a special address derived from any of the sender, invoker, and/or signer addresses. - * Disallow `authorized == tx.origin`. This would make the simple batching use cases impossible, but could be relaxed in the future. + - Set `tx.origin` to a constant `ENTRY_POINT` address for `AUTHCALL`s. + - Set `tx.origin` to the invoker address for `AUTHCALL`s. + - Set `tx.origin` to a special address derived from any of the sender, invoker, and/or signer addresses. + - Disallow `authorized == tx.origin`. This would make the simple batching use cases impossible, but could be relaxed in the future. -## Backwards Compatibility +### `AUTHCALL` cheaper than `CALL` when sending value -No known issues. +Sending non-zero value with `CALL` increases its cost by 9,000. Of that, 6,700 covers the increased overhead of the balance transfer and 2,300 is used as a stipend into the subcall to seed its gas counter. `AUTHCALL` does not provide a stipend and thus only charges the base 6,700. -## Test Cases - -TODO +## Backwards Compatibility -## Implementation +Although this EIP poses no issues for backwards compatibility, there are concerns that it limits future changes to accounts by further enshrining ECDSA signatures. For example, it might be desirable to erradicate the concept of EOAs altogether, and replace them with smart contract wallets that emulate the same behavior. This is fully compatible with the EIP as written, however, it gets tricky if users can then elect to "upgrade" their smart contract wallets to use other methods of authentication -- e.g. convert into a multisig. Without any changes, `AUTH` would not respect this new logic and continue allowing the old private key to perform actions on behalf of the account. -https://github.com/quilt/go-ethereum/tree/eip-3074 +A solution to this would be at the same time that EOAs are removed, to modify the logic of `AUTH` to actually call into the account with some standard message and allow the account to determine if the signature / witness is valid. Further research should be done to understand how invokers would need to change in this situation and how best to write them in a future-compatible manner. ## Security Considerations ### Secure Invokers -The following is a non-exhaustive list of checks/pitfalls/conditions that invokers _should_ be wary of: +The following is a non-exhaustive list of checks/pitfalls/conditions that invokers *should* be wary of: - Replay protection (ex. a nonce) should be implemented by the invoker, and included in `commit`. Without it, a malicious actor can reuse a signature, repeating its effects. - `value` should be included in `commit`. Without it, a malicious sponsor could cause unexpected effects in the callee. - `gas` should be included in `commit`. Without it, a malicious sponsor could cause the callee to run out of gas and fail, griefing the sponsee. - - The current chain id should be included in `commit` and checked against `CHAINID` on *every transaction*. Without it, a malicious sponsor could replay a signature on a different chain. - `addr` and `calldata` should be included in `commit`. Without them, a malicious actor may call arbitrary functions in arbitrary contracts. -A poorly implemented invoker can _allow a malicious actor to take near complete control over a signer's EOA_. +A poorly implemented invoker can *allow a malicious actor to take near complete control over a signer's EOA*. ### Allowing `tx.origin` as Signer @@ -293,4 +328,4 @@ The authors of this EIP believe the risks of allowing `authorized` to equal `tx. ## 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-3076.md b/EIPS/eip-3076.md index c55c8ae9dbbd0a..24b8245676872d 100644 --- a/EIPS/eip-3076.md +++ b/EIPS/eip-3076.md @@ -1,22 +1,19 @@ --- eip: 3076 title: Slashing Protection Interchange Format +description: A JSON interchange format for proof of stake validators to migrate slashing protection data between clients. author: Michael Sproul (@michaelsproul), Sacha Saint-Leger (@sachayves), Danny Ryan (@djrtwo) -discussions-to: https://ethereum-magicians.org/t/eip-3076-validator-client-interchange-format-slashing-protection/ -status: Review +discussions-to: https://ethereum-magicians.org/t/eip-3076-validator-client-interchange-format-slashing-protection/4883 +status: Last Call +last-call-deadline: 2021-11-03 type: Standards Track -category: Core +category: Interface created: 2020-10-27 -updated: 2021-02-08 --- -## Simple Summary - -A JSON interchange format for proof of stake validators to migrate slashing protection data between clients. - ## Abstract -A standard format for transferring a key's signing history allows validators to easily switch between clients without the risk of signing conflicting messages. While a [common keystore format](https://eips.ethereum.org/EIPS/eip-2335) provides part of the solution, it does not contain any information about a key's signing history. For a validator moving their keys from client A to client B, this could lead to scenarios in which client B inadvertently signs a message that conflicts with an earlier message signed with client A. The interchange format described here provides a solution to this problem. +A standard format for transferring a key's signing history allows validators to easily switch between clients without the risk of signing conflicting messages. While a common keystore format provides part of the solution, it does not contain any information about a key's signing history. For a validator moving their keys from client A to client B, this could lead to scenarios in which client B inadvertently signs a message that conflicts with an earlier message signed with client A. The interchange format described here provides a solution to this problem. ## Motivation @@ -24,7 +21,7 @@ The proof of stake (PoS) protocol penalises validators for voting in ways that c For a validator following the protocol correctly, there is, in principle, no risk of being slashed. However, changing clients (from client A to client B, say) can result in a slashing risk if client B is unaware of the blocks and attestations that were signed with client A. -This can can occur if client A and client B do not agree on what the present time is. For example, say client A's time is accidentally set to a day in the future (225 epochs), and a validator switches from client A to client B without giving B a record of the blocks and attestations signed with A. The validator in question now runs the risk of attesting to two different blocks in the same epoch (a slashable offence) for the next 225 epochs (since they've already voted on these epochs with client A, and now stand to vote on them again with client B). Such time-skew bugs have been observed in the wild. +This can occur if client A and client B do not agree on what the present time is. For example, say client A's time is accidentally set to a day in the future (225 epochs), and a validator switches from client A to client B without giving B a record of the blocks and attestations signed with A. The validator in question now runs the risk of attesting to two different blocks in the same epoch (a slashable offence) for the next 225 epochs (since they've already voted on these epochs with client A, and now stand to vote on them again with client B). Such time-skew bugs have been observed in the wild. Another situation in which slashing protection is critical is in the case of re-orgs. During a re-org it is possible for a validator to be assigned new attestation duties for an epoch in which it has already signed an attestation. In this case it is essential that the record of the previous attestation is available, even if the validator just moved from one client to another in the space of a single epoch. @@ -171,12 +168,14 @@ A valid interchange file is one that adheres to the following JSON schema, and i After importing an interchange file with data field `data`, a signer must respect the following conditions: -1. Refuse to sign any block that is slashable with respect to the blocks contained in `data.signed_blocks`. For details of what constitutes a slashable block, see [process_proposer_slashing][pps]. If the `signing_root` is absent from a block, a signer must assume that any new block with the same `slot` is slashable with respect to the imported block. +1. Refuse to sign any block that is slashable with respect to the blocks contained in `data.signed_blocks`. For details of what constitutes a slashable block, see `process_proposer_slashing` (from `consensus-specs`). If the `signing_root` is absent from a block, a signer must assume that any new block with the same `slot` is slashable with respect to the imported block. 2. Refuse to sign any block with `slot <= min(b.slot for b in data.signed_blocks if b.pubkey == proposer_pubkey)`, except if it is a repeat signing as determined by the `signing_root`. -3. Refuse to sign any attestation that is slashable with respect to the attestations contained in `data.signed_attestations`. For details of what constitutes a slashable attestation, see [is_slashable_attestation_data][isad]. +3. Refuse to sign any attestation that is slashable with respect to the attestations contained in `data.signed_attestations`. For details of what constitutes a slashable attestation, see `is_slashable_attestation_data`. + 4. Refuse to sign any attestation with source epoch less than the minimum source epoch present in that signer's attestations (as seen in `data.signed_attestations`). In pseudocode: + ```python3 source.epoch < min(att.source_epoch @@ -184,7 +183,8 @@ source.epoch < if att.pubkey == attester_pubkey) ``` -5. Refuse to sign any attestation with target epoch less than or equal to the minimum target epoch present in that signer's attestations (as seen in `data.signed_attestations`). In pseudocode: +{:start="5"} +5. Refuse to sign any attestation with target epoch less than or equal to the minimum target epoch present in that signer's attestations (as seen in `data.signed_attestations`), except if it is a repeat signing as determined by the `signing_root`. In pseudocode: ```python3 target_epoch <= @@ -199,14 +199,9 @@ target_epoch <= - A signed block or attestation's `signing_root` refers to the message data (hash tree root) that gets signed with a BLS signature. It allows validators to re-sign and re-broadcast blocks or attestations if asked. -- The `signed_blocks` `signing_root`s are calculated using [`compute_signing_root(block, domain)`][csr]: where `block` is the block (of type `BeaconBlock` or `BeaconBlockHeader`) that was signed, and `domain` is equal to [`compute_domain(DOMAIN_BEACON_PROPOSER, fork, metadata.genesis_validators_root)`][cd]. - -- The `signed_attestations` `signing_root`s are calculated using [`compute_signing_root(attestation, domain)`][csr]: where `attestation` is the attestation (of type `AttestationData`) that was signed, and `domain` is equal to [`compute_domain(DOMAIN_BEACON_ATTESTER, fork, metadata.genesis_validators_root)`][cd]. +- The `signed_blocks` `signing_root`s are calculated using `compute_signing_root(block, domain)`: where `block` is the block (of type `BeaconBlock` or `BeaconBlockHeader`) that was signed, and `domain` is equal to `compute_domain(DOMAIN_BEACON_PROPOSER, fork, metadata.genesis_validators_root)`. -[pps]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#proposer-slashings -[isad]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#is_slashable_attestation_data -[csr]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#compute_signing_root -[cd]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#compute_domain +- The `signed_attestations` `signing_root`s are calculated using `compute_signing_root(attestation, domain)`: where `attestation` is the attestation (of type `AttestationData`) that was signed, and `domain` is equal to `compute_domain(DOMAIN_BEACON_ATTESTER, fork, metadata.genesis_validators_root)`. ## Rationale @@ -246,21 +241,21 @@ In order to minimise risk and complexity, the format has been designed to map cl For implementers who use a complete record of signed messages to implement their slashing protection database, we make the following recommendations: -* You MUST ensure that, in addition to importing all of the messages from an interchange, all the [conditions](#conditions) are enforced. In particular, conditions (2), (4) and (5) may not have been enforced by your implementation before adopting the interchange format. Our recommendation is to enforce these rules at all times, to keep the implementation clean and minimise the attack surface. For example: your slashing protection mechanism should not sign a block with a slot number less than, or equal to, the minimum slot number of a previously signed block, _irrespective_ of whether that minimum-slot block was imported from an interchange file, or inserted as part of your database's regular operation. -* If your database records the signing roots of messages in addition to their slot/epochs, you should ensure that imported messages without signing roots are assigned a suitable dummy signing root internally. We suggest using a special "null" value which is distinct from all other signing roots, although a value like `0x0` may be used instead (as it is extremely unlikely to collide with any real signing root). -* Care must be taken to avoid signing messages within a gap in the database (an area of unknown signing activity). This could occur if two interchanges were imported with a large gap between the last entry of the first and the first entry of the second. Signing in this gap is not safe, and would violate conditions (2), (4) and (5). It can be avoided by storing an explicit low watermark in addition to the actual messages of the slashing protection database, or by pruning on import so that the oldest messages from the interchange become the oldest messages in the database. +- You MUST ensure that, in addition to importing all of the messages from an interchange, all the [conditions](#conditions) are enforced. In particular, conditions (2), (4) and (5) may not have been enforced by your implementation before adopting the interchange format. Our recommendation is to enforce these rules at all times, to keep the implementation clean and minimise the attack surface. For example: your slashing protection mechanism should not sign a block with a slot number less than, or equal to, the minimum slot number of a previously signed block, _irrespective_ of whether that minimum-slot block was imported from an interchange file, or inserted as part of your database's regular operation. +- If your database records the signing roots of messages in addition to their slot/epochs, you should ensure that imported messages without signing roots are assigned a suitable dummy signing root internally. We suggest using a special "null" value which is distinct from all other signing roots, although a value like `0x0` may be used instead (as it is extremely unlikely to collide with any real signing root). +- Care must be taken to avoid signing messages within a gap in the database (an area of unknown signing activity). This could occur if two interchanges were imported with a large gap between the last entry of the first and the first entry of the second. Signing in this gap is not safe, and would violate conditions (2), (4) and (5). It can be avoided by storing an explicit low watermark in addition to the actual messages of the slashing protection database, or by pruning on import so that the oldest messages from the interchange become the oldest messages in the database. ### Advice for Minimal Databases For implementers who wish to implement their slashing protection database by storing only the latest block and attestation for each validator, we make the following recommendations: -* During import, make sure you take the _maximum_ slot block and _maximum_ source and target attestations for each validator. Although the [conditions](#conditions) require the minimums to be enforced, taking the maximums from an interchange file and merging them with any existing values in the database is the recommended approach. For example, if the interchange file includes blocks for validator `V` at slots 4, 98 and 243, then the latest signed block for validator `V` should be updated to the one from slot 243. However, if the database has already included a block for this validator at a slot greater than 243, for example, slot 351, then the database's existing value should remain unchanged. +- During import, make sure you take the _maximum_ slot block and _maximum_ source and target attestations for each validator. Although the [conditions](#conditions) require the minimums to be enforced, taking the maximums from an interchange file and merging them with any existing values in the database is the recommended approach. For example, if the interchange file includes blocks for validator `V` at slots 4, 98 and 243, then the latest signed block for validator `V` should be updated to the one from slot 243. However, if the database has already included a block for this validator at a slot greater than 243, for example, slot 351, then the database's existing value should remain unchanged. ### General Recommendations -* To avoid exporting an outdated interchange file -- an action which creates a slashing risk -- your implementation should only allow the slashing protection database to be exported when the validator client or signer is _stopped_ -- in other words, when the client or signer is no longer adding new messages to the database. -* Similarly, your implementation should only allow an interchange file to be imported when the validator client is stopped. +- To avoid exporting an outdated interchange file -- an action which creates a slashing risk -- your implementation should only allow the slashing protection database to be exported when the validator client or signer is _stopped_ -- in other words, when the client or signer is no longer adding new messages to the database. +- Similarly, your implementation should only allow an interchange file to be imported when the validator client is stopped. ## 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-3085.md b/EIPS/eip-3085.md index 964d3697a95aa8..1e081ddf3d50ac 100644 --- a/EIPS/eip-3085.md +++ b/EIPS/eip-3085.md @@ -3,7 +3,7 @@ eip: 3085 title: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`) author: Erik Marks (@rekmarks), Pedro Gomes (@pedrouid) discussions-to: https://ethereum-magicians.org/t/eip-3085-wallet-addethereumchain/5469 -status: Review +status: Stagnant type: Standards Track category: Interface created: 2020-11-01 @@ -246,4 +246,4 @@ If the user denies the request, the wallet should return the same user rejection ## 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-3091.md b/EIPS/eip-3091.md index c6d06834eb3570..7393f572ea25cb 100644 --- a/EIPS/eip-3091.md +++ b/EIPS/eip-3091.md @@ -3,7 +3,7 @@ eip: 3091 title: Block Explorer API Routes author: Pedro Gomes (@pedrouid) discussions-to: https://ethereum-magicians.org/t/eip-3091-block-explorer-api-routes/4907 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-11-02 @@ -40,4 +40,4 @@ This EIP was designed with existing API routes in mind to reduce disruption. Inc TBD ## 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-3102.md b/EIPS/eip-3102.md index 54442ddd2d1bc1..456f053f5b5091 100644 --- a/EIPS/eip-3102.md +++ b/EIPS/eip-3102.md @@ -2,7 +2,6 @@ eip: 3102 title: Binary trie structure author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin) -status: Draft discussions-to: https://ethresear.ch/t/binary-trie-format/7621 status: Draft type: Standards Track @@ -180,4 +179,4 @@ Issues could arise when performing the transition. In particular, a heavy conver ## 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-3135.md b/EIPS/eip-3135.md index 5dbe26b3773e45..bc7133d5c15fea 100644 --- a/EIPS/eip-3135.md +++ b/EIPS/eip-3135.md @@ -3,7 +3,7 @@ eip: 3135 title: Exclusive Claimable Token author: Zhenyu Sun (@Ungigdu) discussions-to: https://github.com/ethereum/EIPs/issues/3132 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2020-08-10 @@ -265,4 +265,4 @@ By restricting claim function to issuer, there is no race condition on chain lay ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-3143.md b/EIPS/eip-3143.md index af60a8a77d5b79..1b48eac57f9836 100644 --- a/EIPS/eip-3143.md +++ b/EIPS/eip-3143.md @@ -3,7 +3,7 @@ eip: 3143 title: Increase block rewards to 5 ETH author: Ben Tinner (@Terra854) discussions-to: https://ethereum-magicians.org/t/eip-3143-increase-block-rewards-to-5-eth/5061 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-12-01 @@ -50,4 +50,4 @@ There are no known backward compatibility issues with the introduction of this E There are no known security issues presented by this change. ## 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-3155.md b/EIPS/eip-3155.md index 5bf68b5d03a140..e235e1e157607d 100644 --- a/EIPS/eip-3155.md +++ b/EIPS/eip-3155.md @@ -3,7 +3,7 @@ eip: 3155 title: EVM trace specification author: Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden) discussions-to: https://ethereum-magicians.org/t/eip-3155-create-evm-trace-specification/5007 -status: Draft +status: Stagnant type: Standards Track category: Interface created: 2020-12-07 @@ -164,4 +164,4 @@ Exposing an endpoint for creating traces publicly could open up a denial of serv Clients should consider putting trace endpoints behind a separate flag from other endpoints. ## 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-3156.md b/EIPS/eip-3156.md index ba5b23dce3f98a..7c1e5929accb0a 100644 --- a/EIPS/eip-3156.md +++ b/EIPS/eip-3156.md @@ -1,7 +1,7 @@ --- eip: 3156 title: Flash Loans -author: Alberto Cuesta Cañada (@albertocuestacanada), Fiona Kobayashi (@fifikobayashi), fubuloubu (@fubuloubu), Austin Williams (@onewayfunction) +author: Alberto Cuesta Cañada (@alcueca), Fiona Kobayashi (@fifikobayashi), fubuloubu (@fubuloubu), Austin Williams (@onewayfunction) discussions-to: https://ethereum-magicians.org/t/erc-3156-flash-loans-review-discussion/5077 status: Final type: Standards Track @@ -23,7 +23,7 @@ Flash loans allow smart contracts to lend an amount of tokens without a requirem Early adopters of the flash loan pattern have produced different interfaces and different use patterns. The diversification is expected to intensify, and with it the technical debt required to integrate with diverse flash lending patterns. -Some of the high level diferences in the approaches across the protocols include: +Some of the high level differences in the approaches across the protocols include: - Repayment approaches at the end of the transaction, where some pull the principal plus the fee from the loan receiver, and others where the loan receiver needs to manually return the principal and the fee to the lender. - Some lenders offer the ability to repay the loan using a token that is different to what was originally borrowed, which can reduce the overall complexity of the flash transaction and gas fees. - Some lenders offer a single entry point into the protocol regardless of whether you're buying, selling, depositing or chaining them together as a flash loan, whereas other protocols offer discrete entry points. @@ -206,7 +206,7 @@ contract FlashBorrower is IERC3156FlashBorrower { uint256 amount, uint256 fee, bytes calldata data - ) external override returns(bool) { + ) external override returns(bytes32) { require( msg.sender == address(lender), "FlashBorrower: Untrusted lender" @@ -257,7 +257,7 @@ import "../interfaces/IERC3156FlashLender.sol"; contract FlashMinter is ERC20, IERC3156FlashLender { bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); - uint256 public fee; // 1 == 0.0001 %. + uint256 public fee; // 1 == 0.01 %. /** * @param fee_ The percentage of the loan `amount` that needs to be repaid, in addition to `amount`. @@ -364,7 +364,7 @@ contract FlashLender is IERC3156FlashLender { bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); mapping(address => bool) public supportedTokens; - uint256 public fee; // 1 == 0.0001 %. + uint256 public fee; // 1 == 0.01 %. /** @@ -514,4 +514,4 @@ Assume a smart contract that flash lends its native token. The same smart contra 4. Liquidate the `lender` for profit. ## 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-3198.md b/EIPS/eip-3198.md index ab71c32d095cc2..fdad71935dfae2 100644 --- a/EIPS/eip-3198.md +++ b/EIPS/eip-3198.md @@ -3,8 +3,7 @@ eip: 3198 title: BASEFEE opcode author: Abdelhamid Bakhta (@abdelhamidbakhta), Vitalik Buterin (@vbuterin) discussions-to: https://ethereum-magicians.org/t/eip-3198-basefeeopcode/5162 -status: Last Call -review-period-end: 2021-07-14 +status: Final type: Standards Track category: Core created: 2021-01-13 @@ -60,4 +59,4 @@ Consumed gas: `2` The value of the base fee is not sensitive and is publicly accessible in the block header. There are no known security implications with this opcode. ## 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-3220.md b/EIPS/eip-3220.md index 8aef74d1759ea6..949413a3f24231 100644 --- a/EIPS/eip-3220.md +++ b/EIPS/eip-3220.md @@ -3,7 +3,7 @@ eip: 3220 title: Crosschain Identifier Specification author: Weijia Zhang (@weijia31415), Peter Robinson (@drinkcoffee) discussions-to: https://ethereum-magicians.org/t/eip-3220-crosschain-id-specification/5446 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-10-21 @@ -45,10 +45,10 @@ Hence there is need for a more robust blockchain identifier that will overcome t | Name | Size(bytes) | Description | |---------------|-------------|-------------| | Truncated Block Hash | 16 | This is the block hash of the genesis block or the block hash of of the block immediate prior to the fork for a fork of a blockchain. The 16 bytes is the 16 least significant bytes, assuming network byte order.| -|Native Chain ID| 4 | This is the **Chain Id** value that should be used with the blockchain when signing transactions. For blockchains that do not have a concept of **Chain Id**, this value is zero.| -|Chain Type| 2 | Researve 0x00 as undefined chaintype. 0x01 as mainnet type. 0x1[0-A]: testnet, 0x2[0-A]: private development network| +|Native Chain ID| 8 | This is the **Chain Id** value that should be used with the blockchain when signing transactions. For blockchains that do not have a concept of **Chain Id**, this value is zero.| +|Chain Type| 2 | Reserve 0x00 as undefined chaintype. 0x01 as mainnet type. 0x1[0-A]: testnet, 0x2[0-A]: private development network| | Governance Identifier | 2 | For new blockchains, a governance_identifier can be specified to identify an original **owner** of a blockchain, to help settle forked / main chain disputes. For all existing blockchains and for blockchains that do not have the concept of an **owner**, this field is zero. | -| Reserved | 7 | Reserved for future use. Use 000000 for now. | +| Reserved | 3 | Reserved for future use. Use 000000 for now. | | Checksum | 1 | Used to verify the integrity of the identifier. This integrity check is targeted at detecting Crosschain Identifiers mis-typed by human users. The value is calculated as the truncated SHA256 message digest of the rest of the identifier, using the least significant byte, assuming network byte order. Note that this checksum byte only detects integrity with a probability of one in 256. This probability is adequate for the intended usage of detecting typographical errors by people manually entering the Crosschain Identifier. | @@ -58,7 +58,8 @@ We have considered various alternative specifications such as using a random uni ## Backwards Compatibility -Crosschainid can be backward compatible with EIP-155. The crosschain id contains a 4 byte segment to record the chainid based on EIP-155. +Crosschainid can be backward compatible with EIP-155. The crosschain id contains an 8 byte segment to record the `Native Chain ID`. +For Ethereum chains, that can be used for a value intended to be used with EIP-155. ## Security Considerations @@ -72,4 +73,4 @@ For blockchains that do not cryptographically sign crosschain id into the blocks ## 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-3224.md b/EIPS/eip-3224.md index 94b48a572b15f6..0d3ab96f0c6c50 100644 --- a/EIPS/eip-3224.md +++ b/EIPS/eip-3224.md @@ -1,21 +1,16 @@ --- eip: 3224 -title: Described Data and Described Transactions +title: Described Data +description: Contract method to compute human-readable descriptions for signable data. author: Richard Moore (@ricmoo), Nick Johnson (@arachnid) discussions-to: https://github.com/ethereum/EIPs/issues/3225 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2021-01-23 requires: 191 --- -## Simple Summary - -A technique for contract authors to enable wallets to provide -a human-readable description of what a given contract -interaction (via a transaction or signed message) will perform. - ## Abstract @@ -266,8 +261,8 @@ there should be no concerns with backwards compatibility. All test cases operate against the published and verified contracts: -- [Formatter](https://ropsten.etherscan.io/address/0x7a89c0521604008c93c97aa76950198bca73d933#code) -- [TestFormatter](https://ropsten.etherscan.io/address/0xab3045aa85cbcabb06ed3f3fe968fa5457727270#code) +- Formatter: Ropsten @ 0x7a89c0521604008c93c97aa76950198bca73d933 +- TestFormatter: Ropsten @ 0xab3045aa85cbcabb06ed3f3fe968fa5457727270 The private key used for signing messages and transactions is: @@ -402,12 +397,12 @@ Wallets must be careful when displaying text provided by contracts and proper efforts must be taken to sanitize it, for example, be sure to consider: -- HTML could be enbedded to attempt to trick web-based wallets into [executing code](https://en.wikipedia.org/wiki/Code_injection) using the script tag (possibly uploading any private keys to a server) +- HTML could be embedded to attempt to trick web-based wallets into executing code using the script tag (possibly uploading any private keys to a server) - In general, extreme care must be used when rendering HTML; consider the ENS names `not-ricmoo.eth` or ` ricmoo.eth`, which if rendered without care would appear as `ricmoo.eth`, which it is not -- Other marks which require escaping could be included, such as quotes (`"`), formatting (`\n` (new line), `\f` (form feed), `\t` (tab), any of many [non-standard whitespaces](https://en.wikipedia.org/wiki/Whitespace_character#Spaces_in_Unicode)), back-slassh (`\`) -- UTF-8 has had bugs in the past which could allow arbitrary code execution and [crashing renderers](https://osxdaily.com/2015/05/27/bug-crashes-messages-app-ios-workaround/); consider using the UTF-8 replacement character (or *something*) for code-points outside common planes or common sub-sets within planes -- [Homoglyphs attacks](https://en.wikipedia.org/wiki/IDN_homograph_attack) -- [Right-to-left](https://en.wikipedia.org/wiki/Right-to-left_mark) mark may affect rendering +- Other marks which require escaping could be included, such as quotes (`"`), formatting (`\n` (new line), `\f` (form feed), `\t` (tab), any of many non-standard whitespaces), back-slassh (`\`) +- UTF-8 has had bugs in the past which could allow arbitrary code execution and crashing renderers; consider using the UTF-8 replacement character (or *something*) for code-points outside common planes or common sub-sets within planes +- Homoglyphs attacks +- Right-to-left marks may affect rendering - Many other things, deplnding on your environment ### Distinguished Signed Data @@ -444,4 +439,4 @@ future. ## 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-3234.md b/EIPS/eip-3234.md index cc8d88a883a3f3..df9e3864da9a96 100644 --- a/EIPS/eip-3234.md +++ b/EIPS/eip-3234.md @@ -3,7 +3,7 @@ eip: 3234 title: Batch Flash Loans author: Alberto Cuesta Cañada (@albertocuestacanada), Fiona Kobayashi (@fifikobayashi), fubuloubu (@fubuloubu), Austin Williams (@onewayfunction) discussions-to: https://ethereum-magicians.org/t/erc-3234-batch-flash-loans/5271 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2021-01-31 @@ -223,4 +223,4 @@ In early implementations of the Yield Protocol flash loaned fyDai could be redee 4. Liquidate the Yield Protocol CDP vault in MakerDAO. ## 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-3238.md b/EIPS/eip-3238.md index 4ed573cce5ec4e..3fa3a6c9bed155 100644 --- a/EIPS/eip-3238.md +++ b/EIPS/eip-3238.md @@ -5,7 +5,7 @@ author: Afri Schoedon (@q9f) discussions-to: https://github.com/ethereum/EIPs/issues/3239 type: Standards Track category: Core -status: Draft +status: Stagnant created: 2021-01-25 --- @@ -36,4 +36,4 @@ Alternatively, in order to maintain stability of the system, a it can be conside There are no known security issues with this proposal. ## 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-3267.md b/EIPS/eip-3267.md index fc489da16a4996..c44fe3e481a54a 100644 --- a/EIPS/eip-3267.md +++ b/EIPS/eip-3267.md @@ -3,7 +3,7 @@ eip: 3267 title: Giving Ethereum fees to Future Salaries author: Victor Porton (@vporton), Victor Porton discussions-to: https://ethereum-magicians.org/t/discussion-of-eip-3267/5343 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-02-13 @@ -37,7 +37,7 @@ Paradoxically, it will directly benefit miners/validators, see the discussion. `MineFraction` = `TBD` (0..1) -[The contracts source](../assets/eip-3267/contracts/README.md) +[The contract's source](../assets/eip-3267/contracts/README.md) Prior to `FORK_BLOCK_NUMBER`, `SalaryWithDAO` and `DefaultDAOInterface` contracts will be deployed to the network and exist at the above specified addresses. @@ -66,4 +66,4 @@ The security considerations are: See more in the discussion. ## 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-3298.md b/EIPS/eip-3298.md index fa32af840a2ee5..daa52e7df42625 100644 --- a/EIPS/eip-3298.md +++ b/EIPS/eip-3298.md @@ -3,7 +3,7 @@ eip: 3298 title: Removal of refunds author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) discussions-to: https://ethereum-magicians.org/t/eip-3298-removal-of-refunds/5430 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-02-26 @@ -92,4 +92,4 @@ If refunds were to be removed, this would be the comparative table TBD ## 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-3300.md b/EIPS/eip-3300.md index 8ca17c5ee7770d..d120916957c9f7 100644 --- a/EIPS/eip-3300.md +++ b/EIPS/eip-3300.md @@ -3,7 +3,7 @@ eip: 3300 title: Phase out refunds author: William Morriss (@wjmelements) discussions-to: https://ethereum-magicians.org/t/eip-3300-phase-out-refunds/5434 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-02-26 @@ -75,4 +75,4 @@ The effect of this will be increased gas price volatility: higher highs and lowe Because the refund counter is separate from the gas counter, the block-to-block gas changes will not break `eth_estimateGas`. ## 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-3322.md b/EIPS/eip-3322.md index 19d358f2402cc3..e865e06c84c03f 100644 --- a/EIPS/eip-3322.md +++ b/EIPS/eip-3322.md @@ -3,7 +3,7 @@ eip: 3322 title: Account gas storage opcodes author: William Morriss (@wjmelements) discussions-to: https://ethereum-magicians.org/t/eip-3322-efficient-gas-storage/5470 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2020-03-04 @@ -65,4 +65,4 @@ Because the gas is added to the refund counter, no compatibility issues are anti DoS is already limited by the 50% refund limit. ## 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-3326.md b/EIPS/eip-3326.md new file mode 100644 index 00000000000000..20113ebd8457f1 --- /dev/null +++ b/EIPS/eip-3326.md @@ -0,0 +1,130 @@ +--- +eip: 3326 +title: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`) +author: Erik Marks (@rekmarks) +discussions-to: https://ethereum-magicians.org/t/eip-3326-wallet-switchethereumchain +status: Stagnant +type: Standards Track +category: Interface +created: 2021-03-04 +requires: 155, 695 +--- + +## Simple Summary + +An RPC method for switching the wallet's active Ethereum chain. + +## Abstract + +The `wallet_switchEthereumChain` RPC method allows Ethereum applications ("dapps") to request that the wallet switches its active Ethereum chain, if the wallet has a concept thereof. +The caller must specify a chain ID. +The wallet application may arbitrarily refuse or accept the request. +`null` is returned if the active chain was switched, and an error otherwise. + +Important cautions for implementers of this method are included in the [Security Considerations](#security-considerations) section. + +## Motivation + +All dapps require the user to interact with one or more Ethereum chains in order to function. +Some wallets only supports interacting with one chain at a time. +We call this the wallet's "active chain". +`wallet_switchEthereumChain` enables dapps to request that the wallet switches its active chain to whichever one is required by the dapp. +This enables UX improvements for both dapps and wallets. + +## 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](https://www.ietf.org/rfc/rfc2119.txt). + +### `wallet_switchEthereumChain` + +The method accepts a single object parameter with a `chainId` field. +The method returns `null` if the wallet switched its active chain, and an error otherwise. + +The method presupposes that the wallet has a concept of a single "active chain". +The active chain is defined as the chain that the wallet is forwarding RPC requests to. + +#### Parameters + +`wallet_switchEthereumChain` accepts a single object parameter, specified by the following TypeScript interface: + +```typescript +interface SwitchEthereumChainParameter { + chainId: string; +} +``` + +If a field does not meet the requirements of this specification, the wallet **MUST** reject the request. + +- `chainId` + - **MUST** specify the integer ID of the chain as a hexadecimal string, per the [`eth_chainId`](./eip-695.md) Ethereum RPC method. + - The chain ID **MUST** be known to the wallet. + - The wallet **MUST** be able to switch to the specified chain and service RPC requests to it. + +#### Returns + +The method **MUST** return `null` if the request was successful, and an error otherwise. + +If the wallet does not have a concept of an active chain, the wallet **MUST** reject the request. + +### Examples + +These examples use JSON-RPC, but the method could be implemented using other RPC protocols. + +To switch to Mainnet: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_switchEthereumChain", + "params": [ + { + "chainId": "0x1", + } + ] +} +``` + +To switch to the Goerli test chain: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_switchEthereumChain", + "params": [ + { + "chainId": "0x5", + } + ] +} +``` + +## Rationale + +The purpose `wallet_switchEthereumChain` is to provide dapps with a way of requesting to switch the wallet's active chain, which they would otherwise have to ask the user to do manually. + +The method accepts a single object parameter to allow for future extensibility at virtually no cost to implementers and consumers. + +For related work, see [EIP-3085: `wallet_addEthereumChain`](./eip-3085.md) and [EIP-2015: `wallet_updateEthereumChain`](./eip-2015.md). +`wallet_switchEthereumChain` intentionally forgoes the chain metadata parameters included in those EIPs, since it is purely concerned with switching the active chain, regardless of RPC endpoints or any other metadata associated therewith. + +## Security Considerations + +For wallets with a concept of an active chain, switching the active chain has significant implications for pending RPC requests and the user's experience. +If the active chain switches without the user's awareness, a dapp could induce the user to take actions for unintended chains. + +In light of this, the wallet should: + +- Display a confirmation whenever a `wallet_switchEthereumChain` is received, clearly identifying the requester and the chain that will be switched to. + - The confirmation used in [EIP-1102](./eip-1102.md) may serve as a point of reference. +- When switching the active chain, cancel all pending RPC requests and chain-specific user confirmations. + +### Preserving User Privacy + +Automatically rejecting requests for chains that aren't supported or have yet to be added by the wallet allows requesters to infer which chains are supported by the wallet. +Wallet implementers should consider whether this communication channel violates any security properties of the wallet, and if so, take appropriate steps to mitigate it. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3332.md b/EIPS/eip-3332.md index 6ce2475d317601..082ceaa1b78ace 100644 --- a/EIPS/eip-3332.md +++ b/EIPS/eip-3332.md @@ -94,4 +94,4 @@ The strategy described for preventing front-running by setting an upper bound on ## 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-3336.md b/EIPS/eip-3336.md index abc79a0725f35f..81015e9bf3d804 100644 --- a/EIPS/eip-3336.md +++ b/EIPS/eip-3336.md @@ -3,7 +3,7 @@ eip: 3336 title: Paged memory allocation for the EVM author: Nick Johnson (@arachnid) discussions-to: https://ethereum-magicians.org/t/eips-3336-and-3337-improving-the-evms-memory-model/5482 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-03-06 @@ -68,4 +68,4 @@ TBD Potential CPU DoS issues arising from additional work done under the new model are alleviated by charging more for non-page-aligned reads and writes. Charges for memory expansion asymptotically approach those currently in force, so this change will not permit programs to allocate substantially more memory than they can today. ## 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-3337.md b/EIPS/eip-3337.md index 6a93ee2bd7281d..73ac30414505fa 100644 --- a/EIPS/eip-3337.md +++ b/EIPS/eip-3337.md @@ -3,7 +3,7 @@ eip: 3337 title: Frame pointer support for memory load and store operations author: Nick Johnson (@arachnid) discussions-to: https://ethereum-magicians.org/t/eips-3336-and-3337-improving-the-evms-memory-model/5482 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-03-06 @@ -96,4 +96,4 @@ This EIP exclusively introduces new opcodes, and as a result should not impact a DoS risks are mitigated by correct pricing of opcodes to reflect current execution costs. No other security considerations pertain to this EIP. ## 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-3338.md b/EIPS/eip-3338.md index 70e24b25198be3..0b61fe18b671d1 100644 --- a/EIPS/eip-3338.md +++ b/EIPS/eip-3338.md @@ -3,11 +3,11 @@ eip: 3338 title: Limit account nonce to 2^52 author: Micah Zoltu (@MicahZoltu), Alex Beregszaszi (@axic) discussions-to: https://ethereum-magicians.org/t/eip-2681-limit-account-nonce-to-2-64-1/4324 -status: Last Call +status: Withdrawn +withdrawal-reason: Withdrawn in favor of [EIP-2681](./eip-2681.md) type: Standards Track category: Core created: 2021-03-07 -review-period-end: 2021-03-25 --- ## Abstract @@ -52,4 +52,4 @@ None. ## 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-3368.md b/EIPS/eip-3368.md index bf5e49f4a2905e..6c186b22a8edd4 100644 --- a/EIPS/eip-3368.md +++ b/EIPS/eip-3368.md @@ -3,7 +3,7 @@ eip: 3368 title: Increase block rewards to 3 ETH, with 2 Year Decay to 1 ETH Scheduled author: Michael D. Carter (@BitsBeTrippin) discussions-to: https://ethereum-magicians.org/t/eip-3368-block-reward-increase-w-decay-for-next-two-years/5550 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-03-12 @@ -38,7 +38,7 @@ elif block.number > TRANSITION_START_BLOCK_NUMBER: ``` ## Rationale -2 years was chosen because it gives miners sufficient time to find alternative uses for their hardware and/or get their hardware back out onto the open market (e.g., in the form of gaming GPUs) without flooding the market suddenly. This proposal should ONLY be considered as a last resort as something we have in our pocket should the "network need to attract hashing power quickly and then bleed it off over time" rather than "something that is scheduled to be included in X hard fork" ; Recomendation to have in a fast track state, but NOT deployed to mainnet unless needed. +2 years was chosen because it gives miners sufficient time to find alternative uses for their hardware and/or get their hardware back out onto the open market (e.g., in the form of gaming GPUs) without flooding the market suddenly. This proposal should ONLY be considered as a last resort as something we have in our pocket should the "network need to attract hashing power quickly and then bleed it off over time" rather than "something that is scheduled to be included in X hard fork" ; Recommendation to have in a fast track state, but NOT deployed to mainnet unless needed. ## Backwards Compatibility There are no known backward compatibility issues with the introduction of this EIP. diff --git a/EIPS/eip-3372.md b/EIPS/eip-3372.md index 173bb494213748..9ac605a523ee7f 100644 --- a/EIPS/eip-3372.md +++ b/EIPS/eip-3372.md @@ -1,7 +1,7 @@ --- eip: 3372 title: 5 FNV primes for ethash -status: Draft +status: Stagnant type: Standards Track category: Core author: mineruniter969 (@mineruniter969), mineruniter969 @@ -120,4 +120,4 @@ There are no known security issues with this change. ## 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-3382.md b/EIPS/eip-3382.md index d32e00709b9d08..06788cb6911328 100644 --- a/EIPS/eip-3382.md +++ b/EIPS/eip-3382.md @@ -3,7 +3,8 @@ eip: 3382 title: Hardcoded Block Gas Limit author: Philippe Castonguay (@PhABC) discussions-to: https://ethereum-magicians.org/t/eip-3382-hardcoded-gas-limit -status: Review +status: Withdrawn +withdrawal-reason: The author prefers [EIP-3756](./eip-3756.md) type: Standards Track category: Core created: 2021-03-13 @@ -47,4 +48,4 @@ Rapid changes to the gas limit will likely be more difficult to execute, which c ## 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-3386.md b/EIPS/eip-3386.md new file mode 100644 index 00000000000000..81558c2b38439f --- /dev/null +++ b/EIPS/eip-3386.md @@ -0,0 +1,274 @@ +--- +eip: 3386 +title: ERC-721 and ERC-1155 to ERC-20 Wrapper +author: Calvin Koder (@ashrowz) +discussions-to: https://github.com/ethereum/EIPs/issues/3384 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-03-12 +requires: 165 +--- + +## Simple Summary +A standard interface for contracts that create generic ERC-20 tokens which derive from a pool of unique ERC-721/ERC-1155 tokens. + +## Abstract + +This standard outlines a smart contract interface to wrap identifiable tokens with fungible tokens. This allows for derivative [ERC-20](./eip-20.md) tokens to be minted by locking the base [ERC-721](./eip-721.md) non-fungible tokens and [ERC-1155](./eip-1155.md) multi tokens into a pool. The derivative tokens can be burned to redeem base tokens out of the pool. These derivatives have no reference to the unique id of these base tokens, and should have a proportional rate of exchange with the base tokens. As representatives of the base tokens, these generic derivative tokens can be traded and otherwise utilized according to ERC-20, such that the unique identifier of each base token is irrelevant. + +ERC-721 and ERC-1155 tokens are considered valid base, tokens because they have unique identifiers and are transferred according to similar rules. This allows for both ERC-721 NFTs and ERC-1155 Multi-Tokens to be wrapped under a single common interface. + +## Motivation + +The ERC-20 token standard is the most widespread and liquid token standard on Ethereum. ERC-721 and ERC-1155 tokens on the other hand can only be transferred by their individual ids, in whole amounts. Derivative tokens allow for exposure to the base asset while benefiting from contracts which utilize ERC-20 tokens. This allows for the base tokens to be fractionalized, traded and pooled generically on AMMs, collateralized, and be used for any other ERC-20 type contract. Several implementations of this proposal already exist without a common standard. + +Given a fixed exchange rate between base and derivative tokens, the value of the derivative token is proportional to the floor price of the pooled tokens. With the derivative tokens being used in AMMs, there is opportunity for arbitrage between derived token markets and the base NFT markets. By specifying a subset of base tokens which may be pooled, the difference between the lowest and highest value token in the pool may be minimized. This allows for higher value tokens within a larger set to be poolable. Additionally, price calculations using methods such as Dutch auctions, as implemented by NFT20, allow for price discovery of subclasses of base tokens. This allows the provider of a higher value base token to receive a proportionally larger number of derivative tokens than a token worth the floor price would receive. + +## 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](https://www.ietf.org/rfc/rfc2119.txt). + +**Every IWrapper compliant contract must implement the `IWrapper` and `ERC165` interfaces** : + + +```solidity +pragma solidity ^0.8.0; + +/** + @title IWrapper Identifiable Token Wrapper Standard + @dev {Wrapper} refers to any contract implementing this interface. + @dev {Base} refers to any ERC-721 or ERC-1155 contract. It MAY be the {Wrapper}. + @dev {Pool} refers to the contract which holds the {Base} tokens. It MAY be the {Wrapper}. + @dev {Derivative} refers to the ERC-20 contract which is minted/burned by the {Wrapper}. It MAY be the {Wrapper}. + @dev All uses of "single", "batch" refer to the number of token ids. This includes individual ERC-721 tokens by id, and multiple ERC-1155 by id. An ERC-1155 `TransferSingle` event may emit with a `value` greater than `1`, but it is still considered a single token. + @dev All parameters named `_amount`, `_amounts` refer to the `value` parameters in ERC-1155. When using this interface with ERC-721, `_amount` MUST be 1, and `_amounts` MUST be either an empty list or a list of 1 with the same length as `_ids`. +*/ +interface IWrapper /* is ERC165 */ { + /** + * @dev MUST emit when a mint occurs where a single {Base} token is received by the {Pool}. + * The `_from` argument MUST be the address of the account that sent the {Base} token. + * The `_to` argument MUST be the address of the account that received the {Derivative} token(s). + * The `_id` argument MUST be the id of the {Base} token transferred. + * The `_amount` argument MUST be the number of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens minted. + */ + event MintSingle (address indexed _from, address indexed _to, uint256 _id, uint256 _amount, uint256 _value); + + /** + * @dev MUST emit when a mint occurs where multiple {Base} tokens are received by the {Wrapper}. + * The `_from` argument MUST be the address of the account that sent the {Base} tokens. + * The `_to` argument MUST be the address of the account that received the {Derivative} token(s). + * The `_ids` argument MUST be the list ids of the {Base} tokens transferred. + * The `_amounts` argument MUST be the list of the numbers of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens minted. + */ + event MintBatch (address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts, uint256 _value); + + /** + * @dev MUST emit when a burn occurs where a single {Base} token is sent by the {Wrapper}. + * The `_from` argument MUST be the address of the account that sent the {Derivative} token(s). + * The `_to` argument MUST be the address of the account that received the {Base} token. + * The `_id` argument MUST be the id of the {Base} token transferred. + * The `_amount` argument MUST be the number of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens burned. + */ + event BurnSingle (address indexed _from, address indexed _to, uint256 _id, uint256 _amount, uint256 _value); + + /** + * @dev MUST emit when a mint occurs where multiple {Base} tokens are sent by the {Wrapper}. + * The `_from` argument MUST be the address of the account that sent the {Derivative} token(s). + * The `_to` argument MUST be the address of the account that received the {Base} tokens. + * The `_ids` argument MUST be the list of ids of the {Base} tokens transferred. + * The `_amounts` argument MUST be the list of the numbers of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens burned. + */ + event BurnBatch (address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts, uint256 _value); + + /** + * @notice Transfers the {Base} token with `_id` from `msg.sender` to the {Pool} and mints {Derivative} token(s) to `_to`. + * @param _to Target address. + * @param _id Id of the {Base} token. + * @param _amount Amount of the {Base} token. + * + * Emits a {MintSingle} event. + */ + function mint( + address _to, + uint256 _id, + uint256 _amount + ) external; + + /** + * @notice Transfers `_amounts[i]` of the {Base} tokens with `_ids[i]` from `msg.sender` to the {Pool} and mints {Derivative} token(s) to `_to`. + * @param _to Target address. + * @param _ids Ids of the {Base} tokens. + * @param _amounts Amounts of the {Base} tokens. + * + * Emits a {MintBatch} event. + */ + function batchMint( + address _to, + uint256[] calldata _ids, + uint256[] calldata _amounts + ) external; + + /** + * @notice Burns {Derivative} token(s) from `_from` and transfers `_amounts` of some {Base} token from the {Pool} to `_to`. No guarantees are made as to what token is withdrawn. + * @param _from Source address. + * @param _to Target address. + * @param _amount Amount of the {Base} tokens. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function burn( + address _from, + address _to, + uint256 _amount + ) external; + + /** + * @notice Burns {Derivative} token(s) from `_from` and transfers `_amounts` of some {Base} tokens from the {Pool} to `_to`. No guarantees are made as to what tokens are withdrawn. + * @param _from Source address. + * @param _to Target address. + * @param _amounts Amounts of the {Base} tokens. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function batchBurn( + address _from, + address _to, + uint256[] calldata _amounts + ) external; + + /** + * @notice Burns {Derivative} token(s) from `_from` and transfers `_amounts[i]` of the {Base} tokens with `_ids[i]` from the {Pool} to `_to`. + * @param _from Source address. + * @param _to Target address. + * @param _id Id of the {Base} token. + * @param _amount Amount of the {Base} token. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function idBurn( + address _from, + address _to, + uint256 _id, + uint256 _amount + ) external; + + /** + * @notice Burns {Derivative} tokens from `_from` and transfers `_amounts[i]` of the {Base} tokens with `_ids[i]` from the {Pool} to `_to`. + * @param _from Source address. + * @param _to Target address. + * @param _ids Ids of the {Base} tokens. + * @param _amounts Amounts of the {Base} tokens. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function batchIdBurn( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _amounts + ) external; +} +``` + +## Rationale + +### Naming + +The ERC-721/ERC-1155 tokens which are pooled are called {Base} tokens. Alternative names include: +- Underlying. +- NFT. However, ERC-1155 tokens may be considered "semi-fungible". + +The ERC-20 tokens which are minted/burned are called {Derivative} tokens. Alternative names include: +- Wrapped. +- Generic. + +The function names `mint` and `burn` are borrowed from the minting and burning extensions to ERC-20. Alternative names include: +- `mint`/`redeem` ([NFTX](https://nftx.org)) +- `deposit`/`withdraw` ([WrappedKitties](https://wrappedkitties.com/)) +- `wrap`/`unwrap` ([MoonCatsWrapped](https://etherscan.io/address/0x7c40c393dc0f283f318791d746d894ddd3693572)) + +The function names `*idBurn` are chosen to reduce confusion on what is being burned. That is, the {Derivative} tokens are burned in order to redeem the id(s). + +The wrapper/pool itself can be called an "Index fund" according to NFTX, or a "DEX" according to [NFT20](https://nft20.io). However, the {NFT20Pair} contract allows for direct NFT-NFT swaps which are out of the scope of this standard. + +### Minting +Minting requires the transfer of the {Base} tokens into the {Pool} in exchange for {Derivative} tokens. The {Base} tokens deposited in this way MUST NOT be transferred again except through the burning functions. This ensures the value of the {Derivative} tokens is representative of the value of the {Base} tokens. + +Alternatively to transferring the {Base} tokens into the {Pool}, the tokens may be locked as collateral in exchange for {Derivative} loans, as proposed in NFTX litepaper, similarly to Maker vaults. This still follows the general minting pattern of removing transferability of the {Base} tokens in exchange for {Derivative} tokens. + +### Burning +Burning requires the transfer of {Base} tokens out of the {Pool} in exchange for burning {Derivative} tokens. The burn functions are distinguished by the quantity and quality of {Base} tokens redeemed. +- For burning without specifying the `id`: `burn`, `batchBurn`. +- For burning with specifying the `id`(s): `idBurn`, `batchIdBurn`. + +By allowing for specific ids to be targeted, higher value {Base} tokens may be selected out of the pool. NFTX proposes an additional fee to be applied for such targeted withdrawals, to offset the desire to drain the {Pool} of {Base} tokens worth more than the floor price. + +### Pricing +Prices should not be necessarily fixed. therefore, Mint/Burn events MUST include the ERC-20 `_value` minted/burned. + +Existing pricing implementations are as follows (measured in base:derivative): +- Equal: Every {Base} costs 1 {Derivative} + - NFTX + - Wrapped Kitties +- Proportional + - NFT20 sets a fixed rate of 100 {Base} tokens per {Derivative} token. +- Variable + - NFT20 also allows for Dutch auctions when minting. + - NFTX proposes an additional fee to be paid when targeting the id of the {Base} token. + +Due to the variety of pricing implementations, the Mint\* and Burn\* events MUST include the number {Derivative} tokens minted/burned. + +### Inheritance +#### ERC-20 +The {Wrapper} MAY inherit from {ERC20}, in order to directly call `super.mint` and `super.burn`. +If the {Wrapper} does not inherit from {ERC20}, the {Derivative} contract MUST be limited such that the {Wrapper} has the sole power to `mint`, `burn`, and otherwise change the supply of tokens. + +#### ERC721Receiver, ERC1155Receiver +If not inheriting from {ERC721Receiver} and/or {ERC1155Receiver}, the pool MUST be limited such that the base tokens can only be transferred via the Wrapper's `mint`, `burn`. + +There exists only one of each ERC-721 token of with a given (address, id) pair. However, ERC-1155 tokens of a given (address, id) may have quantities greater than 1. Accordingly, the meaning of "Single" and "Batch" in each standard varies. In both standards, "single" refers to a single id, and "batch" refers to multiple ids. In ERC-1155, a single id event/function may involve multiple tokens, according to the `value` field. + +In building a common set of events and functions, we must be aware of these differences in implementation. The current implementation treats ERC-721 tokens as a special case where, in reference to the quantity of each {Base} token: +- All parameters named `_amount`, MUST be `1`. +- All parameters named `_amounts` MUST be either an empty list or a list of `1` with the same length as `_ids`. + +This keeps a consistent enumeration of tokens along with ERC-1155. Alternative implementations include: +- A common interface with specialized functions. EX: `mintFromERC721`. +- Separate interfaces for each type. EX: `ERC721Wrapper`, `ERC1155Wrapper`. + +#### ERC721, ERC1155 +The {Wrapper} MAY inherit from {ERC721} and/or {ERC1155} in order to call `super.mint`, directly. This is optional as minting {Base} tokens is not required in this standard. An "Initial NFT Offering" could use this to create a set of {Base} tokens within the contract, and directly distribute {Derivative} tokens. + +If the {Wrapper} does not inherit from {ERC721} or {ERC1155}, it MUST include calls to {IERC721} and {IERC1155} in order to transfer {Base} tokens. + +### Approval +All of the underlying transfer methods are not tied to the {Wrapper}, but rather call the ERC-20/721/1155 transfer methods. Implementations of this standard MUST: +- Either implement {Derivative} transfer approval for burning, and {Base} transfer approval for minting. +- Or check for Approval outside of the {Wrapper} through {IERC721} / {IERC1155} before attempting to execute. + +## Backwards Compatibility +Most existing implementations inherit from ERC-20, using functions `mint` and `burn`. +Events: +- Mint + - WK: DepositKittyAndMintToken + - NFTX: Mint + +- Burn + - WK: BurnTokenAndWithdrawKity + - NFTX: Redeem + +## Reference Implementation +[ERC-3386 Reference Implementation](https://github.com/ashrowz/erc-3386) + +## Security Considerations +Wrapper contracts are RECOMMENDED to inherit from burnable ERC-20 tokens. If they are not, the supply of the {Derivative} tokens MUST be controlled by the Wrapper. Similarly, price implementations MUST ensure that the supply of {Base} tokens is reflected by the {Derivative} tokens. + +With the functions `idBurn`, `idBurns`, users may target the most valuable NFT within the generic lot. If there is a significant difference between tokens values of different ids, the contract SHOULD consider creating specialized pools (NFTX) or pricing (NFT20) to account for this. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3403.md b/EIPS/eip-3403.md index 8684051733bf06..bee317c77b1197 100644 --- a/EIPS/eip-3403.md +++ b/EIPS/eip-3403.md @@ -3,7 +3,7 @@ eip: 3403 title: Partial removal of refunds author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) discussions-to: https://ethereum-magicians.org/t/eip-3298-removal-of-refunds/5430 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-03-16 @@ -131,4 +131,4 @@ Refunds are not visible to transaction execution, so this should not have any im The maximum amount of gas that can be spent on execution in a block is limited to the gas limit, if we do not count zero-to-nonzero SSTOREs that were later reset back to zero. It is okay to not count those, because if such an SSTORE is reset, storage is not expanded and the client does not need to actually adjust the Merke tree; the gas consumption is refunded, but the effort normally required by the client to process those opcodes is also cancelled. **Clients should make sure to not do a storage write if `new_value = original_value`; this was a prudent optimization since the beginning of Ethereum but it becomes more important now.** ## 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-3416.md b/EIPS/eip-3416.md index 7c9ab1e1de41b7..d84650c14b5047 100644 --- a/EIPS/eip-3416.md +++ b/EIPS/eip-3416.md @@ -3,7 +3,7 @@ eip: 3416 title: Median Gas Premium author: HexZorro (@hexzorro), Mojtaba Tefagh (@mtefagh) discussions-to: https://ethereum-magicians.org/t/eip-3416-median-gas-premium/5755 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-03-18 @@ -101,4 +101,4 @@ TBD. ## 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-3436.md b/EIPS/eip-3436.md index 88dd4b8c0931c7..b2476b0c7f9adf 100644 --- a/EIPS/eip-3436.md +++ b/EIPS/eip-3436.md @@ -3,9 +3,9 @@ eip: 3436 title: Expanded Clique Block Choice Rule author: Danno Ferrin (@shemnon) discussions-to: https://ethereum-magicians.org/t/eip-3436-expanded-clique-block-choice-rule/5809 -status: Draft +status: Stagnant type: Standards Track -category: Networking +category: Core created: 2021-03-25 requires: 225 --- @@ -113,4 +113,4 @@ this time. ## 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-3440.md b/EIPS/eip-3440.md index cca4301a418ae8..a343038554a60f 100644 --- a/EIPS/eip-3440.md +++ b/EIPS/eip-3440.md @@ -3,7 +3,7 @@ eip: 3440 title: ERC-721 Editions Standard author: Nathan Ginnever (@nginnever) discussions-to: https://ethereum-magicians.org/t/eip-3340-nft-editions-standard-extension/6044 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2021-04-20 @@ -297,4 +297,4 @@ contract ArtToken is ERC3440 { This extension gives an artist the ability to designate an original edition, set the maximum supply of editions as well as print the editions and uses the `tokenURI` extension to supply a link to the art work. To minimize the risk of an artist changing this value after selling an original piece this function can only happen once. Ensuring that these functions can only happen once provides consistency with uniqueness and verifiability. Due to this, the reference implementation handles these features in the constructor function. An edition may only be signed once, and care should be taken that the edition is signed correctly before release of the token/s. ## 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-3448.md b/EIPS/eip-3448.md new file mode 100644 index 00000000000000..e28d15f1ff013b --- /dev/null +++ b/EIPS/eip-3448.md @@ -0,0 +1,208 @@ +--- +eip: 3448 +title: MetaProxy Standard +description: A minimal bytecode implementation for creating proxy contracts with immutable metadata attached to the bytecode +author: pinkiebell (@pinkiebell) +discussions-to: https://ethereum-magicians.org/t/erc-3448-metaproxy-factory/5834 +status: Final +type: Standards Track +category: ERC +created: 2021-03-29 +--- + +## Abstract +By standardizing on a known minimal bytecode proxy implementation with support for immutable metadata, this standard allows users and third party tools (e.g. Etherscan) to: +(a) simply discover that a contract will always redirect in a known manner and +(b) depend on the behavior of the code at the destination contract as the behavior of the redirecting contract and +(c) verify/view the attached metadata. + +Tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run along with the associated metadata - and can depend on representations about that code (verified source, third-party audits, etc). +This implementation forwards all calls via `DELEGATECALL` and any (calldata) input plus the metadata at the end of the bytecode to the implementation contract and then relays the return value back to the caller. +In the case where the implementation reverts, the revert is passed back along with the payload data. + +## Motivation +This standard supports use-cases wherein it is desirable to clone exact contract functionality with different parameters at another address. + +## Specification +The exact bytecode of the MetaProxy contract is: +``` + 20 bytes target contract address + ---------------------------------------- +363d3d373d3d3d3d60368038038091363936013d7300000000000000000000000000000000000000005af43d3d93803e603457fd5bf3 +``` +wherein the bytes at indices 21 - 41 (inclusive) are replaced with the 20 byte address of the master functionality contract. +Additionally, everything after the MetaProxy bytecode can be arbitrary metadata and the last 32 bytes (one word) of the bytecode must indicate the length of the metadata in bytes. + +``` +<54 bytes metaproxy> +``` + +## Rationale +The goals of this effort have been the following: +- a cheap way of storing immutable metadata for each child instead of using storage slots +- inexpensive deployment of clones +- handles error return bubbling for revert messages + +## Backwards Compatibility +There are no backwards compatibility issues. + +## Test Cases +Tested with: +- invocation with no arguments +- invocation with arguments +- invocation with return values +- invocation with revert (confirming reverted payload is transferred) + +A solidity contract with the above test cases can be found [in the EIP asset directory](../assets/eip-3448/MetaProxyTest.sol). + +## Reference Implementation +A reference implementation can be found [in the EIP asset directory](../assets/eip-3448/MetaProxyFactory.sol). + +### Deployment bytecode +A annotated version of the deploy bytecode: +``` +// PUSH1 11; +// CODESIZE; +// SUB; +// DUP1; +// PUSH1 11; +// RETURNDATASIZE; +// CODECOPY; +// RETURNDATASIZE; +// RETURN; +``` + +### MetaProxy +A annotated version of the MetaProxy bytecode: +``` +// copy args +// CALLDATASIZE; calldatasize +// RETURNDATASIZE; 0, calldatasize +// RETURNDATASIZE; 0, 0, calldatasize +// CALLDATACOPY; + +// RETURNDATASIZE; 0 +// RETURNDATASIZE; 0, 0 +// RETURNDATASIZE; 0, 0, 0 +// RETURNDATASIZE; 0, 0, 0, 0 + +// PUSH1 54; 54, 0, 0, 0, 0 +// DUP1; 54, 54, 0, 0, 0, 0 +// CODESIZE; codesize, 54, 54, 0, 0, 0, 0 +// SUB; codesize-54, 54, 0, 0, 0, 0 +// DUP1; codesize-54, codesize-54, 54, 0, 0, 0, 0 +// SWAP2; 54, codesize-54, codesize-54, 0, 0, 0, 0 +// CALLDATASIZE; calldatasize, 54, codesize-54, codesize-54, 0, 0, 0, 0 +// CODECOPY; codesize-54, 0, 0, 0, 0 + +// CALLDATASIZE; calldatasize, codesize-54, 0, 0, 0, 0 +// ADD; calldatasize+codesize-54, 0, 0, 0, 0 +// RETURNDATASIZE; 0, calldatasize+codesize-54, 0, 0, 0, 0 +// PUSH20 0; addr, 0, calldatasize+codesize-54, 0, 0, 0, 0 - zero is replaced with shl(96, address()) +// GAS; gas, addr, 0, calldatasize+codesize-54, 0, 0, 0, 0 +// DELEGATECALL; (gas, addr, 0, calldatasize() + metadata, 0, 0) delegatecall to the target contract; +// +// RETURNDATASIZE; returndatasize, retcode, 0, 0 +// RETURNDATASIZE; returndatasize, returndatasize, retcode, 0, 0 +// SWAP4; 0, returndatasize, retcode, 0, returndatasize +// DUP1; 0, 0, returndatasize, retcode, 0, returndatasize +// RETURNDATACOPY; (0, 0, returndatasize) - Copy everything into memory that the call returned + +// stack = retcode, 0, returndatasize # this is for either revert(0, returndatasize()) or return (0, returndatasize()) + +// PUSH1 _SUCCESS_; push jumpdest of _SUCCESS_ +// JUMPI; jump if delegatecall returned `1` +// REVERT; (0, returndatasize()) if delegatecall returned `0` +// JUMPDEST _SUCCESS_; +// RETURN; (0, returndatasize()) if delegatecall returned non-zero (1) +``` + +### Examples +The following code snippets serve only as suggestions and are not a discrete part of this standard. + +#### Proxy construction with bytes from abi.encode +```solidity +/// @notice MetaProxy construction via abi encoded bytes. +function createFromBytes ( + address a, + uint256 b, + uint256[] calldata c +) external payable returns (address proxy) { + // creates a new proxy where the metadata is the result of abi.encode() + proxy = MetaProxyFactory._metaProxyFromBytes(address(this), abi.encode(a, b, c)); + require(proxy != address(0)); + // optional one-time setup, a constructor() substitute + MyContract(proxy).init{ value: msg.value }(); +} +``` + +#### Proxy construction with bytes from calldata +```solidity +/// @notice MetaProxy construction via calldata. +function createFromCalldata ( + address a, + uint256 b, + uint256[] calldata c +) external payable returns (address proxy) { + // creates a new proxy where the metadata is everything after the 4th byte from calldata. + proxy = MetaProxyFactory._metaProxyFromCalldata(address(this)); + require(proxy != address(0)); + // optional one-time setup, a constructor() substitute + MyContract(proxy).init{ value: msg.value }(); +} +``` + +#### Retrieving the metadata from calldata and abi.decode +```solidity +/// @notice Returns the metadata of this (MetaProxy) contract. +/// Only relevant with contracts created via the MetaProxy standard. +/// @dev This function is aimed to be invoked with- & without a call. +function getMetadataWithoutCall () public pure returns ( + address a, + uint256 b, + uint256[] memory c +) { + bytes memory data; + assembly { + let posOfMetadataSize := sub(calldatasize(), 32) + let size := calldataload(posOfMetadataSize) + let dataPtr := sub(posOfMetadataSize, size) + data := mload(64) + // increment free memory pointer by metadata size + 32 bytes (length) + mstore(64, add(data, add(size, 32))) + mstore(data, size) + let memPtr := add(data, 32) + calldatacopy(memPtr, dataPtr, size) + } + return abi.decode(data, (address, uint256, uint256[])); +} +``` + +#### Retrieving the metadata via a call to self +```solidity +/// @notice Returns the metadata of this (MetaProxy) contract. +/// Only relevant with contracts created via the MetaProxy standard. +/// @dev This function is aimed to to be invoked via a call. +function getMetadataViaCall () public pure returns ( + address a, + uint256 b, + uint256[] memory c +) { + assembly { + let posOfMetadataSize := sub(calldatasize(), 32) + let size := calldataload(posOfMetadataSize) + let dataPtr := sub(posOfMetadataSize, size) + calldatacopy(0, dataPtr, size) + return(0, size) + } +} +``` + +Apart from the examples above, it is also possible to use Solidity Structures or any custom data encoding. + +## Security Considerations +This standard only covers the bytecode implementation and does not include any serious side effects of itself. +The reference implementation only serves as a example. It is highly recommended to research side effects depending on how the functionality is used and implemented in any project. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3450.md b/EIPS/eip-3450.md index b6c13f50f93341..7030877a628f5c 100644 --- a/EIPS/eip-3450.md +++ b/EIPS/eip-3450.md @@ -3,7 +3,7 @@ eip: 3450 title: Standardized Shamir Secret Sharing Scheme for BIP-39 Mnemonics author: Daniel Streit (@danielstreit) discussions-to: https://ethereum-magicians.org/t/erc-3450-standard-for-applying-shamirs-to-bip-39-mnemonics/5844 -status: Draft +status: Stagnant type: Standards Track category: ERC created: 2021-03-29 @@ -187,4 +187,4 @@ The additional data may hint to an attacker of the existence of other keys and t ## 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-3455.md b/EIPS/eip-3455.md new file mode 100644 index 00000000000000..076766dde3a724 --- /dev/null +++ b/EIPS/eip-3455.md @@ -0,0 +1,71 @@ +--- +eip: 3455 +title: SUDO Opcode +description: A new opcode is introduced to allow calling from an arbitrary sender address. +author: William Morriss (@wjmelements), Baptiste Vauthey (@thabaptiser) +discussions-to: https://ethereum-magicians.org/t/eip-3455-sudo-opcode/5860 +status: Draft +type: Standards Track +category: Core +created: 2021-04-01 +--- + +## Abstract +A new opcode, `SUDO`, is introduced with the same parameters as `CALL`, plus another parameter to specify the sender address. + +## Motivation +There are many use cases for being able to set the sender. + +Many tokens are stuck irretrievably because nobody has the key for the owner address. +In particular, at address zero there is approximately 17 billion USD in tokens and ether, according to etherscan. +With `SUDO`, anyone could free that value, leading to an economic boom that would end poverty and world hunger. +Instead it is sitting there idle like the gold in Fort Knox. +`SUDO` fixes this. + +It is a common mistake to send [ERC-20](./eip-20.md) tokens to the token address instead of the intended recipient. +This happens because users paste the token address into the recipient fields. +Currently there is no way to recover these tokens. +`SUDO` fixes this. + +Many scammers have fraudulently received tokens and ETH via trust-trading. +Their victims currently have no way to recover their funds. +`SUDO` fixes this. + +Large amounts of users have accidentally locked up tokens and ether by losing their private keys. +This is inefficient and provides a bad user experience. +To accommodate new and inexperienced users, there needs to be a way to recover funds after the private key has been lost. +`SUDO` fixes this. + +Finally, there are many tokens and ether sitting in smart contracts locked due to a bug. +We could finally close EIP issue #156. +We cannot currently reclaim ether from stuck accounts. +`SUDO` fixes this. + +## Specification +Adds a new opcode (`SUDO`) at `0xf8`. +`SUDO` pops 8 parameters from the stack. +Besides the sender parameter, the parameters shall match `CALL`. + +1. Gas: Integer; Maximum gas allowance for message call, safely using current gas counter if the counter is lower +2. Sender: Address, truncated to lower 40 bytes; Sets `CALLER` inside the call frame +3. To: Address, truncated to lower 40 bytes; sets `ADDRESS` +4. Value: Integer, raises exception amount specified is less than the value in Sender account; transferred with call to recipient balance, sets `CALLVALUE` +5. InStart: Integer; beginning of memory to use for `CALLDATA` +6. InSize: Integer; length of memory to use for `CALLDATA` +7. OutStart: Integer; beginning of memory to replace with `RETURNDATA` +8. OutSize: Integer; maximum `RETURNDATA` to place in memory + +Following execution, `SUDO` pushes a result value to the stack, indicating success or failure. +If the call ended with `STOP`, `RETURN`, or `SELFDESTRUCT`, `1` is pushed. +If the call ended with `REVERT`, `INVALID`, or an EVM assertion, `0` is pushed. + +## Rationale +The `GAS` parameter is first so that callers can tediously compute how much of their remaining gas to send at the last possible moment. +The remaining parameters inherited from `CALL` are in the same order, with sender inserted between. + + +## Security Considerations +It will be fine. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3475.md b/EIPS/eip-3475.md new file mode 100644 index 00000000000000..f55b6dc3e3334d --- /dev/null +++ b/EIPS/eip-3475.md @@ -0,0 +1,445 @@ +--- +eip: 3475 +title: Abstract Storage Bonds +description: Interface for creating tokenized obligations with abstract on-chain metadata storage +author: Yu Liu (@yuliu-debond), Varun Deshpande (@dr-chain), Cedric Ngakam (@drikssy), Dhruv Malik (@dhruvmalik007), Samuel Gwlanold Edoumou (@Edoumou), Toufic Batrice (@toufic0710) +discussions-to: https://ethereum-magicians.org/t/eip-3475-multiple-callable-bonds-standard/8691 +status: Final +type: Standards Track +category: ERC +created: 2021-04-05 +requires: 20, 721, 1155 +--- + +## Abstract + +- This EIP allows the creation of tokenized obligations with abstract on-chain metadata storage. Issuing bonds with multiple redemption data cannot be achieved with existing token standards. + +- This EIP enables each bond class ID to represent a new configurable token type and corresponding to each class, corresponding bond nonces to represent an issuing condition or any other form of data in uint256. Every single nonce of a bond class can have its metadata, supply, and other redemption conditions. + +- Bonds created by this EIP can also be batched for issuance/redemption conditions for efficiency on gas costs and UX side. And finally, bonds created from this standard can be divided and exchanged in a secondary market. + +## Motivation + +Current LP (Liquidity Provider) tokens are simple [EIP-20](./eip-20.md) tokens with no complex data structure. To allow more complex reward and redemption logic to be stored on-chain, we need a new token standard that: + +- Supports multiple token IDs +- Can store on-chain metadata +- Doesn't require a fixed storage pattern +- Is gas-efficient. + +Also Some benefits: + +- This EIP allows the creation of any obligation with the same interface. +- It will enable any 3rd party wallet applications or exchanges to read these tokens' balance and redemption conditions. +- These bonds can also be batched as tradeable instruments. Those instruments can then be divided and exchanged in secondary markets. + +## Specification + +**Definition** + +Bank: an entity that issues, redeems, or burns bonds after getting the necessary amount of liquidity. Generally, a single entity with admin access to the pool. + +**Functions** + +```solidity +pragma solidity ^0.8.0; + +/** +* transferFrom +* @param _from argument is the address of the bond holder whose balance is about to decrease. +* @param _to argument is the address of the bond recipient whose balance is about to increase. +* @param _transactions is the `Transaction[] calldata` (of type ['classId', 'nonceId', '_amountBonds']) structure defined in the rationale section below. +* @dev transferFrom MUST have the `isApprovedFor(_from, _to, _transactions[i].classId)` approval to transfer `_from` address to `_to` address for given classId (i.e for Transaction tuple corresponding to all nonces). +e.g: +* function transferFrom(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [IERC3475.Transaction(1,14,500)]); +* transfer from `_from` address, to `_to` address, `500000000` bonds of type class`1` and nonce `42`. +*/ + +function transferFrom(address _from, address _to, Transaction[] calldata _transactions) external; + +/** +* transferAllowanceFrom +* @dev allows the transfer of only those bond types and nonces being allotted to the _to address using allowance(). +* @param _from is the address of the holder whose balance is about to decrease. +* @param _to is the address of the recipient whose balance is about to increase. +* @param _transactions is the `Transaction[] calldata` structure defined in the section `rationale` below. +* @dev transferAllowanceFrom MUST have the `allowance(_from, msg.sender, _transactions[i].classId, _transactions[i].nonceId)` (where `i` looping for [ 0 ...Transaction.length - 1] ) +e.g: +* function transferAllowanceFrom(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [IERC3475.Transaction(1,14,500)]); +* transfer from `_from` address, to `_to` address, `500000000` bonds of type class`1` and nonce `42`. +*/ + +function transferAllowanceFrom(address _from,address _to, Transaction[] calldata _transactions) public ; + +/** +* issue +* @dev allows issuing any number of bond types (defined by values in Transaction tuple as param) to an address. +* @dev it MUST be issued by a single entity (for instance, a role-based ownable contract that has integration with the liquidity pool of the deposited collateral by `_to` address). +* @param `_to` argument is the address to which the bond will be issued. +* @param `_transactions` is the `Transaction[] calldata` (ie array of issued bond class, bond nonce and amount of bonds to be issued). +* @dev transferAllowanceFrom MUST have the `allowance(_from, msg.sender, _transactions[i].classId, _transactions[i].nonceId)` (where `i` looping for [ 0 ...Transaction.length - 1] ) +e.g: +example: issue(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,[IERC3475.Transaction(1,14,500)]); +issues `1000` bonds with a class of `0` to address `0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef` with a nonce of `5`. +*/ +function issue(address _to, Transaction[] calldata _transaction) external; + +/** +* redeem +* @dev permits redemption of bond from an address. +* @dev the calling of this function needs to be restricted to the bond issuer contract. +* @param `_from` is the address from which the bond will be redeemed. +* @param `_transactions` is the `Transaction[] calldata` structure (i.e., array of tuples with the pairs of (class, nonce and amount) of the bonds that are to be redeemed). Further defined in the rationale section. +* @dev redeem function for a given class, and nonce category MUST BE done after certain conditions for maturity (can be end time, total active liquidity, etc.) are met. +* @dev furthermore, it SHOULD ONLY be called by the bank or secondary market maker contract. +e.g: +* redeem(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, [IERC3475.Transaction(1,14,500)]); +means “redeem from wallet address(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef), 500000000 of bond class1 and nonce 42. +*/ + +function redeem(address _from, Transaction[] calldata _transactions) external; + +/** +* burn +* @dev permits nullifying of the bonds (or transferring given bonds to address(0)). +* @dev burn function for given class and nonce MUST BE called by only the controller contract. +* @param _from is the address of the holder whose bonds are about to burn. +* @param `_transactions` is the `Transaction[] calldata` structure (i.e., array of tuple with the pairs of (class, nonce and amount) of the bonds that are to be burned). further defined in the rationale. +* @dev burn function for a given class, and nonce category MUST BE done only after certain conditions for maturity (can be end time, total active liquidity, etc). +* @dev furthermore, it SHOULD ONLY be called by the bank or secondary market maker contract. +* e.g: +* burn(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B,[IERC3475.Transaction(1,14,500)]); +* means burning 500000000 bonds of class 1 nonce 42 owned by address 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B. +*/ +function burn(address _from, Transaction[] calldata _transactions) external; + +/** +* approve +* @dev Allows `_spender` to withdraw from the msg.sender the bonds of `_amount` and type (classId and nonceId). +* @dev If this function is called again, it overwrites the current allowance with the amount. +* @dev `approve()` should only be callable by the bank, or the owner of the account. +* @param `_spender` argument is the address of the user who is approved to transfer the bonds. +* @param `_transactions` is the `Transaction[] calldata` structure (ie array of tuple with the pairs of (class,nonce, and amount) of the bonds that are to be approved to be spend by _spender). Further defined in the rationale section. +* e.g: +* approve(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B,[IERC3475.Transaction(1,14,500)]); +* means owner of address 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is approved to manage 500 bonds from class 1 and Nonce 14. +*/ + +function approve(address _spender, Transaction[] calldata _transactions) external; + +/** +* SetApprovalFor +* @dev enable or disable approval for a third party (“operator”) to manage all the Bonds in the given class of the caller’s bonds. +* @dev If this function is called again, it overwrites the current allowance with the amount. +* @dev `approve()` should only be callable by the bank or the owner of the account. +* @param `_operator` is the address to add to the set of authorized operators. +* @param `classId` is the class id of the bond. +* @param `_approved` is true if the operator is approved (based on the conditions provided), false meaning approval is revoked. +* @dev contract MUST define internal function regarding the conditions for setting approval and should be callable only by bank or owner. +* e.g: setApprovalFor(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B,0,true); +* means that address 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is authorized to transfer bonds from class 0 (across all nonces). +*/ + +function setApprovalFor(address _operator, bool _approved) external returns(bool approved); + +/** +* totalSupply +* @dev Here, total supply includes burned and redeemed supply. +* @param classId is the corresponding class Id of the bond. +* @param nonceId is the nonce Id of the given bond class. +* @return the supply of the bonds +* e.g: +* totalSupply(0, 1); +* it finds the total supply of the bonds of classid 0 and bond nonce 1. +*/ +function totalSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* redeemedSupply +* @dev Returns the redeemed supply of the bond identified by (classId,nonceId). +* @param classId is the corresponding class id of the bond. +* @param nonceId is the nonce id of the given bond class. +* @return the supply of bonds redeemed. +*/ +function redeemedSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* activeSupply +* @dev Returns the active supply of the bond defined by (classId,NonceId). +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @return the non-redeemed, active supply. +*/ +function activeSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* burnedSupply +* @dev Returns the burned supply of the bond in defined by (classId,NonceId). +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @return gets the supply of bonds for given classId and nonceId that are already burned. +*/ +function burnedSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* balanceOf +* @dev Returns the balance of the bonds (nonReferenced) of given classId and bond nonce held by the address `_account`. +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @param _account address of the owner whose balance is to be determined. +* @dev this also consists of bonds that are redeemed. +*/ +function balanceOf(address _account, uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* classMetadata +* @dev Returns the JSON metadata of the classes. +* @dev The metadata SHOULD follow a set of structures explained later in the metadata.md +* @param metadataId is the index-id given bond class information. +* @return the JSON metadata of the nonces. — e.g. `[title, type, description]`. +*/ +function classMetadata(uint256 metadataId) external view returns (Metadata memory); + +/** +* nonceMetadata +* @dev Returns the JSON metadata of the nonces. +* @dev The metadata SHOULD follow a set of structures explained later in metadata.md +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @param metadataId is the index of the JSON storage for given metadata information. more is defined in metadata.md. +* @returns the JSON metadata of the nonces. — e.g. `[title, type, description]`. +*/ +function nonceMetadata(uint256 classId, uint256 metadataId) external view returns (Metadata memory); + +/** +* classValues +* @dev allows anyone to read the values (stored in struct Values for different class) for given bond class `classId`. +* @dev the values SHOULD follow a set of structures as explained in metadata along with correct mapping corresponding to the given metadata structure +* @param classId is the corresponding classId of the bond. +* @param metadataId is the index of the JSON storage for given metadata information of all values of given metadata. more is defined in metadata.md. +* @returns the Values of the class metadata. — e.g. `[string, uint, address]`. +*/ +function classValues(uint256 classId, uint256 metadataId) external view returns (Values memory); + +/** +* nonceValues +* @dev allows anyone to read the values (stored in struct Values for different class) for given bond (`nonceId`,`classId`). +* @dev the values SHOULD follow a set of structures explained in metadata along with correct mapping corresponding to the given metadata structure +* @param classId is the corresponding classId of the bond. +* @param metadataId is the index of the JSON storage for given metadata information of all values of given metadata. More is defined in metadata.md. +* @returns the Values of the class metadata. — e.g. `[string, uint, address]`. +*/ +function nonceValues(uint256 classId, uint256 nonceId, uint256 metadataId) external view returns (Values memory); + +/** +* getProgress +* @dev Returns the parameters to determine the current status of bonds maturity. +* @dev the conditions of redemption SHOULD be defined with one or several internal functions. +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonceId of the given bond class . +* @returns progressAchieved defines the metric (either related to % liquidity, time, etc.) that defines the current status of the bond. +* @returns progressRemaining defines the metric that defines the remaining time/ remaining progress. +*/ +function getProgress(uint256 classId, uint256 nonceId) external view returns (uint256 progressAchieved, uint256 progressRemaining); + +/** +* allowance +* @dev Authorizes to set the allowance for given `_spender` by `_owner` for all bonds identified by (classId, nonceId). +* @param _owner address of the owner of bond(and also msg.sender). +* @param _spender is the address authorized to spend the bonds held by _owner of info (classId, nonceId). +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonceId of the given bond class. +* @notice Returns the _amount which spender is still allowed to withdraw from _owner. +*/ +function allowance(address _owner, address _spender, uint256 classId, uint256 nonceId) external returns(uint256); + +/** +* isApprovedFor +* @dev returns true if address _operator is approved for managing the account’s bonds class. +* @notice Queries the approval status of an operator for a given owner. +* @dev _owner is the owner of bonds. +* @dev _operator is the EOA /contract, whose status for approval on bond class for this approval is checked. +* @returns “true” if the operator is approved, “false” if not. +*/ +function isApprovedFor(address _owner, address _operator) external view returns (bool); +``` + +### Events + +```solidity +/** +* Issue +* @notice Issue MUST trigger when Bonds are issued. This SHOULD not include zero value Issuing. +* @dev This SHOULD not include zero value issuing. +* @dev Issue MUST be triggered when the operator (i.e Bank address) contract issues bonds to the given entity. +* eg: emit Issue(_operator, 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,[IERC3475.Transaction(1,14,500)]); +* issue by address(operator) 500 Bonds(nonce14,class 1) to address 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef. +*/ + +event Issue(address indexed _operator, address indexed _to, Transaction[] _transactions); + +/** +* Redeem +* @notice Redeem MUST trigger when Bonds are redeemed. This SHOULD not include zero value redemption. +*e.g: emit Redeem(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x492Af743654549b12b1B807a9E0e8F397E44236E,[IERC3475.Transaction(1,14,500)]); +* emit event when 5000 bonds of class 1, nonce 14 owned by address 0x492Af743654549b12b1B807a9E0e8F397E44236E are being redeemed by 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef. +*/ + +event Redeem(address indexed _operator, address indexed _from, Transaction[] _transactions); + + +/** +* Burn. +* @dev `Burn` MUST trigger when the bonds are being redeemed via staking (or being invalidated) by the bank contract. +* @dev `Burn` MUST trigger when Bonds are burned. This SHOULD not include zero value burning. +* e.g : emit Burn(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x492Af743654549b12b1B807a9E0e8F397E44236E,[IERC3475.Transaction(1,14,500)]); +* emits event when 500 bonds of owner 0x492Af743654549b12b1B807a9E0e8F397E44236E of type (class 1, nonce 14) are burned by operator 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef. +*/ + +event burn(address _operator, address _owner, Transaction[] _transactions); + +/** +* Transfer +* @dev its emitted when the bond is transferred by address(operator) from owner address(_from) to address(_to) with the bonds transferred, whose params are defined by _transactions struct array. +* @dev Transfer MUST trigger when Bonds are transferred. This SHOULD not include zero value transfers. +* @dev Transfer event with the _from `0x0` MUST not create this event(use `event Issued` instead). +* e.g emit Transfer(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x492Af743654549b12b1B807a9E0e8F397E44236E, _to, [IERC3475.Transaction(1,14,500)]); +* transfer by address(_operator) amount 500 bonds with (Class 1 and Nonce 14) from 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, to address(_to). +*/ + +event Transfer(address indexed _operator, address indexed _from, address indexed _to, Transaction[] _transactions); + +/** +* ApprovalFor +* @dev its emitted when address(_owner) approves the address(_operator) to transfer his bonds. +* @notice Approval MUST trigger when bond holders are approving an _operator. This SHOULD not include zero value approval. +* eg: emit ApprovalFor(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x492Af743654549b12b1B807a9E0e8F397E44236E, true); +* this means 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef gives 0x492Af743654549b12b1B807a9E0e8F397E44236E access permission for transfer of its bonds. +*/ + +event ApprovalFor(address indexed _owner, address indexed _operator, bool _approved); +``` + +**Metadata**: +The metadata of a bond class or nonce is stored as an array of JSON objects, represented by the following types. + +**NOTE: all of the metadata schemas are referenced from [here](../assets/eip-3475/Metadata.md)** + +### 1. Description: + +This defines the additional information about the nature of data being stored in the nonce/class metadata structures. They are defined using the structured explained [here](../assets/eip-3475/Metadata.md#1-description-metadata). this will then be used by the frontend of the respective entities participating in the bond markets to interpret the data which is compliant with their jurisdiction. + +### 2. Nonce: + +The key value for indexing the information is the 'class' field. Following are the rules: + +- The title can be any alphanumeric type that is differentiated by the description of metadata (although it can be dependent on certain jurisdictions). +- The title SHOULD not be EMPTY. + +Some specific examples of metadata can be the localization of bonds, jurisdiction details etc., and they can be found in the [metadata.md](../assets/eip-3475/Metadata.md) example description. + +### 3. Class metadata: + +This structure defines the details of the class information (symbol, risk information, etc.). the example is explained [here](../assets/eip-3475/Metadata.md) in the class metadata section. + +### 4. Decoding data + +First, the functions for analyzing the metadata (i.e `ClassMetadata` and `NonceMetadata`) are to be used by the corresponding frontend to decode the information of the bond. + +This is done via overriding the function interface for functions `classValues` and `nonceValues` by defining the key (which SHOULD be an index) to read the corresponding information stored as a JSON object. + +```JSON +{ +"title": "symbol", +"_type": "string", +"description": "defines the unique identifier name in following format: (symbol, bondType, maturity in months)", +"values": ["Class Name 1","Class Name 2","DBIT Fix 6M"], +} +``` + +e.g. In the above example, to get the `symbol` of the given class id, we can use the class id as a key to get the `symbol` value in the values, which then can be used for fetching the detail for instance. + +## Rationale + +### Metadata structure + +Instead of storing the details about the class and their issuances to the user (ie nonce) externally, we store the details in the respective structures. Classes represent the different bond types, and nonces represent the various period of issuances. Nonces under the same class share the same metadata. Meanwhile, nonces are non-fungible. Each nonce can store a different set of metadata. Thus, upon transfer of a bond, all the metadata will be transferred to the new owner of the bond. + +```solidity + struct Values{ + string stringValue; + uint uintValue; + address addressValue; + bool boolValue; + bytes bytesValue; + } +``` + +```solidity + struct Metadata { + string title; + string _type; + string description; + } +``` + +### Batch function + + This EIP supports batch operations. It allows the user to transfer different bonds along with their metadata to a new address instantaneously in a single transaction. After execution, the new owner holds the right to reclaim the face value of each of the bonds. This mechanism helps with the "packaging" of bonds–helpful in use cases like trades on a secondary market. + +```solidity + struct Transaction { + uint256 classId; + uint256 nonceId; + uint256 _amount; + } +``` + +Where: +The `classId` is the class id of the bond. + +The `nonceId` is the nonce id of the given bond class. This param is for distinctions of the issuing conditions of the bond. + +The `_amount` is the amount of the bond for which the spender is approved. + +### AMM optimization + + One of the most obvious use cases of this EIP is the multilayered pool. The early version of AMM uses a separate smart contract and an [EIP-20](./eip-20.md) LP token to manage a pair. By doing so, the overall liquidity inside of one pool is significantly reduced and thus generates unnecessary gas spent and slippage. Using this EIP standard, one can build a big liquidity pool with all the pairs inside (thanks to the presence of the data structures consisting of the liquidity corresponding to the given class and nonce of bonds). Thus by knowing the class and nonce of the bonds, the liquidity can be represented as the percentage of a given token pair for the owner of the bond in the given pool. Effectively, the [EIP-20](./eip-20.md) LP token (defined by a unique smart contract in the pool factory contract) is aggregated into a single bond and consolidated into a single pool. + +- The reason behind the standard's name (abstract storage bond) is its ability to store all the specifications (metadata/values and transaction as defined in the following sections) without needing external storage on-chain/off-chain. + +## Backwards Compatibility + +Any contract that inherits the interface of this EIP is compatible. This compatibility exists for issuer and receiver of the bonds. Also any client EOA wallet can be compatible with the standard if they are able to sign `issue()` and `redeem()` commands. + +However, any existing [EIP-20](./eip-20.md) token contract can issue its bonds by delegating the minting role to a bank contract with the interface of this standard built-in. Check out our reference implementation for the correct interface definition. + +To ensure the indexing of transactions throughout the bond lifecycle (i.e "Issue", "Redeem" and "Transfer" functions), events cited in specification section MUST be emitted when such transaction is passed. + +**Note that the this standard interface is also compatible with [EIP-20](./eip-20.md) and [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md)interface.** + +However, creating a separate bank contract is recommended for reading the bonds and future upgrade needs. + +Acceptable collateral can be in the form of fungible (like [EIP-20](./eip-20.md)), non-fungible ([EIP-721](./eip-721.md), [EIP-1155](./eip-1155.md)) , or other bonds represented by this standard. + +## Test Cases + +Test-case for the minimal reference implementation is [here](../assets/eip-3475/ERC3475.test.ts). Use the Truffle box to compile and test the contracts. + +## Reference Implementation + +- [Interface](../assets/eip-3475/interfaces/IERC3475.sol). + +- [Basic Example](../assets/eip-3475/ERC3475.sol). + - This demonstration shows only minimalist implementation. + +## Security Considerations + +- The `function setApprovalFor(address _operatorAddress)` gives the operator role to `_operatorAddress`. It has all the permissions to transfer, burn and redeem bonds by default. + +- If the owner wants to give a one-time allocation to an address for specific bonds(classId,bondsId), he should call the `function approve()` giving the `Transaction[]` allocated rather than approving all the classes using `setApprovalFor`. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3508.md b/EIPS/eip-3508.md index 76c3439d489bf5..8c22e237901e5b 100644 --- a/EIPS/eip-3508.md +++ b/EIPS/eip-3508.md @@ -3,7 +3,7 @@ eip: 3508 title: Transaction Data Opcodes author: Alex Papageorgiou (@alex-ppg) discussions-to: https://ethereum-magicians.org/t/eip-draft-transaction-data-opcodes/6017 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-04-16 @@ -97,4 +97,4 @@ The gas reduction, however, would be an implementation-based optimization that w ## 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-3520.md b/EIPS/eip-3520.md index eaccf5257d6296..8e83f74a042b28 100644 --- a/EIPS/eip-3520.md +++ b/EIPS/eip-3520.md @@ -3,7 +3,7 @@ eip: 3520 title: Transaction Destination Opcode author: Alex Papageorgiou (@alex-ppg) discussions-to: https://ethereum-magicians.org/t/eip-3520-transaction-destination-opcode/6058 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-04-16 @@ -86,4 +86,4 @@ The behaviour of the `ENTRYPOINT` opcode during a contract creation will result ## 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-3521.md b/EIPS/eip-3521.md index 0fb6028c046ec7..b88a08bcd21f6d 100644 --- a/EIPS/eip-3521.md +++ b/EIPS/eip-3521.md @@ -3,7 +3,7 @@ eip: 3521 title: Reduce access list cost author: Matt Garnett (@lightclient) discussions-to: https://ethereum-magicians.org/t/eip-3521-reduce-access-list-cost/6072 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-04-15 @@ -102,4 +102,4 @@ No issues. None. ## 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-3525.md b/EIPS/eip-3525.md new file mode 100644 index 00000000000000..93bb1fc1d4c420 --- /dev/null +++ b/EIPS/eip-3525.md @@ -0,0 +1,575 @@ +--- +eip: 3525 +title: Semi-Fungible Token +description: Defines a specification where ERC-721 compatible tokens with the same SLOT and different IDs are fungible. +author: Will Wang (@will42w), Mike Meng , Yi Cai (@YeeTsai) , Ryan Chow , Zhongxin Wu (@Nerverwind), AlvisDu (@AlvisDu) +discussions-to: https://ethereum-magicians.org/t/eip-3525-the-semi-fungible-token +status: Final +type: Standards Track +category: ERC +created: 2020-12-01 +requires: 20, 165, 721 +--- + +## Abstract + +This is a standard for semi-fungible tokens. The set of smart contract interfaces described in this document defines an [ERC-721](./eip-721.md) compatible token standard. This standard introduces an `` triple scalar model that represents the semi-fungible structure of a token. It also introduces new transfer models as well as approval models that reflect the semi-fungible nature of the tokens. + +Token contains an ERC-721 equivalent ID property to identify itself as a universally unique entity, so that the tokens can be transferred between addresses and approved to be operated in ERC-721 compatible way. + +Token also contains a `value` property, representing the quantitative nature of the token. The meaning of the 'value' property is quite like that of the 'balance' property of an [ERC-20](./eip-20.md) token. Each token has a 'slot' attribute, ensuring that the value of two tokens with the same slot be treated as fungible, adding fungibility to the value property of the tokens. + +This EIP introduces new token transfer models for semi-fungibility, including value transfer between two tokens of the same slot and value transfer from a token to an address. + +## Motivation + +Tokenization is one of the most important trends by which to use and control digital assets in crypto. Traditionally, there have been two approaches to do so: fungible and non-fungible tokens. Fungible tokens generally use the ERC-20 standard, where every unit of an asset is identical to each other. ERC-20 is a flexible and efficient way to manipulate fungible tokens. Non-fungible tokens are predominantly ERC-721 tokens, a standard capable of distinguishing digital assets from one another based on identity. + +However, both have significant drawbacks. For example, ERC-20 requires that users create a separate ERC-20 contract for each individual data structure or combination of customizable properties. In practice, this results in an extraordinarily large amount of ERC-20 contracts that need to be created. On the other hand, ERC-721 tokens provide no quantitative feature, significantly undercutting their computability, liquidity, and manageability. For example, if one was to create financial instruments such as bonds, insurance policy, or vesting plans using ERC-721, no standard interfaces are available for us to control the value in them, making it impossible, for example, to transfer a portion of the equity in the contract represented by the token. + +A more intuitive and straightforward way to solve the problem is to create a semi-fungible token that has the quantitative features of ERC-20 and qualitative attributes of ERC-721. The backwards-compatibility with ERC-721 of such semi-fungible tokens would help utilize existing infrastructures already in use and lead to faster adoption. + +## Specification + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. + +**Every [ERC-3525](./eip-3525.md) compliant contract must implement the ERC-3525, ERC-721 and [ERC-165](./eip-165.md) interfaces** + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard + * Note: the ERC-165 identifier for this interface is 0xd5358140. + */ +interface IERC3525 /* is IERC165, IERC721 */ { + /** + * @dev MUST emit when value of a token is transferred to another token with the same slot, + * including zero value transfers (_value == 0) as well as transfers when tokens are created + * (`_fromTokenId` == 0) or destroyed (`_toTokenId` == 0). + * @param _fromTokenId The token id to transfer value from + * @param _toTokenId The token id to transfer value to + * @param _value The transferred value + */ + event TransferValue(uint256 indexed _fromTokenId, uint256 indexed _toTokenId, uint256 _value); + + /** + * @dev MUST emit when the approval value of a token is set or changed. + * @param _tokenId The token to approve + * @param _operator The operator to approve for + * @param _value The maximum value that `_operator` is allowed to manage + */ + event ApprovalValue(uint256 indexed _tokenId, address indexed _operator, uint256 _value); + + /** + * @dev MUST emit when the slot of a token is set or changed. + * @param _tokenId The token of which slot is set or changed + * @param _oldSlot The previous slot of the token + * @param _newSlot The updated slot of the token + */ + event SlotChanged(uint256 indexed _tokenId, uint256 indexed _oldSlot, uint256 indexed _newSlot); + + /** + * @notice Get the number of decimals the token uses for value - e.g. 6, means the user + * representation of the value of a token can be calculated by dividing it by 1,000,000. + * Considering the compatibility with third-party wallets, this function is defined as + * `valueDecimals()` instead of `decimals()` to avoid conflict with ERC-20 tokens. + * @return The number of decimals for value + */ + function valueDecimals() external view returns (uint8); + + /** + * @notice Get the value of a token. + * @param _tokenId The token for which to query the balance + * @return The value of `_tokenId` + */ + function balanceOf(uint256 _tokenId) external view returns (uint256); + + /** + * @notice Get the slot of a token. + * @param _tokenId The identifier for a token + * @return The slot of the token + */ + function slotOf(uint256 _tokenId) external view returns (uint256); + + /** + * @notice Allow an operator to manage the value of a token, up to the `_value`. + * @dev MUST revert unless caller is the current owner, an authorized operator, or the approved + * address for `_tokenId`. + * MUST emit the ApprovalValue event. + * @param _tokenId The token to approve + * @param _operator The operator to be approved + * @param _value The maximum value of `_toTokenId` that `_operator` is allowed to manage + */ + function approve( + uint256 _tokenId, + address _operator, + uint256 _value + ) external payable; + + /** + * @notice Get the maximum value of a token that an operator is allowed to manage. + * @param _tokenId The token for which to query the allowance + * @param _operator The address of an operator + * @return The current approval value of `_tokenId` that `_operator` is allowed to manage + */ + function allowance(uint256 _tokenId, address _operator) external view returns (uint256); + + /** + * @notice Transfer value from a specified token to another specified token with the same slot. + * @dev Caller MUST be the current owner, an authorized operator or an operator who has been + * approved the whole `_fromTokenId` or part of it. + * MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist. + * MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match. + * MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the + * operator. + * MUST emit `TransferValue` event. + * @param _fromTokenId The token to transfer value from + * @param _toTokenId The token to transfer value to + * @param _value The transferred value + */ + function transferFrom( + uint256 _fromTokenId, + uint256 _toTokenId, + uint256 _value + ) external payable; + + + /** + * @notice Transfer value from a specified token to an address. The caller should confirm that + * `_to` is capable of receiving ERC-3525 tokens. + * @dev This function MUST create a new ERC-3525 token with the same slot for `_to`, + * or find an existing token with the same slot owned by `_to`, to receive the transferred value. + * MUST revert if `_fromTokenId` is zero token id or does not exist. + * MUST revert if `_to` is zero address. + * MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the + * operator. + * MUST emit `Transfer` and `TransferValue` events. + * @param _fromTokenId The token to transfer value from + * @param _to The address to transfer value to + * @param _value The transferred value + * @return ID of the token which receives the transferred value + */ + function transferFrom( + uint256 _fromTokenId, + address _to, + uint256 _value + ) external payable returns (uint256); +} +``` + +The slot's enumeration extension is OPTIONAL. This allows your contract to publish its full list of `SLOT`s and make them discoverable. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for slot enumeration + * @dev Interfaces for any contract that wants to support enumeration of slots as well as tokens + * with the same slot. + * Note: the ERC-165 identifier for this interface is 0x3b741b9e. + */ +interface IERC3525SlotEnumerable is IERC3525 /* , IERC721Enumerable */ { + + /** + * @notice Get the total amount of slots stored by the contract. + * @return The total amount of slots + */ + function slotCount() external view returns (uint256); + + /** + * @notice Get the slot at the specified index of all slots stored by the contract. + * @param _index The index in the slot list + * @return The slot at `index` of all slots. + */ + function slotByIndex(uint256 _index) external view returns (uint256); + + /** + * @notice Get the total amount of tokens with the same slot. + * @param _slot The slot to query token supply for + * @return The total amount of tokens with the specified `_slot` + */ + function tokenSupplyInSlot(uint256 _slot) external view returns (uint256); + + /** + * @notice Get the token at the specified index of all tokens with the same slot. + * @param _slot The slot to query tokens with + * @param _index The index in the token list of the slot + * @return The token ID at `_index` of all tokens with `_slot` + */ + function tokenInSlotByIndex(uint256 _slot, uint256 _index) external view returns (uint256); +} +``` + +The slot level approval is OPTIONAL. This allows any contract that wants to support approval for slots, which allows an operator to manage one's tokens with the same slot. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for approval of slot level + * @dev Interfaces for any contract that wants to support approval of slot level, which allows an + * operator to manage one's tokens with the same slot. + * See https://eips.ethereum.org/EIPS/eip-3525 + * Note: the ERC-165 identifier for this interface is 0xb688be58. + */ +interface IERC3525SlotApprovable is IERC3525 { + /** + * @dev MUST emit when an operator is approved or disapproved to manage all of `_owner`'s + * tokens with the same slot. + * @param _owner The address whose tokens are approved + * @param _slot The slot to approve, all of `_owner`'s tokens with this slot are approved + * @param _operator The operator being approved or disapproved + * @param _approved Identify if `_operator` is approved or disapproved + */ + event ApprovalForSlot(address indexed _owner, uint256 indexed _slot, address indexed _operator, bool _approved); + + /** + * @notice Approve or disapprove an operator to manage all of `_owner`'s tokens with the + * specified slot. + * @dev Caller SHOULD be `_owner` or an operator who has been authorized through + * `setApprovalForAll`. + * MUST emit ApprovalSlot event. + * @param _owner The address that owns the ERC-3525 tokens + * @param _slot The slot of tokens being queried approval of + * @param _operator The address for whom to query approval + * @param _approved Identify if `_operator` would be approved or disapproved + */ + function setApprovalForSlot( + address _owner, + uint256 _slot, + address _operator, + bool _approved + ) external payable; + + /** + * @notice Query if `_operator` is authorized to manage all of `_owner`'s tokens with the + * specified slot. + * @param _owner The address that owns the ERC-3525 tokens + * @param _slot The slot of tokens being queried approval of + * @param _operator The address for whom to query approval + * @return True if `_operator` is authorized to manage all of `_owner`'s tokens with `_slot`, + * false otherwise. + */ + function isApprovedForSlot( + address _owner, + uint256 _slot, + address _operator + ) external view returns (bool); +} +``` + + +### ERC-3525 Token Receiver + +If a smart contract wants to be informed when they receive values from other addresses, it should implement all of the functions in the `IERC3525Receiver` interface, in the implementation it can decide whether to accept or reject the transfer. See "Transfer Rules" for further detail. + +```solidity + pragma solidity ^0.8.0; + +/** + * @title ERC-3525 token receiver interface + * @dev Interface for a smart contract that wants to be informed by ERC-3525 contracts when receiving values from ANY addresses or ERC-3525 tokens. + * Note: the ERC-165 identifier for this interface is 0x009ce20b. + */ +interface IERC3525Receiver { + /** + * @notice Handle the receipt of an ERC-3525 token value. + * @dev An ERC-3525 smart contract MUST check whether this function is implemented by the recipient contract, if the + * recipient contract implements this function, the ERC-3525 contract MUST call this function after a + * value transfer (i.e. `transferFrom(uint256,uint256,uint256,bytes)`). + * MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256, + * uint256,bytes)'))`) if the transfer is accepted. + * MUST revert or return any value other than 0x009ce20b if the transfer is rejected. + * @param _operator The address which triggered the transfer + * @param _fromTokenId The token id to transfer value from + * @param _toTokenId The token id to transfer value to + * @param _value The transferred value + * @param _data Additional data with no specified format + * @return `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))` + * unless the transfer is rejected. + */ + function onERC3525Received(address _operator, uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external returns (bytes4); + +} +``` + +### Token Manipulation + +#### Scenarios + +**_Transfer:_** + +Besides ERC-721 compatible token transfer methods, this EIP introduces two new transfer models: value transfer from ID to ID, and value transfer from ID to address. + +```solidity +function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable; + +function transferFrom(uint256 _fromTokenId, address _to, uint256 _value) external payable returns (uint256 toTokenId_); +``` + +The first one allows value transfers from one token (specified by `_fromTokenId`) to another token (specified by `_toTokenId`) within the same slot, resulting in the `_value` being subtracted from the value of the source token and added to the value of the destination token; + +The second one allows value transfers from one token (specified by `_fromTokenId`) to an address (specified by `_to`), the value is actually transferred to a token owned by the address, and the id of the destination token should be returned. Further explanation can be found in the 'design decision' section for this method. + +#### Rules + +**_approving rules:_** + +This EIP provides four kinds of approving functions indicating different levels of approvals, which can be described as full level approval, slot level approval, token ID level approval as well as value level approval. + +- `setApprovalForAll`, compatible with ERC-721, SHOULD indicate the full level of approval, which means that the authorized operators are capable of managing all the tokens, including their values, owned by the owner. +- `setApprovalForSlot` (optional) SHOULD indicate the slot level of approval, which means that the authorized operators are capable of managing all the tokens with the specified slot, including their values, owned by the owner. +- The token ID level `approve` function, compatible with ERC-721, SHOULD indicate that the authorized operator is capable of managing only the specified token ID, including its value, owned by the owner. +- The value level `approve` function, SHOULD indicate that the authorized operator is capable of managing the specified maximum value of the specified token owned by the owner. +- For any approving function, the caller MUST be the owner or has been approved with a higher level of authority. + +**_transferFrom rules:_** + +- The `transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value)` function, SHOULD indicate value transfers from one token to another token, in accordance with the rules below: + + - MUST revert unless `msg.sender` is the owner of `_fromTokenId`, an authorized operator or an operator who has been approved the whole token or at least `_value` of it. + - MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist. + - MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match. + - MUST revert if `_value` exceeds the value of `_fromTokenId` or its allowance to the operator. + - MUST check for the `onERC3525Received` function if the owner of _toTokenId is a smart contract, if the function exists, MUST call this function after the value transfer, MUST revert if the result is not equal to 0x009ce20b; + - MUST emit `TransferValue` event. + +- The `transferFrom(uint256 _fromTokenId, address _to, uint256 _value)` function, which transfers value from one token ID to an address, SHOULD follow the rule below: + + - MUST either find a ERC-3525 token owned by the address `_to` or create a new ERC-3525 token, with the same slot of `_fromTokenId`, to receive the transferred value. + - MUST revert unless `msg.sender` is the owner of `_fromTokenId`, an authorized operator or an operator who has been approved the whole token or at least `_value` of it. + - MUST revert if `_fromTokenId` is zero token id or does not exist. + - MUST revert if `_to` is zero address. + - MUST revert if `_value` exceeds the value of `_fromTokenId` or its allowance to the operator. + - MUST check for the `onERC3525Received` function if the _to address is a smart contract, if the function exists, MUST call this function after the value transfer, MUST revert if the result is not equal to 0x009ce20b; + - MUST emit `Transfer` and `TransferValue` events. + + +### Metadata + +#### Metadata Extensions + +ERC-3525 metadata extensions are compatible ERC-721 metadata extensions. + +This optional interface can be identified with the ERC-165 Standard Interface Detection. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for metadata + * @dev Interfaces for any contract that wants to support query of the Uniform Resource Identifier + * (URI) for the ERC-3525 contract as well as a specified slot. + * Because of the higher reliability of data stored in smart contracts compared to data stored in + * centralized systems, it is recommended that metadata, including `contractURI`, `slotURI` and + * `tokenURI`, be directly returned in JSON format, instead of being returned with a url pointing + * to any resource stored in a centralized system. + * See https://eips.ethereum.org/EIPS/eip-3525 + * Note: the ERC-165 identifier for this interface is 0xe1600902. + */ +interface IERC3525Metadata is + IERC3525 /* , IERC721Metadata */ +{ + /** + * @notice Returns the Uniform Resource Identifier (URI) for the current ERC-3525 contract. + * @dev This function SHOULD return the URI for this contract in JSON format, starting with + * header `data:application/json;`. + * See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for contract URI. + * @return The JSON formatted URI of the current ERC-3525 contract + */ + function contractURI() external view returns (string memory); + + /** + * @notice Returns the Uniform Resource Identifier (URI) for the specified slot. + * @dev This function SHOULD return the URI for `_slot` in JSON format, starting with header + * `data:application/json;`. + * See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for slot URI. + * @return The JSON formatted URI of `_slot` + */ + function slotURI(uint256 _slot) external view returns (string memory); +} +``` + +#### ERC-3525 Metadata URI JSON Schema + +This is the "ERC-3525 Metadata JSON Schema for `contractURI()`" referenced above. + +```json +{ + "title": "Contract Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Contract Name" + }, + "description": { + "type": "string", + "description": "Describes the contract" + }, + "image": { + "type": "string", + "description": "Optional. Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing what this contract represents." + }, + "external_link": { + "type": "string", + "description": "Optional. A URI pointing to an external resource." + }, + "valueDecimals": { + "type": "integer", + "description": "The number of decimal places that the balance should display - e.g. 18, means to divide the token value by 1000000000000000000 to get its user representation." + } + } +} +``` + +This is the "ERC-3525 Metadata JSON Schema for `slotURI(uint)`" referenced above. + +```json +{ + "title": "Slot Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset category to which this slot represents" + }, + "description": { + "type": "string", + "description": "Describes the asset category to which this slot represents" + }, + "image": { + "type": "string", + "description": "Optional. Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing the asset category to which this slot represents." + }, + "properties": { + "type": "array", + "description": "Each item of `properties` SHOULD be organized in object format, including name, description, value, order (optional), display_type (optional), etc." + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of this property." + }, + "description": { + "type": "string", + "description": "Describes this property." + } + "value": { + "description": "The value of this property, which may be a string or a number." + }, + "is_intrinsic": { + "type": "boolean", + "description": "According to the definition of `slot`, one of the best practice to generate the value of a slot is utilizing the `keccak256` algorithm to calculate the hash value of multi properties. In this scenario, the `properties` field should contain all the properties that are used to calculate the value of `slot`, and if a property is used in the calculation, is_intrinsic must be TRUE." + }, + "order": { + "type": "integer", + "description": "Optional, related to the value of is_intrinsic. If is_intrinsic is TRUE, it must be the order of this property appeared in the calculation method of the slot." + }, + "display_type": { + "type": "string", + "description": "Optional. Specifies in what form this property should be displayed." + } + } + } + } + } +} +``` + + +This is the "ERC-3525 Metadata JSON Schema for `tokenURI(uint)`" referenced above. + +```json +{ + "title": "Token Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this token represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this token represents" + }, + "image": { + "type": "string", + "description": "Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing the asset to which this token represents." + }, + "balance": { + "type": "integer", + "description": "THe value held by this token." + }, + "slot": { + "type": "integer", + "description": "The id of the slot that this token belongs to." + }, + "properties": { + "type": "object", + "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays. Optional, you can use the same schema as the properties section of ERC-3525 Metadata JSON Schema for slotURI(uint) if you need a better description attribute." + } + } +} +``` + + +## Rationale + +### Metadata generation + +This token standard is designed to represent semi-fungible assets, which are most suited for financial instruments rather than collectibles or in-game items. For maximum transparency and safety of digital assets, we strongly recommend that all implementations should generate metadata directly from contract code rather than giving out an off-chain server URL. + +### Design decision: Value transfer from token to address + +The 'value' of a token is a property of the token and is not linked to an address, so to transfer the value to an address would be actually transferring it to a token owned by that address, not the address itself. + +From the implementation perspective, the process of transferring values from token to address could be done as follows: (1) create a new token for the recipient's address, (2) transfer the value to the new token from the 'source token'. So that this method is not fully independent from the ID-to-ID transfer method, and can be viewed as syntactic sugar that wraps the process described above. + +In a special case, if the destination address owns one or more tokens with the same slot value as the source token, this method will have an alternative implementation as follows: (1) find one token owned by the address with the same slot value of the source token, (2) transfer the value to the found token. + +Both implementations described above should be treated as compliant with this standard. + +The purpose of maintaining id-to-address transfer function is to maximize the compatibility with most wallet apps, since for most of the token standards, the destination of token transfer are addresses. This syntactic wrapping will help wallet apps easily implement the value transfer function from a token to any address. + +### Design decision: Notification/acceptance mechanism instead of 'Safe Transfer' + +ERC-721 and some later token standards introduced 'Safe Transfer' model, for better control of the 'safety' when transferring tokens, this mechanism leaves the choice of different transfer modes (safe/unsafe) to the sender, and may cause some potential problems: + +1. In most situations the sender does not know how to choose between two kinds of transfer methods (safe/unsafe); +2. If the sender calls the `safeTransferFrom` method, the transfer may fail if the recipient contract did not implement the callback function, even if that contract is capable of receiving and manipulating the token without issue. + +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 ERC-3525 contracts MUST check for the existence of `onERC3525Received` on the recipient contract and call the function when it exists; +3. Any smart contract can implement `onERC3525Received` function for the purpose of being notified after receiving values; this function MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))`) if the transfer is accepted, or any other value if the transfer is rejected. + +There is a special case for this notification/acceptance mechanism: since ERC-3525 allows value transfer from an address to itself, when a smart contract which implements `onERC3525Received` transfers value to itself, `onERC3525Received` will also be called. This allows for the contract to implement different rules of acceptance between self-value-transfer and receiving value from other addresses. + +### Design decision: Relationship between different approval models + +For semantic compatibility with ERC-721 as well as the flexibility of value manipulation of tokens, we decided to define the relationships between some of the levels of approval like that: + +1. Approval of an id will lead to the ability to partially transfer values from this id by the approved operator; this will simplify the value approval for an id. However, the approval of total values in a token should not lead to the ability to transfer the token entity by the approved operator. +2. `setApprovalForAll` will lead to the ability to partially transfer values from any token, as well as the ability to approve partial transfer of values from any token to a third party; this will simplify the value transfer and approval of all tokens owned by an address. + +## Backwards Compatibility + +As mentioned in the beginning, this EIP is backward compatible with ERC-721. + +## Reference Implementation + +- [ERC-3525 implementation](../assets/eip-3525/contracts/ERC3525.sol) + +## Security Considerations + +The value level approval and slot level approval (optional) is isolated from ERC-721 approval models, so that approving value should not affect ERC-721 level approvals. Implementations of this EIP must obey this principle. + +Since this EIP is ERC-721 compatible, any wallets and smart contracts that can hold and manipulate standard ERC-721 tokens will have no risks of asset loss for ERC-3525 tokens due to incompatible standards implementations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3529.md b/EIPS/eip-3529.md index 8534afe219bf83..f962230d6f7855 100644 --- a/EIPS/eip-3529.md +++ b/EIPS/eip-3529.md @@ -3,8 +3,7 @@ eip: 3529 title: Reduction in refunds author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) discussions-to: https://ethereum-magicians.org/t/eip-3529-reduction-in-refunds-alternative-to-eip-3298-and-3403-that-better-preserves-existing-clearing-incentives/6097 -status: Last Call -review-period-end: 2021-07-14 +status: Final type: Standards Track category: Core created: 2021-04-22 @@ -138,4 +137,4 @@ Refunds are not visible to transaction execution, so this should not have any im The maximum amount of gas that can be spent on execution in a block is limited to the gas limit, if we do not count zero-to-nonzero `SSTORE`s that were later reset back to zero. It is okay to not count those, because if such an `SSTORE` is reset, storage is not expanded and the client does not need to actually adjust the Merke tree; the gas consumption is refunded, but the effort normally required by the client to process those opcodes is also cancelled. **Clients should make sure to not do a storage write if `new_value = original_value`; this was a prudent optimization since the beginning of Ethereum but it becomes more important now.** ## 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-3534.md b/EIPS/eip-3534.md index 62d755f5d4e562..2876b58f15765c 100644 --- a/EIPS/eip-3534.md +++ b/EIPS/eip-3534.md @@ -3,7 +3,7 @@ eip: 3534 title: Restricted Chain Context Type Transactions author: Isaac Ardis (@whilei) discussions-to: https://ethereum-magicians.org/t/eip-3534-restricted-chain-context-transaction-type/6112 -status: Draft +status: Stagnant type: Standards Track category: Core created: 2021-04-20 @@ -309,4 +309,4 @@ However, transactions which are considered by a miner to be undesirable can be s ## Copyright -Copyright and related rights waved via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waved via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3540.md b/EIPS/eip-3540.md index a3d1df835d297e..8dcb0a8c1c0102 100644 --- a/EIPS/eip-3540.md +++ b/EIPS/eip-3540.md @@ -1,218 +1,314 @@ --- eip: 3540 -title: EVM Object Format (EOF) v1 -author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0) -discussions-to: https://ethereum-magicians.org/t/evm-object-format-eof/5727/4 -status: Draft +title: EOF - EVM Object Format v1 +description: EOF is an extensible and versioned container format for EVM bytecode with a once-off validation at deploy time. +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0), Matt Garnett (@lightclient) +discussions-to: https://ethereum-magicians.org/t/evm-object-format-eof/5727 +status: Review type: Standards Track category: Core created: 2021-03-16 +requires: 3541, 3860, 4750, 5450 --- ## Abstract -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. It is introduced via two hard forks to avoid breaking any existing executable contracts. +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,
+``` ## Motivation -On-chain deployed EVM bytecode contains no pre-defined structure today. Code is typically validated in clients to the extent of `JUMPDEST` analysis at runtime, every single time prior to execution. This poses not only an overhead, but also a challenge for introducing new or deprecating old features. [This initial proposal](https://notes.ethereum.org/@axic/evm-object-format) explains some of these challenges. +On-chain deployed EVM bytecode contains no pre-defined structure today. Code is typically validated in clients to the extent of `JUMPDEST` analysis at runtime, every single time prior to execution. This poses not only an overhead, but also a challenge for introducing new or deprecating existing features. Validating code during the contract creation process allows code versioning without an additional version field in the account. Versioning is a useful tool for introducing or deprecating features, especially for larger changes (such as significant changes to control flow, or features like account abstraction). 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. 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) or removing `JUMPDEST`s entirely. + +- 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 the execution of a code section ends with a terminating instruction. (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 a specific section for the [EIP-2938 Account Abstraction](./eip-2938.md) "top-level AA execution frame", simplifying the proposal. +- 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 will be achieved by choosing a byte sequence for the *magic* that doesn't exist in any of the already deployed contracts. To prevent the growth of the search space and to limit the analysis to the contracts existing before these changes, we split changes into two hard forks, and disallow the starting byte of the format (the first byte of the magic) in the first hard fork. +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. -### First hard fork +### Remarks -After `block.number == HF1_BLOCK` new contract creation (via create transaction, `CREATE` or `CREATE2` instructions) results in an exceptional abort if the _code_'s first byte is `0xEF`. +The *initcode* is the code executed in the context of the *create* transaction, `CREATE`, or `CREATE2` instructions. The *initcode* returns *code* (via the `RETURN` instruction), which is inserted into the account. See section 7 ("Contract Creation") in the Yellow Paper for more information. -#### Remarks +The opcode `0xEF` is currently an undefined instruction, therefore: *It pops no stack items and pushes no stack items, and it causes an exceptional abort when executed.* This means *initcode* or already deployed *code* starting with this instruction will continue to abort execution. -*For purely reference purposes we call the `0xEF` byte the `FORMAT`.* +Unless otherwised specified, all integers are encoded in big-endian byte order. -The *initcode* is the code executed in the context of the *create* transaction, `CREATE`, or `CREATE2` instructions. The *initcode* returns *code* (via the `RETURN` instruction), which is inserted into the account. See section 7 ("Contract Creation") in the Yellow Paper for more information. +### Code validation -The opcode `0xEF` is currently an unimplemented instruction, therefore: *It pops no stack items and pushes no stack items, and it causes an exceptional abort when executed.* This means *initcode* or already deployed *code* starting with this instruction will continue to abort execution. +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. -### Between the two hard forks +At `block.number == HF_BLOCK` new contract creation is modified: -Next step to be conducted manually: Once the first hard fork went live, all existing contracts at `block.number == HF1_BLOCK` having their first byte as the `FORMAT` are examined off-chain. The goal is to find the shortest sequence after the `FORMAT` not matching any existing contract. We expect a 1- or 2-byte sequence would suffice. We call this byte sequence the *magic*, which will be used in the second hard fork. +- 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. -### Second hard fork +For a create transaction, if *initcode* or *code* is invalid, the contract creation results in an exceptional abort. Such a transaction is valid and may be included in a block. Therefore, the transaction sender's nonce is increased. -In this fork 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. +For the `CREATE` and `CREATE2` instructions, if *initcode* or *code* is invalid, instructions' execution ends with the result `0` pushed on stack. The *initcode* validation happens just before its execution and validation failure is observable as if execution results in an exceptional abort. I.e. in case *initcode* or returned *code* is invalid the caller's nonce remains increased and all creation gas is deducted. -At `block.number == HF2_BLOCK` new contract creation is modified: -- if *initcode* or *code* starts with the *EOF prefix* (*TBD*), it is considered to be EOF formatted and will undergo validation specified in the following sections, -- if *code* starts with `0xEF`, creation continues to result in an exceptional abort (the rule introduced in HF1), -- otherwise code is considered *legacy code* and the following rules do not apply to it. +### Container specification -Note that the *EOF prefix* here means the to be determined combination of `FORMAT` and *magic*. +EOF container is a binary format with the capability of providing the EOF version number and a list of EOF sections. -#### Container specification +The container starts with the EOF header: -The format starts with the header: +| description | length | value | | +|-------------|----------|------------|--------------------| +| magic | 2-bytes | 0xEF00 | | +| version | 1-byte | 0x01–0xFF | EOF version number | -| description | length | value | | -|-------------|---------|-------|-| -| format | 1-byte | 0xEF | | -| magic | n-byte(s) | TBD | n >= 0 (zero in the best case) | -| version | 1-byte | 0x01 | means EOF1 | +The EOF header is followed by at least one section header. Each section header contains two fields, `section_kind` and either `section_size` or `section_size_list`, depending on the kind. `section_size_list` is a list of size values when multiple sections of this kind are allowed. -This is followed by one or more section headers: +| description | length | value | | +|-------------------|---------|---------------|-------------------| +| section_kind | 1-byte | 0x01–0xFF | `uint8` | +| section_size | 2-bytes | 0x0000–0xFFFF | `uint16` | +| section_size_list | dynamic | n/a | `uint16, uint16+` | -| description | length | | -|--------------|---------|-| -| section_kind | 1-byte | Encoded as a 8-bit unsigned number. | -| section_size | 2-bytes | Encoded as a 16-bit unsigned big-endian number. | +The list of section headers is terminated with the *section headers terminator byte* `0x00`. The body content follows immediately after. -The section kinds are defined as follows: +#### Container validation rules -| section_kind | meaning | -|--------------|------------| -| 0 | terminator | -| 1 | code | -| 2 | data | +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*. +3. There MUST be at least one section (and therefore section header). +4. Section content size MUST be equal to size declared in its header. +5. Stray bytes outside of sections MUST NOT be present. This includes trailing bytes after the last section. -If the terminator is encountered, section size MUST NOT follow. +### EOF version 1 -The section contents follow after the header, in the order and size they are defined, without any padding bytes. +EOF version 1 is made up of 5 EIPs, including this one: [EIP-3540](./eip-3540.md), [EIP-3670](./eip-3670.md), [EIP-4200](./eip-4200.md), [EIP-4750](./eip-4750.md), and [EIP-5450](./eip-5450.md). Some values in this specification are only discussed briefly. To understand the full scope of EOF, it is necessary to review each EIP in-depth. + +#### Container + +The EOF version 1 container consists of a `header` and `body`. -To summarise, the bytecode has the following format: ``` -format, magic, version, (section_kind, section_size)+, 0,
+container := header, body +header := magic, version, kind_type, type_size, kind_code, num_code_sections, code_size+, kind_data, data_size, terminator +body := type_section, code_section+, data_section +type_section := (inputs, outputs, max_stack_height)+ ``` -#### Validation rules +*note: `,` is a concatenation operator and `+` should be interpreted as "one or more" of the preceding item* + +##### Header + +| name | length | value | description | +|-------------------|----------|---------------|------------------------------------------------------------------------------------| +| magic | 2 bytes | 0xEF00 | EOF prefix | +| version | 1 byte | 0x01 | EOF version | +| kind_type | 1 byte | 0x01 | kind marker for EIP-4750 type section header | +| type_size | 2 bytes | 0x0004-0xFFFC | uint16 denoting the length of the type section content, 4 bytes per code segment | +| kind_code | 1 byte | 0x02 | kind marker for code size section | +| num_code_sections | 2 bytes | 0x0001-0x0400 | uint16 denoting the number of the code sections | +| code_size | 2 bytes | 0x0001-0xFFFF | uint16 denoting the length of the code section content | +| kind_data | 1 byte | 0x03 | kind marker for data size section | +| data_size | 2 bytes | 0x0000-0xFFFF | uint16 integer denoting the length of the data section content | +| terminator | 1 byte | 0x00 | marks the end of the header | -A bytestream starting with the *EOF prefix* declares itself conforming to the rules according to its version. +##### Body -1. The rules of `version=1` are specified below: -- `section_size` MUST NOT be 0. -- Exactly one code section MUST be present. -- The code section MUST be the first section. -- A single data section MAY follow the code section. -- Stray bytes outside of sections MUST NOT be present. This includes trailing bytes after the last section. -2. Any other version is invalid. +| name | length | value | description | +|------------------|----------|--------------|----------------------------------------------------| +| type_section | variable | n/a | stores EIP-4750 and EIP-5450 code section metadata | +| inputs | 1 byte | 0x00-0x7F | number of stack elements the code section consumes | +| outputs | 1 byte | 0x00-0x7F | number of stack elements the code section returns | +| max_stack_height | 2 bytes | 0x0000-0x3FF | max height of operand stack during execution | +| code_section | variable | n/a | arbitrary bytecode | +| data_section | variable | n/a | arbitrary sequence of bytes | -(*Note:* Contract creation code SHOULD set the section size of the data section so that the constructor arguments fit it.) +See [EIP-4750](./eip-4750.md) for more information on the type section content. -#### Changes to execution semantics +#### 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 type section header MUST be present immediately following the EOF version. Each code section MUST have a specified type signature in the type body. + - Exactly one code section header MUST be present immediately following the type section. A maximum of 1024 individual code sections are allowed. + - Exactly one data section header MUST be present immediately following the code section. +2. Any version other than `0x01` is invalid. + +(*Remark:* Contract creation code SHOULD set the section size of the data section so that the constructor arguments fit it.) + +### Changes to execution semantics For clarity, the *container* refers to the complete account code, while *code* refers to the contents of the code section only. -1. Jumpdest analysis is only run on the *code*. -2. Execution starts at the first byte of the *code*, and `PC` is set to this position within the container format (e.g. `PC=10` for a *container* with a code and data section). -3. If `PC` goes outside of the code section bounds, execution aborts with failure. -4. `PC` returns the current position within the *container*. -5. `JUMP`/`JUMPI` uses an absolute offset within the *container*. -6. `CODECOPY`/`CODESIZE`/`EXTCODECOPY`/`EXTCODESIZE`/`EXTCODEHASH` keeps operating on the entire *container*. -7. The input to `CREATE`/`CREATE2` is still the entire *container*. +1. Execution starts at the first byte of the first code section, and PC is set to 0. +2. Execution stops if `PC` goes outside the code section bounds. +3. `PC` returns the current position within the *code*. +4. `CODECOPY`/`CODESIZE`/`EXTCODECOPY`/`EXTCODESIZE`/`EXTCODEHASH` keeps operating on the entire *container*. +5. The input to `CREATE`/`CREATE2` is still the entire *container*. +6. The size limit for deployed code as specified in [EIP-170](./eip-170.md) and for initcode as specified in [EIP-3860](./eip-3860.md) is applied to the entire *container* size, not to the *code* size. This also means if initcode validation fails, it is still charged the EIP-3860 `initcode_cost`. +7. When an EOF1 contract performs a `DELEGATECALL` the target must be EOF1. If it is not EOF1, the `DELEGATECALL` execution finishes as a failed call by pushing `0` to the stack. Only initial gas cost of `DELEGATECALL` is consumed (similarly to the call depth check) and the target address still becomes warm. + +(*Remark:* Due to [EIP-4750](./eip-4750.md), `JUMP` and `JUMPI` are disabled and therefore are not discussed in relation to EOF.) -#### Changes to contract creation semantics +### Changes to contract creation semantics -For clarity, the *EOF prefix* together with a version number $v$ is denoted as the *EOF$v$ prefix*, e.g. *EOF1 prefix*. +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. +3. If *initcode's container* is valid EOF1 code the resulting *code's container* MUST be valid EOF1 code (i.e. it MUST NOT be empty and MUST NOT produce legacy code). +4. If `CREATE` or `CREATE2` instruction is executed in an EOF1 code the instruction's *initcode* MUST be valid EOF1 code (i.e. EOF1 contracts MUST NOT produce legacy code). + +See [Code validation](#code-validation) above for specification of behaviour in case one of these conditions is not satisfied. ## Rationale -EVM and/or account versioning has been discussed numerous times over the past years. This proposal aims to learn from them. A good starting point collecting the previous proposals is [this thread](https://ethereum-magicians.org/t/ethereum-account-versioning/3508). +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 EOF$n$ 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 serializing `JUMPDEST` map in the EOF container and eliminate the need of implicit `JUMPDEST` analysis required before execution. -- Or 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. -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 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 main advantage is it can be enabled via a single hard-fork. -- A second advantage is better backwards compatibility: data contracts starting with the `0xEF` byte or the *EOF prefix* can be deployed. This is a dubious benefit however. +- 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 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. -### Initcode vs code +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: -After HF1 and before HF2 the `FORMAT` first byte check only applies to _code_. Applying the rule also to _initcode_ would have the same effects because is not distinguishable from executing _initcode_: if it starts with `FORMAT` execution would exceptionally abort. Yet, we decided against introducing an explicit check for _initcode_ for the simplicity of the HF1 spec. +- 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. ### 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. + +Moreover, it is not allowed to create legacy contracts from EOF1 ones. And the EOF version of *initcode* must match the EOF version of the produced *code*. +The rule can be generalized in the future: EOFn contract must only create EOFm contracts, where m ≥ n. -Because _initcode_ and _code_ is 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. +This guarantees that a cluster of EOF contracts will never spawn new legacy contracts. +Furthermore, some exotic contract creation combinations are eliminated (e.g. EOF1 contract creating new EOF1 contract with legacy *initcode*). -To limit the number of exotic bytecode version combinations, additional restrictions are considered, but current not part of the specification: +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. -1. The EOF version of _initcode_ must much the version of _code_. -2. An EOF1 contract must not create legacy contracts. +### The MAGIC -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. +1. The first byte `0xEF` was chosen because it is reserved for this purpose by [EIP-3541](./eip-3541.md). -### The FORMAT byte +2. The second byte `0x00` was chosen to avoid clashes with three contracts which were deployed on **Mainnet**: + - `0xca7bf67ab492b49806e24b6e2e4ec105183caa01`: `EFF09f918bf09f9fa9` + - `0x897da0f23ccc5e939ec7a53032c5e80fd1a947ec`: `EF` + - `0x6e51d4d9be52b623a3d3a2fa8d3c5e3e01175cd0`: `EF` -The `0xEF` byte was chosen because it resembles **E**xecutable **F**ormat. It has a long history of being proposed for this use case, starting with [this](https://github.com/ethereum/EIPs/issues/154). +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. ### 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 they do not require a specific "header terminator". -- Whether to have a header terminator or to encode `number_of_sections` or `total_size_of_headers`. Both of the latter 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 their 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. -### Simplified implementations +- 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 of how large of a value these fields should be able to hold. A terminator byte seems to avoid the problem of choosing a size which is too small without any perceptible downside, so it is the path taken. +- 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 20 additional bytes required. -Given the rigid rules of EOF1 it is possible to implement support for the container in clients using very simple pattern matching (the following assumes `magic = 0x00`): +``` +EF0001 010004 020001 0001 03 00 00000000 FE +``` -1. If code starts with `0xEF 0x00 0x01 codelen1 codelen2 0x02 datalen1 datalen2 0x00`, then calculate `total_size = (9 + (codelen1 << 8 | codelen2) + (datalen1 << 8 | datalen2))`. If `total_size == container_size` then it is a valid EOF1 code with a code and data section. -2. If code starts with `0xEF 0x00 0x01 codelen1 codelen2 0x00`, then calculate `total_size = 7 + (codelen1 << 8 | codelen2)`. If `total_size == container_size` then it is a valid EOF1 code with a code section. -3. Otherwise if it starts with `0xEF`, it is invalid. -4. Otherwise if it does not start with `0xEF`, it is valid legacy code. +It is possible in the future that this data will be accessible with data-specific opcodes, such as `DATACOPY` or `EXTDATACOPY`. Until then, callers will need to determine the data offset manually. -However, future versions may introduce more sections or loosen up restrictions, requiring clients to actually parse sections instead of pattern matching. +### PC starts with 0 at the code section -## Backwards Compatibility +The value for `PC` is specified to start at 0 and to be within the active *code* section. We considered keeping `PC` to operate on the whole *container* and be consistent with `CODECOPY`/`EXTCODECOPY` but in the end decided otherwise. This also feels more natural and easier to implement in EVM: the new EOF EVM should only care about traversing *code* and accessing other parts of the *container* only on special occasions (e.g. in `CODECOPY` instruction). -This is a breaking change given new code starting with the `0xEF` byte (unless it is followed by a valid EOF header) will not be deployable, and contract creation will result in a failure. However, given bytecode is executed starting at its first byte, code already deployed with `0xEF` as the first byte is not executable anyway. +### EOF1 contracts can only `DELEGATECALL` EOF1 contracts -While this means no currently executable contract is affected, it does rejects deployment of new data contracts starting with the `0xEF` byte. +Currently contracts can selfdestruct in three different ways (directly through `SELFDESTRUCT`, indirectly through `CALLCODE` and indirectly through `DELEGATECALL`). [EIP-3670](./eip-3670.md) disables the first two possibilities, however the third possibility remains. Allowing EOF1 contracts to only `DELEGATECALL` other EOF1 contracts allows the following strong statement: EOF1 contract can never be destructed. Attacks based on `SELFDESTRUCT` completely disappear for EOF1 contracts. These include destructed library contracts (e.g. Parity Multisig). -## Test Cases +## Backwards Compatibility + +This is a breaking change given that any code starting with `0xEF` was not deployable before (and resulted in exceptional abort if executed), but now some subset of such codes can be deployed and executed successfully. -- evmone: https://github.com/ethereum/evmone/pull/303/files#diff-290487661e637566ddd17991f9a76ffc62da8c36fb7af7d4e4105a259b2218b7R139 -- Python validator: https://gist.github.com/axic/c7a3cbeafad0ca867b04b784c1a757a8 +The choice of `MAGIC` guarantees that none of the contracts existing on the chain are affected by the new rules. -## Reference Implementation +## Test Cases -- evmone: https://github.com/ethereum/evmone/pull/303 -- Solidity: https://github.com/ethereum/solidity/tree/eof1 +### Contract creation + +All cases should be checked for creation transaction, `CREATE` and `CREATE2`. + +- Legacy init code + - Returns legacy code + - Returns valid EOF1 code + - Returns invalid EOF1 code, contract creation fails + - Returns 0xEF not followed by EOF1 code, contract creation fails +- Valid EOF1 init code + - Returns legacy code, contract creation fails + - Returns valid EOF1 code + - Returns invalid EOF1 code, contract creation fails + - Returns 0xEF not followed by EOF1 code, contract creation fails +- Invalid EOF1 init code + +### Contract execution + +- EOF code containing `PC` opcode - offset inside code section is returned +- EOF code containing `CODECOPY/CODESIZE` - works as in legacy code + - `CODESIZE` returns the size of entire container + - `CODECOPY` can copy from code section + - `CODECOPY` can copy from data section + - `CODECOPY` can copy from the EOF header + - `CODECOPY` can copy entire container +- `EXTCODECOPY/EXTCODESIZE/EXTCODEHASH` with the EOF *target* contract - works as with legacy target contract + - `EXTCODESIZE` returns the size of entire target container + - `EXTCODEHASH` returns the hash of entire target container + - `EXTCODECOPY` can copy from target's code section + - `EXTCODECOPY` can copy from target's data section + - `EXTCODECOPY` can copy from target's EOF header + - `EXTCODECOPY` can copy entire target container + - Results don't differ when executed inside legacy or EOF contract +- EOF1 `DELEGATECALL` + - `DELEGATECALL` to EOF1 code succeeds + - `DELEGATECALL` to EOF0 code fails + - `DELEGATECALL` to empty container fails ## Security Considerations -TBA +With the anticipated EOF extensions, the validation is expected to have linear computational and space complexity. +We think that the validation cost is sufficiently covered by: + +- [EIP-3860](./eip-3860.md) for *initcode*, +- high per-byte cost of deploying *code*. ## 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-3541.md b/EIPS/eip-3541.md index ded2631983f8e4..fc364983e4b04d 100644 --- a/EIPS/eip-3541.md +++ b/EIPS/eip-3541.md @@ -1,10 +1,9 @@ --- eip: 3541 -title: Reject new contracts starting with the 0xEF byte +title: Reject new contract code starting with the 0xEF byte author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0), Alexey Akhunov (@AlexeyAkhunov), Christian Reitwiessner (@chriseth), Martin Swende (@holiman) -discussions-to: https://ethereum-magicians.org/t/evm-object-format-eof/5727/4 -status: Last Call -review-period-end: 2021-07-14 +discussions-to: https://ethereum-magicians.org/t/evm-object-format-eof/5727 +status: Final type: Standards Track category: Core created: 2021-03-16 @@ -38,7 +37,7 @@ The `0xEF` byte was chosen because it resembles **E**xecutable **F**ormat. Contracts using unassigned opcodes are generally understood to be at risk of changing semantics. Hence using the unassigned `0xEF` should have lesser effects, than choosing an assigned opcode, such as `0xFD` (`REVERT`), `0xFE` (`INVALID)`, or `0xFF` (`SELFDESTRUCT`). Arguably while such contracts may not be very useful, they are still using valid opcodes. -Analysis at block 18084433 showed that there are 0 existing contracts starting with the `0xEF` byte, as opposed to 1, 4, and 12 starting with `0xFD`, `0xFE`, and `0xFF`, respectively. +Analysis in May 2021, on `18084433` contracts in state, showed that there are 0 existing contracts starting with the `0xEF` byte, as opposed to 1, 4, and 12 starting with `0xFD`, `0xFE`, and `0xFF`, respectively. ## Test Cases @@ -67,4 +66,4 @@ The authors are not aware of any security or DoS risks posed by this change. ## 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-3554.md b/EIPS/eip-3554.md index 1ace2f1e633f8e..046317046a6422 100644 --- a/EIPS/eip-3554.md +++ b/EIPS/eip-3554.md @@ -3,8 +3,7 @@ eip: 3554 title: Difficulty Bomb Delay to December 2021 author: James Hancock (@madeoftin) discussions-to: https://ethereum-magicians.org/t/eip-3554-ice-age-delay-targeting-december-2021/6188 -status: Last Call -review-period-end: 2021-07-14 +status: Final type: Standards Track category: Core created: 2021-05-06 @@ -17,7 +16,7 @@ Delays the difficulty bomb to show effect the first week of December 2021. Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 9,700,000 blocks later than the actual block number. ## Motivation -Targeting for the Shanghai upgrade and/or the Merge to occur before Decemember 2021. Either the bomb can be readjusted at that time, or removed all together. +Targeting for the Shanghai upgrade and/or the Merge to occur before December 2021. Either the bomb can be readjusted at that time, or removed all together. ## Specification #### Relax Difficulty with Fake Block Number @@ -56,4 +55,4 @@ No known backward compatibility issues. Misjudging the effects of the difficulty can mean longer blocktimes than anticipated until a hardfork is released. Wild shifts in difficulty can affect this number severely. Also, gradual changes in blocktimes due to longer-term adjustments in difficulty can affect the timing of difficulty bomb epochs. This affects the usability of the network but unlikely to have security ramifications. ## 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-3561.md b/EIPS/eip-3561.md new file mode 100644 index 00000000000000..f3fbee881717ab --- /dev/null +++ b/EIPS/eip-3561.md @@ -0,0 +1,282 @@ +--- +eip: 3561 +title: Trust Minimized Upgradeability Proxy +description: proxy with a delay before specified upgrade goes live +author: Sam Porter (@SamPorter1984) +discussions-to: https://ethereum-magicians.org/t/trust-minimized-proxy/5742 +status: Review +type: Standards Track +category: ERC +created: 2021-05-09 +--- + +## Abstract + +Removing trust from upgradeability proxy is necessary for anonymous developers. In order to accomplish this, instant and potentially malicious upgrades must be prevented. This EIP introduces additional storage slots for upgradeability proxy which are assumed to decrease trust in interaction with upgradeable smart contracts. Defined by the admin implementation logic can be made an active implementation logic only after Zero Trust Period allows. + +## Motivation + +Anonymous developers who utilize upgradeability proxies typically struggle to earn the trust of the community. + +Fairer, better future for humanity absolutely requires some developers to stay anonymous while still attract vital attention to solutions they propose and at the same time leverage the benefits of possible upgradeability. + +## Specification + +The specification is an addition to the standard [EIP-1967](./eip-1967.md) transparent proxy design. +The specification focuses on the slots it adds. All admin interactions with trust minimized proxy must emit an event to make admin actions trackable, and all admin actions must be guarded with `onlyAdmin()` modifier. + +### Next Logic Contract Address + +Storage slot `0x19e3fabe07b65998b604369d85524946766191ac9434b39e27c424c976493685` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.next.logic')) - 1)`). +Desirable implementation logic address must be first defined as next logic, before it can function as actual logic implementation stored in EIP-1967 `IMPLEMENTATION_SLOT`. +Admin interactions with next logic contract address correspond with these methods and events: + +```solidity +// Sets next logic contract address. Emits NextLogicDefined +// If current implementation is address(0), then upgrades to IMPLEMENTATION_SLOT +// immedeatelly, therefore takes data as an argument +function proposeTo(address implementation, bytes calldata data) external IfAdmin +// As soon UPGRADE_BLOCK_SLOT allows, sets the address stored as next implementation +// as current IMPLEMENTATION_SLOT and initializes it. +function upgrade(bytes calldata data) external IfAdmin +// cancelling is possible for as long as upgrade() for given next logic was not called +// emits NextLogicCanceled +function cancelUpgrade() external onlyAdmin; + +event NextLogicDefined(address indexed nextLogic, uint earliestArrivalBlock); // important to have +event NextLogicCanceled(address indexed oldLogic); +``` + +### Upgrade Block + +Storage slot `0xe3228ec3416340815a9ca41bfee1103c47feb764b4f0f4412f5d92df539fe0ee` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.next.logic.block')) - 1)`). +On/after this block next logic contract address can be set to EIP-1967 `IMPLEMENTATION_SLOT` or, in other words, `upgrade()` can be called. Updated automatically according to Zero Trust Period, shown as `earliestArrivalBlock` in the event `NextLogicDefined`. + +### Propose Block + +Storage slot `0x4b50776e56454fad8a52805daac1d9fd77ef59e4f1a053c342aaae5568af1388` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.propose.block')) - 1)`). +Defines after/on which block *proposing* next logic is possible. Required for convenience, for example can be manually set to a year from given time. Can be set to maximum number to completely seal the code. +Admin interactions with this slot correspond with this method and event: + +```solidity +function prolongLock(uint b) external onlyAdmin; +event ProposingUpgradesRestrictedUntil(uint block, uint nextProposedLogicEarliestArrival); +``` + +### Zero Trust Period + +Storage slot `0x7913203adedf5aca5386654362047f05edbd30729ae4b0351441c46289146720` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.zero.trust.period')) - 1)`). +Zero Trust Period in amount of blocks, can only be set higher than previous value. While it is at default value(0), the proxy operates exactly as standard EIP-1967 transparent proxy. After zero trust period is set, all above specification is enforced. +Admin interactions with this slot should correspond with this method and event: + +```solidity +function setZeroTrustPeriod(uint blocks) external onlyAdmin; +event ZeroTrustPeriodSet(uint blocks); +``` + +### Implementation Example + +```solidity +pragma solidity >=0.8.0; //important + +// EIP-3561 trust minimized proxy implementation https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3561.md +// Based on EIP-1967 upgradeability proxy: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1967.md + +contract TrustMinimizedProxy { + event Upgraded(address indexed toLogic); + event AdminChanged(address indexed previousAdmin, address indexed newAdmin); + event NextLogicDefined(address indexed nextLogic, uint earliestArrivalBlock); + event ProposingUpgradesRestrictedUntil(uint block, uint nextProposedLogicEarliestArrival); + event NextLogicCanceled(); + event ZeroTrustPeriodSet(uint blocks); + + bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + bytes32 internal constant LOGIC_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 internal constant NEXT_LOGIC_SLOT = 0x19e3fabe07b65998b604369d85524946766191ac9434b39e27c424c976493685; + bytes32 internal constant NEXT_LOGIC_BLOCK_SLOT = 0xe3228ec3416340815a9ca41bfee1103c47feb764b4f0f4412f5d92df539fe0ee; + bytes32 internal constant PROPOSE_BLOCK_SLOT = 0x4b50776e56454fad8a52805daac1d9fd77ef59e4f1a053c342aaae5568af1388; + bytes32 internal constant ZERO_TRUST_PERIOD_SLOT = 0x7913203adedf5aca5386654362047f05edbd30729ae4b0351441c46289146720; + + constructor() payable { + require( + ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) && + LOGIC_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) && + NEXT_LOGIC_SLOT == bytes32(uint256(keccak256('eip3561.proxy.next.logic')) - 1) && + NEXT_LOGIC_BLOCK_SLOT == bytes32(uint256(keccak256('eip3561.proxy.next.logic.block')) - 1) && + PROPOSE_BLOCK_SLOT == bytes32(uint256(keccak256('eip3561.proxy.propose.block')) - 1) && + ZERO_TRUST_PERIOD_SLOT == bytes32(uint256(keccak256('eip3561.proxy.zero.trust.period')) - 1) + ); + _setAdmin(msg.sender); + } + + modifier IfAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + function _logic() internal view returns (address logic) { + assembly { + logic := sload(LOGIC_SLOT) + } + } + + function _nextLogic() internal view returns (address nextLogic) { + assembly { + nextLogic := sload(NEXT_LOGIC_SLOT) + } + } + + function _proposeBlock() internal view returns (uint b) { + assembly { + b := sload(PROPOSE_BLOCK_SLOT) + } + } + + function _nextLogicBlock() internal view returns (uint b) { + assembly { + b := sload(NEXT_LOGIC_BLOCK_SLOT) + } + } + + function _zeroTrustPeriod() internal view returns (uint ztp) { + assembly { + ztp := sload(ZERO_TRUST_PERIOD_SLOT) + } + } + + function _admin() internal view returns (address adm) { + assembly { + adm := sload(ADMIN_SLOT) + } + } + + function _setAdmin(address newAdm) internal { + assembly { + sstore(ADMIN_SLOT, newAdm) + } + } + + function changeAdmin(address newAdm) external IfAdmin { + emit AdminChanged(_admin(), newAdm); + _setAdmin(newAdm); + } + + function upgrade(bytes calldata data) external IfAdmin { + require(block.number >= _nextLogicBlock(), 'too soon'); + address logic; + assembly { + logic := sload(NEXT_LOGIC_SLOT) + sstore(LOGIC_SLOT, logic) + } + (bool success, ) = logic.delegatecall(data); + require(success, 'failed to call'); + emit Upgraded(logic); + } + + fallback() external payable { + _fallback(); + } + + receive() external payable { + _fallback(); + } + + function _fallback() internal { + require(msg.sender != _admin()); + _delegate(_logic()); + } + + function cancelUpgrade() external IfAdmin { + address logic; + assembly { + logic := sload(LOGIC_SLOT) + sstore(NEXT_LOGIC_SLOT, logic) + } + emit NextLogicCanceled(); + } + + function prolongLock(uint b) external IfAdmin { + require(b > _proposeBlock(), 'can be only set higher'); + assembly { + sstore(PROPOSE_BLOCK_SLOT, b) + } + emit ProposingUpgradesRestrictedUntil(b, b + _zeroTrustPeriod()); + } + + function setZeroTrustPeriod(uint blocks) external IfAdmin { + // before this set at least once acts like a normal eip 1967 transparent proxy + uint ztp; + assembly { + ztp := sload(ZERO_TRUST_PERIOD_SLOT) + } + require(blocks > ztp, 'can be only set higher'); + assembly { + sstore(ZERO_TRUST_PERIOD_SLOT, blocks) + } + _updateNextBlockSlot(); + emit ZeroTrustPeriodSet(blocks); + } + + function _updateNextBlockSlot() internal { + uint nlb = block.number + _zeroTrustPeriod(); + assembly { + sstore(NEXT_LOGIC_BLOCK_SLOT, nlb) + } + } + + function _setNextLogic(address nl) internal { + require(block.number >= _proposeBlock(), 'too soon'); + _updateNextBlockSlot(); + assembly { + sstore(NEXT_LOGIC_SLOT, nl) + } + emit NextLogicDefined(nl, block.number + _zeroTrustPeriod()); + } + + function proposeTo(address newLogic, bytes calldata data) external payable IfAdmin { + if (_zeroTrustPeriod() == 0 || _logic() == address(0)) { + _updateNextBlockSlot(); + assembly { + sstore(LOGIC_SLOT, newLogic) + } + (bool success, ) = newLogic.delegatecall(data); + require(success, 'failed to call'); + emit Upgraded(newLogic); + } else { + _setNextLogic(newLogic); + } + } + + function _delegate(address logic_) internal { + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), logic_, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } +} +``` + +## Rationale + +An argument "just don't make such contracts upgadeable at all" fails when it comes to complex systems which do or do not heavily rely on human factor, which might manifest itself in unprecedented ways. It might be impossible to model some systems right on first try. Using decentralized governance for upgrade management coupled with EIP-1967 proxy might become a serious bottleneck for certain protocols before they mature and data is at hand. + +A proxy without a time delay before an actual upgrade is obviously abusable. A time delay is probably unavoidable, even if it means that inexperienced developers might not have confidence using it. Albeit this is a downside of this EIP, it's a critically important option to have in smart contract development today. + +## Security Considerations + +Users must ensure that a trust-minimized proxy they interact with does not allow overflows, ideally represents the exact copy of the code in implementation example above, and also they must ensure that Zero Trust Period length is reasonable(at the very least two weeks if upgrades are usually being revealed beforehand, and in most cases at least a month). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3569.md b/EIPS/eip-3569.md new file mode 100644 index 00000000000000..e2419c3d9f8f7b --- /dev/null +++ b/EIPS/eip-3569.md @@ -0,0 +1,139 @@ +--- +eip: 3569 +title: Sealed NFT Metadata Standard +author: Sean Papanikolas (@pizzarob) +discussions-to: https://ethereum-magicians.org/t/eip-3569-sealed-nft-metadata-standard/7130 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-05-07 +--- + +## Simple Summary + +The Sealed NFT Metadata Extension provides a mechanism to immortalize NFT metadata in a cost-effective manner. + +## Abstract + +This standard accomplishes three things; it provides a way for potential collectors to verify that the NFT metadata will not change, allows creators to immortalize metadata for multiple tokens at one time, and allows metadata for many NFTs to be read and cached from one file. A creator can call the `seal` function for a range of one or many sequential NFTs. Included as an argument is a URI which points to a decentralized storage service like IPFS and will be stored in the smart contract. The URI will return a JSON object in which the keys are token IDs and the values are either a string which is a URI pointing to a metadata file stored on a decentralized file system, or raw metadata JSON for each token ID. The token ID(s) will then be marked as sealed in the smart contract and cannot be sealed again. The `seal` function can be called after NFT creation, or during the NFT creation process. + +## Motivation + +In the original ERC-721 standard, the metadata extension specifies a `tokenURI` function which returns a URI for a single token ID. This may be hosted on IPFS or might be hosted on a centralized server. There's no guarantee that the NFT metadata will not change. This is the same for the ERC-1155 metadata extension. In addition to that - if you want to update the metadata for many NFTs you would need to do so in O(n) time, which as we know is not financially feasible at scale. By allowing for a decentralized URI to point to a JSON object of many NFT IDs we can solve this issue by providing metadata for many tokens at one time rather than one at a time. We can also provide methods which give transparency into whether the NFT has be explicitly "sealed" and that the metadata is hosted on a decentralized storage space. + +There is not a way for the smart contract layer to communicate with a storage layer and as such we need a solution which provides a way for potential NFT collectors on Ethereum to verify that their NFT will not be "rug pulled". This standard provides a solution for that. By allowing creators to seal their NFTs during or after creation, they are provided with full flexibility when it comes to creating their NFTs. Decentralized storage means permanence - in the fast-moving world of digital marketing campaigns, or art projects mistakes can happen. As such, it is important for creators to have flexibility when creating their projects. Therefore, this standard allows creators to opt in at a time of their choosing. Mistakes do happen and metadata should be flexible enough so that creators can fix mistakes or create dynamic NFTs (see Beeple's CROSSROAD NFT). If there comes a time when the NFT metadata should be immortalized, then the creator can call the `seal` method. Owners, potential owners, or platforms can verify that the NFT was sealed and can check the returned URI. If the `sealedURI` return value is not hosted on a decentralized storage platform, or the `isSealed` method does not return `true` for the given NFT ID then it can be said that one cannot trust that these NFTs will not change at a future date and can then decide if they want to proceed with collecting the given NFT. + +## Specification + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +``` +interface SealedMetadata { + /** + @notice This function is used to set a sealed URI for the given range of tokens. + @dev + - If the sealed URI is being set for one token then the fromTokenId and toTokenId + values MUST be the same. + + - If any token within the range of tokens specified has already + been sealed then this function MUST throw. + + - This function MAY be called at the time of NFT creation, or after the NFTs have been created. + + - It is RECOMMENDED that this function only be executable by either the creator of the smart contract, + or the creator of the NFTs, but this is OPTIONAL and should be implemented based on use case. + + - This function MUST emit the Sealed event + + - The URI argument SHOULD point to a JSON file hosted within a decentralized file system like IPFS + + @param fromTokenId The first token in a consecutive range of tokens + @param toTokenId The ending token in a consecutive range of tokens + @param uri A URI which points to a JSON file hosted on a decentralized file system. + */ + function seal(uint256 fromTokenId, uint256 toTokenId, string memory uri) external; + + /** + @notice This function returns the URI which the sealed metadata can be found for the given token ID + @dev + - This function MUST throw if the token ID does not exist, or is not sealed + + @param tokenId Token ID to retrieve the sealed URI for + + @return The sealed URI in which the metadata for the given token ID can be found + */ + function sealedURI(uint256 tokenId) external view returns (string); + + /** + @notice This function returns a boolean stating if the token ID is sealed or not + @dev This function should throw if the token ID does not exist + + @param tokenId The token ID that will be checked if sealed or not + + @return Boolean stating if token ID is sealed + */ + function isSealed(uint256 tokenId) external view returns (bool) + + /// @dev This emits when a range of tokens is sealed + event Sealed(uint256 indexed fromTokenId, uint256 indexed toTokenId, string memory uri); + +} +``` + +### Sealed Metadata JSON Format + +The sealed metadata JSON file MAY contain metadata for many different tokens. The top level keys of the JSON object MUST be token IDs. + +``` + +type ERC721Metadata = { + name?: string; + image?: string; + description?: string; +} + +type SealedMetaDataJson = { + [tokenId: string]: string | ERC721Metadata; +} + +const sealedMetadata: SealedMetaDataJson = { + '1': { + name: 'Metadata for token with ID 1' + }, + '2': { + name: 'Metadata for token with ID 2' + }, + // Example pointing to another file + '3': 'ipfs://SOME_HASH_ON_IPFS' +}; +``` + +## Rationale + +**Rationale for rule not explicitly requiring that sealed URI be hosted on decentralized filestorage** + +In order for this standard to remain future proof there is no validation within the smart contract that would verify the sealed URI is hosted on IPFS or another decentralized file storage system. The standard allows potential collectors and platforms to validate the URI on the client. + +**Rationale to include many NFT metadata objects, or URIs in one JSON file** + +By including metadata for many NFTs in one JSON file we can eliminate the need for many transactions to set the metadata for multiple NFTs. Given that this file should not change NFT platforms, or explorers can cache the metadata within the file. + +**Rationale for emitting `Sealed` event** + +Platforms and explorers can use the `Sealed` event to automatically cache metadata, or update information regarding specified NFTs. + +**Rationale for allowing URIs as values in the JSON file** + +If a token's metadata is very large, or there are many tokens you can save file space by referencing another URI rather than storing the metadata JSON within the top level metadata file. + +## Backwards Compatibility + +There is no backwards compatibility with existing standards. This is an extension which could be added to existing NFT standards. + +## Security Considerations + +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-3584.md b/EIPS/eip-3584.md new file mode 100644 index 00000000000000..c5261b116041c6 --- /dev/null +++ b/EIPS/eip-3584.md @@ -0,0 +1,125 @@ +--- +eip: 3584 +title: Block Access List +author: Gajinder Singh (@g11in), Piper Merriam (@pipermerriam) +discussions-to: https://ethresear.ch/t/block-access-list-v0-1/9505 +status: Stagnant +type: Standards Track +category: Core +created: 2021-05-22 +requires: 2929, 2930 +--- + +## Simple Summary +A proposal to build a block's `access_list` and include its fingerprint `AccessListRoot` in the block header. + +## Abstract +[EIP-2929](./eip-2929.md)/[EIP-2930](./eip-2930.md) centers around normalizing the (low) gas costs of data/storage accesses made by a transaction as well as providing for (and encouraging) a new transaction type format: +``` +0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, access_list, yParity, senderR, senderS]) +``` +that makes upfront `access_list` declarations, where `access_list` is some `[[{20 bytes}, [{32 bytes}...]]...]` map of `AccessedAddress=> AccessedStorageKeys`. + +The first *accesses* of these upfront *declarations* are charged at discounted price (roughly ~`10%`) and first accesses outside this list are charged higher price. Reason is upfront access declaration provides for a way to *preload/optimize/batch* loading these locations while executing the transaction. +This inadvertently leads to generation of transaction `access_list` that has all (first) accesses (declared or not) made by a transaction. +This proposal is to collate these *transaction* `access_list`s for all the transactions in a **block** `access_list` document and include its *fingerprint* in the block header. + +## Motivation +Motivation for collating the *transaction* `access_list`s for all the transactions in a **block**’s `access_list` is to have an *access index* of the block with following benefits: +1. Block execution/validation optimizations/parallelization/cache warm-up by enabling construction of *a partial order* for access and hence execution (hint: *chains* in this *poset* can be parallelized). +2. Enabling partial inspection and fetching/serving of a block data/state by *light sync* or *fast sync* protocols concerned with a subset of addresses. +3. Possible future extension of this list to serve as index for bundling, serving and fetching witness data for *stateless* protocols. + +## Specification +A block `access_list` represents: +``` +Set [ + AccessedAddress, + List [AccessedStorageKeys] , + Set [ AccessedInBlockTransactionNumber, List [ AccessedStorageKeys ]] +] +``` +A **canonical** construction of such an `access_list` is specified as below. + +### Canonical Block Access List +An `access_list` is defined to be comprised of many `access_list_entry` elements: +``` +access_list := [access_list_entry, ...] +``` + +An `access_list_entry` is a 3-tuple of: +* address +* sorted list of storage keys of the address accessed across the entire block +* sorted list of 2-tuples of: + * transaction index in which the address or any of its storage keys were accessed + * sorted list of storage keys which were accessed + +``` +access_list := [access_list_entry, ...] +access_list_entry := [address, storage_keys, accesses_by_txn_index] +address := bytes20 +accesses_by_txn_index := [txn_index_and_keys, ...] +txn_index_and_keys := [txn_index, storage_keys] +txn_index := uint64 # or uint256 or whatever +storage_keys := [storage_key, ...] +storage_key := bytes32 +``` + +Additional sorting rules for the above are that: +* `access_list` is sorted by the `address` +* `storage_keys` is sorted +* `accesses_by_txn_index` is sorted by `txn_index` + +Additional validation rules for the above are that: +* Each unique `address` may only appear at most once in `access_list` +* Each `storage_key` may only appear at most once in `storage_keys` +* Each `txn_index` may only appear at most once in `txn_index_and_keys` + +All sorting is in increasing order. + +### AccessListRoot +An `AccessListRoot` is a URN *like* encoding `Hash/Commitment` of the canonical `access_list` as well as the construction type ( `sha256` ) and serialization type ( `json` ), i.e. +``` +AccessListRoot := "urn:sha256:json:0x${ SHA256( access_list.toJSONString('utf8') ).toHexString() }" +``` +where `0x${ SHA256 (...)...}` is the `SHA256` hashed `32` bytes hex string as indicated by leading `0x`. + +### Additional Block Validation +Validating a new block requires an additional validation check that the block’s `AccessListRoot` matches the one generated by executing the block using the construction as defined by the `AccessListRoot` URN. + +## Rationale +### Sorting of canonical `access_list` +It is specified to be sorted in lexicographic ordering or integer sorting wherever applicable and specified. Sorting with respect to access time was considered but didn't seem to provide any additional benefit at the cost of adding implementation complexity and bookkeeping. + +### `AccessListRoot` +`AccessListRoot` is generated to prevent any *griefing* attacks and hence will need to be included (and validated) in the *block header*. +Even though `AccessListRoot` is currently specified to be a simple `sha256` hash of the canonical `access_list`, it would be beneficial to consider other constructions +* a tree structure (`merkle`/`verkle`). It will be a bit more expensive but will enable partial downloading, inspection and validation of the `access_list`. +* a normal `kate` commitment can also be generated to enable this partial capability and is recommended as validating partial fetch of access list chunks would be very simple. + +Also serialization of the `access_list` is currently specified as a normal `JSON String` dump and these parameters could vary from construction to construction, but for the sake of simplicity, it can always be `sha256` hashed to get a consistent `32` bytes hex string root. + +So this AccessListRoot could evolve to `urn:merkle:ssz:...` or to `urn:kate:...` or to any other scheme as per requirement. And the idea of having the `AccessListRoot` as URN *like* structure is to enable upgradation to these paths without affecting block structure. + + +### Future extensions of `access_list` +We can extend the notion of a block’s `access_list` to include witnesses: +``` +access_list := Set[ + Address, + List [ AddressWitnesses ], + Set [ AccessedStorageKey, List [ StorageKeyWitnesses] ], + Set [ AccessedInBlockTransactionNumber, List [ AccessedStorageKeys ] ] +] +``` +and then get to define the a canonical specification for building the fingerprint. +This will allow an incremental path to partial or full statelessness, where it would be easy to bundle/request **witnesses** using this `access_list`. + +## Backwards Compatibility +The extra block validation will only be mandatory post the block number this EIP comes into effect, but the clients can still provide a way to generate (and possibly store) this access list on request (via the `JSON/RPC` api). However this is optional and client dependent. + +## Security Considerations +There are no known security issues as a result of this change. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3589.md b/EIPS/eip-3589.md new file mode 100644 index 00000000000000..1bfeb2edead5bd --- /dev/null +++ b/EIPS/eip-3589.md @@ -0,0 +1,198 @@ +--- +eip: 3589 +title: Assemble assets into NFTs +author: Zhenyu Sun (@Ungigdu), Xinqi Yang (@xinqiyang) +discussions-to: https://github.com/ethereum/EIPs/issues/3590 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-05-24 +requires: 721 +--- + +## Simple Summary +This standard defines a ERC-721 token called assembly token which can represent a combination of assets. + +## Abstract +The ERC-1155 multi-token contract defines a way to batch transfer tokens, but those tokens must be minted by the ERC-1155 contract itself. This EIP is an ERC-721 extension with ability to assemble assets such as ether, ERC-20 tokens, ERC-721 tokens and ERC-1155 tokens into one ERC-721 token whose token id is also the asset's signature. As assets get assembled into one, batch transfer or swap can be implemented very easily. + +## Motivation +As NFT arts and collectors rapidly increases, some collectors are not satisfied with traditional trading methods. When two collectors want to swap some of their collections, currently they can list their NFTs on the market and notify the other party to buy, but this is inefficient and gas-intensive. Instead, some collectors turn to social media or chat group looking for a trustworthy third party to swap NFTs for them. The third party takes NFTs from both collector A and B, and transfer A's collections to B and B's to A. This is very risky. + +The safest way to do batch swap, is to transform batch swap into atomic swap, i.e. one to one swap. But first we should "assemble" those ether, ERC-20 tokens, ERC-721 tokens and ERC-1155 tokens together, and this is the main purpose of this EIP. + +## 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. + +ERC-721 compliant contracts MAY implement this ERC to provide a standard method to assemble assets. + +`mint` and `safeMint` assemble assets into one ERC-721 token. `mint` SHOULD be implemented for normal ERC-20 tokens whose `_transfer` is lossless. `safeMint` MUST takes care for lossy token such as PIG token whose `_transfer` function is taxed. + +`_salt` of `hash` function MAY be implemented other way, even provided as user input. But the token id MUST be generated by `hash` function. + +Implementations of the standard MAY supports different set of assets. + +Implementers of this standard MUST have all of the following functions: + +``` +pragma solidity ^0.8.0; + +interface AssemblyNFTInterface { + + event AssemblyAsset(address indexed firstHolder, + uint256 indexed tokenId, + uint256 salt, + address[] addresses, + uint256[] numbers); + + /** + * @dev hash function assigns the combination of assets with salt to bytes32 signature that is also the token id. + * @param `_salt` prevents hash collision, can be chosen by user input or increasing nonce from contract. + * @param `_addresses` concat assets addresses, e.g. [ERC-20_address1, ERC-20_address2, ERC-721_address_1, ERC-1155_address_1, ERC-1155_address_2] + * @param `_numbers` describes how many eth, ERC-20 token addresses length, ERC-721 token addresses length, ERC-1155 token addresses length, + * ERC-20 token amounts, ERC-721 token ids, ERC-1155 token ids and amounts. + */ + function hash(uint256 _salt, address[] memory _addresses, uint256[] memory _numbers) external pure returns (uint256 tokenId); + + /// @dev to assemble lossless assets + /// @param `_to` the receiver of the assembly token + function mint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external returns(uint256 tokenId); + + /// @dev mint with additional logic that calculates the actual received value for tokens. + function safeMint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external returns(uint256 tokenId); + + /// @dev burn this token and releases assembled assets + /// @param `_to` to which address the assets is released + function burn(address _to, uint256 _tokenId, uint256 _salt, address[] calldata _addresses, uint256[] calldata _numbers) external; + +} + +``` + +## Rationale +There are many reasons why people want to pack their NFTs together. For example, a collector want to pack a set of football players into a football team; a collector has hundreds of of NFTs with no categories to manage them; a collector wants to buy a full collection of NFTs or none of them. They all need a way a assemble those NFTs together. + +The reason for choosing ERC-721 standard as a wrapper is ERC-721 token is already widely used and well supported by NFT wallets. And assembly token itself can also be assembled again. Assembly token is easier for smart contract to use than a batch of assets, in scenarios like batch trade, batch swap or collections exchange. + +This standard has AssemblyAsset event which records the exact kinds and amounts of assets the assembly token represents. The wallet can easily display those NFTs to user just by the token id. + +## Backwards Compatibility +This proposal combines already available 721 extensions and is backwards compatible with the ERC-721 standard. + +## Implementation +``` +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; +import "./AssemblyNFTInterface.sol"; + +abstract contract AssemblyNFT is ERC721, ERC721Holder, ERC1155Holder, AssemblyNFTInterface{ + using SafeERC20 for IERC20; + + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC1155Receiver) returns (bool) { + return ERC721.supportsInterface(interfaceId) || ERC1155Receiver.supportsInterface(interfaceId); + } + + uint256 nonce; + + /** + * layout of _addresses: + * erc20 addresses | erc721 addresses | erc1155 addresses + * layout of _numbers: + * eth | erc20.length | erc721.length | erc1155.length | erc20 amounts | erc721 ids | erc1155 ids | erc1155 amounts + */ + + function hash(uint256 _salt, address[] memory _addresses, uint256[] memory _numbers) public pure override returns (uint256 tokenId){ + bytes32 signature = keccak256(abi.encodePacked(_salt)); + for(uint256 i=0; i< _addresses.length; i++){ + signature = keccak256(abi.encodePacked(signature, _addresses[i])); + } + for(uint256 j=0; j<_numbers.length; j++){ + signature = keccak256(abi.encodePacked(signature, _numbers[j])); + } + assembly { + tokenId := signature + } + } + + function mint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external override returns(uint256 tokenId){ + require(_to != address(0), "can't mint to address(0)"); + require(msg.value == _numbers[0], "value not match"); + require(_addresses.length == _numbers[1] + _numbers[2] + _numbers[3], "2 array length not match"); + require(_addresses.length == _numbers.length -4 - _numbers[3], "numbers length not match"); + uint256 pointerA; //points to first erc20 address, if there is any + uint256 pointerB =4; //points to first erc20 amount, if there is any + for(uint256 i = 0; i< _numbers[1]; i++){ + require(_numbers[pointerB] > 0, "transfer erc20 0 amount"); + IERC20(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB++]); + } + for(uint256 j = 0; j< _numbers[2]; j++){ + IERC721(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB++]); + } + for(uint256 k =0; k< _numbers[3]; k++){ + IERC1155(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB], _numbers[_numbers[3] + pointerB++], ""); + } + tokenId = hash(nonce, _addresses, _numbers); + super._mint(_to, tokenId); + emit AssemblyAsset(_to, tokenId, nonce, _addresses, _numbers); + nonce ++; + } + + function safeMint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external override returns(uint256 tokenId){ + require(_to != address(0), "can't mint to address(0)"); + require(msg.value == _numbers[0], "value not match"); + require(_addresses.length == _numbers[1] + _numbers[2] + _numbers[3], "2 array length not match"); + require(_addresses.length == _numbers.length -4 - _numbers[3], "numbers length not match"); + uint256 pointerA; //points to first erc20 address, if there is any + uint256 pointerB =4; //points to first erc20 amount, if there is any + for(uint256 i = 0; i< _numbers[1]; i++){ + require(_numbers[pointerB] > 0, "transfer erc20 0 amount"); + IERC20 token = IERC20(_addresses[pointerA++]); + uint256 orgBalance = token.balanceOf(address(this)); + token.safeTransferFrom(_msgSender(), address(this), _numbers[pointerB]); + _numbers[pointerB++] = token.balanceOf(address(this)) - orgBalance; + } + for(uint256 j = 0; j< _numbers[2]; j++){ + IERC721(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB++]); + } + for(uint256 k =0; k< _numbers[3]; k++){ + IERC1155(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB], _numbers[_numbers[3] + pointerB++], ""); + } + tokenId = hash(nonce, _addresses, _numbers); + super._mint(_to, tokenId); + emit AssemblyAsset(_to, tokenId, nonce, _addresses, _numbers); + nonce ++; + } + + function burn(address _to, uint256 _tokenId, uint256 _salt, address[] calldata _addresses, uint256[] calldata _numbers) override external { + require(_msgSender() == ownerOf(_tokenId), "not owned"); + require(_tokenId == hash(_salt, _addresses, _numbers)); + super._burn(_tokenId); + payable(_to).transfer(_numbers[0]); + uint256 pointerA; //points to first erc20 address, if there is any + uint256 pointerB =4; //points to first erc20 amount, if there is any + for(uint256 i = 0; i< _numbers[1]; i++){ + require(_numbers[pointerB] > 0, "transfer erc20 0 amount"); + IERC20(_addresses[pointerA++]).safeTransfer(_to, _numbers[pointerB++]); + } + for(uint256 j = 0; j< _numbers[2]; j++){ + IERC721(_addresses[pointerA++]).safeTransferFrom(address(this), _to, _numbers[pointerB++]); + } + for(uint256 k =0; k< _numbers[3]; k++){ + IERC1155(_addresses[pointerA++]).safeTransferFrom(address(this), _to, _numbers[pointerB], _numbers[_numbers[3] + pointerB++], ""); + } + } + +} +``` + +## Security Considerations +Before using `mint` or `safeMint` functions, user should be aware that some implementations of tokens are pausable. If one of the assets get paused after assembled into one NFT, the `burn` function may not be executed successfully. Platforms using this standard should make support lists or block lists to avoid this situation. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3607.md b/EIPS/eip-3607.md new file mode 100644 index 00000000000000..78ed1ee17a83aa --- /dev/null +++ b/EIPS/eip-3607.md @@ -0,0 +1,96 @@ +--- +eip: 3607 +title: Reject transactions from senders with deployed code +description: Do not allow transactions for which `tx.sender` has any code deployed. +author: Dankrad Feist (@dankrad), Dmitry Khovratovich (@khovratovich), Marius van der Wijden (@MariusVanDerWijden) +discussions-to: https://github.com/ethereum/EIPs/issues/3608 +status: Final +type: Standards Track +category: Core +created: 2021-06-10 +--- + +## Abstract + +Ethereum addresses are currently only 160 bits long. This means it is possible to create a collision between a contract account and an Externally Owned Account (EOA) using an estimated `2**80` computing operations, which is feasible now given a large budget (ca. 10 billion USD). The fix in this EIP prevents the worst possible attack, where a safe looking contract (e.g. a token wrapper or an AMM-type contract) is deployed to attract user funds, which can then be spent using the EOA key for the same address. The fix is to never allow to use an address that already has code deployed as an EOA address. + +## Motivation + +### Generating address collisions + +By creating keys for `2**80` EOAs and simulating the deployment of `2**80` contracts from these EOAs (one each), one expects to find about one collision where an EOA has the same address as one contract. + +This very simple form of the attack requires the storage of `2**80` addresses, which is a practical barrier: It would require `2.4*10**25` bytes of memory (24 Yottabyte). However, there are cycle finding algorithms that can perform the collision search without requiring large amounts of storage. An estimate for the complexity has been made [here](https://hackmd.io/Vzhp5YJyTT-LhWm_s0JQpA). We estimate that a collision between a contract and an EOA could be found in about one year with an investment of ca. US$10 billion in hardware and electricity. + +### Background + +There is currently a discussion to move to 256-bit addresses on Ethereum, which would increase collision resistance to a complexity of `2**128` which is currently thought infeasible for the foreseeable future. However, with 160 bit addresses, the collision problem can be effectively solved now, as demonstrated above. + +Most attacks that can occur via address collisions are quite impractical: They involve users sending funds to an address before a contract is deployed. This is a very rare application in practice and users can easily circumvent the attack by never sending funds to a contract until it has been safely deployed with enough confirmations. + +However, the yellow paper does not explicitly specify how a client should handle the case where a transaction is sent from an account that already has contract code deployed; presumably because this was considered infeasible at the time. The assumption is that most client would allow this transaction in their current state. + +This EIP is to specify this behaviour to always forbid such transactions. This fixes most realistic or serious attacks due to address collisions. + + +## Specification + +Any transaction where `tx.sender` has a `CODEHASH != EMPTYCODEHASH` MUST be rejected as invalid, where `EMPTYCODEHASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. +The invalid transaction MUST be rejected by the client and not be included in a block. +A block containing such a transaction MUST be considered invalid. + +## Rationale + +We note that it was always the expected that a contract account's behaviour is constrained by the code in that contract -- which means that the account's funds should not suddenly be spendable by some private key. It was just implicitly assumed in the past that a 160 bit address length is enough to provide collision resistance, and thus that this case could never occur. In that sense, this EIP should be seen as a clarification of protocol behaviour in a previously undefined case rather than an explicit upgrade of consensus rules. + +This does not exclude all possible attack vectors, only the most serious one. Further possible attack vectors via address collisions between contracts and EOAs are: +1. An attacker can convince a user to send funds to an account before it is deployed. Some applications require this behaviour (e.g. state channels). +2. A chain reorg can happen after a contract is deployed. If the reorg removes the contract deployment transaction the funds can still be accessed using the private key. +3. A contract can self destruct, with the stated intention that ERC20s (or other tokens) in the contract would be burned. However, they can now be accessed by a key for that address. + +All these scenarios are much harder to exploit for an attacker, and likely have much lower yield making the attacks unlikely to be economically viable. + +## Backwards Compatibility + +It is unlikely that an attack like this has already occurred on the Ethereum mainnet, or we would very likely have heard of it. It is inconceivable that someone would use this as a "feature" to make a contract an EOA at the same time, when they could simply do this by adding some methods to the contract instead of spending billions on building hardware to find hash collisions. + +Private networks may have deployed contracts which also work as EOAs at genesis and should check that this upgrade does not impact their workflows. + +Clients might choose to disable this rule for RPC calls like `eth_call` and `eth_estimateGas` as some Multi-Sig contracts use these calls to create transactions as if they originated from the multisig contract itself. + +## Test Cases + +Given a genesis allocation of +``` +Address: 0x71562b71999873DB5b286dF957af199Ec94617F7 +Balance: 1000000000000000000 // 1 ether +Nonce: 0, +Code: 0xB0B0FACE", +``` +Every transaction sent by the private key corresponding to `0x715656...` ( +`b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291`) should be rejected. +These transaction must be rejected and not included in a block. + +## Reference Implementation + +The following check must be added to the state transition checks after checking that the nonce of the sender is correct. +The sender is the address recovered from the signature of the transaction. +``` +// Make sure the sender is an EOA +Set ch to the CodeHash of the sender account +if ch is not equal to EmptyCodeHash then + return ErrSenderNoEOA +end if +``` + +A diff to implement EIP-3607 in go-ethereum can be found [here](../assets/eip-3607/geth.diff) + +## Security Considerations + +This EIP is a strict security upgrade: It simply makes some transactions that were formerly valid now invalid. There is no legitimate use for such transactions, so there should be no security downsides. + +This EIP can be implemented as a soft fork because the new validity rules are a strict superset of the previous validity rules. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3643.md b/EIPS/eip-3643.md new file mode 100644 index 00000000000000..68efd27186b544 --- /dev/null +++ b/EIPS/eip-3643.md @@ -0,0 +1,408 @@ +--- +eip: 3643 +title: T-REX - Token for Regulated EXchanges +description: An institutional grade security token contract that provides interfaces for the management and compliant transfer of security tokens. +author: Joachim Lebrun (@Joachim-Lebrun), Tony Malghem (@TonyMalghem), Kevin Thizy (@Nakasar), Luc Falempin (@lfalempin), Adam Boudjemaa (@Aboudjem) +discussions-to: https://ethereum-magicians.org/t/eip-3643-proposition-of-the-t-rex-token-standard-for-securities/6844 +status: Review +type: Standards Track +category: ERC +created: 2021-07-09 +requires: 20, 173 +--- + +## Abstract + +The T-REX token is an institutional grade security token standard. This standard provides a library of interfaces for the management and compliant transfer of security tokens, using an automated onchain validator system leveraging onchain identities for eligibility checks. + +The standard defines several interfaces that are described hereunder: + +- Token +- Identity Registry +- Identity Registry Storage +- Compliance +- Trusted Issuers Registry +- Claim Topics Registry + +## Motivation + +The advent of blockchain technology has brought about a new era of efficiency, accessibility, and liquidity in the world of asset transfer. This is particularly evident in the realm of cryptocurrencies, where users can transfer token ownership peer-to-peer without intermediaries. However, when it comes to tokenized securities or security tokens, the situation is more complex due to the need for compliance with securities laws. These tokens cannot be permissionless like utility tokens; they must be permissioned to track ownership and ensure that only eligible investors can hold tokens. + +The existing Ethereum protocol, while powerful and versatile, does not fully address the unique challenges posed by security tokens. There is a need for a standard that supports compliant issuance and management of permissioned tokens, suitable for representing a wide range of asset classes, including small businesses and real estate. + +The proposed [ERC-3643](./eip-3643.md) standard is motivated by this need. It aims to provide a comprehensive framework for managing the lifecycle of security tokens, from issuance to transfers between eligible investors, while enforcing compliance rules at every stage. The standard also supports additional features such as token pausing and freezing, which can be used to manage the token in response to regulatory requirements or changes in the status of the token or its holders. + +Moreover, the standard is designed to work in conjunction with an on-chain Identity system, allowing for the validation of the identities and credentials of investors through signed attestations issued by trusted claim issuers. This ensures compliance with legal and regulatory requirements for the trading of security tokens. + +In summary, the motivation behind the proposed standard is to bring the benefits of blockchain technology to the world of securities, while ensuring compliance with existing securities laws. It aims to provide a robust, flexible, and efficient framework for the issuance and management of security tokens, thereby accelerating the evolution of capital markets. + +## Specification + +The proposed standard has the following requirements: + +- **MUST** be [ERC-20](./eip-20.md) compatible. +- **MUST** be used in combination with an onchain Identity system +- **MUST** be able to apply any rule of compliance that is required by the regulator or by the token issuer (about the factors of eligibility of an identity or about the rules of the token itself) +- **MUST** have a standard interface to pre-check if a transfer is going to pass or fail before sending it to the blockchain +- **MUST** have a recovery system in case an investor loses access to his private key +- **MUST** be able to freeze tokens on the wallet of investors if needed, partially or totally +- **MUST** have the possibility to pause the token +- **MUST** be able to mint and burn tokens +- **MUST** define an Agent role and an Owner (token issuer) role +- **MUST** be able to force transfers from an Agent wallet +- **MUST** be able to issue transactions in batch (to save gas and to have all the transactions performed in the same block) + +While this standard is backwards compatible with ERC-20 and all ERC-20 functions can be called on an ERC-3643 token, the implementation of these functions differs due to the permissioned nature of ERC-3643. Each token transfer under this standard involves a compliance check to validate the transfer and the eligibility of the stakeholder’s identities. + +### Agent Role Interface + +The standard defines an Agent role, which is crucial for managing access to various functions of the smart contracts. The interface for the Agent role is as follows: + +```solidity +interface IAgentRole { + + // events + event AgentAdded(address indexed _agent); + event AgentRemoved(address indexed _agent); + + // functions + // setters + function addAgent(address _agent) external; + function removeAgent(address _agent) external; + + // getters + function isAgent(address _agent) external view returns (bool); +} + ``` + +The `IAgentRole` interface allows for the addition and removal of agents, as well as checking if an address is an agent. In this standard, it is the owner role, as defined by [ERC-173](./eip-173.md), that has the responsibility of appointing and removing agents. Any contract that fulfills the role of a Token contract or an Identity Registry within the context of this standard must be compatible with the `IAgentRole` interface. + +### Main functions + +#### Transfer + +To be able to perform a transfer on T-REX you need to fulfill several conditions : + +- The sender **MUST** hold enough free balance (total balance - frozen tokens, if any) +- The receiver **MUST** be whitelisted on the Identity Registry and verified (hold the necessary claims on his onchain Identity) +- The sender's wallet **MUST NOT** be frozen +- The receiver's wallet **MUST NOT** be frozen +- The token **MUST NOT** be paused +- The transfer **MUST** respect all the rules of compliance defined in the Compliance smart contract (canTransfer needs to return TRUE) + +Here is an example of `transfer` function implementation : + +```solidity +function transfer(address _to, uint256 _amount) public override whenNotPaused returns (bool) { + require(!_frozen[_to] && !_frozen[msg.sender], "ERC-3643: Frozen wallet"); + require(_amount <= balanceOf(msg.sender) - (_frozenTokens[msg.sender]), "ERC-3643: Insufficient Balance"); + require( _tokenIdentityRegistry.isVerified(to), "ERC-3643: Invalid identity" ); + require( _tokenCompliance.canTransfer(from, to, amount), "ERC-3643: Compliance failure" ); + _transfer(msg.sender, _to, _amount); + _tokenCompliance.transferred(msg.sender, _to, _amount); + return true; + } + ``` + +The `transferFrom` function works the same way while the `mint` function and the `forcedTransfer` function only require the receiver to be whitelisted and verified on the Identity Registry (they bypass the compliance rules). The `burn` function bypasses all checks on eligibility. + +#### isVerified + +The `isVerified` function is called from within the transfer functions `transfer`, `transferFrom`, `mint` and +`forcedTransfer` to instruct the `Identity Registry` to check if the receiver is a valid investor, i.e. if his +wallet address is in the `Identity Registry` of the token, and if the `Identity`contract linked to his wallet +contains the claims (see [Claim Holder](../assets/eip-3643/ONCHAINID/IERC735.sol)) required in the `Claim Topics Registry` and +if these claims are signed by an authorized Claim Issuer as required in the `Trusted Issuers Registry`. +If all the requirements are fulfilled, the `isVerified` function returns `TRUE`, otherwise it returns `FALSE`. An +implementation of this function can be found on the T-REX repository of Tokeny. + +#### canTransfer + +The `canTransfer` function is also called from within transfer functions. This function checks if the transfer is compliant with global compliance rules applied to the token, in opposition with `isVerified` that only checks the eligibility of an investor to hold and receive tokens, the `canTransfer` function is looking at global compliance rules, e.g. check if the transfer is compliant in the case there is a fixed maximum number of token holders to respect (can be a limited number of holders per country as well), check if the transfer respects rules setting a maximum amount of tokens per investor, ... +If all the requirements are fulfilled, the `canTransfer` function will return `TRUE` otherwise it will return +`FALSE` and the transfer will not be allowed to happen. An implementation of this function can be found on the T-REX +repository of Tokeny. + +#### Other functions + +Description of other functions of the ERC-3643 can be found in the `interfaces` folder. An implementation of the +ERC-3643 suite of smart contracts can be found on the T-REX repository of Tokeny. + +### Token interface + +ERC-3643 permissioned tokens build upon the standard ERC-20 structure, but with additional functions to ensure compliance in the transactions of the security tokens. The functions `transfer` and `transferFrom` are implemented in a conditional way, allowing them to proceed with a transfer only if the transaction is valid. The permissioned tokens are allowed to be transferred only to validated counterparties, in order to avoid tokens being held in wallets/Identity contracts of ineligible/unauthorized investors. The ERC-3643 standard also supports the recovery of security tokens in case an investor loses access to their wallet private key. A history of recovered tokens is maintained on the blockchain for transparency reasons. + +ERC-3643 tokens implement a range of additional functions to enable the owner or their appointed agents to manage supply, transfer rules, lockups, and any other requirements in the management of a security. The standard relies on ERC-173 to define contract ownership, with the owner having the responsibility of appointing agents. Any contract that fulfills the role of a Token contract within the context of this standard must be compatible with the `IAgentRole` interface. + +A detailed description of the functions can be found in the [interfaces folder](../assets/eip-3643/interfaces/IERC3643.sol). + +```solidity +interface IERC3643 is IERC20 { + + // events + event UpdatedTokenInformation(string _newName, string _newSymbol, uint8 _newDecimals, string _newVersion, address _newOnchainID); + event IdentityRegistryAdded(address indexed _identityRegistry); + event ComplianceAdded(address indexed _compliance); + event RecoverySuccess(address _lostWallet, address _newWallet, address _investorOnchainID); + event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); + event TokensFrozen(address indexed _userAddress, uint256 _amount); + event TokensUnfrozen(address indexed _userAddress, uint256 _amount); + event Paused(address _userAddress); + event Unpaused(address _userAddress); + + + // functions + // getters + function onchainID() external view returns (address); + function version() external view returns (string memory); + function identityRegistry() external view returns (IIdentityRegistry); + function compliance() external view returns (ICompliance); + function paused() external view returns (bool); + function isFrozen(address _userAddress) external view returns (bool); + function getFrozenTokens(address _userAddress) external view returns (uint256); + + // setters + function setName(string calldata _name) external; + function setSymbol(string calldata _symbol) external; + function setOnchainID(address _onchainID) external; + function pause() external; + function unpause() external; + function setAddressFrozen(address _userAddress, bool _freeze) external; + function freezePartialTokens(address _userAddress, uint256 _amount) external; + function unfreezePartialTokens(address _userAddress, uint256 _amount) external; + function setIdentityRegistry(address _identityRegistry) external; + function setCompliance(address _compliance) external; + + // transfer actions + function forcedTransfer(address _from, address _to, uint256 _amount) external returns (bool); + function mint(address _to, uint256 _amount) external; + function burn(address _userAddress, uint256 _amount) external; + function recoveryAddress(address _lostWallet, address _newWallet, address _investorOnchainID) external returns (bool); + + // batch functions + function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external; + function batchForcedTransfer(address[] calldata _fromList, address[] calldata _toList, uint256[] calldata _amounts) external; + function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external; + function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; + function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; +} + +``` + +### Identity Registry Interface + +The Identity Registry is linked to storage that contains a dynamic whitelist of identities. It establishes the link between a wallet address, an Identity smart contract, and a country code corresponding to the investor's country of residence. This country code is set in accordance with the ISO-3166 standard. The Identity Registry also includes a function called `isVerified()`, which returns a status based on the validity of claims (as per the security token requirements) in the user’s Identity contract. + +The standard relies on ERC-173 to define contract ownership, with the owner having the responsibility of appointing agents. Any contract that fulfills the role of an Identity Registry within the context of this standard must be compatible with the `IAgentRole` interface. The Identity Registry is managed by the agent wallet(s), meaning only the agent(s) can add or remove identities in the registry. Note that the agent role on the Identity Registry is set by the owner, therefore the owner could set themselves as the agent if they want to maintain full control. There is a specific identity registry for each security token. + +A detailed description of the functions can be found in the [interfaces folder](../assets/eip-3643/interfaces/IIdentityRegistry.sol). + +Note that [`IClaimIssuer`](../assets/eip-3643/ONCHAINID/IClaimIssuer.sol) and [`IIdentity`](../assets/eip-3643/ONCHAINID/IIdentity.sol) are needed in this interface as they are required for the Identity eligibility checks. + +```solidity +interface IIdentityRegistry { + + + // events + event ClaimTopicsRegistrySet(address indexed claimTopicsRegistry); + event IdentityStorageSet(address indexed identityStorage); + event TrustedIssuersRegistrySet(address indexed trustedIssuersRegistry); + event IdentityRegistered(address indexed investorAddress, IIdentity indexed identity); + event IdentityRemoved(address indexed investorAddress, IIdentity indexed identity); + event IdentityUpdated(IIdentity indexed oldIdentity, IIdentity indexed newIdentity); + event CountryUpdated(address indexed investorAddress, uint16 indexed country); + + + // functions + // identity registry getters + function identityStorage() external view returns (IIdentityRegistryStorage); + function issuersRegistry() external view returns (ITrustedIssuersRegistry); + function topicsRegistry() external view returns (IClaimTopicsRegistry); + + //identity registry setters + function setIdentityRegistryStorage(address _identityRegistryStorage) external; + function setClaimTopicsRegistry(address _claimTopicsRegistry) external; + function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external; + + // registry actions + function registerIdentity(address _userAddress, IIdentity _identity, uint16 _country) external; + function deleteIdentity(address _userAddress) external; + function updateCountry(address _userAddress, uint16 _country) external; + function updateIdentity(address _userAddress, IIdentity _identity) external; + function batchRegisterIdentity(address[] calldata _userAddresses, IIdentity[] calldata _identities, uint16[] calldata _countries) external; + + // registry consultation + function contains(address _userAddress) external view returns (bool); + function isVerified(address _userAddress) external view returns (bool); + function identity(address _userAddress) external view returns (IIdentity); + function investorCountry(address _userAddress) external view returns (uint16); +} +``` + +### Identity Registry Storage Interface + +The Identity Registry Storage stores the identity addresses of all the authorized investors in the security token(s) linked to the storage contract. These are all identities of investors who have been authorized to hold the token(s) after having gone through the appropriate KYC and eligibility checks. The Identity Registry Storage can be bound to one or several Identity Registry contract(s). The goal of the Identity Registry storage is to separate the Identity Registry functions and specifications from its storage. This way, it is possible to keep one single Identity Registry contract per token, with its own Trusted Issuers Registry and Claim Topics Registry, but with a shared whitelist of investors used by the `isVerifed()` function implemented in the Identity Registries to check the eligibility of the receiver in a transfer transaction. + +The standard relies on ERC-173 to define contract ownership, with the owner having the responsibility of appointing agents(in this case through the `bindIdentityRegistry` function). Any contract that fulfills the role of an Identity Registry Storage within the context of this standard must be compatible with the `IAgentRole` interface. The Identity Registry Storage is managed by the agent addresses (i.e. the bound Identity Registries), meaning only the agent(s) can add or remove identities in the registry. Note that the agent role on the Identity Registry Storage is set by the owner, therefore the owner could set themselves as the agent if they want to modify the storage manually. Otherwise it is the bound Identity Registries that are using the agent role to write in the Identity Registry Storage. + +A detailed description of the functions can be found in the [interfaces folder](../assets/eip-3643/interfaces/IIdentityRegistryStorage.sol). + +```solidity +interface IIdentityRegistryStorage { + + //events + event IdentityStored(address indexed investorAddress, IIdentity indexed identity); + event IdentityUnstored(address indexed investorAddress, IIdentity indexed identity); + event IdentityModified(IIdentity indexed oldIdentity, IIdentity indexed newIdentity); + event CountryModified(address indexed investorAddress, uint16 indexed country); + event IdentityRegistryBound(address indexed identityRegistry); + event IdentityRegistryUnbound(address indexed identityRegistry); + + //functions + // storage related functions + function storedIdentity(address _userAddress) external view returns (IIdentity); + function storedInvestorCountry(address _userAddress) external view returns (uint16); + function addIdentityToStorage(address _userAddress, IIdentity _identity, uint16 _country) external; + function removeIdentityFromStorage(address _userAddress) external; + function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external; + function modifyStoredIdentity(address _userAddress, IIdentity _identity) external; + + // role setter + function bindIdentityRegistry(address _identityRegistry) external; + function unbindIdentityRegistry(address _identityRegistry) external; + + // getter for bound IdentityRegistry role + function linkedIdentityRegistries() external view returns (address[] memory); +} +``` + +### Compliance Interface + +The Compliance contract is used to set the rules of the offering itself and ensures these rules are respected during the whole lifecycle of the token. For example, the Compliance contract will define the maximum amount of investors per country, the maximum amount of tokens per investor, and the accepted countries for the circulation of the token (using the country code corresponding to each investor in the Identity Registry). The Compliance smart contract can be either “tailor-made”, following the legal requirements of the token issuer, or can be deployed under a generic modular form, which can then add and remove external compliance `Modules` to fit the legal requirements of the token in the same way as a custom "tailor-made" contract would. + +This contract is triggered at every transaction by the Token and returns `TRUE` if the transaction is compliant with the rules of the offering and `FALSE` otherwise. + +The standard relies on ERC-173 to define contract ownership, with the owner having the responsibility of setting the Compliance parameters and binding the Compliance to a Token contract. + +A detailed description of the functions can be found in the [interfaces folder](../assets/eip-3643/interfaces/ICompliance.sol). + +```solidity +interface ICompliance { + + // events + event TokenBound(address _token); + event TokenUnbound(address _token); + + // functions + // initialization of the compliance contract + function bindToken(address _token) external; + function unbindToken(address _token) external; + + // check the parameters of the compliance contract + function isTokenBound(address _token) external view returns (bool); + function getTokenBound() external view returns (address); + + // compliance check and state update + function canTransfer(address _from, address _to, uint256 _amount) external view returns (bool); + function transferred(address _from, address _to, uint256 _amount) external; + function created(address _to, uint256 _amount) external; + function destroyed(address _from, uint256 _amount) external; +} +``` + +### Trusted Issuer's Registry Interface + +The Trusted Issuer's Registry stores the contract addresses ([IClaimIssuer](../assets/eip-3643/ONCHAINID/IClaimIssuer.sol)) of all the trusted claim issuers for a specific security token. The Identity contract ([IIdentity](../assets/eip-3643/ONCHAINID/IIdentity.sol)) of token owners (the investors) must have claims signed by the claim issuers stored in this smart contract in order to be able to hold the token. + +The standard relies on ERC-173 to define contract ownership, with the owner having the responsibility of managing this registry as per their requirements. This includes the ability to add, remove, and update the list of Trusted Issuers. + +A detailed description of the functions can be found in the [interfaces folder](../assets/eip-3643/interfaces/ITrustedIssuersRegistry.sol). + +```solidity +interface ITrustedIssuersRegistry { + + // events + event TrustedIssuerAdded(IClaimIssuer indexed trustedIssuer, uint[] claimTopics); + event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer); + event ClaimTopicsUpdated(IClaimIssuer indexed trustedIssuer, uint[] claimTopics); + + // functions + // setters + function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint[] calldata _claimTopics) external; + function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external; + function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint[] calldata _claimTopics) external; + + // getters + function getTrustedIssuers() external view returns (IClaimIssuer[] memory); + function isTrustedIssuer(address _issuer) external view returns(bool); + function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view returns(uint[] memory); + function getTrustedIssuersForClaimTopic(uint256 claimTopic) external view returns (IClaimIssuer[] memory); + function hasClaimTopic(address _issuer, uint _claimTopic) external view returns(bool); +} +``` + +### Claim Topics Registry Interface + +The Claim Topics Registry stores all the trusted claim topics for the security token. The Identity contract ([IIdentity](../assets/eip-3643/ONCHAINID/IIdentity.sol)) of token owners must contain claims of the claim topics stored in this smart contract. + +The standard relies on ERC-173 to define contract ownership, with the owner having the responsibility of managing this registry as per their requirements. This includes the ability to add and remove required Claim Topics. + +A detailed description of the functions can be found in the [interfaces folder](../assets/eip-3643/interfaces/IClaimTopicsRegistry.sol). + +```solidity +interface IClaimTopicsRegistry { + + // events + event ClaimTopicAdded(uint256 indexed claimTopic); + event ClaimTopicRemoved(uint256 indexed claimTopic); + + // functions + // setters + function addClaimTopic(uint256 _claimTopic) external; + function removeClaimTopic(uint256 _claimTopic) external; + + // getter + function getClaimTopics() external view returns (uint256[] memory); +} +``` + +## Rationale + +### Transfer Restrictions + +Transfers of securities can fail for a variety of reasons. This is in direct contrast to utility tokens, which generally only require the sender to have a sufficient balance. These conditions can be related to the status of an investor’s wallet, the identity of the sender and receiver of the securities (i.e., whether they have been through a KYC process, whether they are accredited or an affiliate of the issuer) or for reasons unrelated to the specific transfer but instead set at the token level (i.e., the token contract enforces a maximum number of investors or a cap on the percentage held by any single investor). For ERC-20 tokens, the `balanceOf` and `allowance` functions provide a way to check that a transfer is likely to succeed before executing the transfer, which can be executed both on-chain and off-chain. For tokens representing securities, the T-REX standard introduces a function `canTransfer` which provides a more general-purpose way to achieve this. I.e., when the reasons for failure are related to the compliance rules of the token and a function `isVerified` which allows checking the eligibility status of the identity of the investor. Transfers can also fail if the address of the sender and/or receiver is frozen, or if the free balance of the sender (total balance - frozen tokens) is lower than the amount to transfer. Ultimately, the transfer could be blocked if the token is `paused`. + +### Identity Management + +Security and compliance of transfers are enforced through the management of on-chain identities. These include: + +- Identity contract: A unique identifier for each investor, which is used to manage their identity and claims. +- Claim: Signed attestations issued by a trusted claim issuer that confirm certain attributes or qualifications of the token holders, such as their identity, location, investor status, or KYC/AML clearance. +- Identity Storage/Registry: A storage system for all Identity contracts and their associated wallets, which is used to + verify the eligibility of investors during transfers. + +### Token Lifecycle Management + +The T-REX standard provides a comprehensive framework for managing the lifecycle of security tokens. This includes the issuance of tokens, transfers between eligible investors, and the enforcement of compliance rules at every stage of the token's lifecycle. The standard also supports additional features such as token pausing and freezing, which can be used to manage the token in response to regulatory requirements or changes in the status of the token or its holders. + +### Additional Compliance Rules + +The T-REX standard supports the implementation of additional compliance rules through modular compliance. These modules can be used to enforce a wide range of rules and restrictions, such as caps on the number of investors or the percentage of tokens held by a single investor, restrictions on transfers between certain types of investors, and more. This flexibility allows issuers to tailor the compliance rules of their tokens to their specific needs and regulatory environment. + +## Backwards Compatibility + +T-REX tokens should be backwards compatible with ERC-20 and ERC-173 +and should be able to interact with a [Claim Holder contract](../assets/eip-3643/ONCHAINID/IERC735.sol) to validate +the claims linked to an [Identity contract](../assets/eip-3643/ONCHAINID/IIdentity.sol). + + +## Security Considerations + +This specification has been audited by Kapersky and Hacken, and no notable security considerations were found. +While the audits were primarily focused on the specific implementation by Tokeny, they also challenged and validated the core principles of the T-REX standard. The auditing teams approval of these principles provides assurance that the standard itself is robust and does not present any significant security concerns. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3651.md b/EIPS/eip-3651.md new file mode 100644 index 00000000000000..d94c694c6f1229 --- /dev/null +++ b/EIPS/eip-3651.md @@ -0,0 +1,45 @@ +--- +eip: 3651 +title: Warm COINBASE +description: Starts the `COINBASE` address warm +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-3651-warm-coinbase/6640 +status: Final +type: Standards Track +category: Core +created: 2021-07-12 +requires: 2929 +--- + +## Abstract + +The `COINBASE` address shall be warm at the start of transaction execution, in accordance with the actual cost of reading that account. + +## Motivation + +Direct `COINBASE` payments are becoming increasingly popular because they allow conditional payments, which provide benefits such as implicit cancellation of transactions that would revert. +But accessing `COINBASE` is overpriced; the address is initially cold under the access list framework introduced in [EIP-2929](./eip-2929.md). +This gas cost mismatch can incentivize alternative payments besides ETH, such as [ERC-20](./eip-20.md), but ETH should be the primary means of paying for transactions on Ethereum. + +## Specification + +At the start of transaction execution, `accessed_addresses` shall be initialized to also include the address returned by `COINBASE` (`0x41`). + +## Rationale + +The addresses currently initialized warm are the addresses that should already be loaded at the start of transaction validation. +The `ORIGIN` address is always loaded to check its balance against the gas limit and the gas price. +The `tx.to` address is always loaded to begin execution. +The `COINBASE` address should also be always be loaded because it receives the block reward and the transaction fees. + +## Backwards Compatibility + +There are no known backward compatibility issues presented by this change. + +## Security Considerations + +There are no known security considerations introduced by this change. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3668.md b/EIPS/eip-3668.md new file mode 100644 index 00000000000000..b04d3dc51311c3 --- /dev/null +++ b/EIPS/eip-3668.md @@ -0,0 +1,410 @@ +--- +eip: 3668 +title: "CCIP Read: Secure offchain data retrieval" +description: CCIP Read provides a mechanism to allow a contract to fetch external data. +author: Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/durin-secure-offchain-data-retrieval/6728 +status: Final +type: Standards Track +category: ERC +created: 2020-07-19 +--- + +## Abstract +Contracts wishing to support lookup of data from external sources may, instead of returning the data directly, revert using `OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData)`. Clients supporting this specification then make an RPC call to a URL from `urls`, supplying `callData`, and getting back an opaque byte string `response`. Finally, clients call the function specified by `callbackFunction` on the contract, providing `response` and `extraData`. The contract can then decode and verify the returned data using an implementation-specific method. + +This mechanism allows for offchain lookups of data in a way that is transparent to clients, and allows contract authors to implement whatever validation is necessary; in many cases this can be provided without any additional trust assumptions over and above those required if data is stored onchain. + +## Motivation +Minimising storage and transaction costs on Ethereum has driven contract authors to adopt a variety of techniques for moving data offchain, including hashing, recursive hashing (eg Merkle Trees/Tries) and L2 solutions. While each solution has unique constraints and parameters, they all share in common the fact that enough information is stored onchain to validate the externally stored data when required. + +Thus far, applications have tended to devise bespoke solutions rather than trying to define a universal standard. This is practical - although inefficient - when a single offchain data storage solution suffices, but rapidly becomes impractical in a system where multiple end-users may wish to make use of different data storage and availability solutions based on what suits their needs. + +By defining a common specification allowing smart contract to fetch data from offchain, we facilitate writing clients that are entirely agnostic to the storage solution being used, which enables new applications that can operate without knowing about the underlying storage details of the contracts they interact with. + +Examples of this include: + - Interacting with 'airdrop' contracts that store a list of recipients offchain in a merkle trie. + - Viewing token information for tokens stored on an L2 solution as if they were native L1 tokens. + - Allowing delegation of data such as ENS domains to various L2 solutions, without requiring clients to support each solution individually. + - Allowing contracts to proactively request external data to complete a call, without requiring the caller to be aware of the details of that data. + +## Specification +### Overview +Answering a query via CCIP read takes place in three steps: + + 1. Querying the contract. + 2. Querying the gateway using the URL provided in (1). + 3. Querying or sending a transaction to the contract using the data from (1) and (2). + +In step 1, a standard blockchain call operation is made to the contract. The contract reverts with an error that specifies the data to complete the call can be found offchain, and provides the url to a service that can provide the answer, along with additional contextual information required for the call in step (3). + +In step 2, the client calls the gateway service with the `callData` from the revert message in step (1). The gateway responds with an answer `response`, whose content is opaque to the client. + +In step 3, the client calls the original contract, supplying the `response` from step (2) and the `extraData` returned by the contract in step (1). The contract decodes the provided data and uses it to validate the response and act on it - by returning information to the client or by making changes in a transaction. The contract could also revert with a new error to initiate another lookup, in which case the protocol starts again at step 1. + +``` +┌──────┐ ┌────────┐ ┌─────────────┐ +│Client│ │Contract│ │Gateway @ url│ +└──┬───┘ └───┬────┘ └──────┬──────┘ + │ │ │ + │ somefunc(...) │ │ + ├─────────────────────────────────────────────────►│ │ + │ │ │ + │ revert OffchainData(sender, urls, callData, │ │ + │ callbackFunction, extraData) │ │ + │◄─────────────────────────────────────────────────┤ │ + │ │ │ + │ HTTP request (sender, callData) │ │ + ├──────────────────────────────────────────────────┼────────────►│ + │ │ │ + │ Response (result) │ │ + │◄─────────────────────────────────────────────────┼─────────────┤ + │ │ │ + │ callbackFunction(result, extraData) │ │ + ├─────────────────────────────────────────────────►│ │ + │ │ │ + │ answer │ │ + │◄─────────────────────────────────────────────────┤ │ + │ │ │ +``` + +### Contract interface + +A CCIP read enabled contract MUST revert with the following error whenever a function that requires offchain data is called: + +```solidity +error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData) +``` + +`sender` is the address of the contract that raised the error, and is used to determine if the error was thrown by the contract the client called, or 'bubbled up' from a nested call. + +`urls` specifies a list of URL templates to services (known as gateways) that implement the CCIP read protocol and can formulate an answer to the query. `urls` can be the empty list `[]`, in which case the client MUST specify the URL template. The order in which URLs are tried is up to the client, but contracts SHOULD return them in order of priority, with the most important entry first. + +Each URL may include two substitution parameters, `{sender}` and `{data}`. Before a call is made to the URL, `sender` is replaced with the lowercase 0x-prefixed hexadecimal formatted `sender` parameter, and `data` is replaced by the the 0x-prefixed hexadecimal formatted `callData` parameter. + +`callData` specifies the data to call the gateway with. This value is opaque to the client. Typically this will be ABI-encoded, but this is an implementation detail that contracts and gateways can standardise on as desired. + +`callbackFunction` is the 4-byte function selector for a function on the original contract to which a callback should be sent. + +`extraData` is additional data that is required by the callback, and MUST be retained by the client and provided unmodified to the callback function. This value is opaque to the client. + +The contract MUST also implement a callback method for decoding and validating the data returned by the gateway. The name of this method is implementation-specific, but it MUST have the signature `(bytes response, bytes extraData)`, and MUST have the same return type as the function that reverted with `OffchainLookup`. + +If the client successfully calls the gateway, the callback function specified in the `OffchainLookup` error will be invoked by the client, with `response` set to the value returned by the gateway, and `extraData` set to the value returned in the contract's `OffchainLookup` error. The contract MAY initiate another CCIP read lookup in this callback, though authors should bear in mind that the limits on number of recursive invocations will vary from client to client. + +In a call context (as opposed to a transaction), the return data from this call will be returned to the user as if it was returned by the function that was originally invoked. + +#### Example + +Suppose a contract has the following method: + +```solidity +function balanceOf(address addr) public view returns(uint balance); +``` + +Data for these queries is stored offchain in some kind of hashed data structure, the details of which are not important for this example. The contract author wants the gateway to fetch the proof information for this query and call the following function with it: + +```solidity +function balanceOfWithProof(bytes calldata response, bytes calldata extraData) public view returns(uint balance); +``` + +One example of a valid implementation of `balanceOf` would thus be: + +```solidity +function balanceOf(address addr) public view returns(uint balance) { + revert OffchainLookup( + address(this), + [url], + abi.encodeWithSelector(Gateway.getSignedBalance.selector, addr), + ContractName.balanceOfWithProof.selector, + abi.encode(addr) + ); +} +``` + +Note that in this example the contract is returning `addr` in both `callData` and `extraData`, because it is required both by the gateway (in order to look up the data) and the callback function (in order to verify it). The contract cannot simply pass it to the gateway and rely on it being returned in the response, as this would give the gateway an opportunity to respond with an answer to a different query than the one that was initially issued. + +#### Recursive calls in CCIP-aware contracts + +When a CCIP-aware contract wishes to make a call to another contract, and the possibility exists that the callee may implement CCIP read, the calling contract MUST catch all `OffchainLookup` errors thrown by the callee, and revert with a different error if the `sender` field of the error does not match the callee address. + +The contract MAY choose to replace all `OffchainLookup` errors with a different error. Doing so avoids the complexity of implementing support for nested CCIP read calls, but renders them impossible. + +Where the possibility exists that a callee implements CCIP read, a CCIP-aware contract MUST NOT allow the default solidity behaviour of bubbling up reverts from nested calls. This is to prevent the following situation: + + 1. Contract A calls non-CCIP-aware contract B. + 2. Contract B calls back to A. + 3. In the nested call, A reverts with `OffchainLookup`. + 4. Contract B does not understand CCIP read and propagates the `OffchainLookup` to its caller. + 5. Contract A also propagates the `OffchainLookup` to its caller. + +The result of this sequence of operations would be an `OffchainLookup` that looks valid to the client, as the `sender` field matches the address of the contract that was called, but does not execute correctly, as it only completes a nested invocation. + +#### Example + +The code below demonstrates one way that a contract may support nested CCIP read invocations. For simplicity this is shown using Solidity's try/catch syntax, although as of this writing it does not yet support catching custom errors. + +```solidity +contract NestedLookup { + error InvalidOperation(); + error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData); + + function a(bytes calldata data) external view returns(bytes memory) { + try target.b(data) returns (bytes memory ret) { + return ret; + } catch OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData) { + if(sender != address(target)) { + revert InvalidOperation(); + } + revert OffchainLookup( + address(this), + urls, + callData, + NestedLookup.aCallback.selector, + abi.encode(address(target), callbackFunction, extraData) + ); + } + } + + function aCallback(bytes calldata response, bytes calldata extraData) external view returns(bytes memory) { + (address inner, bytes4 innerCallbackFunction, bytes memory innerExtraData) = abi.decode(extraData, (address, bytes4, bytes)); + return abi.decode(inner.call(abi.encodeWithSelector(innerCallbackFunction, response, innerExtraData)), (bytes)); + } +} +``` + +### Gateway Interface +The URLs returned by a contract may be of any schema, but this specification only defines how clients should handle HTTPS URLs. + +Given a URL template returned in an `OffchainLookup`, the URL to query is composed by replacing `sender` with the lowercase 0x-prefixed hexadecimal formatted `sender` parameter, and replacing `data` with the the 0x-prefixed hexadecimal formatted `callData` parameter. + +For example, if a contract returns the following data in an `OffchainLookup`: + +``` +urls = ["https://example.com/gateway/{sender}/{data}.json"] +sender = "0xaabbccddeeaabbccddeeaabbccddeeaabbccddee" +callData = "0x00112233" +``` + +The request URL to query is `https://example.com/gateway/0xaabbccddeeaabbccddeeaabbccddeeaabbccddee/0x00112233.json`. + +If the URL template contains the `{data}` substitution parameter, the client MUST send a GET request after replacing the substitution parameters as described above. + +If the URL template does not contain the `{data}` substitution parameter, the client MUST send a POST request after replacing the substitution parameters as described above. The POST request MUST be sent with a Content-Type of `application/json`, and a payload matching the following schema: + +``` +{ + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "0x-prefixed hex string containing the `callData` from the contract" + }, + "sender": { + "type": "string", + "description": "0x-prefixed hex string containing the `sender` parameter from the contract" + } + } +} +``` + +Compliant gateways MUST respond with a Content-Type of `application/json`, with the body adhering to the following JSON schema: +``` +{ + "type": "object", + "properties": { + "data": { + "type": "string", + "description: "0x-prefixed hex string containing the result data." + } + } +} +``` + +Unsuccessful requests MUST return the appropriate HTTP status code - for example, 404 if the `sender` address is not supported by this gateway, 400 if the `callData` is in an invalid format, 500 if the server encountered an internal error, and so forth. If the Content-Type of a 4xx or 5xx response is `application/json`, it MUST adhere to the following JSON schema: +``` +{ + "type": "object", + "properties": { + "message": { + "type": "string", + "description: "A human-readable error message." + } + } +} +``` + +#### Examples + +***GET request*** + +``` +# Client returned a URL template `https://example.com/gateway/{sender}/{data}.json` +# Request +curl -D - https://example.com/gateway/0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8/0xd5fa2b00.json + +# Successful result + HTTP/2 200 + content-type: application/json; charset=UTF-8 + ... + + {"data": "0xdeadbeefdecafbad"} + +# Error result + HTTP/2 404 + content-type: application/json; charset=UTF-8 + ... + + {"message": "Gateway address not supported."} +} +``` + +***POST request*** + +``` +# Client returned a URL template `https://example.com/gateway/{sender}.json` +# Request +curl -D - -X POST -H "Content-Type: application/json" --data '{"data":"0xd5fa2b00","sender":"0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8"}' https://example.com/gateway/0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8.json + +# Successful result + HTTP/2 200 + content-type: application/json; charset=UTF-8 + ... + + {"data": "0xdeadbeefdecafbad"} + +# Error result + HTTP/2 404 + content-type: application/json; charset=UTF-8 + ... + + {"message": "Gateway address not supported."} +} +``` + +Clients MUST support both GET and POST requests. Gateways may implement either or both as needed. + +### Client Lookup Protocol + +A client that supports CCIP read MUST make contract calls using the following process: + + 1. Set `data` to the call data to supply to the contract, and `to` to the address of the contract to call. + 2. Call the contract at address `to` function normally, supplying `data` as the input data. If the function returns a successful result, return it to the caller and stop. + 3. If the function returns an error other than `OffchainLookup`, return it to the caller in the usual fashion. + 4. Otherwise, decode the `sender`, `urls`, `callData`, `callbackFunction` and `extraData` arguments from the `OffchainLookup` error. + 5. If the `sender` field does not match the address of the contract that was called, return an error to the caller and stop. + 6. Construct a request URL by replacing `sender` with the lowercase 0x-prefixed hexadecimal formatted `sender` parameter, and replacing `data` with the the 0x-prefixed hexadecimal formatted `callData` parameter. The client may choose which URLs to try in which order, but SHOULD prioritise URLs earlier in the list over those later in the list. + 7. Make an HTTP GET request to the request URL. + 8. If the response code from step (5) is in the range 400-499, return an error to the caller and stop. + 9. If the response code from step (5) is in the range 500-599, go back to step (5) and pick a different URL, or stop if there are no further URLs to try. + 10. Otherwise, replace `data` with an ABI-encoded call to the contract function specified by the 4-byte selector `callbackFunction`, supplying the data returned from step (7) and `extraData` from step (4), and return to step (1). + +Clients MUST handle HTTP status codes appropriately, employing best practices for error reporting and retries. + +Clients MUST handle HTTP 4xx and 5xx error responses that have a content type other than application/json appropriately; they MUST NOT attempt to parse the response body as JSON. + +This protocol can result in multiple lookups being requested by the same contract. Clients MUST implement a limit on the number of lookups they permit for a single contract call, and this limit SHOULD be at least 4. + +The lookup protocol for a client is described with the following pseudocode: + +```javascript +async function httpcall(urls, to, callData) { + const args = {sender: to.toLowerCase(), data: callData.toLowerCase()}; + for(const url of urls) { + const queryUrl = url.replace(/\{([^}]*)\}/g, (match, p1) => args[p1]); + // First argument is URL to fetch, second is optional data for a POST request. + const response = await fetch(queryUrl, url.includes('{data}') ? undefined : args); + const result = await response.text(); + if(result.statusCode >= 400 && result.statusCode <= 499) { + throw new Error(data.error.message); + } + if(result.statusCode >= 200 && result.statusCode <= 299) { + return result; + } + } +} +async function durin_call(provider, to, data) { + for(let i = 0; i < 4; i++) { + try { + return await provider.call(to, data); + } catch(error) { + if(error.code !== "CALL_EXCEPTION") { + throw(error); + } + const {sender, urls, callData, callbackFunction, extraData} = error.data; + if(sender !== to) { + throw new Error("Cannot handle OffchainLookup raised inside nested call"); + } + const result = httpcall(urls, to, callData); + data = abi.encodeWithSelector(callbackFunction, result, extraData); + } + } + throw new Error("Too many CCIP read redirects"); +} +``` + +Where: + - `provider` is a provider object that facilitates Ethereum blockchain function calls. + - `to` is the address of the contract to call. + - `data` is the call data for the contract. + +If the function being called is a standard contract function, the process terminates after the original call, returning the same result as for a regular call. Otherwise, a gateway from `urls` is called with the `callData` returned by the `OffchainLookup` error, and is expected to return a valid response. The response and the `extraData` are then passed to the specified callback function. This process can be repeated if the callback function returns another `OffchainLookup` error. + +### Use of CCIP read for transactions +While the specification above is for read-only contract calls (eg, `eth_call`), it is simple to use this method for sending transactions (eg, `eth_sendTransaction` or `eth_sendRawTransaction`) that require offchain data. While 'preflighting' a transaction using `eth_estimateGas` or `eth_call`, a client that receives an `OffchainLookup` revert can follow the procedure described above in [Client lookup protocol](#client-lookup-protocol), substituting a transaction for the call in the last step. This functionality is ideal for applications such as making onchain claims supported by offchain proof data. + +### Glossary + - Client: A process, such as JavaScript executing in a web browser, or a backend service, that wishes to query a blockchain for data. The client understands how to fetch data using CCIP read. + - Contract: A smart contract existing on Ethereum or another blockchain. + - Gateway: A service that answers application-specific CCIP read queries, usually over HTTPS. + +## Rationale +### Use of `revert` to convey call information +For offchain data lookup to function as desired, clients must either have some way to know that a function depends on this specification for functionality - such as a specifier in the ABI for the function - or else there must be a way for the contract to signal to the client that data needs to be fetched from elsewhere. + +While specifying the call type in the ABI is a possible solution, this makes retrofitting existing interfaces to support offchain data awkward, and either results in contracts with the same name and arguments as the original specification, but with different return data - which will cause decoding errors for clients that do not expect this - or duplicating every function that needs support for offchain data with a different name (eg, `balanceOf -> offchainBalanceOf`). Neither solutions is particularly satisfactory. + +Using a revert, and conveying the required information in the revert data, allows any function to be retrofitted to support lookups via CCIP read so long as the client understands the specification, and so facilitates translation of existing specifications to use offchain data. + +### Passing contract address to the gateway service +`address` is passed to the gateway in order to facilitate the writing of generic gateways, thus reducing the burden on contract authors to provide their own gateway implementations. Supplying `address` allows the gateway to perform lookups to the original contract for information needed to assist with resolution, making it possible to operate one gateway for any number of contracts implementing the same interface. + +### Existence of `extraData` argument +`extraData` allows the original contract function to pass information to a subsequent invocation. Since contracts are not persistent, without this data a contract has no state from the previous invocation. Aside from allowing arbitrary contextual information to be propagated between the two calls, this also allows the contract to verify that the query the gateway answered is in fact the one the contract originally requested. + +### Use of GET and POST requests for the gateway interface +Using a GET request, with query data encoded in the URL, minimises complexity and enables entirely static implementations of gateways - in some applications a gateway can simply be an HTTP server or IPFS instance with a static set of responses in text files. + +However, URLs are limited to 2 kilobytes in size, which will impose issues for more complex uses of CCIP read. Thus, we provide for an option to use POST data. This is made at the contract's discretion (via the choice of URL template) in order to preserve the ability to have a static gateway operating exclusively using GET when desired. + +## Backwards Compatibility +Existing contracts that do not wish to use this specification are unaffected. Clients can add support for CCIP read to all contract calls without introducing any new overhead or incompatibilities. + +Contracts that require CCIP read will not function in conjunction with clients that do not implement this specification. Attempts to call these contracts from non-compliant clients will result in the contract throwing an exception that is propagaged to the user. + +## Security Considerations + +### Gateway Response Data Validation +In order to prevent a malicious gateway from causing unintended side-effects or faulty results, contracts MUST include sufficient information in the `extraData` argument to allow them to verify the relevance and validity of the gateway's response. For example, if the contract is requesting information based on an `address` supplied to the original call, it MUST include that address in the `extraData` so that the callback can verify the gateway is not providing the answer to a different query. + +Contracts must also implement sufficient validation of the data returned by the gateway to ensure it is valid. The validation required is application-specific and cannot be specified on a global basis. Examples would include verifying a Merkle proof of inclusion for an L2 or other Merkleized state, or verifying a signature by a trusted signer over the response data. + +### Client Extra Data Validation +In order to prevent a malicious client from causing unintended effects when making transactions using CCIP read, contracts MUST implement appropriate checks on the `extraData` returned to them in the callback. Any sanity/permission checks performed on input data for the initial call MUST be repeated on the data passed through the `extraData` field in the callback. For example, if a transaction should only be executable by an authorised account, that authorisation check MUST be done in the callback; it is not sufficient to perform it with the initial call and embed the authorised address in the `extraData`. + +### HTTP requests and fingerprinting attacks +Because CCIP read can cause a user's browser to make HTTP requests to an address controlled by the contract, there is the potential for this to be used to identify users - for example, to associate their wallet address with their IP address. + +The impact of this is application-specific; fingerprinting a user when they resolve an ENS domain may have little privacy impact, as the attacker will not learn the user's wallet address, only the fact that the user is resolving a given ENS name from a given IP address - information they can also learn from running a DNS server. On the other hand, fingerprinting a user when they attempt a transaction to transfer an NFT may give an attacker everything they need to identify the IP address of a user's wallet. + +To minimise the security impact of this, we make the following recommendations: + + 1. Client libraries should provide clients with a hook to override CCIP read calls - either by rewriting them to use a proxy service, or by denying them entirely. This mechanism or another should be written so as to easily facilitate adding domains to allowlists or blocklists. + 2. Client libraries should disable CCIP read for transactions (but not for calls) by default, and require the caller to explicitly enable this functionality. Enablement should be possible both on a per-contract, per-domain, or global basis. + 3. App authors should not supply a 'from' address for contract calls ('view' operations) where the call could execute untrusted code (that is, code not authored or trusted by the application author). As a precuationary principle it is safest to not supply this parameter at all unless the author is certain that no attacker-determined smart contract code will be executed. + 4. Wallet authors that are responsible for fetching user information - for example, by querying token contracts - should either ensure CCIP read is disabled for transactions, and that no contract calls are made with a 'from' address supplied, or operate a proxy on their users' behalf, rewriting all CCIP read calls to take place via the proxy, or both. + +We encourage client library authors and wallet authors not to disable CCIP read by default, as many applications can be transparently enhanced with this functionality, which is quite safe if the above precautions are observed. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3670.md b/EIPS/eip-3670.md new file mode 100644 index 00000000000000..93faca67630760 --- /dev/null +++ b/EIPS/eip-3670.md @@ -0,0 +1,138 @@ +--- +eip: 3670 +title: EOF - Code Validation +description: Validate EOF bytecode for correctness at the time of deployment. +author: Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-3670-eof-code-validation/6693 +status: Review +type: Standards Track +category: Core +created: 2021-06-23 +requires: 3540 +--- + +## 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. + +## 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 fewer paths to decide which instruction is valid in +the current execution context. + +If there's a desire to introduce new instructions without bumping the EOF version, having undefined instructions already deployed could potentially break such contracts, as some instructions might change their behavior. Rejecting to deploy undefined instructions allows +introducing new instructions with or without bumping the EOF version. + +### EOF1 forward compatibility + +The EOF1 format provides following forward compatibility properties: + +1. New instructions can be defined for previously unassigned opcodes. These instructions may have immediate values. +2. Mandatory EOF sections may be made optional. +3. New optional EOF sections may be introduced. They can be placed in any order in relation to previously defined sections. + +## Specification + +*Remark:* We rely on the notation of *initcode*, *code* and *creation* as defined by [EIP-3540](./eip-3540.md). + +This feature is introduced on the same block EIP-3540 is enabled, therefore every EOF1-compatible bytecode MUST be validated according to these rules. + +1. Previously deprecated instructions `CALLCODE` (0xf2) and `SELFDESTRUCT` (0xff) are invalid and their opcodes are undefined. +2. At contract creation time *instructions validation* is performed on both *initcode* and *code*. The code is invalid if any of the checks below fails. For each instruction: + 1. Check if the opcode is defined. The `INVALID` (0xfe) is considered defined. + 2. Check if all instructions' immediate bytes are present in the code (code does not end in the middle of instruction). + +## Rationale + +### Immediate data + +Allowing implicit zero immediate data for `PUSH` instructions introduces inefficiencies to EVM implementations without any practical use-case (the value of a `PUSH` instruction at the code end cannot be observed by EVM). This EIP requires all immediate bytes to be explicitly present in the code. + +### Rejection of deprecated instructions + +The deprecated instructions `CALLCODE` (0xf2) and `SELFDESTRUCT` (0xff) are removed from the `valid_opcodes` list to prevent their use in the future. + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The validation does not cover legacy bytecode (code which is not EOF formatted). + +## Test Cases + +### Contract creation + +Each case should be tested for creation transaction, `CREATE` and `CREATE2`. + +- Invalid initcode +- Valid initcode returning invalid code +- Valid initcode returning valid code + +### Valid codes + +- EOF code containing `INVALID` +- EOF code with data section containing bytes that are undefined instructions +- Legacy code containing undefined instruction +- Legacy code ending with incomplete PUSH instruction + +### Invalid codes + +- EOF code containing undefined instruction +- EOF code ending with incomplete `PUSH` instruction + - This can include `PUSH` instruction unreachable by execution, e.g. after `STOP` + +## Reference Implementation + +```python +# The ranges below are as specified in the Yellow Paper. +# Note: range(s, e) excludes e, hence the +1 +valid_opcodes = [ + *range(0x00, 0x0b + 1), + *range(0x10, 0x1d + 1), + 0x20, + *range(0x30, 0x3f + 1), + *range(0x40, 0x48 + 1), + *range(0x50, 0x5b + 1), + *range(0x60, 0x6f + 1), + *range(0x70, 0x7f + 1), + *range(0x80, 0x8f + 1), + *range(0x90, 0x9f + 1), + *range(0xa0, 0xa4 + 1), + # Note: 0xfe is considered assigned. + 0xf0, 0xf1, 0xf3, 0xf4, 0xf5, 0xfa, 0xfd, 0xfe +] + +immediate_sizes = 256 * [0] +immediate_sizes[0x60:0x7f + 1] = range(1, 32 + 1) # PUSH1..PUSH32 + + +# Raises ValidationException on invalid code +def validate_instructions(code: bytes): + # Note that EOF1 already asserts this with the code section requirements + assert len(code) > 0 + + pos = 0 + while pos < len(code): + # Ensure the opcode is valid + opcode = code[pos] + if opcode not in valid_opcodes: + raise ValidationException("undefined opcode") + + # Skip immediate data + pos += 1 + immediate_sizes[opcode] + + # Ensure last instruction's immediate doesn't go over code end + if pos != len(code): + raise ValidationException("truncated immediate") +``` + +## Security Considerations + +See [Security Considerations of EIP-3540](./eip-3540.md#security-considerations). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3675.md b/EIPS/eip-3675.md new file mode 100644 index 00000000000000..efa51f543490bd --- /dev/null +++ b/EIPS/eip-3675.md @@ -0,0 +1,341 @@ +--- +eip: 3675 +title: Upgrade consensus to Proof-of-Stake +description: Specification of the consensus mechanism upgrade on Ethereum Mainnet that introduces Proof-of-Stake +author: Mikhail Kalinin (@mkalinin), Danny Ryan (@djrtwo), Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/eip-3675-upgrade-consensus-to-proof-of-stake/6706 +status: Final +type: Standards Track +category: Core +created: 2021-07-22 +requires: 2124 +--- + + +## Abstract + +This EIP deprecates Proof-of-Work (PoW) and supersedes it with the new Proof-of-Stake consensus mechanism (PoS) driven by the beacon chain. Information on the bootstrapping of the new consensus mechanism is documented in [EIP-2982](./eip-2982.md). Full specification of the beacon chain can be found in the `ethereum/consensus-specs` repository. + +This document specifies the set of changes to the block structure, block processing, fork choice rule and network interface introduced by the consensus upgrade. + + +## Motivation + +The beacon chain network has been up and running since December 2020. Neither safety nor liveness failures were detected during this period of time. This long period of running without failure demonstrates the sustainability of the beacon chain system and its readiness to become a security provider for the Ethereum Mainnet. + +To understand the motivation of introducing the Proof-of-Stake consensus see the Motivation section of [EIP-2982](./eip-2982.md#motivation). + + +## Specification + +### Definitions + +* **PoW block**: Block that is built and verified by the existing proof-of-work mechanism. In other words, a block of the Ethereum network before the consensus upgrade. +* **PoS block**: Block that is built and verified by the new proof-of-stake mechanism. +* **Terminal PoW block**: A PoW block that satisfies the following conditions -- +`pow_block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY` *and* `pow_block.parent_block.total_difficulty < TERMINAL_TOTAL_DIFFICULTY`. +There can be more than one terminal PoW block in the network, e.g. multiple children of the same pre-terminal block. +* **`TERMINAL_TOTAL_DIFFICULTY`** The amount of total difficulty reached by the network that triggers the consensus upgrade. Ethereum Mainnet configuration **MUST** have this parameter set to the value `58750000000000000000000`. +* **`TRANSITION_BLOCK`** The earliest PoS block of the canonical chain, i.e. the PoS block with the lowest block height. +* **`POS_FORKCHOICE_UPDATED`** An event occurring when the state of the proof-of-stake fork choice is updated. +* **`FORK_NEXT_VALUE`** A block number set to the `FORK_NEXT` parameter for the upcoming consensus upgrade. +* **`TERMINAL_BLOCK_HASH`** Designates the hash of the terminal PoW block if set, i.e. if not stubbed with `0x0000000000000000000000000000000000000000000000000000000000000000`. +* **`TERMINAL_BLOCK_NUMBER`** Designates the number of the terminal PoW block if `TERMINAL_BLOCK_HASH` is set. + +#### PoS events + +Events having the `POS_` prefix in the name (PoS events) are emitted by the new proof-of-stake consensus mechanism. They signify the corresponding assertion that has been made regarding a block specified by the event. The underlying logic of PoS events can be found in the beacon chain specification. On the occurrence of each PoS event the corresponding action that is specified by this EIP **MUST** be taken. + +The details provided below must be taken into account when reading those parts of the specification that refer to the PoS events: +* Reference to a block that is contained by PoS events is provided in a form of a block hash unless another is explicitly specified. +* A `POS_FORKCHOICE_UPDATED` event contains references to the head of the canonical chain and to the most recent finalized block. Before the first finalized block occurs in the system the finalized block hash provided by this event is stubbed with `0x0000000000000000000000000000000000000000000000000000000000000000`. +* **`FIRST_FINALIZED_BLOCK`** The first finalized block that is designated by `POS_FORKCHOICE_UPDATED` event and has the hash that differs from the stub. + + +### Client software configuration + +The following set of parameters is a part of client software configuration and **MUST** be included into its binary distribution: +* `TERMINAL_TOTAL_DIFFICULTY` +* `FORK_NEXT_VALUE` +* `TERMINAL_BLOCK_HASH` +* `TERMINAL_BLOCK_NUMBER` + +*Note*: If `TERMINAL_BLOCK_HASH` is stubbed with `0x0000000000000000000000000000000000000000000000000000000000000000` then `TERMINAL_BLOCK_HASH` and `TERMINAL_BLOCK_NUMBER` parameters **MUST NOT** take an effect. + + +### PoW block processing + +PoW blocks that are descendants of any terminal PoW block **MUST NOT** be imported. This implies that a terminal PoW block will be the last PoW block in the canonical chain. + + +### Constants + +| Name | Value | +|-|-| +| **`MAX_EXTRA_DATA_BYTES`** | `32` | + +### Block structure + +Beginning with `TRANSITION_BLOCK`, a number of previously dynamic block fields are deprecated by enforcing these values to instead be constants. Each block field listed in the table below **MUST** be replaced with the corresponding constant value. + +| Field | Constant value | Comment | +|-|-|-| +| **`ommersHash`** | `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` | `= Keccak256(RLP([]))` | +| **`difficulty`** | `0` | | +| **`mixHash`** | `0x0000000000000000000000000000000000000000000000000000000000000000` | | +| **`nonce`** | `0x0000000000000000` | | +| **`ommers`** | `[]` | `RLP([]) = 0xc0` | + +Beginning with `TRANSITION_BLOCK`, the validation of the block's **`extraData`** field changes: The length of the block's **`extraData`** **MUST** be less than or equal to **`MAX_EXTRA_DATA_BYTES`** bytes. + +*Note*: Logic and validity conditions of block fields that are *not* specified here **MUST** remain unchanged. Additionally, the overall block format **MUST** remain unchanged. + +*Note*: Subsequent EIPs may override the constant values specified above to provide additional functionality. For an example, see [EIP-4399](./eip-4399.md). + + +### Block validity + +Beginning with `TRANSITION_BLOCK`, the block validity conditions **MUST** be altered by the following: +* Remove verification of the block's **`difficulty`** value with respect to the difficulty formula. +* Remove verification of the block's **`nonce`** and **`mixHash`** values with respect to the Ethash function. +* Remove all validation rules that are evaluated over the list of ommers and each member of this list. +* Add verification of the fields noted in the [block structure](#block-structure) section. + +*Note*: If one of the new rules fails then the block **MUST** be invalidated. + +*Note*: Validity rules that are not specified in the list above **MUST** remain unchanged. + +#### Transition block validity + +In addition to satisfying the above conditions, `TRANSITION_BLOCK` **MUST** be a child of a terminal PoW block. That is, a parent of `TRANSITION_BLOCK` **MUST** satisfy terminal PoW block conditions. + + +### Block and ommer rewards + +Beginning with `TRANSITION_BLOCK`, block and ommer rewards are deprecated. Specifically, the following actions **MUST** be taken: +* Remove increasing the balance of the block's **`beneficiary`** account by the block reward. +* Remove increasing the balance of the block's **`beneficiary`** account by the ommer inclusion reward per each ommer. +* Remove increasing the balance of the ommer's **`beneficiary`** account by the ommer block reward per each ommer. + +*Note*: Transaction fee mechanics affecting the block's `beneficiary` account **MUST** remain unchanged. + + +### Fork choice rule + +If set, `TERMINAL_BLOCK_HASH` parameter affects the PoW heaviest chain rule in the following way: +* Canonical blockchain **MUST** contain a block with the hash defined by `TERMINAL_BLOCK_HASH` parameter at the height defined by `TERMINAL_BLOCK_NUMBER` parameter. + +*Note*: This rule is akin to block hash whitelisting functionality already present in client software implementations. + +As of the first `POS_FORKCHOICE_UPDATED` event, the fork choice rule **MUST** be altered in the following way: +* Remove the existing PoW heaviest chain rule. +* Adhere to the new PoS LMD-GHOST rule. + +The new PoS LMD-GHOST fork choice rule is specified as follows. On each occurrence of a `POS_FORKCHOICE_UPDATED` event including the first one, the following actions **MUST** be taken: +* Consider the chain starting at genesis and ending with the head block nominated by the event as the canonical blockchain. +* Set the head of the canonical blockchain to the corresponding block nominated by the event. +* Beginning with the `FIRST_FINALIZED_BLOCK`, set the most recent finalized block to the corresponding block nominated by the event. + +Changes to the block tree store that are related to the above actions **MUST** be applied atomically. + +*Note*: This rule **MUST** be strictly enforced. "Optimistic" updates to the head **MUST NOT** be made. That is -- if a new block is processed on top of the current head block, this new block becomes the new head if and only if an accompanying `POS_FORKCHOICE_UPDATED` event occurs. + +### Network + +#### Fork identifier + +For the purposes of the [EIP-2124](./eip-2124.md) fork identifier, nodes implementing this EIP **MUST** set the `FORK_NEXT` parameter to the `FORK_NEXT_VALUE`. + +#### devp2p + +The networking stack **SHOULD NOT** send the following messages if they advertise the descendant of any terminal PoW block: +* `NewBlockHashes (0x01)` +* `NewBlock (0x07)` + +Beginning with receiving the `FIRST_FINALIZED_BLOCK`, the networking stack **MUST** discard the following ingress messages: +* `NewBlockHashes (0x01)` +* `NewBlock (0x07)` + +Beginning with receiving the finalized block next to the `FIRST_FINALIZED_BLOCK`, the networking stack **MUST** remove the handlers corresponding to the following messages: +* `NewBlockHashes (0x01)` +* `NewBlock (0x07)` + +Peers that keep sending these messages after the handlers have been removed **SHOULD** be disconnected. + +*Note:* The logic of message handlers that are not affected by this section **MUST** remain unchanged. + + +## Rationale + +The changes specified in this EIP target a minimal requisite set of consensus and client software modifications to safely replace the existing proof-of-work consensus algorithm with the new proof-of-stake consensus represented by the already in-production beacon chain. + +This EIP was designed to minimize the complexity of hot-swapping the live consensus of the Ethereum network. Both the safety of the operation and time to production were taken into consideration. Additionally, a minimal changeset helps ensure that *most* smart contracts and services will continue to function as intended during and after the transition with little to no required intervention. + +### Total difficulty triggering the upgrade + +See [Security considerations](#terminal-total-difficulty-vs-block-number). + +### Parameterizing terminal block hash + +See [Security considerations](#terminal-pow-block-overriding). + +### Halting the import of PoW blocks + +See [Security considerations](#halt-the-importing-of-pow-blocks). + +### Replacing block fields with constants + +Deprecated block fields are replaced with constant values to ensure the block format remains backwards compatible. Preserving the block format aids existing smart contracts and services in providing uninterrupted service during and after this transition. + +Particularly, this is important for those smart contracts that verify Merkle proofs of transaction/receipt inclusion and state by validating the hash of externally provided block header against the corresponding value returned by the `BLOCKHASH` operation. + +This change introduces an additional validity rule that enforces the replacement of deprecated block fields. + +### Replacing `difficulty` with `0` + +After deprecating the proof-of-work the notion of difficulty no longer exists and replacing the block header **`difficulty`** field with `0` constant is semantically sound. + +### Changing block validity rules + +The rule set enforcing the PoW seal validity is replaced with the corresponding PoS rules along with the consensus upgrade as the rationale behind this change. + +An additional rule validating a set of deprecated block fields is required by the block format changes introduced by this specification. + +### Removing block rewards + +Existing rewards for producing and sealing blocks are deprecated along with the PoW mechanism. The new PoS consensus becomes both responsible for sealing blocks and for issuing block rewards once this specification enters into effect. + +### Supplanting fork choice rule + +The fork choice rule of the PoW mechanism becomes completely irrelevant after the upgrade and is replaced with the corresponding rule of the new PoS consensus mechanism. + +### Remove of `POS_CONSENSUS_VALIDATED` + +In prior draft versions of this EIP, an additional POS event -- `POS_CONSENSUS_VALIDATED` -- was required as a validation condition for blocks. This event gave the signal to either fully incorporate or prune the block from the block tree. + +This event was removed for two reasons: +1. This event was an unnecessary optimization to allow for pruning of "bad" blocks from the block tree. This optimization was unnecessary because the PoS consensus would never send `POS_FORKCHOICE_UPDATED` for any such bad blocks or their descendants, and eventually any such blocks would be able to be pruned after a PoS finality event of an alternative branch in the block tree. +2. This event was dangerous in some scenarios because a block could be referenced by two *different* and conflicting PoS branches. Thus for the same block in some scenarios, both a `POS_CONSENSUS_VALIDATED == TRUE` and `POS_CONSENSUS_VALIDATED == FALSE` event could sent, entirely negating the ability to safely perform the optimization in (1). + +### EIP-2124 fork identifier + +The value of `FORK_NEXT` in EIP-2124 refers to the block number of the next fork a given node knows about and `0` otherwise. + +The number of `TRANSITION_BLOCK` cannot be known ahead of time given the dynamic nature of the transition trigger condition. As the block will not be known a priori, nodes can't use its number for `FORK_NEXT` and in light of this fact an explicitly set `FORK_NEXT_VALUE` is used instead. + +### Removing block gossip + +After the upgrade of the consensus mechanism only the beacon chain network will have enough information to validate a block. Thus, block gossip provided by the `eth` network protocol will become unsafe and is deprecated in favour of the block gossip existing in the beacon chain network. + +It is recommended for the client software to not propagate descendants of any terminal PoW block to reduce the load on processing the P2P component and stop operating in the environment with unknown security properties. + +### Restricting the length of `extraData` + +The `extraData` field is defined as a maximum of `32` bytes in the yellow paper. Thus mainnet and most PoW testnets cap the value at `32` bytes. `extraData` fields of greater length are used by clique testnets and other networks to carry special signature/consensus schemes. This EIP restricts the length of `extraData` to `32` bytes because any network that is transitioning from another consensus mechanism to a beacon chain PoS consensus mechanism no longer needs extended or unbounded `extraData`. + +## Backwards Compatibility + +This EIP introduces backward incompatibilities in block validity, block rewards and fork choice rule. + +The design of the consensus upgrade specified by this document does not introduce backward incompatibilities for existing applications and services built on top of Ethereum except for those that are described in the [EVM](#evm) section below or heavily depends on the PoW consensus in any other way. + + +### EVM + +Although this EIP does not introduce any explicit changes to the EVM there are a couple of places where it may affect the logic of existing smart contracts. + +#### DIFFICULTY + +`DIFFICULTY` operation will always return `0` after this EIP takes effect and deprecates the **`difficulty`** field by replacing it with `0` constant. + +*Note:* Altering the `DIFFICULTY` semantics to return randomness accumulated by the beacon chain is under consideration but will be introduced in a separate EIP. + +#### BLOCKHASH + +Pseudo-random numbers obtained as the output of `BLOCKHASH` operation become more insecure after this EIP takes effect and the PoW mechanism (which decreases the malleability of block hashes) gets supplanted by PoS. + + +## Test Cases + +* Block validity + * Beginning with `TRANSITION_BLOCK`, block is invalidated if any of the following is true: + * `ommersHash != Keccak256(RLP([]))` + * `difficulty != 0` + * `nonce != 0x0000000000000000` + * `len(extraData) > MAX_EXTRA_DATA_BYTES` + * Beginning with `TRANSITION_BLOCK`, block rewards aren't added to `beneficiary` account +* Client software adheres to PoS LMD-GHOST rule + * Head and finalized blocks are set according to the recent `POS_FORKCHOICE_UPDATED` event + * No fork choice state is updated unless `POS_FORKCHOICE_UPDATED` event is received +* Transition process + * Client software doesn't process any PoW block beyond a terminal PoW block + * Beginning with `TRANSITION_BLOCK`, client software applies new block validity rules + * Beginning with the first `POS_FORKCHOICE_UPDATED`, client software switches its fork choice rule to PoS LMD-GHOST + * `TRANSITION_BLOCK` must be a child of a terminal PoW block + * `NewBlockHashes (0x01)` and `NewBlock (0x07)` network messages are discarded after receiving the `FIRST_FINALIZED_BLOCK` + + +## Security Considerations + +### Beacon chain + +See Security Considerations section of [EIP-2982](./eip-2982.md#security-considerations). + +### Transition process + +The transition process used to take this specification into effect is a more sophisticated version of a hardfork -- the regular procedure of applying backwards incompatible changes in the Ethereum network. This process has multiple successive steps instead of the normal block-height point condition of simpler hardforks. + +The complexity of this upgrade process stems from this fork targeting the underlying consensus mechanism rather than the execution layer within the consensus mechanism. Although the design seeks simplicity where possible, safety and liveness considerations during this transition have been prioritized. + +#### Terminal total difficulty vs block number + +Using a pre-defined block number for the hardfork is unsafe in this context due to the PoS fork choice taking priority during the transition. + +An attacker may use a minority of hash power to build a malicious chain fork that would satisfy the block height requirement. Then the first PoS block may be maliciously proposed on top of the PoW block from this adversarial fork, becoming the head and subverting the security of the transition. + +To protect the network from this attack scenario, difficulty accumulated by the chain (total difficulty) is used to trigger the upgrade. + +#### Ability to jump between terminal PoW blocks + +There could be the case when a terminal PoW block is not observed by the majority of network participants due to (temporal) network partitioning. In such a case, this minority would switch their fork choice to the new rule provided by the PoS rooted on the minority terminal PoW block that they observed. + +The transition process allows the network to re-org between forks with different terminal PoW blocks as long as (a) these blocks satisfy the terminal PoW block conditions and (b) the `FIRST_FINALIZED_BLOCK` has not yet been received. This provides resilience against adverse network conditions during the transition process and prevents irreparable forks/partitions. + +#### Halt the importing of PoW blocks + +Suppose the part of the client software that is connected to the beacon chain network goes offline before the Ethereum network reaches the `TERMINAL_TOTAL_DIFFICULTY` and stays offline while the network meets this threshold. Such an event makes the client software unable to switch to PoS and allows it to keep following the PoW chain if this chain is being built beyond the terminal PoW block. Depending on how long the beacon chain part was offline, it could result in different adverse effects such as: +* The client has no post-state for the terminal PoW block (the state has been pruned) which prevents it from doing the re-org to the PoS chain and leaving syncing from scratch as the only option to recover. +* An application, a user or a service uses the data from the wrong fork (PoW chain that is kept being built) which can cause security issues on their side. + +Not importing PoW blocks that are beyond the terminal PoW block prevents these adverse effects on safety/re-orgs in the event of software or configuration failures *in favor* of a liveness failure. + +#### Terminal PoW block overriding + +There is a mechanism allowing for accelerating the consensus upgrade in emergency cases. +This EIP considers the following emergency case scenarios for the acceleration to come into effect: +* A drop of the network hashing rate which delays the upgrade significantly. +* Attacks on the PoW network before the upgrade. + +The first case can be safely accelerated by updating the following parameters: +* `TERMINAL_TOTAL_DIFFICULTY` -- reset to a value that is closer in time than the original one. +* `FORK_NEXT_VALUE` -- adjust accordingly. + +The second, more dire attack scenario requires a more invasive override: +* `TERMINAL_BLOCK_HASH` -- set to the hash of a certain block to become the terminal PoW block. +* `TERMINAL_BLOCK_NUMBER` -- set to the number of a block designated by `TERMINAL_BLOCK_HASH`. +* `TERMINAL_TOTAL_DIFFICULTY` -- set to the total difficulty value of a block designated by `TERMINAL_BLOCK_HASH`. +* `FORK_NEXT_VALUE` -- adjust accordingly. + +*Note*: Acceleration in the second case is considered for the most extreme of scenarios because it will result in a non-trivial liveness failure on Ethereum Mainnet. + +### Ancient blocks are no longer a requisite for a network security + +Keeping historical blocks starting from genesis is essential in the PoW network. A header of every block that belongs to a particular chain is required to justify the validity of this chain with respect to the PoW seal. + +Validating the entire history of the chain is not required by the new PoS mechanism. Instead, the sync process in the PoS network relies on weak subjectivity checkpoints, which are historical snapshots shared by peers on the network. This means historical blocks beyond weak subjectivity checkpoint are no longer a requisite for determining the canonical blockchain. + +Specification of weak subjectivity checkpoints can be found in the `ethereum/consensus-specs` repository. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3690.md b/EIPS/eip-3690.md new file mode 100644 index 00000000000000..40ebc7b8858fe4 --- /dev/null +++ b/EIPS/eip-3690.md @@ -0,0 +1,267 @@ +--- +eip: 3690 +title: EOF - JUMPDEST Table +description: A special EOF section for storing the list of JUMPDESTs, which simplifies execution time analysis. +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0) +discussions-to: https://ethereum-magicians.org/t/eip-3690-eof-jumpdest-table/6806 +status: Stagnant +type: Standards Track +category: Core +created: 2021-06-23 +requires: 3540, 3670 +--- + +## Abstract + +Introduce a section in the EOF format ([EIP-3540](./eip-3540.md)) for storing the list of `JUMPDEST`s, validate the correctness of this list at the time of contract creation, and remove the need for `JUMPDEST`-analysis at execution time. In EOF contracts, the `JUMPDEST` instruction is not needed anymore and becomes invalid. Legacy contracts are entirely unaffected by this change. + +## Motivation + +Currently existing contracts require no validation of correctness, but every time they are executed, a list must be built containing all the valid jump-destinations. This is an overhead which can be avoided, albeit the effect of the overhead depends on the client implementation. + +With the structure provided by EIP-3540 it is easy to store and transmit a table of valid jump-destinations instead of using designated `JUMPDEST` (0x5b) opcodes in the code. + +The goal of this change is that we trade less complexity (and processing time) at execution time for more complexity at contract creation time. Through benchmarks we have identified that the mandatory execution preparation time is the same as before for extreme cases (i.e. deliberate edge cases), while it is ~10x faster for the average case. + +Finally, this change puts an implicit bound on "initcode analysis" which is now limited to jumpdests section loading of max size of 0xffff. The legacy code remains vulnerable. + +## Specification + +This feature is introduced on the very same block [EIP-3540](./eip-3540.md) is enabled, therefore every EOF1-compatible bytecode MUST have a JUMPDEST-table if it uses jumps. + +*Remark:* We rely on the notation of *initcode*, *code* and *creation* as defined by [EIP-3540](./eip-3540.md), and extend validation rules of [EIP-3670](./eip-3670.md). + +### EOF container changes + +1. A new EOF section called `jumpdests` (`section_kind = 3`) is introduced. It contains a sequence of *n* unsigned integers *jumploci*. +2. The *jumploci* values are encoded with [unsigned LEB128](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128). + + | description | encoding | + |---------------------|-----------------| + | jumploc0 | unsigned LEB128 | + | jumploc1 | unsigned LEB128 | + | ... | | + | jumplocn | unsigned LEB128 | + +3. The jump destinations represent the set of valid code positions as arguments to jump instructions. They are delta-encoded therefore partial sum must be performed to retrieve the absolute offsets. + ```python + def jumpdest(n: int, jumpdests_table: list[int]) -> int: + return sum(jumpdests_table[:n+1]) + ``` + +### Validation rules + +> This section extends contract creation validation rules (as defined in EIP-3540). + +4. The `jumpdests` section MUST be present if and only if the `code` section contains `JUMP` or `JUMPI` opcodes. +5. If the `jumpdests` section is present it MUST directly precede the `code` section. In this case a valid EOF bytecode will have the form of `format, magic, version, [jumpdests_section_header], code_section_header, [data_section_header], 0, [jumpdests_section_contents], code_section_contents, [data_section_contents]`. +6. The LEB128 encoding of a `jumploc` must be valid: the encoding must be complete and not read out of `jumpdests` section. As an additional constraint, the shortest possible encoding must be used. +7. With an exception of the first entry, the value of `jumploc` MUST NOT be 0. +8. Every `jumploc` MUST point to a valid opcode. They MUST NOT point into PUSH-data or outside of the code section. +9. The `JUMPDEST` (0x5b) instruction becomes undefined (Note: According to rules of EIP-3670, deploying the code will fail if it contains `JUMPDEST`) + +### Execution + +10. When executing `JUMP` or `JUMPI` instructions, the jump destination MUST be in the `jumpdests` table. Otherwise, the execution aborts with *bad jump destination*. In case of `JUMPI`, the check is done only when the jump is to be taken (no change to the previous behaviour). + +## Rationale + +### Jumpdests section is bounded + +The length of the `jumpdests` section is bounded by the EOF maximum section size value 0xffff. Moreover, for deployed code this additionally limited by the max bytecode size 0x6000. Then any valid `jumpdests` section may not be more larger than 0x3000. + +### Delta encoding + +Delta-encoding is very efficient for this job. From a quick analysis of a small set of contracts `JUMPDEST` opcodes are relatively close to each other. In the delta-encoding the values almost never exceed 128. Combined with any form of variable-length quantity (VLQ) where values < 128 occupy one byte, encoding of single jumpdest takes ~1 byte. We also remove `JUMPDEST` opcodes from the code section therefore the total bytecode length remains the same if extreme examples are ignored. + +By extreme examples we mean contracts having a distance between two subsequent JUMPDESTs larger than 128. Then the LEB128 encoding of such distance requires more than one byte and the total bytecode size will increase by the additional number of bytes used. + +### LEB128 for offsets + +The LEB128 encoding is the most popular VLQ used in DWARF and WebAssembly. + +LEB128 allows encoding a fixed value with arbitrary number of bytes by having zero payloads for most significant bits of the value. To ensure there exists only single encoding of a given value, we additionally require the shortest possible LEB128 encoding to be used. This constraint is also required by WebAssembly. + +### Size-prefix for offsets + +This is another option for encoding inspired by UTF-8. The benefit is that the number of following bytes is encoded in the first byte (the top two bits), so the expected length is known upfront. + +A simple decoder is the following: +```python +def decode(input: bytes) -> int: + size_prefix = input[0] >> 6 + if size_prefix == 0: + return input[0] & 0x3f + elif size_prefix == 1: + return (input[0] & 0x3f) << 8 | input[1] + elif size_prefix == 2: + return (input[0] & 0x3f) << 16 | (input[1] << 8) | input[2] + # Do not support case 3 + assert(False) +``` + +### Empty table + +In case code does not use jumps, an empty JUMPDEST table is represented by omitting `jumpdests` section as opposed to a section that is always present, but allowed to be empty. This is consistent with the requirement of EIP-3540 for section size to be non-zero. Additionally, omitting the section saves 3 bytes of code storage. + +### Why jumpdests before code? + +The contents of `jumpdests` section are always needed to start EVM execution. For chunked and/or merkleized bytecode it is more efficient to have `jumpdests` just after the EOF header so they can share the same first chunk(s). + +### Code chunking / merkleization + +In code chunking the contract code is split into (fixed size) chunks. Due to the requirement of jumpdest-analysis, it must be known where the first instruction starts in a given chunk, in case the split happened within a PUSH-data. This is commonly accomplished with reserving the first byte of the chunk as the "first instruction offset" (FIO) field. + +With this EIP, code chunking does not need to have such a field. However, the jumpdest table must be provided instead (for all the chunks up until the last jump location used during execution). + +### Benchmarks / performance analysis + +We compared the performance of `jumpdests` section loading to JUMPDEST analysis in evmone/Baseline interpreter. In both cases a bitset of valid jumpdest positions is built. + +We used the worst case for `jumpdests` section as the benchmark baseline. This is the case where every position in the code section is valid jumpdest. I.e. the bytecode has as many jumpdests as possible making the jumpdests section as large as possible. The encoded representation is `0x00, 0x01, 0x01, 0x01, ...`. + +This also happen to be the worst case for the JUMPDEST analysis. + +Further, we picked 5 popular contracts from the Ethereum mainnet. + +| case | size | num JUMPDESTs | JUMPDEST analysis (cycles/byte) | jumpdests load (cycles/byte) | performance change | +| ----------------- | ----- | ----- | ---- | ---- | ------- | +| worst | 65535 | 65535 | 9.11 | 9.36 | 2.75% | +| RoninBridge | 1760 | 71 | 3.57 | | -89.41% | +| UniswapV2ERC20 | 2319 | 61 | 2.10 | | -88.28% | +| DepositContract | 6358 | 123 | 1.86 | | -90.24% | +| TetherToken | 11075 | 236 | 1.91 | | -89.58% | +| UniswapV2Router02 | 21943 | 468 | 2.26 | | -91.17% | + +For the worst case the performance difference between JUMPDEST analysis and jumpdests section loading is very small. The performance very slow compared to memory copy (0.15 cycles/byte). + +However, the maximum length for the worst cases is different. For JUMPDEST analysis this is 24576 (0x6000) for deployed contracts and only limited by EVM memory cost in case of _initcode_ (can be over 1MB). For jumpdests sections, the limit is 12288 for deployed contracts (the deployed bytecode length limit must be split equally between jumpdests and code sections). For _initcode_ case, the limit is 65535 because this is the maximum section size allowed by EOF. + +For "popular" contracts the gained efficiency is ~10x because the jumpdests section is relatively small compared to the code section and therefore there is much less bytes to loop over than in JUMPDEST analysis. + +## Reference Implementation + +We extend the `validate_code()` function of [EIP-3670](./eip-3670.md): +```python +# The same table as in EIP-3670 +valid_opcodes = ... + +# Remove JUMPDEST from the list of valid opcodes +valid_opcodes.remove(0x5b) + +# This helper decodes a single unsigned LEB128 encoded value +# This will abort on truncated (short) input +def leb128u_decode(input: bytes) -> (int, int): + ret = 0 + shift = 0 + consumed_bytes = 0 + while True: + # Check for truncated input + assert(consumed_bytes < len(input)) + # Only allow up to 4-byte long leb128 encodings + assert(consumed_bytes <= 3) + input_byte = input[consumed_bytes] + consumed_bytes += 1 + ret |= (input_byte & 0x7f) << shift + if (input_byte & 0x80) == 0: + # Do not allow additional leading zero bits. + assert(input_byte != 0 || consumed_bytes == 0) + break + shift += 7 + return (ret, consumed_bytes) + +# This helper parses the jumpdest table into a list of relative offsets +# This will abort on truncated (short) input +def parse_table(input: bytes) -> list[int]: + jumpdests = [] + pos = 0 + while pos < len(input): + value, consumed_bytes = leb128u_decode(input[pos:]) + jumpdests.append(value) + pos += consumed_bytes + return jumpdests + +# This helper translates the delta offsets into absolute ones +# This will abort on invalid 0-value entries +def process_jumpdests(delta: list[int]) -> list[int]: + jumpdests = [] + partial_sum = 0 + first = True + for d in delta: + if first: + first = False + else: + assert(d != 0) + partial_sum += d + jumpdests.append(partial_sum) + return jumpdests + +# Fails with assertion on invalid code +# Expects list of absolute jumpdest offsets +def validate_code(code: bytes, jumpdests: list[int]): + pos = 0 + while pos < len(code): + # Ensure the opcode is valid + opcode = code[pos] + pos += 1 + assert(opcode in valid_opcodes) + + # Remove touched offset + try: + jumpdests.remove(pos) + except ValueError: + pass + + # Skip pushdata + if opcode >= 0x60 and opcode <= 0x7f: + pos += opcode - 0x60 + 1 + + # Ensure last PUSH doesn't go over code end + assert(pos == len(code)) + + # The table is invalid if there are untouched locations + assert(len(jumpdests) == 0) +``` + +## Test Cases + +#### Valid bytecodes + +- No jumpdests +- Every byte is a jumpdest +- Distant jumpdests (0x7f and 0x3f01 bytes apart) +- Max number of jumpdests + - 1-byte offset encoding: initcode of max size (64K) with jumpdest at each byte - table contains 65536 1-byte offsets, first one is 0x00, all others equal 0x01 + - 2-byte offset encoding: inicode of max size with jumpdests 0x80 (128) bytes apart - table contains 512 offsets, first one is 0x7f (127), all others equal 0x8001 + - 3-byte offset encoding: inicode of max size with jumpdests 0x4000 (16384) bytes apart - table contains 4 offsets: 0xFF7F (16383), 0x808001, 0x808001, 0x808001 + +#### Invalid bytecodes + +- Empty jumpdest section +- Multiple jumpdest sections +- jumpdest section after the code section +- jumpdest section after the data section +- Final jumploc in the table is truncated (not a valid LEB128) +- LEB128 encoding with extra 0s (non-minimal encoding) +- Jumpdest location pointing to PUSH data +- Jumpdest location out of code section bounds + - pointing into data section + - pointing into jumpdest section + - pointing outside container bounds +- Duplicate jumpdest locations (0 deltas in table other than 1st offset) +- Code containing `JUMP` but no jumpdest table +- Code containing `JUMPI` but no jumpdest table +- Code containing jumpdest table but not `JUMP`/`JUMPI` +- Code containing `JUMPDEST` + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The requirement of a JUMPDEST table does not cover legacy bytecode. + +## Security Considerations + +The authors are not aware of any security or DoS risks posed by this change. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3709.md b/EIPS/eip-3709.md new file mode 100644 index 00000000000000..3d8e771353d756 --- /dev/null +++ b/EIPS/eip-3709.md @@ -0,0 +1,45 @@ +--- +eip: 3709 +title: Remove Support for Type 1 Transactions +author: Gregory Markou (@GregTheGreek) +discussions-to: https://ethereum-magicians.org/t/eip-3709-deprecate-type-1-transactions/6810 +status: Stagnant +type: Standards Track +category: Interface +created: 2021-08-07 +requires: 1559 +--- + +## Simple Summary + +Deprecates usage of [EIP-2718](./eip-2718.md) `TransactionType` 1 in wallets and providers, upgrading all type 1 transactions to a type 2 transaction. + +## Abstract + +Since both `TransactionType` 1 and 2 contain `access_list`, we propose the removal of offering `TransactionType` 1 from wallets and providers, instead the transaction will be converted to `TransactionType` 2 to make use of the new gas properties introduced by [EIP-1559](./eip-1559.md). + +## Motivation + +[EIP-2930](./eip-2930.md) was introduced as the first `TransactionType`, type 1, with the intention of adding `access_list` to the `TransactionPayload`. [EIP-1559](./eip-1559.md) introduced the second `TransactionType` 2, which is represented as `rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])`. The intention behind EIP-1559 was to enhance the user experience surrounding gas fees, and as we move forward we expect that the majority of the network will begin to using `TransactionType` 2 instead of the legacy style transactions. `TransactionType` 1 is a legacy transaction with the addition of `access_list` meaning that users will not benefit from enhancements made by EIP-1559. `TransactionType` 2 contains `access_list`, thus there is no reason to further support `TransactionType` 1 if the end goal is to push users towards using `TransactionType` 2 anyway. + + +## Specification + +For wallets and providers, if a user submits a transaction for signing with where `TransactionType == 0x1`, the developer should upgrade the transaction to meet the criteria of transaction of type 2. + +The following fields need to be changed, or amended: +- `access_list`: Nothing changes and it should remain in the transaction. +- `type`: Should change from `0x1` to `0x2`. +- `gas_price`: Should be removed in favour of `max_fee_per_gas` & `max_priority_fee_per_gas` (see [EIP-1559](./eip-1559.md) for proper usage). + +## Rationale + +Improve the user experience for submitting transactions, and move away from legacy style transactions. + +## Security Considerations + +There are no known security considerations at this time. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3722.md b/EIPS/eip-3722.md new file mode 100644 index 00000000000000..1b17795bc5269e --- /dev/null +++ b/EIPS/eip-3722.md @@ -0,0 +1,197 @@ +--- +eip: 3722 +title: Poster +description: A ridiculously simple general purpose social media smart contract. +author: Auryn Macmillan (@auryn-macmillan) +discussions-to: https://ethereum-magicians.org/t/eip-poster-a-ridiculously-simple-general-purpose-social-media-smart-contract/6751 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-07-31 +--- + +# Poster + +## Abstract +A ridiculously simple general purpose social media smart contract. +It takes two strings (`content` and `tag`) as parameters and emits those strings, along with msg.sender, as an event. That's it. +The EIP also includes a proposed standard json format for a Twitter-like application, where each `post()` call can include multiple posts and/or operations. The assumption being that application state will be constructed off-chain via some indexer. + +## Motivation +Poster is intended to be used as a base layer for decentralized social media. It can be deployed to the same address (via the singleton factory) on just about any EVM compatible network. Any Ethereum account can make posts to the deployment of Poster on its local network. + +## Specification + +### Contract + +```solidity +contract Poster { + + event NewPost(address indexed user, string content, string indexed tag); + + function post(string calldata content, string calldata tag) public { + emit NewPost(msg.sender, content, tag); + } +} +``` + +### ABI +```json +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "content", + "type": "string" + }, + { + "indexed": true, + "internalType": "string", + "name": "tag", + "type": "string" + } + ], + "name": "NewPost", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "content", + "type": "string" + }, + { + "internalType": "string", + "name": "tag", + "type": "string" + } + ], + "name": "post", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] +``` + +### Standard json format for Twitter-like posts + +```json +{ + "content": [ + { + "type": "microblog", + "text": "this is the first post in a thread" + }, + { + "type": "microblog", + "text": "this is the second post in a thread", + "replyTo": "this[0]" + }, + { + "type": "microblog", + "text": "this is a reply to some other post", + "replyTo": "some_post_id" + }, + { + "type": "microblog", + "text": "this is a post with an image", + "image": "ipfs://ipfs_hash" + }, + { + "type": "microblog", + "text": "this post replaces a previously posted post", + "edit": "some_post_id" + }, + { + "type": "delete", + "target": "some_post_id" + }, + { + "type": "like", + "target": "some_post_id" + }, + { + "type": "repost", + "target": "some_post_id" + }, + { + "type": "follow", + "target": "some_account" + }, + { + "type": "unfollow", + "target": "some_account" + }, + { + "type": "block", + "target": "some_account" + }, + { + "type": "report", + "target": "some_account or some_post_id" + }, + { + "type": "permissions", + "account": "", + "permissions": { + "post": true, + "delete": true, + "like": true, + "follow": true, + "block": true, + "report": true, + "permissions": true + } + }, + { + "type": "microblog", + "text": "This is a post from an account with permissions to post on behalf of another account.", + "from": "" + } + ] +} + +``` + +## Rationale +There was some discussion around whether or not an post ID should also be emitted, whether the content should be a string or bytes, and whether or not anything at all should actually be emitted. + +We decided not to emit an ID, since it meant adding state or complexity to the contract and there is a fairly common pattern of assigning IDs on the indexer layer based on transactionHash + logIndex. + +We decided to emit a string, rather than bytes, simply because that would make content human readable on many existing interfaces, like Etherscan for example. This did, unfortunately, eliminate some of the benefit that we might have gotten from a more compact encoding scheme like CBOR, rather than JSON. But this also would not have satisfied the human readable criteria. + +While there would have been some gas savings if we decided against emitting anything at all, it would have redically increased the node requirements to index posts. As such, we decided it was worth the extra gas to actually emit the content. + +## Reference Implementation + +Poster has been deployed at `0x000000000000cd17345801aa8147b8D3950260FF` on multiple networks using the [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470). If it is not yet deployed on your chosen network, you can use the Singleton Factory to deploy an instance of Poster at the same address on just about any EVM compatible network using these parameters: + +> **initCode:** `0x608060405234801561001057600080fd5b506101f6806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630ae1b13d14610030575b600080fd5b61004361003e3660046100fa565b610045565b005b8181604051610055929190610163565b60405180910390203373ffffffffffffffffffffffffffffffffffffffff167f6c7f3182d7e4cb876251f9ae1489975fdbbf15d9f35d393f2ac9b1ff57cec69f86866040516100a5929190610173565b60405180910390a350505050565b60008083601f8401126100c4578182fd5b50813567ffffffffffffffff8111156100db578182fd5b6020830191508360208285010111156100f357600080fd5b9250929050565b6000806000806040858703121561010f578384fd5b843567ffffffffffffffff80821115610126578586fd5b610132888389016100b3565b9096509450602087013591508082111561014a578384fd5b50610157878288016100b3565b95989497509550505050565b6000828483379101908152919050565b60006020825282602083015282846040840137818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010191905056fea2646970667358221220ee0377bd266748c5dbaf0a3f15ebd97be153932f2d14d460d9dd4271fee541b564736f6c63430008000033` +> +> **salt:** `0x9245db59943806d06245bc7847b3efb2c899d11b621a0f01bb02fd730e33aed2` + +When verifying on the source code on a block explorer, make sure to set the optimizer to `yes` and the runs to `10000000`. + +The source code is available in the [Poster contract repo](https://github.com/ETHPoster/contract/blob/master/contracts/Poster.sol). + + +## Security Considerations +Given the ridiculously simple implementation of Poster, there does not appear to be any real security concerns at the contract level. + +At the application level, clients should confirm that posts including a `"from"` field that differs from `msg.sender` have been authorized by the `"from"` address via a `"permissions"` post, otherwise they should be considerred invalid or a post from `msg.sender`. + +Clients should also be sure to sanitize post data. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3754.md b/EIPS/eip-3754.md new file mode 100644 index 00000000000000..bc4dcdcf2a1267 --- /dev/null +++ b/EIPS/eip-3754.md @@ -0,0 +1,74 @@ +--- +eip: 3754 +title: A Vanilla Non-Fungible Token Standard +description: NFTs for representing abstract ownership +author: Simon Tian (@simontianx) +discussions-to: https://github.com/ethereum/EIPs/issues/3753 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-08-21 +--- + +## Abstract +In this standard, a non-fungible token stands as atomic existence and encourages +layers of abstraction built on top of it. Ideal for representing concepts like +rights, a form of abstract ownership. Such right can take the form of NFT options, +oracle membership, virtual coupons, etc., and can then be made liquid because of +this tokenization. + +## Motivation +Non-fungible tokens are popularized by the [ERC-721](./eip-721.md) NFT standard +for representing "ownership over digital or physical assets". Over the course of +development, reputable NFT projects are about crypto-assets, digital collectibles, +etc. The proposed standard aims to single out a special type of NFTs that are +ideal for representing abstract ownership such as rights. Examples include the +right of making a function call to a smart contract, an NFT option that gives +the owner the right, but not obligation, to purchase an ERC-721 NFT, and the prepaid +membership (time-dependent right) of accessing to data feeds provided by oracles +without having to pay the required token fees. An on-chain subscription business +model can then be made available by this standard. The conceptual clarity of an +NFT is hence improved by this standard. + +## Specification +``` +interface IERC3754 { + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function balanceOf(address owner) external view returns (uint256); + function ownerOf(uint256 tokenId) external view returns (address); + function approve(address to, uint256 tokenId) external; + function getApproved(uint256 tokenId) external view returns (address); + function setApprovalForAll(address operator, bool approved) external; + function isApprovedForAll(address owner, address operator) external view returns (bool); + function transferFrom(address from, address to, uint256 tokenId) external; + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) external; +} +``` + +## Rationale +The NFTs defined in the [ERC-721](./eip-721.md) standard are already largely +accepted and known as representing ownership of digital assets, and the NFTs by +this standard aim to be accepted and known as representing abstract ownership. +This is achieved by allowing and encouraging layers of abstract utilities built +on top of them. Ownership of such NFTs is equivalent with having the rights to +perform functions assigned to such tokens. Transfer of such rights is also made +easier because of this tokenization. To further distinguish this standard +from [ERC-721](./eip-721.md), data fields and functions related to `URI` are +excluded. + +## Backwards Compatibility +There is no further backwards compatibility required. + +## Reference Implementation +https://github.com/simontianx/ERC3754 + +## Security Considerations +The security is enhanced from ERC721, given tokens are minted without having to +provide `URI`s. Errors in dealing with `URI`s can be avoided. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3756.md b/EIPS/eip-3756.md new file mode 100644 index 00000000000000..59fe6a4cd3522d --- /dev/null +++ b/EIPS/eip-3756.md @@ -0,0 +1,55 @@ +--- +eip: 3756 +title: Gas Limit Cap +description: Set an in-protocol cap for the gas limit +author: lightclient (@lightclient) +discussions-to: https://ethereum-magicians.org/t/eip-3756-gas-limit-cap/6921 +status: Stagnant +type: Standards Track +category: Core +created: 2021-08-21 +--- + +## Abstract + +Set an in-protocol cap for the gas limit of 30,000,000. + +## Motivation + +A high gas limit increases pressure on the network. In the benign case, it +increases the size of the state and history faster than we can sustain. In the +malicious case, it amplifies the devastation of certain denial-of-service +attacks. + +## Specification + +As of the fork block `N`, consider blocks with a `gas_limit` greater than +`30,000,000` invalid. + +## Rationale + +### Why Cap the Gas Limit + +The gas limit is currently under the control of block proposers. They have the +ability to increase the gas limit to whatever value they desire. This allows +them to bypass the EIP and All Core Devs processes in protocol decisions that +may negatively affect the security and/or decentralization of the network. + +### No Fixed Gas Limit + +A valuable property of proposers choosing the gas limit is they can scale it +down quickly if the network becomes unstable or is undergoing certain types of +attacks. For this reason, we maintain their ability to lower the gas limit +_below_ 30,000,000. + +## Backwards Compatibility +No backwards compatibility issues. + +## Test Cases +TBD + +## Security Considerations +No security considerations. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3770.md b/EIPS/eip-3770.md new file mode 100644 index 00000000000000..07f48f9f72dcae --- /dev/null +++ b/EIPS/eip-3770.md @@ -0,0 +1,65 @@ +--- +eip: 3770 +title: Chain-specific addresses +description: Prepending chain-specific addresses with a human-readable chain identifier +author: Lukas Schor (@lukasschor), Richard Meissner (@rmeissner), Pedro Gomes (@pedrouid), ligi +discussions-to: https://ethereum-magicians.org/t/chain-specific-addresses/6449 +status: Review +type: Standards Track +category: ERC +created: 2021-08-26 +--- + +## Abstract + +[ERC-3770](./eip-3770.md) introduces a new address standard to be adapted by wallets and dApps to display chain-specific addresses by using a human-reacable prefix. + +## Motivation + +The need for this proposal emerges from the increasing adoption of non-Ethereum Mainnet chains that use the Ethereum Virtual Machine (EVM). In this context, addresses become ambiguous, as the same address may refer to an EOA on chain X or a smart contract on chain Y. This will eventually lead to Ethereum users losing funds due to human error. For example, users sending funds to a smart contract wallet address which was not deployed on a particular chain. + +Therefore we should prefix addresses with a unique identifier that signals to Dapps and wallets on what chain the target account is. In theory, this prefix could be a [EIP-155](./eip-155.md) chainID. However, these chain IDs are not meant to be displayed to users in dApps or wallets, and they were optimized for developer interoperability, rather than human readability. + +## Specification + +This proposal extends addresses with a human-readable blockchain short name. + +### Syntax + +A chain-specific address is prefixed with a chain shortName, separated with a colon sign (:). + +Chain-specific address = "`shortName`" "`:`" "`address`" + +- `shortName` = STRING + +- `address` = STRING + +### Semantics + +``` + +`shortName` is mandatory and MUST be a valid chain short name from https://github.com/ethereum-lists/chains + +`address` is mandatory and MUST be a [ERC-55](./eip-55.md) compatible hexadecimal address + +``` + +### Examples + +![Chain-specific addresses](../assets/eip-3770/examples.png "Examples of chain-specific addresses") + +## Rationale + +To solve the initial problem of user-facing addresses being ambiguous in a multichain context, we need to map EIP-155 chain IDs with a user-facing format of displaying chain identifiers. + +## Backwards Compatibility + +Ethereum addresses without the chain specifier will continue to require additional context to understand which chain the address refers to. + +## Security Considerations + +The Ethereum List curators must consider how similar looking chain short names can be used to confuse users. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3772.md b/EIPS/eip-3772.md new file mode 100644 index 00000000000000..566964c6a48291 --- /dev/null +++ b/EIPS/eip-3772.md @@ -0,0 +1,278 @@ +--- +eip: 3772 +title: Compressed Integers +description: Using lossy compression on uint256 to improve gas costs, ideally by a factor up to 4x. +author: Soham Zemse (@zemse) +discussions-to: https://github.com/ethereum/EIPs/issues/3772 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-08-27 +--- + +## Abstract + +This document specifies compression of `uint256` to smaller data structures like `uint64`, `uint96`, `uint128`, for optimizing costs for storage. The smaller data structure (represented as `cintx`) is divided into two parts, in the first one we store `significant` bits and in the other number of left `shift`s needed on the significant bits to decompress. This document also includes two specifications for decompression due to the nature of compression being lossy, i.e. it causes underflow. + +## Motivation + +- Storage is costly, each storage slot costs almost $0.8 to initialize and $0.2 to update (20 gwei, 2000 ETHUSD). +- Usually, we store money amounts in `uint256` which takes up one entire slot. +- If it's DAI value, the range we work with most is 0.001 DAI to 1T DAI (or 1012). If it's ETH value, the range we work with most is 0.000001 ETH to 1B ETH. Similarly, any token of any scale has a reasonable range of 1015 amounts that we care/work with. +- However, uint256 type allows us to represent $10-18 to $1058, and most of it is a waste. In technical terms, we have the probability distribution for values larger than $1015 and smaller than $10-3 as negligible (i.e. P[val > 1015] ≈ 0 and P[val < 10-3] ≈ 0). +- Number of bits required to represent 1015 values = log2(1015) = 50 bits. So just 50 bits (instead of 256) are reasonably enough to represent a practical range of money, causing a very negligible difference. + +## Specification + +In this specification, the structure for representing a compressed value is represented using `cintx`, where x is the number of bits taken by the entire compressed value. On the implementation level, an `uintx` can be used for storing a `cintx` value. + +### Compression + +#### uint256 into cint64 (up to cint120) + +The rightmost, or least significant, 8 bits in `cintx` are reserved for storing the shift and the rest available bits are used to store the significant bits starting from the first `1` bit in `uintx`. + +```solidity +struct cint64 { uint56 significant; uint8 shift; } + +// ... + +struct cint120 { uint112 significant; uint8 shift; } +``` + +#### uint256 into cint128 (up to cint248) + +The rightmost, or least significant, 7 bits in `cintx` are reserved for storing the shift and the rest available bits are used to store the significant bits starting from the first one bit in `uintx`. + +> In the following code example, `uint7` is used just for representation purposes only, but it should be noted that uints in Solidity are in multiples of 8. + +```solidity +struct cint128 { uint121 significant; uint7 shift; } + +// ... + +struct cint248 { uint241 significant; uint7 shift; } +``` + +Examples: + +``` +Example: +uint256 value: 2**100, binary repr: 1000000...(hundred zeros) +cint64 { significant: 10000000...(55 zeros), shift: 00101101 (45 in decimal)} + +Example: +uint256 value: 2**100-1, binary repr: 111111...(hundred ones) +cint64 { significant: 11111111...(56 ones), shift: 00101100 (44 in decimal) } +``` + +### Decompression + +Two decompression methods are defined: a normal `decompress` and a `decompressRoundingUp`. + +```solidity +library CInt64 { + // packs the uint256 amount into a cint64 + function compress(uint256) internal returns (cint64) {} + + // unpacks cint64, by shifting the significant bits left by shift + function decompress(cint64) internal returns (uint256) {} + + // unpacks cint64, by shifting the significant bits left by shift + // and having 1s in the shift bits + function decompressRoundingUp(cint64) internal returns (uint256) {} +} +``` + +#### Normal Decompression + +The `significant` bits in the `cintx` are moved to a `uint256` space and shifted left by `shift`. + +> NOTE: In the following example, cint16 is used for visual demonstration purposes. But it should be noted that it is definitely not safe for storing money amounts because its significant bits capacity is 8, while at least 50 bits are required for storing money amounts. + +``` +Example: +cint16{significant:11010111, shift:00000011} +decompressed uint256: 11010111000 // shifted left by 3 + +Example: +cint64 { significant: 11111111...(56 ones), shift: 00101100 (44 in decimal) } +decompressed uint256: 1111...(56 ones)0000...(44 zeros) +``` + +#### Decompression along with rounding up + +The `significant` bits in the `cintx` are moved to a `uint256` space and shifted left by `shift` and the least significant `shift` bits are `1`s. + +``` +Example: +cint16{significant:11011110, shift:00000011} +decompressed rounded up value: 11011110111 // shifted left by 3 and 1s instead of 0s + +Example: +cint64 { significant: 11111111...(56 ones), shift: 00101100 (44 in decimal) } +decompressed uint256: 1111...(100 ones) +``` + +This specification is to be used by a new smart contract for managing its internal state so that any state mutating calls to it can be cheaper. These compressed values on a smart contract's state are something that should not be exposed to the external world (other smart contracts or clients). A smart contract should expose a decompressed value if needed. + +## Rationale + +- The `significant` bits are stored in the most significant part of `cintx` while `shift` bits in the least significant part, to help prevent obvious dev mistakes. For e.g. a number smaller than 256-1 its compressed `cint64` value would be itself if the arrangement were to be opposite than specified. If a developer forgets to uncompress a value before using it, this case would still pass if the compressed value is the same as decompressed value. +- It should be noted that using `cint64` doesn't render gas savings automatically. The solidity compiler needs to pack more data into the same storage slot. +- Also the packing and unpacking adds some small cost too. +- Though this design can also be seen as a binary floating point representation, however using floating point numbers on EVM is not in the scope of this ERC. The primary goal of floating point numbers is to be able to represent a wider range in an available number of bits, while the goal of compression in this ERC is to keep as much precision as possible. Hence, it specifies for the use of minimum exponent/shift bits (i.e 8 up to `uint120` and 7 up to `uint248`). + +```solidity +// uses 3 slots +struct UserData1 { + uint64 amountCompressed; + bytes32 hash; + address beneficiary; +} + +// uses 2 slots +struct UserData2 { + uint64 amountCompressed; + address beneficiary; + bytes32 hash; +} +``` + +## Backwards Compatibility + +There are no known backward-incompatible issues. + +## Reference Implementation + +On the implementation level `uint64` may be used directly, or with custom types introduced in 0.8.9. + +```soldity +function compress(uint256 full) public pure returns (uint64 cint) { + uint8 bits = mostSignificantBitPosition(full); + if (bits <= 55) { + cint = uint64(full) << 8; + } else { + bits -= 55; + cint = (uint64(full >> bits) << 8) + bits; + } +} + +function decompress(uint64 cint) public pure returns (uint256 full) { + uint8 bits = uint8(cint % (1 << 9)); + full = uint256(cint >> 8) << bits; +} + +function decompressRoundingUp(uint64 cint) public pure returns (uint256 full) { + uint8 bits = uint8(cint % (1 << 9)); + full = uint256(cint >> 8) << bits + ((1 << bits) - 1); +} +``` + +The above gist has `library CInt64` that contains demonstrative logic for compression, decompression, and arithmetic for `cint64`. The gist also has an example contract that uses the library for demonstration purposes. + +The CInt64 format is intended only for storage, while dev should convert it to uint256 form using suitable logic (decompress or decompressRoundingUp) to perform any arithmetic on it. + +## Security Considerations + +The following security considerations are discussed: + +1. Effects due to lossy compression + - Error estimation for `cint64` + - Handling the error +2. Losing precision due to incorrect use of `cintx` +3. Compressing something other than money `uint256`s. + +### 1. Effects due to lossy compression + +When a value is compressed, it causes underflow, i.e. some less significant bits are sacrificed. This results in a `cintx` value whose decompressed value is less than or equal to the actual `uint256` value. + +```solidity +uint a = 2**100 - 1; // 100 # of 1s in binary format +uint c = a.compress().decompress(); + +a > c; // true +a - (2**(100 - 56) - 1) == c; // true + +// Visual example: +// before: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +// after: 1111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000 +``` + +#### Error estimation for cint64 + +Let's consider we have a `value` of the order 2m (less than 2m and greater than or equal to 2m-1). + +For all values such that 2m - 1 - (2m-56 - 1) <= `value` <= 2m - 1, the compressed value `cvalue` is 2m - 1 - (2m-56 - 1). + +The maximum error is 2m-56 - 1, approximating it to decimal: 10n-17 (log2(56) is 17). Here `n` is number of decimal digits + 1. + +For e.g. compressing a value of the order $1,000,000,000,000 (or 1T or 1012) to `cint64`, the maximum error turns out to be 1012+1-17 = $10-4 = $0.0001. This means the precision after 4 decimal places is lost, or we can say that the uncompressed value is at maximum $0.0001 smaller. Similarly, if someone is storing $1,000,000 into `cint64`, the uncompressed value would be at maximum $0.0000000001 smaller. In comparison, the storage costs are almost $0.8 to initialize and $0.2 to update (20 gwei, 2000 ETHUSD). + +#### Handling the error + +Note that compression makes the value slightly smaller (underflow). But we also have another operation that also does that. In integer math, the division is a lossy operation (causing underflow). For instance, + +```solidity +10000001 / 2 == 5000000 // true +``` + +The result of the division operation is not always exact, but it's smaller than the actual value, in some cases as in the above example. Though, most engineers try to reduce this effect by doing all the divisions at the end. + +``` +1001 / 2 * 301 == 150500 // true +1001 * 301 / 2 == 150650 // true +``` + +The division operation has been in use in the wild, and plenty of lossy integer divisions have taken place, causing DeFi users to get very very slightly less withdrawal amounts, which they don't even notice. If been careful, then the risk is very negligible. Compression is similar, in the sense that it is also a division by 2shift. If been careful with this too, the effects are minimized. + +In general, one should follow the rule: + +1. When a smart contract has to transfer a compressed amount to a user, they should use a rounded down value (by using `amount.decompress()`). +2. When a smart contract has to transferFrom a compressed amount from a user to itself, i.e charging for some bill, they should use a rounded up value (by using `amount.decompressUp()`). + +The above ensures that smart contract does not loose money due to the compression, it is the user who receives less funds or pays more funds. The extent of rounding is something that is negligible enough for the user. Also just to mention, this rounding up and down pattern is observed in many projects including UniswapV3. + +### 2. Losing precision due to incorrect use of `cintx` + +This is an example where dev errors while using compression can be a problem. + +Usual user amounts mostly have an max entropy of 50, i.e. 1015 (or 250) values in use, that is the reason why we find uint56 enough for storing significant bits. However, let's see an example: + +```solidity +uint64 sharesC = // reading compressed value from storage; +uint64 price = // CALL; +uint64 amountC = sharesC.cmuldiv(price, PRICE_UNIT); +user.transfer(amountC.uncompress()); +``` + +The above code results in a serious precision loss. `sharesC` has an entropy of 50, as well as `priceC` also has an entropy of 50. When we multiply them, we get a value that contains entropies of both, and hence, an entropy of 100. After multiplication is done, `cmul` compresses the value, which drops the entropy of `amountC` to 56 (as we have uint56 there to store significant bits). + +To prevent entropy/precision from dropping, we get out from compression. + +```solidity +uint64 sharesC = shares.compress(); +uint64 priceC = price.compress(); +uint256 amount = sharesC.uncompress() * price / PRICE_UNIT; +user.transfer(amount); +``` + +Compression is only useful when writing to storage while doing arithmetic with them should be done very carefully. + +### 3. Compressing something other than money `uint256`s. + +Compressed Integers is intended to only compress money amount. Technically there are about 1077 values that a `uint256` can store but most of those values have a flat distribution i.e. the probability is 0 or extremely negligible. (What is a probability that a user would be depositing 1000T DAI or 1T ETH to a contract? In normal circumstances it doesn't happen, unless someone has full access to the mint function). Only the amounts that people work with have a non-zero distribution ($0.001 DAI to $1T or 1015 to 1030 in uint256). 50 bits are enough to represent this information, just to round it we use 56 bits for precision. + +Using the same method for compressing something else which have a completely different probability distribution will likely result in a problem. It's best to just not compress if you're not sure about the distribution of values your `uint256` is going to take. And also, for things you think you are sure about using compression for, it's better to give more thought if compression can result in edge cases (e.g. in previous multiplication example). + +### 4. Compressing Stable vs Volatile money amounts + +Since we have a dynamic `uint8 shift` value that can move around. So even if you wanted to represent 1 Million SHIBA INU tokens or 0.0002 WBTC (both $10 as of this writing), cint64 will pick its top 56 significant bits which will take care of the value representation. + +It can be a problem for volatile tokens if the coin is extremely volatile wrt user's native currency. Imagine a very unlikely case where a coin goes 256x up (price went up by 1016 lol). In such cases `uint56` might not be enough as even its least significant bit is very valuable. If such insanely volatile tokens are to be stored, you should store more significant bits, i.e. using `cint96` or `cint128`. + +`cint64` has 56 bits for storing significant, when only 50 were required. Hence there are 6 extra bits, which means that it is fine if the $ value of the cryptocurrency stored in cint64 increases by 26 or 64x. If the value goes down it's not a problem. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3779.md b/EIPS/eip-3779.md new file mode 100644 index 00000000000000..f8a4e1d754cdc5 --- /dev/null +++ b/EIPS/eip-3779.md @@ -0,0 +1,413 @@ +--- +eip: 3779 +title: Safer Control Flow for the EVM +description: Ensure an essential level of safety for EVM code. +author: Greg Colvin (@gcolvin), Greg Colvin , Brooklyn Zelenka (@expede) +discussions-to: https://ethereum-magicians.org/t/eip-3779-safe-control-flow-for-the-evm/6975 +status: Withdrawn +type: Standards Track +category: Core +created: 2021-08-30 +withdrawal-reason: material moved to EIP-2315 +--- + +## Abstract + +We define a safe EVM contract as one that cannot encounter an exceptional halting state. In general, we cannot prove safety for Turing-complete programs. But we can prove a useful subset. + +This EIP specifies validity rules to ensure that: +> Valid contracts will not halt with an exception unless they either +> * throw `out of gas` or +> * recursively overflow stack. + +This EIP does not introduce any new opcodes. Rather, it restricts the use of existing and proposed control-flow instructions. The restrictions must be validated at contract initialization time – not at runtime – by the provided algorithm or its equivalent. This algorithm must take time and space near-linear in the size of the contract, so as not to be a denial of service vulnerability. + +This specification is entirely semantic. It imposes no further syntax on bytecode, as none is required to ensure the specified level of safety. Ethereum Virtual Machine bytecode is just that -- a sequence of bytes that when executed causes a sequence of changes to the machine state. The safety we seek here is simply to not, as it were, jam up the gears. + +## Motivation + +### Safety + +For our purposes we define a safe EVM contract as one that cannot encounter an exceptional halting state. From the standpoint of security it would be best if unsafe contracts were never placed on the blockchain. Unsafe code can attempt to overflow stack, underflow stack, execute invalid instructions, and jump to invalid locations. + +Unsafe contracts are exploits waiting to happen. + +Validating contract safety requires traversing the contract code. So in order to prevent denial of service attacks all jumps, including the existing `JUMP` and `JUMPI`, and also the other proposed jumps -- `RJUMP`, `RJUMPI`, `RJUMPSUB` and `RETURNSUB` -- must be validated at initialization time, and in time and space linear in the size of the code. + +#### Static Jumps and Subroutines + +The relative jumps of [EIP-4200](./eip-4200) and the simple subroutines of [EIP-2315](./eip-2315) provide a complete set of static control flow instructions: +> `RJUMP` _offset_ +* Jumps to _IP+offset_. +> `RJUMPI` _offset_ +* Jumps if the top of stack is non-zero. +> `RJUMPSUB` offset +* Pushes _IP+1_ on the return stack and jumps to _IP+offest_. +> `RETURNSUB` +* Jumps to the address popped off the return stack. + +Note that each jump creates at most two paths of control through the code, such that the complexity of traversing the entire control-flow graph is linear in the size of the code. + +#### *Dynamic Jumps* + +Dynamic jumps, where the destination of a `JUMP` or `JUMPI` is not known until runtime, are an obstacle to proving validity in linear time -- any jump can be to any destination in the code, potentially requiring time quadratic in the size of code. For this reason we have two real choices. + +1. Deprecate dynamic jumps. This is easily done: + +> Define `JUMP` and `JUMPI` as `INVALID` for the purposes of EOF Code Validation + +2. Constrain dynamic jumps. This requires static analysis. + +Consider the simplest and most common case. +``` +PUSH address +JUMP +``` +This is effectively a static jump. + +Another important use of `JUMP` is to implement the return jump from a subroutine. So consider this example of calling and returning from a minimal subroutine: +``` +TEST_SQUARE: + jumpdest + push RTN_SQUARE + 0x02 + push SQUARE + jump +RTN_SQUARE + jumpdest + swap1 + jump + +SQUARE: + jumpdest + dup1 + mul + swap1 + jump +``` +The return address -`RTN_SQUARE` - and the destination address - `SQUARE` - are pushed on the stack as constants and remain unchanged as they move on the stack, such that only those constants are passed to each `JUMP`. They are effectively static. We can track the motion of constants on the `data stack` at validation time, so *we do not need unconstrained dynamic jumps to implement subroutines.* + +*The above is the simplest analysis that suffices. A more powerful analysis that takes in more use cases is possible -- slower, but still linear-time.* + +#### Validation + +We can validate the safety of contracts with a static analysis that takes time and space linear in the size of the *code*, as shown below. And since we can, we should. + +### Performance + +Validating safe control flow at initialization time has potential performance advantages. +* Static jumps do not need to be checked at runtime. +* Stack underflow does not need to be checked for at runtime. + +## Specification + +### Validity + +> In theory, theory and practice are the same. In practice, they're not. -- Albert Einstein + +We define a _safe_ EVM contract as one that cannot encounter an exceptional halting state. We validate _safety_ at initialization time to the extent practical. + +#### *Exceptional Halting States* + +The *execution* of each instruction is defined in the Yellow Paper as a change to the EVM state that preserves the invariants of EVM state. At runtime, if the execution of an instruction would violate an invariant the EVM is in an exceptional halting state. The Yellow Paper defined five such states. +1. Insufficient gas +2. More than 1024 stack items +3. Insufficient stack items +4. Invalid jump destination +5. Invalid instruction + +*A program is safe iff no execution can lead to an exceptional halting state.* + +*We would like to consider EVM programs valid iff they are safe.* + +*In practice*, we must be able to validate *code* in linear time to avoid denial of service attacks. And we must support dynamically-priced instructions, loops, and recursion, which can use arbitrary amounts of gas and stack. + +Thus our validation cannot consider concrete computations -- it only performs a limited symbolic execution of the _code_. This means we will reject programs if we detect any invalid execution paths, even if those paths are not reachable at runtime. And we will count as valid programs that may not always produce correct results. + +We can detect only _non-recursive_ stack overflows at *validation time*, so we must check for the first two states at _runtime_: +* `out of gas` and +* stack overflow. + +The remaining three states we can check at *validation time*: +* stack underflow, +* invalid jump, and +* invalid instruction. + +That is to say: +> Valid contracts will not halt with an exception unless they either +> * throw `out of gas` or +> * recursively overflow stack. + +#### *Constraints on Valid Code* + +* Every instruction is valid. +* Every jump is valid: + * Every`JUMP` and `JUMPI` is *static*. + * No `JUMP`, `JUMPI`, `RJUMP`, `RJUMPI`, or `RJUMPSUB` addresses immediate data. +* The stacks are always valid: + * The _number_ of items on the `data stack` is always positive, and at most 1024. + * The _number_ of items on the `return stack` is always positive, and at most 1024. +* The data stack is consistently aligned: + * The _number_ of items on the `data stack` between the current `stack pointer` and the `stack pointer` on entry to the most recent basic block is the same for each _execution_ of a _byte_code_. + +We define a `JUMP` or `JUMPI` instruction to be *static* if its `jumpsrc` argument was first placed on the stack via a `PUSH…` and that value has not changed since, though it may have been copied via a `DUP…` or `SWAP…`. + +The `RJUMP`, `RJUMPI` and `RJUMPSUB`instructions take their destination as an immediate argument, so they are *static*. + +Taken together, these rules allow for code to be validated by traversing the control-flow graph, in time and space linear in the size of the code, following each edge only once. + +_Note: The definition of 'static' for `JUMP` and `JUMPI` is the bare minimum needed to implement subroutines. Deeper analyses could be proposed that would validate a larger and probably more useful set of jumps, at the cost of more expensive (but still linear) validation._ + + +## Rationale + +Demanding *static* destinations for all jumps means that all jump destinations can be validated at initialization time, not runtime. + +Bounding the stack pointers catches all `data stack` and non-recursive`return stack` overflows. + +Requiring a consistently aligned`data stack` prevents stack underflow. It can also catch such errors as misaligned stacks due to irreducible control flows and calls to subroutines with the wrong number of arguments. + +## Backwards Compatibility + +These changes affect the semantics of EVM code – the use of `JUMP`, `JUMPI`, and the stack are restricted, such that some *code* that would otherwise run correctly will nonetheless be invalid EVM *code*. + +## Reference Implementation + +The following is a pseudo-Go 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. + +It runs in time equal to `O(vertices + edges)` in the program's control-flow graph, where edges represent control flow and the vertices represent _basic blocks_ -- thus the algorithm takes time proportional to the size of the _code_. + +_Note: All valid code has a control-flow graph that can be traversed in time and space linear in the length of the code. That means that some other static analyses and code transformations that might otherwise require quadratic time can also be written to run in near-linear time, including one-pass and streaming compilers._ + +### Validation Function + +***Note:** This function is a work in progress, and the version below is known to be incorrect.* + +For simplicity's sake we assume that _jumpdest analysis_ has been done and that we have some helper functions. +* `isValidInstruction(pc)` returns true if `pc` points at a valid instruction +* `isValidJumpdest(dest)` returns true if `dest` is a valid jumpdest +* `immediateData(pc)` returns the immediate data for the instruction at `pc`. +* `advancePC(pc)` returns next `pc`, skipping any immediate data. +* `removed_items(pc)` returns the number of items removed from the `dataStack` by the instruction at `pc`. +* `added_items(pc)` returns the number of items added to the `dataStack` by the instruction at `pc`. + +``` +var bytecode [codeLen]byte +var subMin [codeLen]int +var subMax [codeLen]int +var subDelta [codeLen]int +var visited [codeLen]bool +var dataStack [1024]int + +// validate a path through the control flow of the bytecode at pc +// and return the maximum number of stack items used down that path +// or else the PC and an error +// +// by starting at pc:=0 the entire program is recursively evaluated +// +func validate(pc := 0, sp := 0, rp := 0) int, error { + minStack := 0 + maxStack := 0 + deltaStack := 0 + for pc < codeLen { + if !isValidInstruction(pc) { + return 0,0,0,invalid_instruction + } + + // if we have jumped here before return to break cycle + if visited[pc] { + + // stack is not aligned if deltas not the same + if ??? { + return 0,0,0,invalid_stack + } + return minStack, maxStack, sp + } + visited[pc] = true + switch bytecode[pc] { + + // successful termination + case STOP: + return minStack, maxStack, sp + case RETURN: + return minStack, maxStack, sp + + case SELFDESTRUCT: + return minStack, maxStack, sp + case REVERT: + return minStack, maxStack, sp + case INVALID: + return 0,0,0,invalid_instruction + + case RJUMP: + + // check for valid jump destination + if !isValidJumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + + // reset pc to destination of jump + pc += immediateData(pc) + + case RJUMPI: + + // recurse to validate true side of conditional + jumpdest = pc + immediateData(pc) + if !isValidJumpdest(pc + jumpdest) { + return 0,0,0,invalid_destination + } + minRight, maxLeft, deltaRight, err = + validate(jumpdest, sp, rp) + + err { + return 0,0,0,err + } + + // recurse to validate false side of conditional + pc = advancePC(pc) + minRight, maxRight, deltaRight, err = + validate(pc, sp, rp) + if err { + return 0,0,0,err + } + + // both paths valid, so return max + minStack = min(minStack, min(minLeft, minRight)) + maxStack += max(maxLeft, maxRight) + deltaStack += max(deltaLeft, deltaRight) + return minStack, maxStack, deltaStack + + case RJUMPSUB: + + // check for valid jump destination + jumpdest = immediateData(pc) + if !isValidJumpdest(pc + jumpdest) { + return 0,0,0,invalid_destination + } + + pc += jumpdest + + // recurse to validate subroutine call + minSub, maxSub, deltaSub, err = validate(jumpdest, sp, rp) + if err { + return 0,0,0,err + } + subMin[pc] = minSub + subMax[pc] = maxSub + subDelta[pc] = deltaSub + minStack = min(minStack, sp) + maxStack = max(maxStack, sp) + pc = advancePC(pc) + + case RETURNSUB: + + maxStack = max(maxStack, sp) + return minStack, maxStack, sp, nil + + ///////////////////////////////////////////////////// + // + // The following are to be included only if we take + // + // Option 2 + // + // and do not deprecate JUMP and JUMPI + // + case JUMP: + // pop jump destination + jumpdest = dataStack[--sp] + if !valid_jumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + pc = jumpdest + case JUMPI: + // pop jump destination and conditional + jumpdest = dataStack[--sp] + jumpif = dataStack[--sp] + if sp < 0 {} + return 0,0,0,stack_underflow + } + if !valid_jumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + + // recurse to validate true side of conditional + if !isValidJumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + maxLeft, err = validate(jumpdest, sp, rp) + if err { + return 0,0,0,err + } + + // recurse to validate false side of conditional + pc = advance_pc(pc) + maxRight, err = validate(pc, sp, rp) + if err { + return 0,0,0,err + } + + // both sides valid, return max + maxStack += max(maxLeft, maxRight) + return minStack, maxStack, sp + case PUSH1 <= bytecode[pc] && bytecode[pc] <= PUSH32 { + sp++ + if (sp > 1023) { + return 0,0,0,stack_overflow + } + maxStack = max(maxStack, sp) + dataStack[sp] = immediateData(pc) + pc = advancePC(pc) + case DUP1 <= bytecode[pc] && bytecode[pc] <= DUP32 { + dup = sp - (bytecode[pc] - DUP1) + if dup < 0 { + return 0,0,0,stack_underflow + } + sp++ + if (sp > 1023) { + return 0,0,0,stack_overflow + } + maxStack = max(maxStack, sp) + dataStack[sp] = dataStack[dup] + pc = advancePC(pc) + case SWAP1 <= bytecode[pc] && bytecode[pc] <= SWAP32 { + swap = sp - (bytecode[pc] - SWAP1) + if swap < 0 { + return 0,0,0,stack_underflow + } + temp := dataStack[swap] + dataStack[swap] = dataStack[0] + dataStack[0] = temp + pc = advancePC(pc) + // + ///////////////////////////////////////////////////// + + default: + + // apply other instructions to stack pointer + sp -= removed_items(pc) + if (sp < 0) { + return 0,0,0,stack_underflow + } + minStack = min(minStack, sp) + sp += added_items(pc) + if (sp > 1023) { + return 0,0,0,stack_overflow + } + maxStack = max(maxStack, sp) + pc = advancePC(pc) + } + } + + // successful termination + return minStack, maxStack, sp +} +``` +## Security Considerations + +This EIP is intended to ensure an essential level of safety for EVM code deployed on the blockchain. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-3788.md b/EIPS/eip-3788.md new file mode 100644 index 00000000000000..2c91fe8aaa536d --- /dev/null +++ b/EIPS/eip-3788.md @@ -0,0 +1,60 @@ +--- +eip: 3788 +title: Strict enforcement of chainId +description: Reject transactions that do not explicitly have the same chainId as the node's configuration. +author: Gregory Markou (@GregTheGreek) +discussions-to: https://ethereum-magicians.org/t/discussion-to-eip-3788-strict-enforcement-of-chainid/7001 +status: Stagnant +type: Standards Track +category: Core +created: 2021-09-02 +requires: 155 +--- + +## Abstract + +Reject transactions that do not explicitly have the same chainId as the node's configuration. + +## Motivation + +Per [EIP-155](./eip-155.md) a transaction with a `chainId = 0` is considered to be a valid +transaction. This was a feature to offer developers the ability to sumbit replayable transactions +across different chains. With the rise of evm compatible chains, many of which use forks, or packages +from popular Ethereum clients, we are putting user funds at risk. This is because most wallet +interfaces do not expose the chainId to the user, meaning they typically do not have insight +into what chainId they are signing. Should a malicious actor (or accidental) choose to, they +can easily have users submit transactions with a `chainId = 0` on a non-mainnet network, allowing +the malicious actor to replay the transaction on ethereum mainnet (or other networks for that matter) +as a grief or sophisticated attack. + +## Specification + +As of the fork block `N`, consider transactions with a `chaindId = 0` to be invalid. Such that +transactions are verified based on the nodes configuration. Eg: +``` +if (node.cfg.chainId != tx.chainId) { + // Reject transaction +} +``` + +## Rationale + +The configuration set by the node is the main source of truth, and thus should be explicitly used +when deciding how to filter out a transaction. This check should exist in two places, as a filter +on the JSON-RPC (eg: `eth_sendTransaction`), and strictly enforced on the EVM during transaction +validation. + +This ensures that users will not have transactions pending that will be guaranteed to fail, and +prevents the transaction from being included in a block. + +## Backwards Compatibility +This breaks all applications or tooling that submit transactions with a `chainId == 0` after block number `N`. + +## Test Cases +TBD + +## Security Considerations +It should be noted this will not prevent a malicious actor from deploying a network with `chainId = 1`, or copying any other networks chainId. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3855.md b/EIPS/eip-3855.md new file mode 100644 index 00000000000000..42034250ae8dd4 --- /dev/null +++ b/EIPS/eip-3855.md @@ -0,0 +1,64 @@ +--- +eip: 3855 +title: PUSH0 instruction +description: Introduce a new instruction which pushes the constant value 0 onto the stack +author: Alex Beregszaszi (@axic), Hugo De la cruz (@hugo-dc), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-3855-push0-instruction/7014 +status: Final +type: Standards Track +category: Core +created: 2021-02-19 +--- + +## Abstract + +Introduce the `PUSH0` (`0x5f`) instruction, which pushes the constant value 0 onto the stack. + +## Motivation + +Many instructions expect offsets as inputs, which in a number of cases are zero. A good example is the return data parameters of `CALLs`, which are set to zeroes in case the contract prefers using `RETURNDATA*`. This is only one example, but there are many other reasons why a contract would need to push a zero value. They can achieve that today by `PUSH1 0`, which costs 3 gas at runtime, and is encoded as two bytes which means `2 * 200` gas deployment cost. + +Because of the overall cost many try to use various other instructions to achieve the same effect. Common examples include `PC`, `MSIZE`, `CALLDATASIZE`, `RETURNDATASIZE`, `CODESIZE`, `CALLVALUE`, and `SELFBALANCE`. Some of these cost only 2 gas and are a single byte long, but their value can depend on the context. + +We have conducted an analysis on Mainnet (block ranges 8,567,259…8,582,058 and 12,205,970…12,817,405), and ~11.5% of all the `PUSH*` instructions executed push a value of zero. + +The main motivations for this change include: +1. Reducing contract code size. +2. Reducing the risk of contracts (mis)using various instructions as an optimisation measure. Repricing/changing those instructions can be more risky. +3. Reduce the need to use `DUP` instructions for duplicating zeroes. + +To put the "waste" into perspective, across existing accounts 340,557,331 bytes are wasted on `PUSH1 00` instructions, which means 68,111,466,200 gas was spent to deploy them. In practice a lot of these accounts share identical bytecode with others, so their total stored size in clients is lower, however the deploy time cost must have been paid nevertheless. + +An example for 2) is changing the behaviour of `RETURNDATASIZE` such that it may not be guaranteed to be zero at the beginning of the call frame. + +## Specification + +The instruction `PUSH0` is introduced at `0x5f`. It has no immediate data, pops no items from the stack, and places a single item with the value 0 onto the stack. The cost of this instruction is 2 gas (aka `base`). + +## Rationale + +### Gas cost + +The `base` gas cost is used for instructions which place constant values onto the stack, such as `ADDRESS`, `ORIGIN`, and so forth. + +### Opcode + +`0x5f` means it is in a "contiguous" space with the rest of the `PUSH` implementations and potentially could share the implementation. + +## Backwards Compatibility + +This EIP introduces a new opcode which did not exists previously. Already deployed contracts using this opcode could change their behaviour after this EIP. + +## Test Cases + +- `5F` -- successful execution, stack consist of a single item, set to zero +- `5F5F..5F` (1024 times) -- successful execution, stack consists of 1024 items, all set to zero +- `5F5F..5F` (1025 times) -- execution aborts due to out of stack + +## Security Considerations + +The authors are not aware of any impact on security. Note that jumpdest-analysis is unaffected, as `PUSH0` has no immediate data bytes. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3860.md b/EIPS/eip-3860.md new file mode 100644 index 00000000000000..5f8151e19ad1c9 --- /dev/null +++ b/EIPS/eip-3860.md @@ -0,0 +1,124 @@ +--- +eip: 3860 +title: Limit and meter initcode +description: Limit the maximum size of initcode to 49152 and apply extra gas cost of 2 for every 32-byte chunk of initcode +author: Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0) +discussions-to: https://ethereum-magicians.org/t/eip-3860-limit-and-meter-initcode/7018 +status: Final +type: Standards Track +category: Core +created: 2021-07-16 +requires: 170 +--- + +## Abstract + +We extend [EIP-170](./eip-170.md) by introducing a maximum size limit for `initcode` (`MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 49152`). + +Furthermore, we introduce a charge of `2` gas for every 32-byte chunk of `initcode` to represent the cost of jumpdest-analysis. + +Lastly, the size limit results in the nice-to-have property that EVM code size, code offset (`PC`), and jump offset fits a 16-bit value. + +## Motivation + +During contract creation the client has to perform jumpdest-analysis on the `initcode` prior to execution. The work performed scales linearly with the size of the `initcode`. This work currently is not metered, nor is there a protocol enforced upper bound for the size. + +There are three costs charged today: + +1. Cost for calldata aka `initcode`: 4 gas for a byte with the value of zero, and 16 gas otherwise. +2. Cost for the resulting deployed code: 200 gas per byte. +3. Cost of address calculation (hashing of code) in case of `CREATE2` only: 6 gas per word. + +Only the first cost applies to `initcode`, but only in the case of contract creation transactions. For the case of `CREATE`/`CREATE2` there is no such cost, and it is possible to programmatically generate variations of `initcode` in a relatively cheap manner. In the past it was possible to craft malicious `initcode` due to a vulnerability fixed in 2017 by geth 1.6.5. + +Furthermore, the lack of a limit has caused lengthy discussions for some EVM proposals, influencing the design, or even causing a delay or cancellation of a feature. + +We are motivated by three reasons: + +1. Ensuring `initcode` is fairly charged (most importantly cost is proportional to `initcode`'s length) to minimize the risks for the future. +2. To have a cost system which is extendable in the future. +3. To simplify EVM engines by the explicit limits (code size, code offsets (`PC`), and jump offsets fit 16-bits). + +## Specification + +### Parameters + +| Constant | Value | +| -------------------- | ------------------- | +| `INITCODE_WORD_COST` | `2` | +| `MAX_INITCODE_SIZE` | `2 * MAX_CODE_SIZE` | + +Where `MAX_CODE_SIZE` is defined by [EIP-170](./eip-170.md) as `24576`. + +We define `initcode_cost(initcode)` to equal `INITCODE_WORD_COST * ceil(len(initcode) / 32)`. + +### Rules + +1. If length of transaction data (`initcode`) in a create transaction exceeds `MAX_INITCODE_SIZE`, transaction is invalid. (*Note that this is similar to transactions considered invalid for not meeting the intrinsic gas cost requirement.*) +2. For a create transaction, extend the transaction data cost formula to include `initcode_cost(initcode)`. (*Note that this is included in transaction intrinsic cost, i.e. transaction with not enough gas to cover initcode cost is invalid.*) +3. If length of `initcode` to `CREATE` or `CREATE2` instructions exceeds `MAX_INITCODE_SIZE`, instruction execution exceptionally aborts (as if it runs out of gas). +4. For the `CREATE` and `CREATE2` instructions charge an extra gas cost equaling to `initcode_cost(initcode)`. This cost is deducted before the calculation of the resulting contract address and the execution of `initcode`. (*Note that this means before or at the same time as the hashing cost is applied in `CREATE2`.*) + +## Rationale + +### Gas cost constant + +The value of `INITCODE_WORD_COST` is selected based on performance benchmarks of differing worst-cases per implementation. The baseline for the benchmarks is the performance of `KECCAK256` hashing in geth 1.10.9, which matches the 70 Mgas/s gas limit target on a 4.0 GHz x86_64 CPU. + +| EVM | version | MB/s | B/CPUcycle | CPUcycle/B | cost of 1 B | cost of 32 B | +| --------------- | ------- | ---- | ---- | ---- | ---- | ---- | +| geth/KECCAK256 | 1.10.9 | 357 | 1.8 | 0.6 | 0.2 | 6.0 | +| geth | 1.10.9 | 1091 | 5.5 | 0.2 | 0.1 | 2.0 | +| evmone/Baseline | 0.8.2 | 727 | 3.7 | 0.3 | 0.1 | 2.9 | +| evmone/Advanced | 0.8.2 | 155 | 0.8 | 1.3 | 0.4 | 13.8 | + +### Gas cost per word (32-byte chunk) + +We have chosen the cost of 2 gas per word based on Geth's implementation and comparing with `KECCAK256` performance. This means the per byte cost is `0.0625`. While fractional gas costs are not permitted in the EVM, we can approximate it by charging per-word. + +Moreover, calculating gas per word is compatible with the calculation of `CREATE2`'s *hashcost* of [EIP-1014](./eip-1014.md). Therefore, the same implementation may be used for `CREATE` and `CREATE2` with different cost constants: before activation `0` for `CREATE` and `6` for `CREATE2`, after activation `2` for `CREATE` and `6 + 2` for `CREATE2`. + +### Reason for size limit of initcode + +Estimating and creating worst case scenarios is easier with an upper bound in place, given one parameter for the search is greatly reduced. This allows for selecting a much more optimistic gas per byte. + +Should there be no upper bound, the cost would need to be higher accounting for unknown unknowns. Given most *initcode* (*TODO: state maximum initcode size resulting in deployment seen on mainnet here*) does not exceed the proposed limit, penalising contracts by overly conservative costs seems unnecessary. + +### Effect of size limit of initcode + +In most, if not all cases when a new contract is being created, the resulting runtime code is copied from the initcode itself. For the basic case the `2 * MAX_CODE_SIZE` limit allows `MAX_CODE_SIZE` for runtime code and another `MAX_CODE_SIZE` for contract constructor code. However, the limit may have practical implications for cases where multiple contracts are deployed in a single create transaction. + +### Initcode cost for create transaction + +The initcode cost for create transaction data (0.0625 gas per byte) is negligible compared to the transaction data cost (4 or 16 gas per byte). Despite that, we decided to include it in the specification for consistency, and more importantly for forward compatibility. + +### How to report initcode limit violation? + +We specified that initcode size limit violation for `CREATE`/`CREATE2` results in exceptional abort of the execution. This places it in the group of early out-of-gas checks, including: stack underflow, memory expansion, static call violation, initcode hashing cost, and initcode cost introduced by this EIP. They precede the later "light" checks: call depth and balance. The choice gives consistency to the order of checks and lowers implementation complexity (out-of-gas checks can be performed in any order). + +## Backwards Compatibility + +This EIP requires a "network upgrade", since it modifies consensus rules. + +Already deployed contracts should not be effected, but certain transactions (with `initcode` beyond the proposed limit) would still be includable in a block, but result in an exceptional abort. + +## Test Cases + +Tests should include the following cases: + +- Creation transaction with gas limit enough to cover initcode cost +- Creation transaction with gas limit enough to cover intrinsic cost except initcode cost +- `CREATE`/`CREATE2`/creation transaction with `len(initcode)` at `MAX_INITCODE_SIZE` +- `CREATE`/`CREATE2`/creation transaction with `len(initcode)` at `MAX_INITCODE_SIZE+1` + +## Security Considerations + +For client implementations, this EIP makes attacks based on jumpdest-analysis less problematic, so should increase the robustness of clients. + +For layer 2, this EIP introduces failure-modes where there previously were none. There *could* exist factory-contracts which deploy multi-level contract hierarchies, such that the code for multiple contracts are included in the initcode of the first contract. The author(s) of this EIP are not aware of any such contracts. + +Currently, on London, with `30M` gas limit, it would be possible to trigger jumpdest-analysis of a total `~1.3GB` of initcode. With this EIP, the cost for such an attack would increase by roughly `80M` gas. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3978.md b/EIPS/eip-3978.md new file mode 100644 index 00000000000000..18d167195826e2 --- /dev/null +++ b/EIPS/eip-3978.md @@ -0,0 +1,86 @@ +--- +eip: 3978 +title: Gas refunds on reverts +description: Reprice reverted SSTORE/CREATE/SELFDESTRUCT/LOGX operations gas via gas refund mechanism +author: Anton Bukov (@k06a), Mikhail Melnik (@ZumZoom) +discussions-to: https://ethereum-magicians.org/t/eip-3978-gas-refunds-on-reverts/7071/ +status: Stagnant +type: Standards Track +category: Core +created: 2021-09-16 +updated: 2022-02-14 +requires: 2929 +--- + +## Abstract + +For reverted state modification operations, keep access cost, but refund modification cost. + +## Motivation + +Reverting a transaction, or any of its sub-calls, drops any state modifications that happened inside. +But now, users are being charged for the dropped modifications as if they persisted. + +Since [EIP-3298](./eip-3298.md), the gas refund mechanism works for storage restores only inside the same transaction. But on revert, the gas refund is not increased; it is completely erased. +It can even be cheaper to transfer tokens back at the end of a transaction instead of reverting, to keep the existing gas refund. +This should be changed. + +- Reverted SSTORE deserves to be repriced to SLOAD gas (100 gas) +- Reverted LOG0, LOG1, LOG2, LOG3 and LOG4 deserve to be repriced to 100 gas +- Reverted CALL with value (`positive_value_cost` = 9,000 gas) deserves to be repriced to 100 gas +- Reverted CALL with value and account creation (`value_to_empty_account_cost` = 25,000 gas) deserves to be repriced to 100 gas +- Reverted CREATE and CREATE2 (32,000 gas) deserve to be repriced to 100 gas +- Reverted SELFDESTRUCT (5,000 or 25,000 gas) deserves to be repriced to 100 gas + +Moreover, it seems fair to charge CREATE and CREATE2 operations 32,000 fix price conditionally only if returned bytecode is not empty. + + +## Specification +For each callframe, track `revert_gas_refund`, initially 0. + +The set of operations that modify `revert_gas_refund` are: +- SSTORE +- LOG0, LOG1, LOG2, LOG3, LOG4 +- CALL +- CREATE, CREATE2 +- SELFDESTRUCT + +They increase `revert_gas_refund` as follows: +```javascript +call.revert_gas_refund += operation.gas - WARM_STORAGE_READ_COST +``` + +And in case of revert let's use this value instead of just erasing `gas_refund`: +```javascript +if (call.reverted) { + // existing behavior + tx.gas_refund -= call.gas_refund; + + // New behavior added to existing according to the EIP-3978 + tx.gas_refund += call.revert_gas_refund; +} +``` + +## Rationale + +Gas should reflect the cost of use. +The revert cost reflects the cost of access during execution, but not the cost of modification. + +## Backwards Compatibility + +No known backward incompatibilities. + +## Test Cases + +TBD + +## Reference Implementation + +TBD + +## Security Considerations + +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4.md b/EIPS/eip-4.md index 7ee67e529f41a0..2973e03a8d8bcb 100644 --- a/EIPS/eip-4.md +++ b/EIPS/eip-4.md @@ -5,7 +5,6 @@ author: Joseph Chow (@ethers) status: Final type: Meta created: 2015-11-17 -superseded-by: 1 --- # Abstract diff --git a/EIPS/eip-4200.md b/EIPS/eip-4200.md new file mode 100644 index 00000000000000..17d4a8d12d2b5e --- /dev/null +++ b/EIPS/eip-4200.md @@ -0,0 +1,163 @@ +--- +eip: 4200 +title: EOF - Static relative jumps +description: RJUMP, RJUMPI and RJUMPV instructions with a signed immediate encoding the jump destination +author: Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-3920-static-relative-jumps/7108 +status: Review +type: Standards Track +category: Core +created: 2021-07-16 +requires: 3540, 3670 +--- + +## Abstract + +Three new EVM jump instructions are introduced (`RJUMP`, `RJUMPI` and `RJUMPV`) which encode destinations as signed immediate values. These can be useful in the majority of (but not all) use cases and offer a cost reduction. + +## Motivation + +A recurring discussion topic is that EVM only has a mechanism for dynamic jumps. They provide a very flexible architecture with only 2 (!) instructions. This flexibility comes at a cost however: it makes analysis of code more complicated and it also (partially) resulted in the need to have the `JUMPDEST` marker. + +In a great many cases control flow is actually static and there is no need for any dynamic behaviour, though not every use case can be solved by static jumps. + +There are various ways to reduce the need for dynamic jumps, some examples: + +1. With native support for functions / subroutines +2. A "return to caller" instruction +3. A "switch-case" table with dynamic indexing + +This change does not attempt to solve these, but instead introduces a minimal feature set to allow compilers to decide which is the most adequate option for a given use case. It is expected that compilers will use `RJUMP`/`RJUMPI` almost exclusively, with the exception of returning to the caller continuing to use `JUMP`. + +This functionality does not preclude the EVM from introducing other forms of control flow later on. `RJUMP`/`RJUMPI` can efficiently co-exists with a higher-level declaration of functions, where static relative jumps should be used for intra-function control flow. + +The main benefit of these instruction is reduced gas cost (both at deploy and execution time) and better analysis properties. + +## Specification + +We introduce three new instructions on the same block number [EIP-3540](./eip-3540.md) is activated on: + +1. `RJUMP` (0xe0) - relative jump +2. `RJUMPI` (0xe1) - conditional relative jump +3. `RJUMPV` (0xe2) - relative jump via jump table + +If the code is legacy bytecode, all of these instructions result in an *exceptional halt*. (*Note: This means no change to behaviour.*) + +If the code is valid EOF1: + +1. `RJUMP relative_offset` sets the `PC` to `PC_post_instruction + relative_offset`. +2. `RJUMPI relative_offset` pops a value (`condition`) from the stack, and sets the `PC` to `PC_post_instruction + ((condition == 0) ? 0 : relative_offset)`. +3. `RJUMPV max_index relative_offset+` pops a value (`case`) from the stack, and sets the `PC` to `PC_post_instruction + ((case > max_index) ? 0 : relative_offset[case])`. + +The immediate argument `relative_offset` is encoded as a 16-bit **signed** (two's-complement) big-endian value. Under `PC_post_instruction` we mean the `PC` position after the entire immediate value. + +The immediate encoding of `RJUMPV` is more special: the unsigned 8-bit `max_index` value determines the maximum index in the jump table. The number of `relative_offset` values following is `max_index+1`. This allows table sizes up to 256. The encoding of `RJUMPV` must have at least one `relative_offset` and thus it will take at minimum 4 bytes. Furthermore, the `case > max_index` condition falling through means that in many use cases, one would place the *default* path following the `RJUMPV` instruction. An interesting feature is that `RJUMPV 0 relative_offset` is an inverted-`RJUMPI`, which can be used in many cases instead of `ISZERO RJUMPI relative_offset`. + +We also extend the validation algorithm of [EIP-3670](./eip-3670.md) to verify that each `RJUMP`/`RJUMPI`/`RJUMPV` has a `relative_offset` pointing to an instruction. This means it cannot point to an immediate data of `PUSHn`/`RJUMP`/`RJUMPI`/`RJUMPV`. It cannot point outside of code bounds. It is allowed to point to a `JUMPDEST`, but is not required to. + +Because the destinations are validated upfront, the cost of these instructions are less than their dynamic counterparts: `RJUMP` should cost 2, and `RJUMPI` and `RJUMPV` should cost 4. + +## Rationale + +### Relative addressing + +We chose relative addressing in order to support code which is relocatable. This also means a code snippet can be injected. A technique seen used prior to this EIP to achieve the same goal was to inject code like `PUSHn PC ADD JUMPI`. + +We do not see any significant downside to relative addressing and it allows us to also deprecate the `PC` instruction. + +### Immediate size + +The signed 16-bit immediate means that the largest jump distance possible is 32767. In the case the bytecode at `PC=0` starts with an `RJUMP`, it will be possible to jump as far as `PC=32770`. + +Given `MAX_CODE_SIZE = 24576` (in [EIP-170](./eip-170.md)) and `MAX_INITCODE_SIZE = 49152` (in [EIP-3860](./eip-3860.md)), we think the 16-bit immediate is large enough. + +A version with an 8-bit immediate would only allow moving `PC` backward by 125 or forward by 127 bytes. While that seems to be a good enough distance for many for-loops, it is likely not good enough for cross-function jumps, and since the 16-bit immediate is the same size as what a dynamic jump would take in such cases (3 bytes: `JUMP PUSH1 n`), we think having less instructions is better. + +Should there be a need to have immediate encodings of other size (such as 8-bits, 24-bits or 32-bits), it would be possible to introduce new opcodes, similarly to how multiple `PUSH` instructions exist. + +### `PUSHn JUMP` sequences + +If we chose absolute addressing, then `RJUMP` could be viewed similar to the sequence `PUSHn JUMP` (and `RJUMPI` similar to `PUSHn JUMPI`). In that case one could argue that instead of introducing a new instruction, such sequences should get a discount, because EVMs could optimise them. + +We think this is a bad direction to go: + +1. It further complicates the already complex rules of gas calculation. +2. And it either requires a consensus defined internal representation for EVM code, or forces EVM implementations to do optimisations on their own. + +Both of these are risky. Furthermore we think that EVM implementations should be free to chose what optimisations they apply, and the savings do not need to be passed down at all cost. + +Additionally it requires a potentially significant change to the current implementations which depend on a streaming one-by-one execution without a lookahead. + +### Relation to dynamic jumps + +The goal was not to completely replace the current control flow system of the EVM, but to augment it. There are many cases where dynamic jumps are useful, such as returning to the caller. + +It is possible to introduce a new mechanism for having a pre-defined table of valid jump destinations, and dynamically supplying the index within this table to accomplish some form of dynamic jumps. This is very useful for efficiently encoding a form of "switch-cases" statements. It could also be used for "return to caller" cases, however it is likely inefficient or awkward. + +### Lack of `JUMPDEST` + +`JUMPDEST` serves two purposes: + +1. To efficiently partition code -- this can be useful for pre-calculating total gas usage for a given *block* (i.e. instructions between `JUMPDEST`s), and for JIT/AOT translation. +2. To explicitly show valid locations (otherwise any non-data location would be valid). + +This functionality is not needed for static jumps, as the analysers can easily tell destinations from the static jump immediates during jumpdest-analysis. + +There are two benefits here: + +1. Not wasting a byte for a `JUMPDEST` also means a saving of 200 gas during deployment, for each jump destination. +2. Saving an extra 1 gas per jump during execution, given `JUMPDEST` itself cost 1 gas and is "executed" during jumping. + +### `RJUMPV` fallback case + +If no match is found (i.e. the *default* case) in the `RJUMPV` instruction execution will continue without branching. This allows for gaps in the arguments to be filled with `0`s, and a choice of implementation by the programmer. Alternate options would include exceptional aborts in case of no match. + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted). + +## Test Cases + +### Validation + +#### Valid cases + +- `RJUMP`/`RJUMPI`/`RJUMPV` with `JUMPDEST` as target + - `relative_offset` is positive/negative/`0` +- `RJUMP`/`RJUMPI`/`RJUMPV` with instruction other than `JUMPDEST` as target + - `relative_offset` is positive/negative/`0` +- `RJUMPV` with various valid table sizes from 1 to 256 + +#### Invalid cases + +- `RJUMP`/`RJUMPI`/`RJUMPV` with truncated immediate +- `RJUMP`/`RJUMPI`/`RJUMPV` as a final instruction in code section +- `RJUMP`/`RJUMPI`/`RJUMPV` target outside of code section bounds +- `RJUMP`/`RJUMPI`/`RJUMPV` target push data +- `RJUMP`/`RJUMPI`/`RJUMPV` target another `RJUMP`/`RJUMPI`/`RJUMPV` immediate argument + +### Execution + +- `RJUMP`/`RJUMPI`/`RJUMPV` in legacy code aborts execution +- `RJUMP` + - `relative_offset` is positive/negative/`0` +- `RJUMPI` + - `relative_offset` is positive/negative/`0` + - `condition` equals `0` + - `condition` does not equal `0` +- `RJUMPV 0 relative_offset` + - `case` equals `0` + - `case` does not equal `0` +- `RJUMPV` with table containing positive, negative, `0` offsets + - `case` equals `0` + - `case` does not equal `0` + - `case` outside of table bounds (`case > max_index`, fallback case) + - `case` > 255 + +## Security Considerations + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4337.md b/EIPS/eip-4337.md new file mode 100644 index 00000000000000..8ad63200e4a647 --- /dev/null +++ b/EIPS/eip-4337.md @@ -0,0 +1,1013 @@ +--- +eip: 4337 +title: Account Abstraction Using Alt Mempool +description: An account abstraction proposal which completely avoids consensus-layer protocol changes, instead relying on higher-layer infrastructure. +author: Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat), Kristof Gazso (@kristofgazso), Tjaden Hess (@tjade273) +discussions-to: https://ethereum-magicians.org/t/erc-4337-account-abstraction-via-entry-point-contract-specification/7160 +status: Draft +type: Standards Track +category: ERC +created: 2021-09-29 +--- + +## Abstract + +An account abstraction proposal which completely avoids the need for consensus-layer protocol changes. Instead of adding new protocol features and changing the bottom-layer transaction type, this proposal instead introduces a higher-layer pseudo-transaction object called a `UserOperation`. Users send `UserOperation` objects into a separate mempool. A special class of actor called bundlers package up a set of these objects into a transaction making a `handleOps` call to a special contract, and that transaction then gets included in a block. + +## Motivation + +See also `https://ethereum-magicians.org/t/implementing-account-abstraction-as-part-of-eth1-x/4020` and the links therein for historical work and motivation, and [EIP-2938](./eip-2938.md) for a consensus layer proposal for implementing the same goal. + +This proposal takes a different approach, avoiding any adjustments to the consensus layer. It seeks to achieve the following goals: + +* **Achieve the key goal of account abstraction**: allow users to use smart contract wallets containing arbitrary verification logic instead of EOAs as their primary account. Completely remove any need at all for users to also have EOAs (as status quo SC wallets and [EIP-3074](./eip-3074.md) both require) +* **Decentralization** + * Allow any bundler (think: block builder) to participate in the process of including account-abstracted user operations + * Work with all activity happening over a public mempool; users do not need to know the direct communication addresses (eg. IP, onion) of any specific actors + * Avoid trust assumptions on bundlers +* **Do not require any Ethereum consensus changes**: Ethereum consensus layer development is focusing on the merge and later on scalability-oriented features, and there may not be any opportunity for further protocol changes for a long time. Hence, to increase the chance of faster adoption, this proposal avoids Ethereum consensus changes. +* **Try to support other use cases** + * Privacy-preserving applications + * Atomic multi-operations (similar goal to [EIP-3074](./eip-3074.md)) + * Pay tx fees with [ERC-20](./eip-20.md) tokens, allow developers to pay fees for their users, and [EIP-3074](./eip-3074.md)-like **sponsored transaction** use cases more generally + * Support aggregated signature (e.g. BLS) + +## Specification + +### Definitions + +* **UserOperation** - a structure that describes a transaction to be sent on behalf of a user. To avoid confusion, it is not named "transaction". + * Like a transaction, it contains "sender", "to", "calldata", "maxFeePerGas", "maxPriorityFee", "signature", "nonce" + * unlike a transaction, it contains several other fields, described below + * also, the "signature" field usage is not defined by the protocol, but by each account implementation +* **Sender** - the account contract sending a user operation. +* **EntryPoint** - a singleton contract to execute bundles of UserOperations. Bundlers/Clients whitelist the supported entrypoint. +* **Bundler** - a node (block builder) that can handle UserOperations, + create a valid an EntryPoint.handleOps() transaction, + and add it to the block while it is still valid. + This can be achieved by a number of ways: + * Bundler can act as a block builder itself + * If the bundler is not a block builder, it MUST work with the block building infrastructure such as `mev-boost` or + other kind of PBS (proposer-builder separation) + * The `bundler` can also rely on an experimental `eth_sendRawTransactionConditional` RPC API if it is available. +* **Aggregator** - a helper contract trusted by accounts to validate an aggregated signature. Bundlers/Clients whitelist the supported aggregators. + +To avoid Ethereum consensus changes, we do not attempt to create new transaction types for account-abstracted transactions. Instead, users package up the action they want their account to take in an ABI-encoded struct called a `UserOperation`: + +| Field | Type | Description +| - | - | - | +| `sender` | `address` | The account making the operation | +| `nonce` | `uint256` | Anti-replay parameter (see "Semi-abstracted Nonce Support" ) | +| `initCode` | `bytes` | The initCode of the account (needed if and only if the account is not yet on-chain and needs to be created) | +| `callData` | `bytes` | The data to pass to the `sender` during the main execution call | +| `callGasLimit` | `uint256` | The amount of gas to allocate the main execution call | +| `verificationGasLimit` | `uint256` | The amount of gas to allocate for the verification step | +| `preVerificationGas` | `uint256` | The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata | +| `maxFeePerGas` | `uint256` | Maximum fee per gas (similar to [EIP-1559](./eip-1559.md) `max_fee_per_gas`) | +| `maxPriorityFeePerGas` | `uint256` | Maximum priority fee per gas (similar to EIP-1559 `max_priority_fee_per_gas`) | +| `paymasterAndData` | `bytes` | Address of paymaster sponsoring the transaction, followed by extra data to send to the paymaster (empty for self-sponsored transaction) | +| `signature` | `bytes` | Data passed into the account along with the nonce during the verification step | + +Users send `UserOperation` objects to a dedicated user operation mempool. A specialized class of actors called **bundlers** (either block builders running special-purpose code, or users that can relay transactions to block builders eg. through a bundle marketplace such as Flashbots that can guarantee next-block-or-never inclusion) listen in on the user operation mempool, and create **bundle transactions**. A bundle transaction packages up multiple `UserOperation` objects into a single `handleOps` call to a pre-published global **entry point contract**. + +To prevent replay attacks (both cross-chain and multiple `EntryPoint` implementations), the `signature` should depend on `chainid` and the `EntryPoint` address. + +The core interface of the entry point contract is as follows: + +```solidity +function handleOps(UserOperation[] calldata ops, address payable beneficiary); + +function handleAggregatedOps( + UserOpsPerAggregator[] calldata opsPerAggregator, + address payable beneficiary +); + + +struct UserOpsPerAggregator { + UserOperation[] userOps; + IAggregator aggregator; + bytes signature; +} +function simulateValidation(UserOperation calldata userOp); + +error ValidationResult(ReturnInfo returnInfo, + StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo); + +error ValidationResultWithAggregation(ReturnInfo returnInfo, + StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo, + AggregatorStakeInfo aggregatorInfo); + +struct ReturnInfo { + uint256 preOpGas; + uint256 prefund; + bool sigFailed; + uint48 validAfter; + uint48 validUntil; + bytes paymasterContext; +} + +struct StakeInfo { + uint256 stake; + uint256 unstakeDelaySec; +} + +struct AggregatorStakeInfo { + address actualAggregator; + StakeInfo stakeInfo; +} +``` + +The core interface required for an account to have is: + +```solidity +interface IAccount { + function validateUserOp + (UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) + external returns (uint256 validationData); +} +``` + +The `userOpHash` is a hash over the userOp (except signature), entryPoint and chainId. + +The account: + +* MUST validate the caller is a trusted EntryPoint +* If the account does not support signature aggregation, it MUST validate the signature is a valid signature of the `userOpHash`, and + SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch. Any other error should revert. +* MUST pay the entryPoint (caller) at least the "missingAccountFunds" (which might be zero, in case current account's deposit is high enough) +* The account MAY pay more than this minimum, to cover future transactions (it can always issue `withdrawTo` to retrieve it) +* The return value MUST be packed of `authorizer`, `validUntil` and `validAfter` timestamps. + * authorizer - 0 for valid signature, 1 to mark signature failure. Otherwise, an address of an authorizer contract. This ERC defines "signature aggregator" as authorizer. + * `validUntil` is 6-byte timestamp value, or zero for "infinite". The UserOp is valid only up to this time. + * `validAfter` is 6-byte timestamp. The UserOp is valid only after this time. + +An account that works with aggregated signature, should return its signature aggregator address in the "sigAuthorizer" return value of validateUserOp. +It MAY ignore the signature field + +The core interface required by an aggregator is: + +```solidity +interface IAggregator { + + function validateUserOpSignature(UserOperation calldata userOp) + external view returns (bytes memory sigForUserOp); + + function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatesSignature); + + function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) view external; +} +``` + +* If an account uses an aggregator (returns it from validateUserOp), then its address is returned by `simulateValidation()` reverting with `ValidationResultWithAggregator` instead of `ValidationResult` +* To accept the UserOp, the bundler must call **validateUserOpSignature()** to validate the userOp's signature. +* **aggregateSignatures()** must aggregate all UserOp signature into a single value. +* Note that the above methods are helper method for the bundler. The bundler MAY use a native library to perform the same validation and aggregation logic. +* **validateSignatures()** MUST validate the aggregated signature matches for all UserOperations in the array, and revert otherwise. + This method is called on-chain by `handleOps()` + +#### Semi-abstracted Nonce Support + +In Ethereum protocol, the sequential transaction `nonce` value is used as a replay protection method as well as to +determine the valid order of transaction being included in blocks. + +It also contributes to the transaction hash uniqueness, as a transaction by the same sender with the same +nonce may not be included in the chain twice. + +However, requiring a single sequential `nonce` value is limiting the senders' ability to define their custom logic +with regard to transaction ordering and replay protection. + +Instead of sequential `nonce` we implement a nonce mechanism that uses a single `uint256` nonce value in the `UserOperation`, +but treats it as two values: + +* 192-bit "key" +* 64-bit "sequence" + +These values are represented on-chain in the `EntryPoint` contract. +We define the following method in the `EntryPoint` interface to expose these values: + +```solidity +function getNonce(address sender, uint192 key) external view returns (uint256 nonce); +``` + +For each `key` the `sequence` is validated and incremented sequentially and monotonically by the `EntryPoint` for +each UserOperation, however a new key can be introduced with an arbitrary value at any point. + +This approach maintains the guarantee of `UserOperation` hash uniqueness on-chain on the protocol level while allowing +wallets to implement any custom logic they may need operating on a 192-bit "key" field, while fitting the 32 byte word. + +##### Reading and validating the nonce + +When preparing the UserOp clients may make a view call to this method to determine a valid value for the `nonce` field. + +Bundler's validation of a UserOp should start with `getNonce` to ensure the transaction has a valid `nonce` field. + +If the bundler is willing to accept multiple UserOperations by the same sender into their mempool, +this bundler is supposed to track the `key` and `sequence` pair of the UserOperations already added in the mempool. + +##### Usage examples + +1. Classic sequential nonce. + + In order to require the wallet to have classic, sequential nonce, the validation function should perform: + + ```solidity + require(userOp.nonce> 64; + if (sig == ADMIN_METHODSIG) { + require(key == ADMIN_KEY, "wrong nonce-key for admin operation"); + } else { + require(key == 0, "wrong nonce-key for normal operation"); + } + ``` + +#### Using signature aggregators + +An account signifies it uses signature aggregation returning its address from `validateUserOp`. +During `simulateValidation`, this aggregator is returned (in the `ValidationResultWithAggregator`) + +The bundler should first accept the aggregator (validate its stake info and that it is not throttled/banned) +Then it MUST verify the userOp using `aggregator.validateUserOpSignature()` + +Signature aggregator SHOULD stake just like a paymaster, unless it is exempt due to not accessing global storage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. Bundlers MAY throttle down and ban aggregators in case they take too much +resources (or revert) when the above methods are called in view mode, or if the signature aggregation fails. + +### Required entry point contract functionality + +There are 2 separate entry point methods: `handleOps` and `handleAggregatedOps` + +* `handleOps` handle userOps of accounts that don't require any signature aggregator. +* `handleAggregatedOps` can handle a batch that contains userOps of multiple aggregators (and also requests without any aggregator) +* `handleAggregatedOps` performs the same logic below as `handleOps`, but it must transfer the correct aggregator to each userOp, and also must call `validateSignatures` on each aggregator after doing all the per-account validation. +The entry point's `handleOps` function must perform the following steps (we first describe the simpler non-paymaster case). It must make two loops, the **verification loop** and the **execution loop**. In the verification loop, the `handleOps` call must perform the following steps for each `UserOperation`: + +* **Create the account if it does not yet exist**, using the initcode provided in the `UserOperation`. If the account does not exist, _and_ the initcode is empty, or does not deploy a contract at the "sender" address, the call must fail. +* **Call `validateUserOp` on the account**, passing in the `UserOperation`, the required fee and aggregator (if there is one). The account should verify the operation's signature, and pay the fee if the account considers the operation valid. If any `validateUserOp` call fails, `handleOps` must skip execution of at least that operation, and may revert entirely. +* Validate the account's deposit in the entryPoint is high enough to cover the max possible cost (cover the already-done verification and max execution gas) + +In the execution loop, the `handleOps` call must perform the following steps for each `UserOperation`: + +* **Call the account with the `UserOperation`'s calldata**. It's up to the account to choose how to parse the calldata; an expected workflow is for the account to have an `execute` function that parses the remaining calldata as a series of one or more calls that the account should make. + +![](../assets/eip-4337/image1.png) + +Before accepting a `UserOperation`, bundlers should use an RPC method to locally call the `simulateValidation` function of the entry point, to verify that the signature is correct and the operation actually pays fees; see the [Simulation section below](#simulation) for details. +A node/bundler SHOULD drop (not add to the mempool) a `UserOperation` that fails the validation + +### Extension: paymasters + +We extend the entry point logic to support **paymasters** that can sponsor transactions for other users. This feature can be used to allow application developers to subsidize fees for their users, allow users to pay fees with [ERC-20](./eip-20.md) tokens and many other use cases. When the paymaster is not equal to the zero address, the entry point implements a different flow: + +![](../assets/eip-4337/image2.png) + +During the verification loop, in addition to calling `validateUserOp`, the `handleOps` execution also must check that the paymaster has enough ETH deposited with the entry point to pay for the operation, and then call `validatePaymasterUserOp` on the paymaster to verify that the paymaster is willing to pay for the operation. Note that in this case, the `validateUserOp` is called with a `missingAccountFunds` of 0 to reflect that the account's deposit is not used for payment for this userOp. + +If the paymaster's validatePaymasterUserOp returns a "context", then `handleOps` must call `postOp` on the paymaster after making the main execution call. It must guarantee the execution of `postOp`, by making the main execution inside an inner call context, and if the inner call context reverts attempting to call `postOp` again in an outer call context. + +Maliciously crafted paymasters _can_ DoS the system. To prevent this, we use a reputation system. paymaster must either limit its storage usage, or have a stake. see the [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. + +The paymaster interface is as follows: + +```c++ + function validatePaymasterUserOp + (UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) + external returns (bytes memory context, uint256 validationData); + +function postOp + (PostOpMode mode, bytes calldata context, uint256 actualGasCost) + external; + +enum PostOpMode { + opSucceeded, // user op succeeded + opReverted, // user op reverted. still has to pay for gas. + postOpReverted // user op succeeded, but caused postOp to revert +} +``` + + +```c++ +// add a paymaster stake (must be called by the paymaster) +function addStake(uint32 _unstakeDelaySec) external payable + +// unlock the stake (must wait unstakeDelay before can withdraw) +function unlockStake() external + +// withdraw the unlocked stake +function withdrawStake(address payable withdrawAddress) external +``` + +The paymaster must also have a deposit, which the entry point will charge UserOperation costs from. +The deposit (for paying gas fees) is separate from the stake (which is locked). + +The entry point must implement the following interface to allow paymasters (and optionally accounts) manage their deposit: + +```c++ +// return the deposit of an account +function balanceOf(address account) public view returns (uint256) + +// add to the deposit of the given account +function depositTo(address account) public payable + +// withdraw from the deposit +function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external +``` + +### Client behavior upon receiving a UserOperation + +When a client receives a `UserOperation`, it must first run some basic sanity checks, namely that: + +* Either the `sender` is an existing contract, or the `initCode` is not empty (but not both) +* If `initCode` is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses global state, it must be staked - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. +* The `verificationGasLimit` is sufficiently low (`<= MAX_VERIFICATION_GAS`) and the `preVerificationGas` is sufficiently high (enough to pay for the calldata gas cost of serializing the `UserOperation` plus `PRE_VERIFICATION_OVERHEAD_GAS`) +* The `paymasterAndData` is either empty, or start with the **paymaster** address, which is a contract that (i) currently has nonempty code on chain, (ii) has a sufficient deposit to pay for the UserOperation, and (iii) is not currently banned. During simulation, the paymaster's stake is also checked, depending on its storage usage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. +* The callgas is at least the cost of a `CALL` with non-zero value. +* The `maxFeePerGas` and `maxPriorityFeePerGas` are above a configurable minimum value that the client is willing to accept. At the minimum, they are sufficiently high to be included with the current `block.basefee`. +* The sender doesn't have another `UserOperation` already present in the pool (or it replaces an existing entry with the same sender and nonce, with a higher `maxPriorityFeePerGas` and an equally increased `maxFeePerGas`). Only one `UserOperation` per sender may be included in a single batch. A sender is exempt from this rule and may have multiple `UserOperations` in the pool and in a batch if it is staked (see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) below), but this exception is of limited use to normal accounts. + +If the `UserOperation` object passes these sanity checks, the client must next run the first op simulation, and if the simulation succeeds, the client must add the op to the pool. A second simulation must also happen during bundling to make sure the UserOperation is still valid. + +### Simulation + +#### Simulation Rationale + +In order to add a UserOperation into the mempool (and later to add it into a bundle) we need to "simulate" its validation to make sure it is valid, and that it is capable of paying for its own execution. +In addition, we need to verify that the same will hold true when executed on-chain. +For this purpose, a UserOperation is not allowed to access any information that might change between simulation and execution, such as current block time, number, hash etc. +In addition, a UserOperation is only allowed to access data related to this sender address: Multiple UserOperations should not access the same storage, so that it is impossible to invalidate a large number of UserOperations with a single state change. +There are 3 special contracts that interact with the account: the factory (initCode) that deploys the contract, the paymaster that can pay for the gas, and signature aggregator (described later) +Each of these contracts is also restricted in its storage access, to make sure UserOperation validations are isolated. + +#### Specification: + +To simulate a `UserOperation` validation, the client makes a view call to `simulateValidation(userop)` + +This method always revert with `ValidationResult` as successful response. +If the call reverts with other error, the client rejects this `userOp`. + +The simulated call performs the full validation, by calling: + +1. If `initCode` is present, create the account. +2. `account.validateUserOp`. +3. if specified a paymaster: `paymaster.validatePaymasterUserOp`. + +Either `validateUserOp` or `validatePaymasterUserOp` may return a "validAfter" and "validUntil" timestamps, which is the time-range that this UserOperation is valid on-chain. +The simulateValidation call returns this range. +A node MAY drop a UserOperation if it expires too soon (e.g. wouldn't make it to the next block) +If the `ValidationResult` includes `sigFail`, the client SHOULD drop the `UserOperation`. + +The operations differ in their opcode banning policy. +In order to distinguish between them, there is a call to the NUMBER opcode (`block.number`), used as a delimiter between the 3 functions. +While simulating `userOp` validation, the client should make sure that: + +1. May not invokes any **forbidden opcodes** +2. Must not use GAS opcode (unless followed immediately by one of { `CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL` }.) +3. Storage access is limited as follows: + 1. self storage (of factory/paymaster, respectively) is allowed, but only if self entity is staked + 2. account storage access is allowed (see Storage access by Slots, below), + 3. in any case, may not use storage used by another UserOp `sender` in the same bundle (that is, paymaster and factory are not allowed as senders) +4. Limitation on "CALL" opcodes (`CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL`): + 1. must not use value (except from account to the entrypoint) + 2. must not revert with out-of-gas + 3. destination address must have code (EXTCODESIZE>0) or be a standard Ethereum precompile defined at addresses from `0x01` to `0x09` + 4. cannot call EntryPoint's methods, except `depositFor` (to avoid recursion) +5. `EXTCODEHASH` of every address accessed (by any opcode) does not change between first and second simulations of the op. +6. `EXTCODEHASH`, `EXTCODELENGTH`, `EXTCODECOPY` may not access address with no code. +7. If `op.initcode.length != 0` , allow only one `CREATE2` opcode call (in the first (deployment) block), otherwise forbid `CREATE2`. + +Transient Storage slots defined in [EIP-1153](./eip-1153.md) and accessed using `TLOAD` (`0x5c`) and `TSTORE` (`0x5d`) opcodes +must follow the exact same validation rules as persistent storage if Transient Storage is enabled. + +#### Storage associated with an address + +We define storage slots as "associated with an address" as all the slots that uniquely related on this address, and cannot be related with any other address. +In solidity, this includes all storage of the contract itself, and any storage of other contracts that use this contract address as a mapping key. + +An address `A` is associated with: + +1. Slots of contract `A` address itself. +2. Slot `A` on any other address. +3. Slots of type `keccak256(A || X) + n` on any other address. (to cover `mapping(address => value)`, which is usually used for balance in ERC-20 tokens). + `n` is an offset value up to 128, to allow accessing fields in the format `mapping(address => struct)` + +#### Alternative Mempools + +The simulation rules above are strict and prevent the ability of paymasters and signature aggregators to grief the system. +However, there might be use-cases where specific paymasters (and signature aggregators) can be validated +(through manual auditing) and verified that they cannot cause any problem, while still require relaxing of the opcode rules. +A bundler cannot simply "whitelist" request from a specific paymaster: if that paymaster is not accepted by all +bundlers, then its support will be sporadic at best. +Instead, we introduce the term "alternate mempool". +UserOperations that use whitelisted paymasters (or signature aggregators) are put into a separate mempool. +Only bundlers that support this whitelist will use UserOperations from this mempool. +These UserOperations can be bundled together with UserOperations from the main mempool + +### Bundling + +During bundling, the client should: + +* Exclude UserOps that access any sender address of another UserOp in the same batch. +* Exclude UserOps that access any address created by another UserOp validation in the same batch (via a factory). +* For each paymaster used in the batch, keep track of the balance while adding UserOps. Ensure that it has sufficient deposit to pay for all the UserOps that use it. +* Sort UserOps by aggregator, to create the lists of UserOps-per-aggregator. +* For each aggregator, run the aggregator-specific code to create aggregated signature, and update the UserOps + +After creating the batch, before including the transaction in a block, the client should: + +* Run `debug_traceCall` with maximum possible gas, to enforce the validation opcode and precompile banning and storage access rules, + as well as to verify the entire `handleOps` batch transaction, + and use the consumed gas for the actual transaction execution. +* If the call reverted, check the `FailedOp` event. + A `FailedOp` during `handleOps` simulation is an unexpected event since it was supposed to be caught + by the single-UserOperation simulation. +* If any verification context rule was violated the bundlers should treat it the same as + if this UserOperation reverted with a `FailedOp` event. +* Remove the offending UserOperation from the current bundle and from mempool. +* If the error is caused by a `factory` (error code is `AA1x`) or a `paymaster` (error code is `AA3x`), and the `sender` + of the UserOp **is not** a staked entity, then issue a "ban" (see ["Reputation, throttling and banning"](#reputation-scoring-and-throttlingbanning-for-global-entities)) + for the guilty factory or paymaster. +* If the error is caused by a `factory` (error code is `AA1x`) or a `paymaster` (error code is `AA3x`), and the `sender` + of the UserOp **is** a staked entity, do not ban the `factory` / `paymaster` from the mempool. + Instead, issue a "ban" for the staked `sender` entity. +* Repeat until `debug_traceCall` succeeds. + +As staked entries may use some kind of transient storage to communicate data between UserOperations in the same bundle, +it is critical that the exact same opcode and precompile banning rules as well as storage access rules are enforced +for the `handleOps` validation in its entirety as for individual UserOperations. +Otherwise, attackers may be able to use the banned opcodes to detect running on-chain and trigger a `FailedOp` revert. + +Banning an offending entity for a given bundler is achieved by increasing its `opsSeen` value by `1000000` +and removing all UserOperations for this entity already present in the mempool. +This change will allow the negative reputation value to deteriorate over time consistent with other banning reasons. + +If any of the three conditions is violated, the client should reject the `op`. If both calls succeed (or, if `op.paymaster == ZERO_ADDRESS` and the first call succeeds)without violating the three conditions, the client should accept the op. On a bundler node, the storage keys accessed by both calls must be saved as the `accessList` of the `UserOperation` + +When a bundler includes a bundle in a block it must ensure that earlier transactions in the block don't make any UserOperation fail. It should either use access lists to prevent conflicts, or place the bundle as the first transaction in the block. + +#### Forbidden opcodes + +The forbidden opcodes are to be forbidden when `depth > 2` (i.e. when it is the factory, account, paymaster, or other contracts called by them that are being executed). They are: `GASPRICE`, `GASLIMIT`, `DIFFICULTY`, `TIMESTAMP`, `BASEFEE`, `BLOCKHASH`, `NUMBER`, `SELFBALANCE`, `BALANCE`, `ORIGIN`, `GAS`, `CREATE`, `COINBASE`, `SELFDESTRUCT`. They should only be forbidden during verification, not execution. These opcodes are forbidden because their outputs may differ between simulation and execution, so simulation of calls using these opcodes does not reliably tell what would happen if these calls are later done on-chain. + +Exceptions to the forbidden opcodes: + +1. A single `CREATE2` is allowed if `op.initcode.length != 0` and must result in the deployment of a previously-undeployed `UserOperation.sender`. +2. `GAS` is allowed if followed immediately by one of { `CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL` }. + (that is, making calls is allowed, using `gasleft()` or `gas` opcode directly is forbidden) + +### Reputation scoring and throttling/banning for global entities + +#### Reputation Rationale. + +UserOperation's storage access rules prevent them from interfere with each other. +But "global" entities - paymasters, factories and aggregators are accessed by multiple UserOperations, and thus might invalidate multiple previously-valid UserOperations. + +To prevent abuse, we throttle down (or completely ban for a period of time) an entity that causes invalidation of large number of UserOperations in the mempool. +To prevent such entities from "sybil-attack", we require them to stake with the system, and thus make such DoS attack very expensive. +Note that this stake is never slashed, and can be withdrawn any time (after unstake delay) + +Unstaked entities are allowed, under the rules below. + +When staked, an entity is also allowed to use its own associated storage, in addition to sender's associated storage. + +The stake value is not enforced on-chain, but specifically by each node while simulating a transaction. +The stake is expected to be above MIN_STAKE_VALUE, and unstake delay above MIN_UNSTAKE_DELAY +The value of MIN_UNSTAKE_DELAY is 84600 (one day) +The value of MIN_STAKE_VALUE is determined per chain, and specified in the "bundler specification test suite" + +#### Un-staked entities + +Under the following special conditions, unstaked entities still can be used: + +* An entity that doesn't use any storage at all, or only the senders's storage (not the entity's storage - that does require a stake) +* If the UserOp doesn't create a new account (that is initCode is empty), or the UserOp creates a new account using a + staked `factory` contract, then the entity may also use [storage associated with the sender](#storage-associated-with-an-address)) +* A paymaster that has a “postOp()” method (that is, validatePaymasterUserOp returns “context”) must be staked + +#### Specification. + +In the following specification, "entity" is either address that is explicitly referenced by the UserOperation: sender, factory, paymaster and aggregator. +Clients maintain two mappings with a value for staked entities: + +* `opsSeen: Map[Address, int]` +* `opsIncluded: Map[Address, int]` + +If an entity doesn't use storage at all, or only reference storage associated with the "sender" (see [Storage associated with an address](#storage-associated-with-an-address)), then it is considered "OK", without using the rules below. + +When the client learns of a new staked entity, it sets `opsSeen[entity] = 0` and `opsIncluded[entity] = 0` . + +The client sets `opsSeen[entity] +=1` each time it adds an op with that `entity` to the `UserOperationPool`, and the client sets `opsIncluded[entity] += 1` each time an op that was in the `UserOperationPool` is included on-chain. + +Every hour, the client sets `opsSeen[entity] -= opsSeen[entity] // 24` and `opsIncluded[entity] -= opsIncluded[entity] // 24` for all entities (so both values are 24-hour exponential moving averages). + +We define the **status** of an entity as follows: + +```python +OK, THROTTLED, BANNED = 0, 1, 2 + +def status(paymaster: Address, + opsSeen: Map[Address, int], + opsIncluded: Map[Address, int]): + if paymaster not in opsSeen: + return OK + min_expected_included = opsSeen[paymaster] // MIN_INCLUSION_RATE_DENOMINATOR + if min_expected_included <= opsIncluded[paymaster] + THROTTLING_SLACK: + return OK + elif min_expected_included <= opsIncluded[paymaster] + BAN_SLACK: + return THROTTLED + else: + return BANNED +``` + +Stated in simpler terms, we expect at least `1 / MIN_INCLUSION_RATE_DENOMINATOR` of all ops seen on the network to get included. If an entity falls too far behind this minimum, it gets **throttled** (meaning, the client does not accept ops from that paymaster if there is already an op with that entity, and an op only stays in the pool for 10 blocks), If the entity falls even further behind, it gets **banned**. Throttling and banning naturally decay over time because of the exponential-moving-average rule. + +**Non-bundling clients and bundlers should use different settings for the above params**: + +| Param | Client setting | Bundler setting | +| - | - | - | +| `MIN_INCLUSION_RATE_DENOMINATOR` | 100 | 10 | +| `THROTTLING_SLACK` | 10 | 10 | +| `BAN_SLACK` | 50 | 50 | + +To help make sense of these params, note that a malicious paymaster can at most cause the network (only the p2p network, not the blockchain) to process `BAN_SLACK * MIN_INCLUSION_RATE_DENOMINATOR / 24` non-paying ops per hour. + +## Rationale + +The main challenge with a purely smart contract wallet based account abstraction system is DoS safety: how can a block builder including an operation make sure that it will actually pay fees, without having to first execute the entire operation? Requiring the block builder to execute the entire operation opens a DoS attack vector, as an attacker could easily send many operations that pretend to pay a fee but then revert at the last moment after a long execution. Similarly, to prevent attackers from cheaply clogging the mempool, nodes in the P2P network need to check if an operation will pay a fee before they are willing to forward it. + +In this proposal, we expect accounts to have a `validateUserOp` method that takes as input a `UserOperation`, and verify the signature and pay the fee. This method is required to be almost-pure: it is only allowed to access the storage of the account itself, cannot use environment opcodes (eg. `TIMESTAMP`), and can only edit the storage of the account, and can also send out ETH (needed to pay the entry point). The method is gas-limited by the `verificationGasLimit` of the `UserOperation`; nodes can choose to reject operations whose `verificationGasLimit` is too high. These restrictions allow block builders and network nodes to simulate the verification step locally, and be confident that the result will match the result when the operation actually gets included into a block. + +The entry point-based approach allows for a clean separation between verification and execution, and keeps accounts' logic simple. The alternative would be to require accounts to follow a template where they first self-call to verify and then self-call to execute (so that the execution is sandboxed and cannot cause the fee payment to revert); template-based approaches were rejected due to being harder to implement, as existing code compilation and verification tooling is not designed around template verification. + +### Paymasters + +Paymasters facilitate transaction sponsorship, allowing third-party-designed mechanisms to pay for transactions. Many of these mechanisms _could_ be done by having the paymaster wrap a `UserOperation` with their own, but there are some important fundamental limitations to that approach: + +* No possibility for "passive" paymasters (eg. that accept fees in some ERC-20 token at an exchange rate pulled from an on-chain DEX) +* Paymasters run the risk of getting griefed, as users could send ops that appear to pay the paymaster but then change their behavior after a block + +The paymaster scheme allows a contract to passively pay on users' behalf under arbitrary conditions. It even allows ERC-20 token paymasters to secure a guarantee that they would only need to pay if the user pays them: the paymaster contract can check that there is sufficient approved ERC-20 balance in the `validatePaymasterUserOp` method, and then extract it with `transferFrom` in the `postOp` call; if the op itself transfers out or de-approves too much of the ERC-20s, the inner `postOp` will fail and revert the execution and the outer `postOp` can extract payment (note that because of storage access restrictions the ERC-20 would need to be a wrapper defined within the paymaster itself). + +### First-time account creation + +It is an important design goal of this proposal to replicate the key property of EOAs that users do not need to perform some custom action or rely on an existing user to create their wallet; they can simply generate an address locally and immediately start accepting funds. + +The wallet creation itself is done by a "factory" contract, with wallet-specific data. +The factory is expected to use CREATE2 (not CREATE) to create the wallet, so that the order of creation of wallets doesn't interfere with the generated addresses. +The `initCode` field (if non-zero length) is parsed as a 20-byte address, followed by "calldata" to pass to this address. +This method call is expected to create a wallet and return its address. +If the factory does use CREATE2 or some other deterministic method to create the wallet, it's expected to return the wallet address even if the wallet has already been created. This is to make it easier for clients to query the address without knowing if the wallet has already been deployed, by simulating a call to `entryPoint.getSenderAddress()`, which calls the factory under the hood. +When `initCode` is specified, if either the `sender` address points to an existing contract, or (after calling the initCode) the `sender` address still does not exist, +then the operation is aborted. +The `initCode` MUST NOT be called directly from the entryPoint, but from another address. +The contract created by this factory method should accept a call to `validateUserOp` to validate the UserOp's signature. +For security reasons, it is important that the generated contract address will depend on the initial signature. +This way, even if someone can create a wallet at that address, he can't set different credentials to control it. +The factory has to be staked if it accesses global storage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. + +NOTE: In order for the wallet to determine the "counterfactual" address of the wallet (prior its creation), +it should make a static call to the `entryPoint.getSenderAddress()` + +### Entry point upgrading + +Accounts are encouraged to be DELEGATECALL forwarding contracts for gas efficiency and to allow account upgradability. The account code is expected to hard-code the entry point into their code for gas efficiency. If a new entry point is introduced, whether to add new functionality, improve gas efficiency, or fix a critical security bug, users can self-call to replace their account's code address with a new code address containing code that points to a new entry point. During an upgrade process, it's expected that two mempools will run in parallel. + +### RPC methods (eth namespace) + +#### * eth_sendUserOperation + +eth_sendUserOperation submits a User Operation object to the User Operation pool of the client. The client MUST validate the UserOperation, and return a result accordingly. + +The result `SHOULD` be set to the **userOpHash** if and only if the request passed simulation and was accepted in the client's User Operation pool. If the validation, simulation, or User Operation pool inclusion fails, `result` `SHOULD NOT` be returned. Rather, the client `SHOULD` return the failure reason. + +##### Parameters: + +1. **UserOperation** a full user-operation struct. All fields MUST be set as hex values. empty `bytes` block (e.g. empty `initCode`) MUST be set to `"0x"` +2. **EntryPoint** the entrypoint address the request should be sent through. this MUST be one of the entry points returned by the `supportedEntryPoints` rpc call. + +##### Return value: + +* If the UserOperation is valid, the client MUST return the calculated **userOpHash** for it +* in case of failure, MUST return an `error` result object, with `code` and `message`. The error code and message SHOULD be set as follows: + * **code: -32602** - invalid UserOperation struct/fields + * **code: -32500** - transaction rejected by entryPoint's simulateValidation, during wallet creation or validation + * The `message` field MUST be set to the FailedOp's "`AAxx`" error message from the EntryPoint + * **code: -32501** - transaction rejected by paymaster's validatePaymasterUserOp + * The `message` field SHOULD be set to the revert message from the paymaster + * The `data` field MUST contain a `paymaster` value + * **code: -32502** - transaction rejected because of opcode validation + * **code: -32503** - UserOperation out of time-range: either wallet or paymaster returned a time-range, and it is already expired (or will expire soon) + * The `data` field SHOULD contain the `validUntil` and `validAfter` values + * The `data` field SHOULD contain a `paymaster` value, if this error was triggered by the paymaster + * **code: -32504** - transaction rejected because paymaster (or signature aggregator) is throttled/banned + * The `data` field SHOULD contain a `paymaster` or `aggregator` value, depending on the failed entity + * **code: -32505** - transaction rejected because paymaster (or signature aggregator) stake or unstake-delay is too low + * The `data` field SHOULD contain a `paymaster` or `aggregator` value, depending on the failed entity + * The `data` field SHOULD contain a `minimumStake` and `minimumUnstakeDelay` + * **code: -32506** - transaction rejected because wallet specified unsupported signature aggregator + * The `data` field SHOULD contain an `aggregator` value + * **code: -32507** - transaction rejected because of wallet signature check failed (or paymaster signature, if the paymaster uses its data as signature) + +##### Example: + +Request: + +```json= +{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_sendUserOperation", + "params": [ + { + sender, // address + nonce, // uint256 + initCode, // bytes + callData, // bytes + callGasLimit, // uint256 + verificationGasLimit, // uint256 + preVerificationGas, // uint256 + maxFeePerGas, // uint256 + maxPriorityFeePerGas, // uint256 + paymasterAndData, // bytes + signature // bytes + }, + entryPoint // address + ] +} + +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x1234...5678" +} +``` + +##### Example failure responses: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "message": "AA21 didn't pay prefund", + "code": -32500 + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "message": "paymaster stake too low", + "data": { + "paymaster": "0x123456789012345678901234567890123456790", + "minimumStake": "0xde0b6b3a7640000", + "minimumUnstakeDelay": "0x15180" + }, + "code": -32504 + } +} +``` + + +#### * eth_estimateUserOperationGas + +Estimate the gas values for a UserOperation. +Given UserOperation optionally without gas limits and gas prices, return the needed gas limits. +The signature field is ignored by the wallet, so that the operation will not require user's approval. +Still, it might require putting a "semi-valid" signature (e.g. a signature in the right length) + +**Parameters**: same as `eth_sendUserOperation` + gas limits (and prices) parameters are optional, but are used if specified. + `maxFeePerGas` and `maxPriorityFeePerGas` default to zero, so no payment is required by neither account nor paymaster. + +**Return Values:** + +* **preVerificationGas** gas overhead of this UserOperation +* **verificationGasLimit** actual gas used by the validation of this UserOperation +* **callGasLimit** value used by inner account execution + +##### Error Codes: + +Same as `eth_sendUserOperation` +This operation may also return an error if the inner call to the account contract reverts. + +#### * eth_getUserOperationByHash + +Return a UserOperation based on a hash (userOpHash) returned by `eth_sendUserOperation` + +**Parameters** + +* **hash** a userOpHash value returned by `eth_sendUserOperation` + +**Return value**: + +`null` in case the UserOperation is not yet included in a block, or a full UserOperation, with the addition of `entryPoint`, `blockNumber`, `blockHash` and `transactionHash` + +#### * eth_getUserOperationReceipt + +Return a UserOperation receipt based on a hash (userOpHash) returned by `eth_sendUserOperation` + +**Parameters** + +* **hash** a userOpHash value returned by `eth_sendUserOperation` + +**Return value**: + +`null` in case the UserOperation is not yet included in a block, or: + +* **userOpHash** the request hash +* **entryPoint** +* **sender** +* **nonce** +* **paymaster** the paymaster used for this userOp (or empty) +* **actualGasCost** - actual amount paid (by account or paymaster) for this UserOperation +* **actualGasUsed** - total gas used by this UserOperation (including preVerification, creation, validation and execution) +* **success** boolean - did this execution completed without revert +* **reason** in case of revert, this is the revert reason +* **logs** the logs generated by this UserOperation (not including logs of other UserOperations in the same bundle) +* **receipt** the TransactionReceipt object. + Note that the returned TransactionReceipt is for the entire bundle, not only for this UserOperation. + +#### * eth_supportedEntryPoints + +Returns an array of the entryPoint addresses supported by the client. The first element of the array `SHOULD` be the entryPoint addressed preferred by the client. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_supportedEntryPoints", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + "0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8", + "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6" + ] +} +``` + +#### * eth_chainId + +Returns [EIP-155](./eip-155.md) Chain ID. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_chainId", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x1" +} +``` + +### RPC methods (debug Namespace) + +This api must only be available on testing mode and is required by the compatibility test suite. In production, any `debug_*` rpc calls should be blocked. + +#### * debug_bundler_clearState + +Clears the bundler mempool and reputation data of paymasters/accounts/factories/aggregators. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_clearState", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "ok" +} +``` + +#### * debug_bundler_dumpMempool + +Dumps the current UserOperations mempool + +**Parameters:** + +* **EntryPoint** the entrypoint used by eth_sendUserOperation + +**Returns:** + +`array` - Array of UserOperations currently in the mempool. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_dumpMempool", + "params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + sender, // address + nonce, // uint256 + initCode, // bytes + callData, // bytes + callGasLimit, // uint256 + verificationGasLimit, // uint256 + preVerificationGas, // uint256 + maxFeePerGas, // uint256 + maxPriorityFeePerGas, // uint256 + paymasterAndData, // bytes + signature // bytes + } + ] +} +``` + +#### * debug_bundler_sendBundleNow + +Forces the bundler to build and execute a bundle from the mempool as `handleOps()` transaction. + +Returns: `transactionHash` + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_sendBundleNow", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0xdead9e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576" +} +``` + +#### * debug_bundler_setBundlingMode + +Sets bundling mode. + +After setting mode to "manual", an explicit call to debug_bundler_sendBundleNow is required to send a bundle. + +##### parameters: + +`mode` - 'manual' | 'auto' + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_setBundlingMode", + "params": ["manual"] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "ok" +} +``` + +#### * debug_bundler_setReputation + +Sets reputation of given addresses. parameters: + +**Parameters:** + +* An array of reputation entries to add/replace, with the fields: + + * `address` - The address to set the reputation for. + * `opsSeen` - number of times a user operations with that entity was seen and added to the mempool + * `opsIncluded` - number of times a user operations that uses this entity was included on-chain + * `status` - (string) The status of the address in the bundler 'ok' | 'throttled' | 'banned'. + +* **EntryPoint** the entrypoint used by eth_sendUserOperation + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_setReputation", + "params": [ + [ + { + "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6", + "opsSeen": 20, + "opsIncluded": 13 + } + ], + "0x1306b01bC3e4AD202612D3843387e94737673F53" + ] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "ok" +} +``` + + +#### * debug_bundler_dumpReputation + +Returns the reputation data of all observed addresses. +Returns an array of reputation objects, each with the fields described above in `debug_bundler_setReputation` with the + + +**Parameters:** + +* **EntryPoint** the entrypoint used by eth_sendUserOperation + +**Return value:** + +An array of reputation entries with the fields: + +* `address` - The address to set the reputation for. +* `opsSeen` - number of times a user operations with that entity was seen and added to the mempool +* `opsIncluded` - number of times a user operations that uses this entity was included on-chain +* `status` - (string) The status of the address in the bundler 'ok' | 'throttled' | 'banned'. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_dumpReputation", + "params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + { "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6", + "opsSeen": 20, + "opsIncluded": 19, + "status": "ok" + } + ] +} +``` + +## Backwards Compatibility + +This EIP does not change the consensus layer, so there are no backwards compatibility issues for Ethereum as a whole. Unfortunately it is not easily compatible with pre-[ERC-4337](./eip-4337.md) accounts, because those accounts do not have a `validateUserOp` function. If the account has a function for authorizing a trusted op submitter, then this could be fixed by creating an [ERC-4337](./eip-4337.md) compatible account that re-implements the verification logic as a wrapper and setting it to be the original account's trusted op submitter. + +## Reference Implementation + +See `https://github.com/eth-infinitism/account-abstraction/tree/main/contracts` + +## Security Considerations + +The entry point contract will need to be very heavily audited and formally verified, because it will serve as a central trust point for _all_ [ERC-4337](./eip-4337.md). In total, this architecture reduces auditing and formal verification load for the ecosystem, because the amount of work that individual _accounts_ have to do becomes much smaller (they need only verify the `validateUserOp` function and its "check signature, increment nonce and pay fees" logic) and check that other functions are `msg.sender == ENTRY_POINT` gated (perhaps also allowing `msg.sender == self`), but it is nevertheless the case that this is done precisely by concentrating security risk in the entry point contract that needs to be verified to be very robust. + +Verification would need to cover two primary claims (not including claims needed to protect paymasters, and claims needed to establish p2p-level DoS resistance): + +* **Safety against arbitrary hijacking**: The entry point only calls an account generically if `validateUserOp` to that specific account has passed (and with `op.calldata` equal to the generic call's calldata) +* **Safety against fee draining**: If the entry point calls `validateUserOp` and passes, it also must make the generic call with calldata equal to `op.calldata` + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4341.md b/EIPS/eip-4341.md new file mode 100644 index 00000000000000..6bc8c21655a56c --- /dev/null +++ b/EIPS/eip-4341.md @@ -0,0 +1,124 @@ +--- +eip: 4341 +title: Ordered NFT Batch Standard +description: The ordering information of multiple NFTs is retained and managed +author: Simon Tian (@simontianx) +discussions-to: https://github.com/ethereum/EIPs/issues/3782 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-10-01 +--- + +## Abstract +This standard introduces a smart contract interface that can represent a batch +of non-fungible tokens of which the ordering information shall be retained and +managed. Such information is particularly useful if `tokenId`s are encoded with +the sets of `unicodes` for logographic characters and emojis. As a result, NFTs +can be utilized as carriers of meanings. + +## Motivation +Non-fungible tokens are widely accepted as carriers of crypto-assets, hence in both +[ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md), the ordering information of +multiple NFTs is discarded. However, as proposed in [EIP-3754](./eip-3754.md), +non-fungible tokens are thought of as basic units on a blockchain and can carry +abstract meanings with unicoded `tokenId`s. Transferring such tokens is transmitting +an ordered sequence of unicodes, thus effectively transmitting phrases or meanings +on a blockchain. + +A **[logograph](https://en.wikipedia.org/wiki/Logogram)** is a written character +that represents a word or morpheme, examples include _hanzi_ in Mandarin, _kanji_ +in Japanese, _hanja_ in Korean, and etc. A [unicode](https://en.wikipedia.org/wiki/Unicode) +is an information technology standard for the consistent encoding, representation, and +handling of texts. + +It is natural to combine the two to create unicoded NFTs to represent logographic +characters. Since a rich amount of meanings can be transmitted in just a few +characters in such languages, it is technically practical and valuable to create +a standard for it. Emojis are similar with logographs and can be included as well. +For non-logographic languages such as English, although the same standard can be +applied, it is tedious to represent each letter with an NFT, hence the gain is +hardly justifiable. + +A motivating example is instead of sending the two Chinese characters of the +Great Wall `长城`, two NFTs with IDs `#38271` and `#22478` respectively can be +transferred in a batch. The two IDs are corresponding to the decimal unicode of +the two characters. The receiving end decodes the IDs and retrieves the original +characters. A key point is the ordering information matters in this scenario +since the tuples `(38271, 22478)` and `(22478, 38271)` can be decoded as +`长城` and `城长`, respectively, and both are legitimate words in the Chinese +language. This illustrates the key difference between this standard and [ERC-1155](./eip-1155.md). + +Besides, in the eastern Asian culture, characters are sometimes considered or +practically used as gifts in holidays such as Spring Feastival, etc. +`(24685, 21916, 21457, 36001)` `恭喜发财` can be used literally as a gift to +express the best wishes for financial prosperity. It is therefore cuturally +natural to transfer tokens to express meanings with this standard. + +Also in logographic language systems, ancient teachings are usually written in +concise ways such that a handful of characters can unfold a rich amount of +meanings. Modern people now get a reliable technical means to pass down their +words, poems and proverbs to the future generations by sending tokens. + +Other practical and interesting applications include Chinese chess, wedding +vows, family generation quotes and sayings, funeral commendation words, prayers, +anecdotes and etc. + +## Specification +``` +pragma solidity ^0.8.0; + +/** + @title EIP-4341 Multi Ordered NFT Standard + @dev See https://eips.ethereum.org/EIPS/eip-4341 + */ +interface ERC4341 /* is ERC165 */ { + event Transfer(address indexed from, address indexed to, uint256 id, uint256 amount); + + event TransferBatch(address indexed from, address indexed to, uint256[] ids, uint256[] amounts); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; + + function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; + + function safePhraseTransferFrom(address from, address to, uint256[] calldata phrase, bytes calldata data) external; + + function balanceOf(address owner, uint256 id) external view returns (uint256); + + function balanceOfPhrase(address owner) external view returns (uint256); + + function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory); + + function retrievePhrase(address owner, uint256 phraseId) external view returns (uint256[] memory); + + function setApprovalForAll(address operator, bool approved) external; + + function isApprovedForAll(address owner, address operator) external view returns (bool); +} +``` + +## Rationale +In [ERC-1155](./eip-1155.md) and [ERC-721](./eip-721.md), NFTs are used to represent +crypto-assets, and in this standard together with [EIP-3754](./eip-3754.md), NFTs +are equipped with utilities. In this standard, the ordering information of a batch +of NFTs is retained and managed through a construct `phrase`. + +### Phrase +A `phrase` is usually made of a handful of basic characters or an orderred sequence +of unicodes and is able to keep the ordering information in a batch of tokens. +Technically, it is stored in an array of unsigned integers, and is not supposed +to be disseminated. A phrase does not increase or decrease the amount of any NFT +in anyway. A phrase cannot be transferred, however, it can be retrieved and +decoded to restore the original sequence of unicodes. The phrase information +is kept in storage and hence additional storage than [ERC-1155](./eip-1155.md) is required. + +## Backwards Compatibility +[EIP-3754](./eip-3754.md) is the pre-requisite to this standard. + +## Reference Implementation +https://github.com/simontianx/ERC4341 + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4345.md b/EIPS/eip-4345.md new file mode 100644 index 00000000000000..34cb5bb8904f3b --- /dev/null +++ b/EIPS/eip-4345.md @@ -0,0 +1,60 @@ +--- +eip: 4345 +title: Difficulty Bomb Delay to June 2022 +description: Delays the difficulty bomb to be noticeable in June 2022. +author: Tim Beiko (@timbeiko), James Hancock (@MadeOfTin), Thomas Jay Rush (@tjayrush) +discussions-to: https://ethereum-magicians.org/t/eip-4345-difficulty-bomb-delay-to-may-2022/7209 +status: Final +type: Standards Track +category: Core +created: 2021-10-05 +--- + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 10,700,000 blocks later than the actual block number. + +## Motivation +Targeting for The Merge to occur before June 2022. If it is not ready by then, the bomb can be delayed further. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: +```py + fake_block_number = max(0, block.number - 10_700_000) if block.number >= FORK_BLOCK_NUMBER else block.number +``` +## Rationale + +The following script predicts a ~0.1 second delay to block time by June 2022 and a ~0.5 second delay by July 2022. This gives reason to address because the effect will be seen, but not so much urgency we don't have space to work around if needed. + +```python +def predict_diff_bomb_effect(current_blknum, current_difficulty, block_adjustment, months): + ''' + Predicts the effect on block time (as a ratio) in a specified amount of months in the future. + Vars used for predictions: + current_blknum = 13423376 # Oct 15, 2021 + current_difficulty = 9545154427582720 + block adjustment = 10700000 + months = 7.5 # June 2022 + months = 8.5 # July 2022 + ''' + blocks_per_month = (86400 * 30) // 13.3 + future_blknum = current_blknum + blocks_per_month * months + diff_adjustment = 2 ** ((future_blknum - block_adjustment) // 100000 - 2) + diff_adjust_coeff = diff_adjustment / current_difficulty * 2048 + return diff_adjust_coeff + + +diff_adjust_coeff = predict_diff_bomb_effect(13423376,9545154427582720,10700000,7.5) +diff_adjust_coeff = predict_diff_bomb_effect(13423376,9545154427582720,10700000,8.5) +``` + +## Backwards Compatibility +No known backward compatibility issues. + +## Security Considerations +Misjudging the effects of the difficulty can mean longer blocktimes than anticipated until a hardfork is released. Wild shifts in difficulty can affect this number severely. Also, gradual changes in blocktimes due to longer-term adjustments in difficulty can affect the timing of difficulty bomb epochs. This affects the usability of the network but unlikely to have security ramifications. + +In this specific instance, it is possible that the network hashrate drops considerably before The Merge, which could accelerate the timeline by which the bomb is felt in block times. The offset value chosen aimed to take this into account. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4353.md b/EIPS/eip-4353.md new file mode 100644 index 00000000000000..b0b4a3e0db12c0 --- /dev/null +++ b/EIPS/eip-4353.md @@ -0,0 +1,230 @@ +--- +eip: 4353 +title: Interface for Staked Tokens in NFTs +description: This interface enables access to publicly viewable staking data of an NFT. +author: Rex Creed (@aug2uag), Dane Scarborough +discussions-to: https://ethereum-magicians.org/t/eip-4353-viewing-staked-tokens-in-nft/7234 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-10-08 +requires: 165 +--- + +## Abstract +[EIP-721](./eip-721.md) tokens can be deposited or staked in NFTs for a variety of reasons including escrow, rewards, benefits, and others. There is currently no means of retrieving the number of tokens staked and/or bound to an NFT. This proposal outlines a standard that may be implemented by all wallets and marketplaces easily to correctly retrieve the staked token amount of an NFT. + +## Motivation +Without staked token data, the actual amount of staked tokens cannot be conveyed from token owners to other users, and cannot be displayed in wallets, marketplaces, or block explorers. The ability to identify and verify an exogenous value derived from the staking process may be critical to the aims of an NFT holder. + +## Specification +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC4353 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-4353. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others. + * + * Note: The ERC-165 identifier for this interface is 0x3a3d855f. + * + */ +interface IERC721Staked { + + /** + * @dev Returns uint256 amount of on-chain tokens staked to the NFT. + * + * @dev Wallets and marketplaces would need to call this for displaying + * the amount of tokens staked and/or bound to the NFT. + */ + function stakedAmount(uint256 tokenId) external view returns (uint256); + +} +``` + +### Suggested flow: + +#### Constructor/deployment +* Creator - the owner of an NFT with its own rules for depositing tokens at and/or after the minting of a token. +* Token Amount - the current amount of on-chain [EIP-20](./eip-20.md) or derived tokens bound to an NFT from one or more deposits. +* Withdraw Mechanism - rules based approach for withdrawing staked tokens and making sure to update the balance of the staked tokens. + +### Staking at mint and locking tokens in NFT +The suggested and intended implementation of this standard is to stake tokens at the time of minting an NFT, and not implementing any outbound transfer of tokens outside of `burn`. Therefore, only to stake at minting and withdraw only at burning. + +#### NFT displayed in wallet or marketplace +A wallet or marketplace checks if an NFT has publicly staked tokens available for display - if so, call `stakedAmount(tokenId)` to get the current amount of tokens staked and/or bound to the NFT. + +The logical code looks something like this and inspired by William Entriken: + +```solidity +// contracts/Token.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title Token + * @dev Very simple ERC721 example with stake interface example. + * Note this implementation enforces recommended procedure: + * 1) stake at mint + * 2) withdraw at burn + */ +contract ERC721Staked is ERC721URIStorage, Ownable { + /// @dev track original minter of tokenId + mapping (uint256 => address payable) private payees; + /// @dev map tokens to stored staked token value + mapping (uint256 => uint256) private tokenValue; + + /// @dev metadata + constructor() ERC721 ( + "Staked NFT", + "SNFT" + ){} + + /// @dev mints a new NFT + /// @param _to address that will own the minted NFT + /// @param _tokenId id the NFT + /// @param _uri metadata + function mint( + address payable _to, + uint256 _tokenId, + string calldata _uri + ) + external + payable + onlyOwner + { + _mint(_to, _tokenId); + _setTokenURI(_tokenId, _uri); + payees[_tokenId] = _to; + tokenValue[_tokenId] = msg.value; + } + + /// @dev staked interface + /// @param _tokenId id of the NFT + /// @return _value staked value + function stakedAmount( + uint256 _tokenId + ) external view returns (uint256 _value) { + _value = tokenValue[_tokenId]; + return _value; + } + + /// @dev removes NFT & transfers crypto to minter + /// @param _tokenId the NFT we want to remove + function burn( + uint256 _tokenId + ) + external + onlyOwner + { + super._burn(_tokenId); + payees[_tokenId].transfer(tokenValue[_tokenId]); + tokenValue[_tokenId] = 0; + } + +} +``` + +## Rationale +This standard is completely agnostic to how tokens are deposited or handled by the NFT. It is, therefore, the choice and responsibility of the author to encode and communicate the encoding of their tokenomics to purchasees of their token and/or to make their contracts viewable by purchasees. + +Although the intention of this standard is for tokens staked at mint and withdrawable only upon burn, the interface may be modified for dynamic withdrawing and depositing of tokens especially under DeFi application settings. In its current form, the contract logic may be the determining factor whether a deviation from the standard exists. + +## Backward Compatibility +TBD + +## Test Cases +```js +const { expect } = require("chai"); +const { ethers, waffle } = require("hardhat"); +const provider = waffle.provider; + +describe("StakedNFT", function () { + let _id = 1234567890; + let value = '1.5'; + let Token; + let Interface; + let owner; + let addr1; + let addr2; + + beforeEach(async function () { + Token = await ethers.getContractFactory("ERC721Staked"); + [owner, addr1, ...addr2] = await ethers.getSigners(); + Interface = await Token.deploy(); + }); + + describe("Staked NFT", function () { + it("Should set the right owner", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar') + expect(await Interface.ownerOf(_id)).to.equal(addr1.address); + }); + + it("Should not have staked balance without value", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar') + expect(await Interface.stakedAmount(_id)).to.equal( + ethers.utils.parseEther('0')); + }); + + it("Should set and return the staked amount", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + expect(await Interface.stakedAmount(_id)).to.equal( + ethers.utils.parseEther(value)); + }); + + it("Should decrease owner eth balance on mint (deposit)", async function () { + let balance1 = await provider.getBalance(owner.address); + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + let balance2 = await provider.getBalance(owner.address); + let diff = parseFloat(ethers.utils.formatEther( + balance1.sub(balance2))).toFixed(1); + expect(diff === value); + }); + + it("Should add to payee's eth balance on burn (withdraw)", async function () { + let balance1 = await provider.getBalance(addr1.address); + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + await Interface.burn(_id); + let balance2 = await provider.getBalance(addr1.address); + let diff = parseFloat(ethers.utils.formatEther( + balance2.sub(balance1))).toFixed(1); + expect(diff === value); + }); + + it("Should update balance after transfer", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + await Interface.burn(_id); + expect(await Interface.stakedAmount(_id)).to.equal( + ethers.utils.parseEther('0')); + }); + }); +}); +``` + +## Security Considerations +The purpose of this standard is to simply and publicly identify whether an NFT claims to have staked tokens. + +Staked claims will be unreliable without a locking mechanism enforced, for example, if staked tokens can only be transferred at burn. Otherwise, tokens may be deposited and/or withdrawn at any time via arbitrary methods. Also, contracts that may allow arbitrary transfers without updating the correct balance will result in potential issues. A strict rules-based approach should be taken with these edge cases in mind. + +A dedicated service may exist to verify the claims of a token by analyzing transactions on the explorer. In this manner, verification may be automated to ensure a token's claims are valid. The logical extension of this method may be to extend the interface and support flagging erroneous claims, all the while maintaining a simple goal of validating and verifying a staked amount exists to benefit the operator experience. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4361.md b/EIPS/eip-4361.md new file mode 100644 index 00000000000000..8ee187384a9657 --- /dev/null +++ b/EIPS/eip-4361.md @@ -0,0 +1,402 @@ +--- +eip: 4361 +title: Sign-In with Ethereum +description: Off-chain authentication for Ethereum accounts to establish sessions. +author: Wayne Chang (@wyc), Gregory Rocco (@obstropolos), Brantly Millegan (@brantlymillegan), Nick Johnson (@Arachnid), Oliver Terbu (@awoie) +discussions-to: https://ethereum-magicians.org/t/eip-4361-sign-in-with-ethereum/7263 +status: Review +type: Standards Track +category: ERC +created: 2021-10-11 +requires: 55, 137, 155, 191, 1271, 1328 +--- + +## Abstract + +Sign-In with Ethereum describes how Ethereum accounts authenticate with off-chain services by signing a standard message format parameterized by scope, session details, and security mechanisms (e.g., a nonce). The goals of this specification are to provide a self-custodied alternative to centralized identity providers, improve interoperability across off-chain services for Ethereum-based authentication, and provide wallet vendors a consistent machine-readable message format to achieve improved user experiences and consent management. + +## Motivation + +When signing in to popular non-blockchain services today, users will typically use identity providers (IdPs) that are centralized entities with ultimate control over users' identifiers, for example, large internet companies and email providers. Incentives are often misaligned between these parties. Sign-In with Ethereum offers a new self-custodial option for users who wish to assume more control and responsibility over their own digital identity. + +Already, many services support workflows to authenticate Ethereum accounts using message signing, such as to establish a cookie-based web session which can manage privileged metadata about the authenticating address. This is an opportunity to standardize the sign-in workflow and improve interoperability across existing services, while also providing wallet vendors a reliable method to identify signing requests as Sign-In with Ethereum requests for improved UX. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Overview + +Sign-In with Ethereum (SIWE) works as follows: + +1. The relying party generates a SIWE Message and prefixes the SIWE Message with `\x19Ethereum Signed Message:\n` as defined in [ERC-191](./eip-191.md). +2. The wallet presents the user with a structured plaintext message or equivalent interface for signing the SIWE Message with the [ERC-191](./eip-191.md) signed data format. +3. The signature is then presented to the relying party, which checks the signature's validity and SIWE Message content. +4. The relying party might further fetch data associated with the Ethereum address, such as from the Ethereum blockchain (e.g., ENS, account balances, [ERC-20](./eip-20.md)/[ERC-721](./eip-721.md)/[ERC-1155](./eip-1155.md) asset ownership), or other data sources that might or might not be permissioned. + +### Message Format + +#### ABNF Message Format + +A SIWE Message MUST conform with the following Augmented Backus–Naur Form (ABNF, RFC 5234) expression (note that `%s` denotes case sensitivity for a string term, as per RFC 7405). + +```abnf +sign-in-with-ethereum = + [ scheme "://" ] domain %s" wants you to sign in with your Ethereum account:" LF + address LF + LF + [ statement LF ] + LF + %s"URI: " uri LF + %s"Version: " version LF + %s"Chain ID: " chain-id LF + %s"Nonce: " nonce LF + %s"Issued At: " issued-at + [ LF %s"Expiration Time: " expiration-time ] + [ LF %s"Not Before: " not-before ] + [ LF %s"Request ID: " request-id ] + [ LF %s"Resources:" + resources ] + +scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + ; See RFC 3986 for the fully contextualized + ; definition of "scheme". + +domain = authority + ; From RFC 3986: + ; authority = [ userinfo "@" ] host [ ":" port ] + ; See RFC 3986 for the fully contextualized + ; definition of "authority". + +address = "0x" 40*40HEXDIG + ; Must also conform to captilization + ; checksum encoding specified in EIP-55 + ; where applicable (EOAs). + +statement = *( reserved / unreserved / " " ) + ; See RFC 3986 for the definition + ; of "reserved" and "unreserved". + ; The purpose is to exclude LF (line break). + +uri = URI + ; See RFC 3986 for the definition of "URI". + +version = "1" + +chain-id = 1*DIGIT + ; See EIP-155 for valid CHAIN_IDs. + +nonce = 8*( ALPHA / DIGIT ) + ; See RFC 5234 for the definition + ; of "ALPHA" and "DIGIT". + +issued-at = date-time +expiration-time = date-time +not-before = date-time + ; See RFC 3339 (ISO 8601) for the + ; definition of "date-time". + +request-id = *pchar + ; See RFC 3986 for the definition of "pchar". + +resources = *( LF resource ) + +resource = "- " URI +``` + +#### Message Fields + +This specification defines the following SIWE Message fields that can be parsed from a SIWE Message by following the rules in [ABNF Message Format](#abnf-message-format): + +- `scheme` OPTIONAL. The URI scheme of the origin of the request. Its value MUST be a RFC 3986 URI scheme. +- `domain` REQUIRED. The domain that is requesting the signing. Its value MUST be a RFC 3986 authority. The authority includes an OPTIONAL port. If the port is not specified, the default port for the provided `scheme` is assumed (e.g., 443 for HTTPS). If `scheme` is not specified, HTTPS is assumed by default. +- `address` REQUIRED. The Ethereum address performing the signing. Its value SHOULD be conformant to mixed-case checksum address encoding specified in [ERC-55](./eip-55.md) where applicable. +- `statement` OPTIONAL. A human-readable ASCII assertion that the user will sign which MUST NOT include `'\n'` (the byte `0x0a`). +- `uri` REQUIRED. An RFC 3986 URI referring to the resource that is the subject of the signing (as in the _subject of a claim_). +- `version` REQUIRED. The current version of the SIWE Message, which MUST be `1` for this specification. +- `chain-id` REQUIRED. The [EIP-155](./eip-155.md) Chain ID to which the session is bound, and the network where Contract Accounts MUST be resolved. +- `nonce` REQUIRED. A random string typically chosen by the relying party and used to prevent replay attacks, at least 8 alphanumeric characters. +- `issued-at` REQUIRED. The time when the message was generated, typically the current time. Its value MUST be an ISO 8601 datetime string. +- `expiration-time` OPTIONAL. The time when the signed authentication message is no longer valid. Its value MUST be an ISO 8601 datetime string. +- `not-before` OPTIONAL. The time when the signed authentication message will become valid. Its value MUST be an ISO 8601 datetime string. +- `request-id` OPTIONAL. An system-specific identifier that MAY be used to uniquely refer to the sign-in request. +- `resources` OPTIONAL. A list of information or references to information the user wishes to have resolved as part of authentication by the relying party. Every resource MUST be a RFC 3986 URI separated by `"\n- "` where `\n` is the byte `0x0a`. + +#### Informal Message Template + +A Bash-like informal template of the full SIWE Message is presented below for readability and ease of understanding, and it does not reflect the allowed optionality of the fields. Field descriptions are provided in the following section. A full ABNF description is provided in [ABNF Message Format](#abnf-message-format). + +``` +${scheme}:// ${domain} wants you to sign in with your Ethereum account: +${address} + +${statement} + +URI: ${uri} +Version: ${version} +Chain ID: ${chain-id} +Nonce: ${nonce} +Issued At: ${issued-at} +Expiration Time: ${expiration-time} +Not Before: ${not-before} +Request ID: ${request-id} +Resources: +- ${resources[0]} +- ${resources[1]} +... +- ${resources[n]} +``` + +#### Examples + +The following is an example SIWE Message with an implicit scheme: + +``` +example.com wants you to sign in with your Ethereum account: +0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 + +I accept the ExampleOrg Terms of Service: https://example.com/tos + +URI: https://example.com/login +Version: 1 +Chain ID: 1 +Nonce: 32891756 +Issued At: 2021-09-30T16:25:24Z +Resources: +- ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ +- https://example.com/my-web2-claim.json +``` + +The following is an example SIWE Message with an implicit scheme and explicit port: + +``` +example.com:3388 wants you to sign in with your Ethereum account: +0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 + +I accept the ExampleOrg Terms of Service: https://example.com/tos + +URI: https://example.com/login +Version: 1 +Chain ID: 1 +Nonce: 32891756 +Issued At: 2021-09-30T16:25:24Z +Resources: +- ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ +- https://example.com/my-web2-claim.json +``` + +The following is an example SIWE Message with an explicit scheme: + +``` +https://example.com wants you to sign in with your Ethereum account: +0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 + +I accept the ExampleOrg Terms of Service: https://example.com/tos + +URI: https://example.com/login +Version: 1 +Chain ID: 1 +Nonce: 32891756 +Issued At: 2021-09-30T16:25:24Z +Resources: +- ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ +- https://example.com/my-web2-claim.json +``` + +### Signing and Verifying Messages with Ethereum Accounts + +- For Externally Owned Accounts (EOAs), the verification method specified in [ERC-191](./eip-191.md) MUST be used. + +- For Contract Accounts, + - The verification method specified in [ERC-1271](./eip-1271.md) SHOULD be used, and if it is not, the implementer MUST clearly define the verification method to attain security and interoperability for both wallets and relying parties. + - When performing [ERC-1271](./eip-1271.md) signature verification, the contract performing the verification MUST be resolved from the specified `chain-id`. + - Implementers SHOULD take into consideration that [ERC-1271](./eip-1271.md) implementations are not required to be pure functions, and can return different results for the same inputs depending on blockchain state. This can affect the security model and session validation rules. For example, a service with [ERC-1271](./eip-1271.md) signing enabled could rely on webhooks to receive notifications when state affecting the results is changed. When it receives a notification, it invalidates any matching sessions. + +### Resolving Ethereum Name Service (ENS) Data + +- The relying party or wallet MAY additionally perform resolution of ENS data, as this can improve the user experience by displaying human-friendly information that is related to the `address`. Resolvable ENS data include: + - The [primary ENS name](./eip-181.md). + - The ENS avatar. + - Any other resolvable resources specified in the ENS documentation. +- If resolution of ENS data is performed, implementers SHOULD take precautions to preserve user privacy and consent, as their `address` could be forwarded to third party services as part of the resolution process. + +### Relying Party Implementer Steps + +#### Specifying the Request Origin + +- The `domain` and, if present, the `scheme`, in the SIWE Message MUST correspond to the origin from where the signing request was made. For instance, if the signing request is made within a cross-origin iframe embedded in a parent browser window, the `domain` (and, if present, the `scheme`) have to match the origin of the iframe, rather than the origin of the parent. This is crucial to prevent the iframe from falsely asserting the origin of one of its ancestor windows for security reasons. This behavior is enforced by conforming wallets. + +#### Verifying a signed Message + +- The SIWE Message MUST be checked for conformance to the ABNF Message Format in the previous sections, checked against expected values after parsing (e.g., `expiration-time`, `nonce`, `request-uri` etc.), and its signature MUST be checked as defined in [Signing and Verifying Messages with Ethereum Accounts](#signing-and-verifying-messages-with-ethereum-accounts). + +#### Creating Sessions + +- Sessions MUST be bound to the `address` and not to further resolved resources that can change. + +#### Interpreting and resolving Resources + +- Implementers SHOULD ensure that that URIs in the listed `resources` are human-friendly when expressed in plaintext form. +- The intepretation of the listed `resources` in the SIWE Message is out of scope of this specification. + +### Wallet Implementer Steps + +#### Verifying the Message Format + +- The full SIWE message MUST be checked for conformance to the ABNF defined in [ABNF Message Format](#abnf-message-format). +- Wallet implementers SHOULD warn users if the substring `"wants you to sign in + with your Ethereum account"` appears anywhere in an [ERC-191](./eip-191.md) message signing + request unless the message fully conforms to the format defined [ABNF Message Format](#abnf-message-format). + +#### Verifying the Request Origin + +- Wallet implementers MUST prevent phishing attacks by verifying the origin of the request against the `scheme` and `domain` fields in the SIWE Message. For example, when processing the SIWE message beginning with `"example.com wants you to sign in..."`, the wallet checks that the request actually originated from `https://example.com`. +- The origin SHOULD be read from a trusted data source such as the browser window or over WalletConnect ([ERC-1328](./eip-1328.md)) sessions for comparison against the signing message contents. +- Wallet implementers MAY warn instead of rejecting the verification if the origin is pointing to localhost. + +The following is a RECOMMENDED algorithm for Wallets to conform with the requirements on request origin verification defined by this specification. + +The algorithm takes the following input variables: + +- fields from the SIWE message. +- `origin` of the signing request - in the case of a browser wallet implementation - the origin of the page which requested the signin via the provider. +- `allowedSchemes` - a list of schemes allowed by the Wallet. +- `defaultScheme` - a scheme to assume when none was provided. Wallet implementers in the browser SHOULD use `https`. +- developer mode indication - a setting deciding if certain risks should be a warning instead of rejection. Can be manually configured or derived from `origin` being localhost. + +The algorithm is described as follows: + +- If `scheme` was not provided, then assign `defaultScheme` as `scheme`. +- If `scheme` is not contained in `allowedSchemes`, then the `scheme` is not expected and the Wallet MUST reject the request. Wallet implementers in the browser SHOULD limit the list of `allowedSchemes` to just `'https'` unless a developer mode is activated. +- If `scheme` does not match the scheme of `origin`, the Wallet SHOULD reject the request. Wallet implementers MAY show a warning instead of rejecting the request if a developer mode is activated. In that case the Wallet continues processing the request. +- If the `host` part of the `domain` and `origin` do not match, the Wallet MUST reject the request unless the Wallet is in developer mode. In developer mode the Wallet MAY show a warning instead and continues procesing the request. +- If `domain` and `origin` have mismatching subdomains, the Wallet SHOULD reject the request unless the Wallet is in developer mode. In developer mode the Wallet MAY show a warning instead and continues procesing the request. +- Let `port` be the port component of `domain`, and if no port is contained in `domain`, assign `port` the default port specified for the `scheme`. +- If `port` is not empty, then the Wallet SHOULD show a warning if the `port` does not match the port of `origin`. +- If `port` is empty, then the Wallet MAY show a warning if `origin` contains a specific port. (Note 'https' has a default port of 443 so this only applies if `allowedSchemes` contain unusual schemes) +- Return request origin verification completed. + +#### Creating Sign-In with Ethereum Interfaces + +- Wallet implementers MUST display to the user the following fields from the SIWE Message request by default and prior to signing, if they are present: `scheme`, `domain`, `address`, `statement`, and `resources`. Other present fields MUST also be made available to the user prior to signing either by default or through an extended interface. +- Wallet implementers displaying a plaintext SIWE Message to the user SHOULD require the user to scroll to the bottom of the text area prior to signing. +- Wallet implementers MAY construct a custom SIWE user interface by parsing the ABNF terms into data elements for use in the interface. The display rules above still apply to custom interfaces. + +#### Supporting internationalization (i18n) + +- After successfully parsing the message into ABNF terms, translation MAY happen at the UX level per human language. + +## Rationale + +### Requirements + +Write a specification for how Sign-In with Ethereum should work. The specification should be simple and generally follow existing practices. Avoid feature bloat, particularly the inclusion of lesser-used projects who see getting into the specification as a means of gaining adoption. The core specification should be decentralized, open, non-proprietary, and have long-term viability. It should have no dependence on a centralized server except for the servers already being run by the application that the user is signing in to. The basic specification should include: Ethereum accounts used for authentication, ENS names for usernames (via reverse resolution), and data from the ENS name’s text records for additional profile information (e.g. avatar, social media handles, etc). + +Additional functional requirements: + +1. The user must be presented a human-understandable interface prior to signing, mostly free of machine-targeted artifacts such as JSON blobs, hex codes (aside from the Ethereum address), and baseXX-encoded strings. +2. The application server must be able to implement fully usable support for its end without forcing a change in the wallets. +3. There must be a simple and straightforward upgrade path for both applications and wallets already using Ethereum account-based signing for authentication. +4. There must be facilities and guidelines for adequate mitigation of Man-in-the-Middle (MITM) attacks, replay attacks, and malicious signing requests. + +### Design Goals + +1. Human-Friendly +2. Simple to Implement +3. Secure +4. Machine Readable +5. Extensible + +### Technical Decisions + +- Why [ERC-191](./eip-191.md) (Signed Data Standard) over [EIP-712](./eip-712.md) (Ethereum typed structured data hashing and signing) + - [ERC-191](./eip-191.md) is already broadly supported across wallets UX, while [EIP-712](./eip-712.md) support for friendly user display is pending. **(1, 2, 3, 4)** + - [ERC-191](./eip-191.md) is simple to implement using a pre-set prefix prior to signing, while [EIP-712](./eip-712.md) is more complex to implement requiring the further implementations of a bespoke Solidity-inspired type system, RLP-based encoding format, and custom keccak-based hashing scheme. **(2)** + - [ERC-191](./eip-191.md) produces more human-readable messages, while [EIP-712](./eip-712.md) creates signing outputs for machine consumption, with most wallets not displaying the payload to be signed in a manner friendly to humans. **(1)**![](../assets/eip-4361/signing.png) + + - [EIP-712](./eip-712.md) has the advantage of on-chain representation and on-chain verifiability, such as for their use in metatransactions, but this feature is not relevant for the specification's scope. **(2)** +- Why not use JWTs? Wallets don't support JWTs. The keccak hash function is not assigned by IANA for use as a JOSE algorithm. **(2, 3)** +- Why not use YAML or YAML with exceptions? YAML is loose compared to ABNF, which can readily express character set limiting, required ordering, and strict whitespacing. **(2, 3)** + +### Out of Scope + +The following concerns are out of scope for this version of the specification to define: + +- Additional authentication not based on Ethereum addresses. +- Authorization to server resources. +- Interpretation of the URIs in the `resources` field as claims or other resources. +- The specific mechanisms to ensure domain-binding. +- The specific mechanisms to generate nonces and evaluation of their appropriateness. +- Protocols for use without TLS connections. + +### Considerations for Forwards Compatibility + +The following items are considered for future support in either through an iteration of this specification or new work items using this specification as a dependency. + +- Possible support for Decentralized Identifiers and Verifiable Credentials. +- Possible cross-chain support. +- Possible SIOPv2 support. +- Possible future support for [EIP-712](./eip-712.md). +- Version interpretation rules, e.g., sign with minor revision greater than understood, but not greater major version. + +## Backwards Compatibility + +- Most wallet implementations already support [ERC-191](./eip-191.md), so this is used as a base pattern with additional features. +- Requirements were gathered from existing implementations of similar sign-in workflows, including statements to allow the user to accept a Terms of Service, nonces for replay protection, and inclusion of the Ethereum address itself in the message. + +## Reference Implementation + +A reference implementation is available [here](../assets/eip-4361/example.js). + +## Security Considerations + +### Identifier Reuse + +- Towards perfect privacy, it would be ideal to use a new uncorrelated identifier (e.g., Ethereum address) per digital interaction, selectively disclosing the information required and no more. +- This concern is less relevant to certain user demographics who are likely to be early adopters of this specification, such as those who manage an Ethereum address and/or ENS names intentionally associated with their public presence. These users often prefer identifier reuse to maintain a single correlated identity across many services. +- This consideration will become increasingly important with mainstream adoption. There are several ways to move towards this model, such as using HD wallets, signed delegations, and zero-knowledge proofs. However, these approaches are out of scope for this specification and better suited for follow-on specifications. + +### Key Management + +- Sign-In with Ethereum gives users control through their keys. This is additional responsibility that mainstream users may not be accustomed to accepting, and key management is a hard problem especially for individuals. For example, there is no "forgot password" button as centralized identity providers commonly implement. +- Early adopters of this specification are likely to be already adept at key management, so this consideration becomes more relevant with mainstream adoption. +- Certain wallets can use smart contracts and multisigs to provide an enhanced user experiences with respect to key usage and key recovery, and these can be supported via [ERC-1271](./eip-1271.md) signing. + +### Wallet and Relying Party combined Security + +- Both the wallet and relying party have to implement this specification for improved security to the end user. Specifically, the wallet has to confirm that the SIWE Message is for the correct request origin or provide the user means to do so manually (such as instructions to visually confirming the correct domain in a TLS-protected website prior to connecting via QR code or deeplink), otherwise the user is subject to phishing attacks. + +### Minimizing Wallet and Server Interaction + +- In some implementions of wallet sign-in workflows, the server first sends parameters of the SIWE Message to the wallet. Others generate the SIWE message for signing entirely in the client side (e.g., dapps). The latter approach without initial server interaction SHOULD be preferred when there is a user privacy advantage by minimizing wallet-server interaction. Often, the backend server first produces a `nonce` to prevent replay attacks, which it verifies after signing. Privacy-preserving alternatives are suggested in the next section on preventing replay attacks. +- Before the wallet presents the SIWE message signing request to the user, it MAY consult the server for the proper contents of the message to be signed, such as an acceptable `nonce` or requested set of `resources`. When communicating to the server, the wallet SHOULD take precautions to protect user privacy by mitigating user information revealed as much as possible. +- Prior to signing, the wallet MAY consult the user for preferences, such as the selection of one `address` out of many, or a preferred ENS name out of many. + +### Preventing Replay Attacks + +- A `nonce` SHOULD be selected per session initiation with enough entropy to prevent replay attacks, a man-in-the-middle attack in which an attacker is able to capture the user's signature and resend it to establish a new session for themselves. +- Implementers MAY consider using privacy-preserving yet widely-available `nonce` values, such as one derived from a recent Ethereum block hash or a recent Unix timestamp. + +### Preventing Phishing Attacks + +- To prevent phishing attacks Wallets have to implement request origin verification as described in [Verifying the Request Origin](#verifying-the-request-origin). + +### Channel Security + +- For web-based applications, all communications SHOULD use HTTPS to prevent man-in-the-middle attacks on the message signing. +- When using protocols other than HTTPS, all communications SHOULD be protected with proper techniques to maintain confidentiality, data integrity, and sender/receiver authenticity. + +### Session Invalidation + +There are several cases where an implementer SHOULD check for state changes as they relate to sessions. + +- If an [ERC-1271](./eip-1271.md) implementation or dependent data changes the signature computation, the server SHOULD invalidate sessions appropriately. +- If any resources specified in `resources` change, the server SHOULD invalidate sessions appropriately. However, the interpretation of `resources` is out of scope of this specification. + +### Maximum Lengths for ABNF Terms + +- While this specification does not contain normative requirements around maximum string lengths, implementers SHOULD choose maximum lengths for terms that strike a balance across the prevention of denial of service attacks, support for arbitrary use cases, and user readability. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4393.md b/EIPS/eip-4393.md new file mode 100644 index 00000000000000..5ecd4f11f029d0 --- /dev/null +++ b/EIPS/eip-4393.md @@ -0,0 +1,363 @@ +--- +eip: 4393 +title: Micropayments for NFTs and Multi Tokens +description: An interface for tip tokens that allows tipping to holders of NFTs and multi tokens +author: Jules Lai (@julesl23) +discussions-to: https://ethereum-magicians.org/t/eip-proposal-micropayments-standard-for-nfts-and-multi-tokens/7366 +status: Draft +type: Standards Track +category: ERC +created: 2021-10-24 +requires: 165, 721, 1155 +--- + +## Abstract + +This standard outlines a smart contract interface for tipping to non-fungible and multi tokens. Holders of the tokens are able to withdraw the tips as [EIP-20](./eip-20.md) rewards. + +For the purpose of this EIP, a micropayment is termed as a financial transaction that involves usually a small sum of money called "tips" that are sent to specific [EIP-721](./eip-721.md) NFTs and [EIP-1155](./eip-1155.md) multi tokens, as rewards to their holders. A holder (also referred to as controller) is used as a more generic term for owner, as NFTs may represent non-digital assets such as services. + +## Motivation + +A cheap way to send tips to any type of NFT or multi token. This can be achieved by gas optimising the tip token contract and sending the tips in batches using the `tipBatch` function from the interface. + +To make it easy to implement into dapps a tipping service to reward the NFT and multi token holders. Allows for fairer distribution of revenue back to NFT holders from the user community. + +To make the interface as minimal as possible in order to allow adoption into many different use cases. + +Some use cases include: + +- In game purchases and other virtual goods + +- Tipping messages, posts, music and video content + +- Donations/crowdfunding + +- Distribution of royalties + +- Pay per click advertising + +- Incentivising use of services + +- Reward cards and coupons + +These can all leverage the security, immediacy and transparency of blockchain. + +## Specification + +This standard proposal outlines a generalised way to allow tipping via implementation of an `ITipToken` interface. The interface is intentionally kept to a minimum in order to allow for maximum use cases. + +Smart contracts implementing this EIP standard MUST implement all of the functions in this EIP interface. MUST also emit the events specified in the interface so that a complete state of the tip token contract can be derived from the events emitted alone. + +Smart contracts implementing this EIP standard MUST implement the [EIP-165](./eip-165.md) supportsInterface function and MUST return the constant value true if 0xE47A7022 is passed through the interfaceID argument. Note that revert in this document MAY mean a require, throw (not recommended as depreciated) or revert solidity statement with or without error messages. + +Note that, nft (or NFT in caps) in the code and as mentioned in this document, MAY also refer to an EIP-1155 fungible token. + +```solidity +interface ITipToken { + /** + @dev This emits when the tip token implementation approves the address + of an NFT for tipping. + The holders of the 'nft' are approved to receive rewards. + When an NFT Transfer event emits, this also indicates that the approved + addresses for that NFT (if any) is reset to none. + Note: the ERC-165 identifier for this interface is 0x985A3267. + */ + event ApprovalForNFT( + address[] holders, + address indexed nft, + uint256 indexed id, + bool approved + ); + + /** + @dev This emits when a user has deposited an ERC-20 compatible token to + the tip token's contract address or to an external address. + This also indicates that the deposit has been exchanged for an + amount of tip tokens + */ + event Deposit( + address indexed user, + address indexed rewardToken, + uint256 amount, + uint256 tipTokenAmount + ); + + /** + @dev This emits when a holder withdraws an amount of ERC-20 compatible + reward. This reward comes from the tip token's contract address or from + an external address, depending on the tip token implementation + */ + event WithdrawReward( + address indexed holder, + address indexed rewardToken, + uint256 amount + ); + + /** + @dev This emits when the tip token constructor or initialize method is + executed. + Importantly the ERC-20 compatible token 'rewardToken_' to use as reward + to NFT holders is set at this time and remains the same throughout the + lifetime of the tip token contract. + The 'rewardToken_' and 'tipToken_' MAY be the same. + */ + event InitializeTipToken( + address indexed tipToken_, + address indexed rewardToken_, + address owner_ + ); + + /** + @dev This emits every time a user tips an NFT holder. + Also includes the reward token address and the reward token amount that + will be held pending until the holder withdraws the reward tokens. + */ + event Tip( + address indexed user, + address[] holder, + address indexed nft, + uint256 id, + uint256 amount, + address rewardToken, + uint256[] rewardTokenAmount + ); + + /** + @notice Enable or disable approval for tipping for a single NFT held + by a holder or a multi token shared by holders + @dev MUST revert if calling nft's supportsInterface does not return + true for either IERC721 or IERC1155. + MUST revert if any of the 'holders' is the zero address. + MUST revert if 'nft' has not approved the tip token contract address as operator. + MUST emit the 'ApprovalForNFT' event to reflect approval or not approval. + @param holders The holders of the NFT (NFT controllers) + @param nft The NFT contract address + @param id The NFT token id + @param approved True if the 'holder' is approved, false to revoke approval + */ + function setApprovalForNFT( + address[] calldata holders, + address nft, + uint256 id, + bool approved + ) external; + + /** + @notice Checks if 'holder' and 'nft' with token 'id' have been approved + by setApprovalForNFT + @dev This does not check that the holder of the NFT has changed. That is + left to the implementer to detect events for change of ownership and to + take appropriate action + @param holder The holder of the NFT (NFT controller) + @param nft The NFT contract address + @param id The NFT token id + @return True if 'holder' and 'nft' with token 'id' have previously been + approved by the tip token contract + */ + function isApprovalForNFT( + address holder, + address nft, + uint256 id + ) external returns (bool); + + /** + @notice Sends tip from msg.sender to holder of a single NFT or + to shared holders of a multi token + @dev If 'nft' has not been approved for tipping, MUST revert + MUST revert if 'nft' is zero address. + MUST burn the tip 'amount' to the 'holder' and send the reward to + an account pending for the holder(s). + If 'nft' is a multi token that has multiple holders then each holder + MUST receive tip amount in proportion of their balance of multi tokens + MUST emit the 'Tip' event to reflect the amounts that msg.sender tipped + to holder(s) of 'nft'. + @param nft The NFT contract address + @param id The NFT token id + @param amount Amount of tip tokens to send to the holder of the NFT + */ + function tip( + address nft, + uint256 id, + uint256 amount + ) external; + + /** + @notice Sends a batch of tips to holders of 'nfts' for gas efficiency + @dev If NFT has not been approved for tipping, revert + MUST revert if the input arguments lengths are not all the same + MUST revert if any of the user addresses are zero + MUST revert the whole batch if there are any errors + MUST emit the 'Tip' events so that the state of the amounts sent to + each holder and for which nft and from whom, can be reconstructed. + @param users User accounts to tip from + @param nfts The NFT contract addresses whose holders to tip to + @param ids The NFT token ids that uniquely identifies the 'nfts' + @param amounts Amount of tip tokens to send to the holders of the NFTs + */ + function tipBatch( + address[] calldata users, + address[] calldata nfts, + uint256[] calldata ids, + uint256[] calldata amounts + ) external; + + /** + @notice Deposit an ERC-20 compatible token in exchange for tip tokens + @dev The price of tip tokens can be different for each deposit as + the amount of reward token sent ultimately is a ratio of the + amount of tip tokens to tip over the user's tip tokens balance available + multiplied by the user's deposit balance. + The deposited tokens can be held in the tip tokens contract account or + in an external escrow. This will depend on the tip token implementation. + Each tip token contract MUST handle only one type of ERC-20 compatible + reward for deposits. + This token address SHOULD be passed in to the tip token constructor or + initialize method. SHOULD revert if ERC-20 reward for deposits is + zero address. + MUST emit the 'Deposit' event that shows the user, deposited token details + and amount of tip tokens minted in exchange + @param user The user account + @param amount Amount of ERC-20 token to deposit in exchange for tip tokens. + This deposit is to be used later as the reward token + */ + function deposit(address user, uint256 amount) external payable; + + /** + @notice An NFT holder can withdraw their tips as an ERC-20 compatible + reward at a time of their choosing + @dev MUST revert if not enough balance pending available to withdraw. + MUST send 'amount' to msg.sender account (the holder) + MUST reduce the balance of reward tokens pending by the 'amount' withdrawn. + MUST emit the 'WithdrawReward' event to show the holder who withdrew, the reward + token address and 'amount' + @param amount Amount of ERC-20 token to withdraw as a reward + */ + function withdrawReward(uint256 amount) external payable; + + /** + @notice MUST have identical behaviour to ERC-20 balanceOf and is the amount + of tip tokens held by 'user' + @param user The user account + @return The balance of tip tokens held by user + */ + function balanceOf(address user) external view returns (uint256); + + /** + @notice The balance of deposit available to become rewards when + user sends the tips + @param user The user account + @return The remaining balance of the ERC-20 compatible token deposited + */ + function balanceDepositOf(address user) external view returns (uint256); + + /** + @notice The amount of reward token owed to 'holder' + @dev The pending tokens can come from the tip token contract account + or from an external escrow, depending on tip token implementation + @param holder The holder of NFT(s) (NFT controller) + @return The amount of reward tokens owed to the holder from tipping + */ + function rewardPendingOf(address holder) external view returns (uint256); +} +``` + +### Tipping and rewards to holders + +A user first deposits a compatible EIP-20 to the tip token contract that is then held (less any agreed fee) in escrow, in exchange for tip tokens. These tip tokens can then be sent by the user to NFTs and multi tokens (that have been approved by the tip token contract for tipping) to be redeemed for the original EIP-20 deposits on withdrawal by the holders as rewards. + +### Tip Token transfer and value calculations + +Tip token values are exchanged with EIP-20 deposits and vice-versa. It is left to the tip token implementer to decide on the price of a tip token and hence how much tip to mint in exchange for the EIP-20 deposited. One possibility is to have fixed conversion rates per geographical region so that users from poorer countries are able to send the same number of tips as those from richer nations for the same level of appreciation for content/assets etc. Hence, not skewed by average wealth when it comes to analytics to discover what NFTs are actually popular, allowing creators to have a level playing field. + +Whenever a user sends a tip, an equivalent value of deposited EIP-20 MUST be transferred to a pending account for the NFT or multi token holder, and the tip tokens sent MUST be burnt. This equivalent value is calculated using a simple formula: + +_total user balance of EIP-20 deposit _ tip amount / total user balance of tip tokens\* + +Thus adding \*free\* tips to a user's balance of tips for example, simply dilutes the overall value of each tip for that user, as collectively they still refer to the same amount of EIP-20 deposited. + +Note if the tip token contract inherits from an EIP-20, tips can be transferred from one user to another directly. The deposit amount would be already in the tip token contract (or an external escrow account) so only tip token contract's internal mapping of user account to deposit balances needs to be updated. It is RECOMMENDED that the tip amount be burnt from user A and then minted back to user B in the amount that keeps user B's average EIP-20 deposited value per tip the same, so that the value of the tip does not fluctuate in the process of tipping. + +If not inheriting from EIP-20, then minting the tip tokens MUST emit `event Transfer(address indexed from, address indexed to, uint256 value)` where sender is the zero address for a mint and to is the zero address for a burn. The Transfer event MUST be the same signature as the Transfer function in the `IERC20` interface. + +### Royalty distribution + +EIP-1155 allows for shared holders of a token id. Imagine a scenario where an article represented by an NFT was written by multiple contributors. Here, each contributor is a holder and the fractional sharing percentage between them can be represented by the balance that each holds in the EIP-1155 token id. So for two holders A and B of EIP-1155 token 1, if holder A's balance is 25 and holder B's is 75 then any tip sent to token 1 would distribute 25% of the reward pending to holder A and the remaining 75% pending to holder B. + +Here is an example implementation of ITipToken contract data structures: + +```solidity + /// Mapping from NFT/multi token to token id to holder(s) + mapping(address => mapping(uint256 => address[])) private _tokenIdToHolders; + + /// Mapping from user to user's deposit balance + mapping(address => uint256) private _depositBalances; + + /// Mapping from holder to holder's reward pending amount + mapping(address => uint256) private _rewardsPending; +``` + +This copes with EIP-721 contracts that must have unique token ids and single holders (to be compliant with the standard), and EIP-1155 contracts that can have multiple token ids and multiple holders per instance. The `tip` function implementation would then access `_tokenIdToHolders` via indices NFT/multi token address and token id to distribute to holder's or holders' `_rewardsPending`. + +For scenarios where royalties are to be distributed to holders directly, then implementation of the `tip` method of `ITipToken` contract MAY send the royalty amount straight from the user's account to the holder of a single NFT or to the shared holders of a multi token, less an optional agreed fee. In this case, the tip token type is the reward token. + +### Caveats + +To keep the `ITipToken` interface simple and general purpose, each tip token contract MUST use one EIP-20 compatible deposit type at a time. If tipping is required to support many EIP-20 deposits then each tip token contract MUST be deployed separately per EIP-20 compatible type required. Thus, if tipping is required from both ETH and BTC wrapper EIP-20 deposits then the tip token contract is deployed twice. The tip token contract's constructor is REQUIRED to pass in the address of the EIP-20 token supported for the deposits for the particular tip token contract. Or in the case for upgradeable tip token contracts, an initialize method is REQUIRED to pass in the EIP-20 token address. + +This EIP does not provide details for where the EIP-20 reward deposits are held. It MUST be available at the time a holder withdraws the rewards that they are owed. A RECOMMENDED implementation would be to keep the deposits locked in the tip token contract address. By keeping a mapping structure that records the balances pending to holders then the +deposits can remain where they are when a user tips, and only transferred out to a holder's address when a holder withdraws it as their reward. + +This standard does not specify the type of EIP-20 compatible deposits allowed. Indeed, could be tip tokens themselves. But it is RECOMMENDED that balances of the deposits be checked after transfer to find out the exact amount deposited to keep internal accounting consistent. In case, for example, the EIP-20 contract takes fees and hence reduces the actual amount deposited. + +This standard does not specify any functionality for refunds for deposits nor for tip tokens sent, it is left to the implementor to add this to their smart contract(s). The reasoning for this is to keep the interface light and not to enforce upon implementors the need for refunds but to leave that as a choice. + +### Minimising Gas Costs + +By caching tips off-chain and then batching them up to call the `tipBatch` method of the ITipToken interface then essentially the cost of initialising transactions is paid once rather than once per tip. Plus, further gas savings can be made off-chain if multiple tips sent by the same user to the same NFT token are accumulated together and sent as one entry in the batch. + +Further savings can be made by grouping users together sending to the same NFT, so that checking the validity of the NFT and whether it is an EIP-721 or EIP-1155, is performed once for each group. + +Clever ways to minimise on-chain state updating of the deposit balances for each user and the reward balances of each holder, can help further to minimise the gas costs when sending in a batch if the batch is ordered beforehand. For example, can avoid the checks if the next NFT in the batch is the same. This left to the tip token contract implementer. Whatever optimisation is applied, it MUST still allow information of which account tipped which account and for what NFT to be reconstructed from the Tip and the TipBatch events emitted. + +## Rationale + +### Simplicity + +ITipToken interface uses a minimal number of functions, in order to keep its use as general purpose as possible, whilst there being enough to guide implementation that fulfils its purpose for micropayments to NFT holders. + +### Use of NFTs + +Each NFT is a unique non-fungible token digital asset stored on the blockchain that are uniquely identified by its address and token id. It's truth burnt using cryptographic hashing on a secure blockchain means that it serves as an anchor for linking with a unique digital asset, service or other contractual agreement. Such use cases may include (but only really limited by imagination and acceptance): + +- Digital art, collectibles, music, video, licenses and certificates, event tickets, ENS names, gaming items, objects in metaverses, proof of authenticity of physical items, service agreements etc. + +This mechanism allows consumers of the NFT a secure way to easily tip and reward the NFT holder. + +### New Business Models + +To take the music use case for example. Traditionally since the industry transitioned from audio distributed on physical medium such as CDs, to an online digital distribution model via streaming, the music industry has been controlled by oligopolies that served to help in the transition. They operate a fixed subscription model and from that they set the amount of royalty distribution to content creators; such as the singers, musicians etc. Using tip tokens represent an additional way for fans of music to reward the content creators. Each song or track is represented by an NFT and fans are able to tip the song (hence the NFT) that they like, and in turn the content creators of the NFT are able to receive the EIP-20 rewards that the tips were bought for. A fan led music industry with decentralisation and tokenisation is expected to bring new revenue, and bring fans and content creators closer together. + +Across the board in other industries a similar ethos can be applied where third party controllers move to a more facilitating role rather than a monetary controlling role that exists today. + +### Guaranteed audit trail + +As the Ethereum ecosystem continues to grow, many dapps are relying on traditional databases and explorer API services to retrieve and categorize data. This EIP standard guarantees that event logs emitted by the smart contract MUST provide enough data to create an accurate record of all current tip token and EIP-20 reward balances. A database or explorer can provide indexed and categorized searches of every tip token and reward sent to NFT holders from the events emitted by any tip token contract that implements this standard. Thus, the state of the tip token contract can be reconstructed from the events emitted alone. + +## Backwards Compatibility + +A tip token contract can be fully compatible with EIP-20 specification and inherit some functions such as transfer if the tokens are allowed to be sent directly to other users. Note that balanceOf has been adopted and MUST be the number of tips held by a user's address. If inheriting from, for example, OpenZeppelin's implementation of EIP-20 token then their contract is responsible for maintaining the balance of tip token. Therefore, tip token balanceOf function SHOULD simply directly call the parent (super) contract's balanceOf function. + +What hasn't been carried over to tip token standard, is the ability for a spender of other users' tips. For the moment, this standard does not foresee a need for this. + +This EIP does not stress a need for tip token secondary markets or other use cases where identifying the tip token type with names rather than addresses might be useful, so these functions were left out of the ITipToken interface and is the remit for implementers. + +## Security Considerations + +Though it is RECOMMENDED that users' deposits are kept locked in the tip token contract or external escrow account, and SHOULD NOT be used for anything but the rewards for holders, this cannot be enforced. This standard stipulates that the rewards MUST be available for when holders withdraw their rewards from the pool of deposits. + +Before any users can tip an NFT, the holder of the NFT has to give their approval for tipping from the tip token contract. This standard stipulates that holders of the NFTs receive the rewards. It SHOULD be clear in the tip token contract code that it does so, without obfuscation to where the rewards go. Any fee charges SHOULD be made obvious to users before acceptance of their deposit. There is a risk that rogue implementers may attempt to \*hijack\* potential tip income streams for their own purposes. But additionally the number and frequency of transactions of the tipping process should make this type of fraud quicker to be found out. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4396.md b/EIPS/eip-4396.md new file mode 100644 index 00000000000000..da534ddaf36a3a --- /dev/null +++ b/EIPS/eip-4396.md @@ -0,0 +1,158 @@ +--- +eip: 4396 +title: Time-Aware Base Fee Calculation +description: Accounts for block time in the base fee calculation to target a stable throughput by time instead of by block. +author: Ansgar Dietrichs (@adietrichs) +discussions-to: https://ethereum-magicians.org/t/eip-4396-time-aware-base-fee-calculation/7363 +status: Stagnant +type: Standards Track +category: Core +created: 2021-10-28 +--- + +## Abstract +This EIP proposes accounting for time between blocks in the base fee calculation to target a stable throughput by time, instead of by block. Aiming to minimize changes to the calculation, it only introduces a variable block gas target proportional to the block time. The EIP can, in principle, be applied to either a Proof-of-Work or a Proof-of-Stake chain, however the security implications for the Proof-of-Work case remain unexplored. + +## Motivation + +The current base fee calculation chooses the gas usage of a block as the signal to determine whether demand for block space is too low (indicating that the base fee should be lowered) or too high (indicating that the base fee should be increased). While simple, this choice of signal has drawbacks: it does not take the block time into account. Assuming a relatively constant demand, a proposer constructing a block after 20 seconds will have transactions available with twice the gas of a proposer constructing a block after 10 seconds. Using the same gas target for both is accordingly sub-optimal. In practice, there are several undesirable consequences of this flawed signal: + +### Base Fee Volatility Under Proof-of-Work + +Under Proof-of-Work (PoW), block times are stochastic, and for that reason there exists large block time variability. This variability contributes to the base fee volatility, where the base fee can be expected to oscillate around the equilibrium value even under perfectly stable demand. + +### Missed Slots + +Under Proof-of-Stake (PoS), block times are ideally uniform (always 12s), but missed slots lead to individual blocks with increased block time (24s, 36s, ...). Such missed slots will result in the next block being overfull, and with the current update rule, signal a fake demand spike and thus cause a small unwarranted base fee spike. + +More importantly, these missed slots directly reduce the overall throughput of the execution chain by the gas target of one block. While the next block can be expected to include the "delayed" transactions of the missed slot, the resulting base fee spike then results in some number of under-full blocks. In the end the block space of the missed slot is lost for the chain. + +This is particularly problematic because a Denial-of-Service (DoS) attack on block proposers can cause them to miss slots, and compromises the overall chain performance. + +### Throughput Degradation During Consensus Issues + +A more severe version of individual missed slots can be caused by consensus issues that prevent a significant portion of block proposers from continuing to create blocks. This can be due to block proposers forking off (and creating blocks on their own fork), being unable to keep up with the current chain head for another reason, or simply being unable to create valid blocks. + +In all these situations, average block times go up significantly, causing chain throughput to fall by the same fraction. While this effect is already present under PoW, the self-healing mechanism of difficulty adjustments is relatively quick to kick in and restore normal block times. On the other hand, under PoS the automatic self-healing mechanism can be extremely slow: potentially several months to return to normal with up to a third of slots missed, or several weeks if more than a third of slots are missed. + +For all these reasons, it would be desirable to target a stable throughput per time instead of per block, by taking block time into account during the base fee calculation. + +To maximize the chance of this EIP being included in the merge fork, the adjustments are kept to a minimum, with more involved changes discussed in the rationale section. + +## Specification +Using the pseudocode language of [EIP-1559](./eip-1559.md), the updated base fee calculation becomes: + +```python +... + +BASE_FEE_MAX_CHANGE_DENOMINATOR = 8 +BLOCK_TIME_TARGET = 12 +MAX_GAS_TARGET_PERCENT = 95 + +class World(ABC): + def validate_block(self, block: Block) -> None: + parent_gas_limit = self.parent(block).gas_limit + parent_block_time = self.parent(block).timestamp - self.parent(self.parent(block)).timestamp + parent_base_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER + parent_adjusted_gas_target = min(parent_base_gas_target * parent_block_time // BLOCK_TIME_TARGET, parent_gas_limit * MAX_GAS_TARGET_PERCENT // 100) + parent_base_fee_per_gas = self.parent(block).base_fee_per_gas + parent_gas_used = self.parent(block).gas_used + + ... + + if parent_gas_used == parent_adjusted_gas_target: + expected_base_fee_per_gas = parent_base_fee_per_gas + elif parent_gas_used > parent_adjusted_gas_target: + gas_used_delta = parent_gas_used - parent_adjusted_gas_target + base_fee_per_gas_delta = max(parent_base_fee_per_gas * gas_used_delta // parent_base_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR, 1) + expected_base_fee_per_gas = parent_base_fee_per_gas + base_fee_per_gas_delta + else: + gas_used_delta = parent_adjusted_gas_target - parent_gas_used + base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta // parent_base_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR + expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta + + ... + + ... +``` + +## Rationale + +### Mechanism + +The proposed new base fee calculation only adjusts the block gas target by scaling it with the block time, capped at a maximum percent of the overall block gas limit: + +#### Current Base Fee Calculation + +![](../assets/eip-4396/old_formula.png) + +#### Proposed Base Fee Calculation + +![](../assets/eip-4396/new_formula.png) + +This new calculation thus targets a stable throughput per time instead of per block. + +### Limitations + +Under PoS, block time increases always come in multiples of full blocks (e.g. a single missed slot = 24s instead of 12s block time). Accounting for this already requires doubling the block gas target, even for a single missed slot. However, with the block elasticity currently set to 2, this target would be equal to the block gas limit. Having the new target equal to the block gas limit is less than ideal, and thus is reduced slightly, according to the `MAX_GAS_TARGET_PERCENT` parameter. The reason for the existence of this parameter is twofold: + +- Ensure that the signal remains meaningful: A target equal to or greater than the gas limit could never be reached, so the base fee would always be reduced after a missed slot. +- Ensure that the base fee can still react to genuine demand increases: During times of many offline block proposers (and thus many missed slots), genuine demand increases still need a way to eventually result in a base fee increase, to avoid a fallback to a first-price priority fee auction. + +However, this means that even a single missed slot cannot be fully compensated. Even worse, any second or further sequential missed slot cannot be compensated for at all, as the gas target is already at its max. This effect becomes more pronounced as the share of offline validators increases: + +![](../assets/eip-4396/degradation.png) + +As can be observed, while this EIP does indeed increase the robustness of the network throughput in cases of offline validators, it does so imperfectly. Furthermore, there is a tradeoff effected by the `MAX_GAS_TARGET_PERCENT` parameter, with a higher value resulting in a higher network robustness, but a more impaired base fee adjustment mechanism during times of frequent missed slots. + +### Possible Extensions + +These limitations directly result from the design goal of a minimal change, to maximize chances of being included in the merge. There are natural ways of extending the EIP design to more effectively handle offline validators, at the expense of somewhat more extensive changes: + +#### Persistent Multi-Slot Buffer + +To be able to compensate multiple consecutive missed slots, a gas buffer could be introduced, that would allow the gas beyond the block elasticity to be carried forward to future blocks. To avoid long-run buffer accumulation that would delay a return to normal operations once block proposers are back online, a cap on the buffer would be added. Even for a relatively small buffer cap, the throughput robustness is significantly improved: + +![](../assets/eip-4396/degradation_buffers.png) + +With an elasticity still at 2, there is no way of avoiding the eventual breakdown for more than 50% offline block proposers. + +The main implementation complexity for this approach comes from the introduction of the buffer as a new persistent field. To retain the ability for calculating base fees only based on headers, it would have to be added to the block header. + +#### Increased Block Elasticity + +In addition to the introduction of a buffer, increasing the block elasticity is another tool for increasing throughput robustness. The following diagram shows the effect of different elasticity levels, both in the presence and absence of a persistent buffer: + +![](../assets/eip-4396/degradation_elasticity.png) + +Again, a clear positive effect can be observed. + +The main additional complexity here would come from the increased peak load (networking, compute & disk access) of multiple sequential overfull blocks. Note though that PoS with its minimum block time of 12s significantly reduces worst case peak stress as compared to PoW. + +## Backwards Compatibility + +The EIP has minimal impact on backwards compatibility, only requiring updates to existing base fee calculation tooling. + +## Test Cases +tbd + +## Reference Implementation +tbd + +## Security Considerations + +### Timestamp Manipulation + +Under PoW, miners are in control over the timestamp field of their blocks. While there are some enforced limits to valid timestamps, implications regarding potential timestamp manipulation are nontrivial and remain unexplored for this EIP. + +Under PoS, each slot has a [fixed assigned timestamp](https://github.com/ethereum/consensus-specs/blob/v1.1.3/specs/merge/beacon-chain.md#process_execution_payload), rendering any timestamp manipulation by block proposers impossible. + +### Suppressing Base Fee Increases +As discussed in the rationale, a high value for `MAX_GAS_TARGET_PERCENT` during times of many offline block proposers results in a small remaining signal space for genuine demand increases that should result in base fee increases. This in turn decreases the cost for block proposers for suppresing these base fee increases, instead forcing the fallback to a first-price priority fee auction. + +While the arguments of incentive incompatibility for base fee suppression of the the base EIP-1559 case still apply here, with a decreasing cost of this individually irrational behavior the risk for overriding psychological factors becomes more significant. + +Even in such a case the system degradation would however be graceful, as it would only temporarily suspend the base fee burn. As soon as the missing block proposers would come back online, the system would return to its ordinary EIP-1559 equilibrium. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4399.md b/EIPS/eip-4399.md new file mode 100644 index 00000000000000..eb7d731fa68ab1 --- /dev/null +++ b/EIPS/eip-4399.md @@ -0,0 +1,155 @@ +--- +eip: 4399 +title: Supplant DIFFICULTY opcode with PREVRANDAO +description: Expose beacon chain randomness in the EVM by supplanting DIFFICULTY opcode semantics +author: Mikhail Kalinin (@mkalinin), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4399-supplant-difficulty-opcode-with-random/7368 +status: Final +type: Standards Track +category: Core +created: 2021-10-30 +requires: 3675 +--- + +## Abstract + +This EIP supplants the semantics of the return value of existing `DIFFICULTY (0x44)` opcode and renames the opcode to `PREVRANDAO (0x44)`. + +The return value of the `DIFFICULTY (0x44)` instruction after this change is the output of the randomness beacon provided by the beacon chain. + + +## Motivation + +Applications may benefit from using the randomness accumulated by the beacon chain. Thus, randomness outputs produced by the beacon chain should be accessible in the EVM. + +At the point of `TRANSITION_BLOCK` of the Proof-of-Stake (PoS) upgrade described in [EIP-3675](./eip-3675.md), the `difficulty` block field **MUST** be `0` thereafter because there is no longer any Proof-of-Work (PoW) seal on the block. This means that the `DIFFICULTY (0x44)` instruction no longer has it's previous semantic meaning, nor a clear "correct" value to return. + +Given prior analysis on the usage of `DIFFICULTY`, the value returned by the instruction mixed with other values is a common pattern used by smart contracts to obtain randomness. The instruction with the same number as the `DIFFICULTY` opcode returning outputs of the beacon chain RANDAO implementation makes the upgrade to PoS backwards compatible for existing smart contracts obtaining randomness from the `DIFFICULTY` instruction. + +Additionally, changes proposed by this EIP allow for smart contracts to determine whether the upgrade to the PoS has already happened. This can be done by analyzing the return value of the `DIFFICULTY` instruction. A value greater than `2**64` indicates that the transaction is being executed in the PoS block. Decompilers and other similar tooling may also use this trick to discern the new semantics of the instruction if data of the block including the transaction in question is available. + + +## Specification + +### Definitions + +* **`TRANSITION_BLOCK`** The definition of this block can be found in the Definitions section of [EIP-3675](./eip-3675.md#definitions). + +### Block structure + +Beginning with `TRANSITION_BLOCK`, client software **MUST** set the value of the `mixHash`, i.e. the field with the number `13` (0-indexed) in a block header, to the latest RANDAO mix of the post beacon state of the previous block. + +### EVM + +Beginning with `TRANSITION_BLOCK`, the `DIFFICULTY (0x44)` instruction **MUST** return the value of the `mixHash` field. + +*Note*: The gas cost of the `DIFFICULTY (0x44)` opcode remains unchanged. + +### Renaming + +The `mixHash` field **SHOULD** further be renamed to `prevRandao`. + +The `DIFFICULTY (0x44)` opcode **SHOULD** further be renamed to `PREVRANDAO (0x44)`. + + +## Rationale + +### Including RANDAO output in the block header + +Including a RANDAO output in the block header provides a straightforward method of accessing it from inside of the EVM as block header data is already available in the EVM context. + +Additionally, this ensures that the execution layer can be fully executed with the block alone rather than requiring extra inputs from the PoS consensus layer. + +Mixing the randomness into a block header may contribute to uniqueness of the block hash in the case when values of other fields of the block header match the corresponding values of the header of another block. + +### Using `mixHash` field instead of `difficulty` + +The `mixHash` header field is used instead of `difficulty` to avoid a class of hidden forkchoice bugs after the PoS upgrade. + +Client software implementing pre-EIP-3675 logic heavily depends on the `difficulty` value as total difficulty computation is the basis of the PoW fork choice rule. Setting the `difficulty` field to `0` at the PoS upgrade aims to reduce the surface of bugs related to the total difficulty value growing after the upgrade. + +Additionally, any latent total difficulty computation after the PoS upgrade would become overflow prone if the randomness output supplanted the value of the `difficulty` field. + +### Reusing existing field instead of appending a new one + +The `mixHash` field is deprecated at the PoS upgrade and set to zero bytes array thereafter. Reusing an existing field as a place for the randomness output saves 32 bytes per block and effectively removes the deprecation of one of the fields induced by the upgrade. + +### Reusing the `DIFFICULTY` opcode instead of introducing a new one + +See the [Motivation](#motivation). + +### Renaming the field and the opcode + +The renaming should be done to make the field and the opcode names semantically sound. + +### Using `TRANSITION_BLOCK` rather than a block or slot number + +By utilizing `TRANSITION_BLOCK` to trigger the change in logic defined in this EIP rather than a block or slot number, this EIP is tightly coupled to the PoS upgrade defined by [EIP-3675](./eip-3675.md). + +By tightly coupling to the PoS upgrade, we ensure that there is no discontinuity for the usecase of this opcode for randomness -- the primary [motivation](#motivation) for re-using `DIFFICULTY` rather than creating a new opcode. + +### Using `2**64` threshold to determine PoS blocks + +The probability of RANDAO value to fall into the range between `0` and `2**64` and, thus, to be mixed with PoW difficulty values, is drastically low. Though, proposed threshold might seem to have insufficient distance from difficulty values on Ethereum Mainnet (they are currently around `2**54`), it requires a thousand times increase of the hashrate to make this threshold insecure. Such an increase is considered impossible to occur before the upcoming consensus upgrade. + + +## Backwards Compatibility + +This EIP introduces backward incompatible changes to the execution and validation of EVM state transitions. As written, this EIP utilizes `TRANSITION_BLOCK` and is thus tightly coupled with the PoS upgrade introduced in [EIP-3675](./eip-3675.md). If this EIP is to be adopted, it **MUST** be scheduled at the same time as EIP-3675. + +Additionally, the changes proposed might be backward incompatible for the following categories of applications: +* Applications that use the value returned by the `DIFFICULTY` opcode as the PoW `difficulty` parameter +* Applications with logic that depends on the `DIFFICULTY` opcode returning a relatively small number with respect to the full 256-bit size of the field. + +The first category is already affected by switching the consensus mechanism to PoS and no additional breaking changes are introduced by this specification. + +The second category is comprised of applications that use the return value of the `DIFFICULTY` opcode in operations that might cause either overflow or underflow errors. While it is theoretically possible to author an application where a change in the range of possible values this opcode may return could lead to a security vulnerability, the chances of that are negligible. + + +## Test Cases + +* In one of ancestors of `TRANSITION_BLOCK` deploy a contract that stores return value of `DIFFICULTY (0x44)` to the state +* Check that value returned by `DIFFICULTY (0x44)` in transaction executed within the parent of `TRANSITION_BLOCK` equals `difficulty` field value +* Check that value returned by `PREVRANDAO (0x44)` in transaction executed within `TRANSITION_BLOCK` equals `prevRandao` field value + + +## Security Considerations + +The `PREVRANDAO (0x44)` opcode in PoS Ethereum (based on the beacon chain RANDAO implementation) is a source of randomness with different properties to the randomness supplied by `BLOCKHASH (0x40)` or `DIFFICULTY (0x44)` opcodes in the PoW network. + +### Biasability + +The beacon chain RANDAO implementation gives every block proposer 1 bit of influence power per slot. Proposer may deliberately refuse to propose a block on the opportunity cost of proposer and transaction fees to prevent beacon chain randomness (a RANDAO mix) from being updated in a particular slot. + +An effect of proposer's influence power is limited in time and lasts until the first honest RANDAO reveal is made afterwards. This limitation does also exist in the case when proposers of `n` consecutive slots are colluding to get `n` bits of influence power. Simply speaking, one honest block proposal is enough to unbias the RANDAO even if it was biased during several slots in a row. + +Additionally, semantics of the `PREVRANDAO (0x44)` instruction gives proposers another way to gain 1 bit of influence power on applications. Biased proposer may censor a rolling the dice transaction to force it to be included into the next block, thus, force it to use a RANDAO mix that the proposer knows in advance. The opportunity cost in this case would be negligible. + +### Predictability + +Obviously, historical randomness provided by any decentralized oracle is 100% predictable. On the contrary, the randomness that is revealed in the future is predictable up to a limited extent. + +A list of inputs influencing future randomness on the beacon chain consists of but is not limited to the following items: +* **Accumulated randomness.** A RANDAO mix produced by the beacon chain in the last slot of epoch `N` is the main input to the function defining block proposers in each slot of epoch `N + MIN_SEED_LOOKAHEAD + 1`, i.e. it is the main factor defining future RANDAO revealers. +* **Number of active validators.** A number of active validators throughout an epoch is another input to the block proposer function. +* **Effective balance.** All else being equal, the lower the effective balance of a validator the lower the chance this validator has to be designated as a proposer in a slot. +* **Accidentally missed proposals.** Network conditions and other factors that are resulting in accidentally missed proposals is a source of highly qualitative entropy that impacts RANDAO mixes. Usual rate of missed proposals on the Mainnet is about `1%`. + +These inputs may be predictable and malleable on a short range of slots but the longer the attempted lookahead the more entropy is accumulated by the beacon chain. + +### Tips for application developers + +The following tips attempt to reduce predictability and biasability of randomness outputs returned by `PREVRANDAO (0x44)`: + +1. Make your applications rely on the future randomness with a reasonably high lookahead. For example, an application stops accepting bids at the end of epoch `K` and uses a RANDAO mix produced in slot `K + N + ε` to roll the dice, where `N` is a lookahead in epochs and `ε` is a few slots into epoch `N + 1`. +2. At least four epochs of lookahead results in the following outcome: + * A proposer set of epoch `N + 1` isn't known at the end of epoch `K` breaking a direct link between bidders and dice rollers + * A number of active validators is updated at the end of each epoch affecting a set of proposers of next epochs, thus, impacting a RANDAO mix used by the application to roll the dice + * Due to Mainnet statistics, there is about a `100%` chance for the network to accidentally miss a proposal during this period of time which reduces predictability of a RANDAO mix used to roll the dice. +3. Setting `ε` to a small number, e.g. 2 or 4 slots, gives a third party a little time to gain influence power on the future randomness that is being used to roll the dice. This amount of time is defined by `MIN_SEED_LOOKAHEAD` parameter and is about 6 minutes on the Mainnet. + +A reasonably high distance between bidding and rolling the dice attempts to leave low chance for bidders controlling a subset of validators to directly exploit their influence power. Ultimately, this chance depends on the type of the game and on a number of controlled validators. For instance, a chance of a single validator to affect a one-time game is negligible, and becomes bigger for multiple validators in a repeated game scenario. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4400.md b/EIPS/eip-4400.md new file mode 100644 index 00000000000000..500ff8ff26002d --- /dev/null +++ b/EIPS/eip-4400.md @@ -0,0 +1,113 @@ +--- +eip: 4400 +title: EIP-721 Consumable Extension +description: Interface extension for EIP-721 consumer role +author: Daniel Ivanov (@Daniel-K-Ivanov), George Spasov (@Perseverance) +discussions-to: https://ethereum-magicians.org/t/EIP-4400-EIP721consumer-extension/7371 +status: Final +type: Standards Track +category: ERC +created: 2021-10-30 +requires: 165, 721 +--- + +## Abstract + +This specification defines standard functions outlining a `consumer` role for instance(s) of [EIP-721](./eip-721.md). An implementation allows reading the current `consumer` for a given NFT (`tokenId`) along with a standardized event for when an `consumer` has changed. The proposal depends on and extends the existing [EIP-721](./eip-721.md). + +## Motivation + +Many [EIP-721](./eip-721.md) contracts introduce their own custom role that grants permissions for utilising/consuming a given NFT instance. The need for that role stems from the fact that other than owning the NFT instance, there are other actions that can be performed on an NFT. For example, various metaverses use `operator` / `contributor` roles for Land (EIP-721), so that owners of the land can authorise other addresses to deploy scenes to them (f.e. commissioning a service company to develop a scene). + +It is common for NFTs to have utility other than ownership. That being said, it requires a separate standardized consumer role, allowing compatibility with user interfaces and contracts, managing those contracts. + +Having a `consumer` role will enable protocols to integrate and build on top of dApps that issue EIP-721 tokens. One example is the creation of generic/universal NFT renting marketplaces. + +Example of kinds of contracts and applications that can benefit from this standard are: +- metaverses that have land and other types of digital assets in those metaverses (scene deployment on land, renting land / characters / clothes / passes to events etc.) +- NFT-based yield-farming. Adopting the standard enables the "staker" (owner of the NFT) to have access to the utility benefits even after transferring his NFT to the staking contract + +## Specification + +The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +Every contract compliant to the `EIP721Consumable` extension MUST implement the `IEIP721Consumable` interface. The **consumer extension** is OPTIONAL for EIP-721 contracts. + +```solidity +/// @title EIP-721 Consumer Role extension +/// Note: the EIP-165 identifier for this interface is 0x953c8dfa +interface IEIP721Consumable /* is EIP721 */ { + + /// @notice Emitted when `owner` changes the `consumer` of an NFT + /// The zero address for consumer indicates that there is no consumer address + /// When a Transfer event emits, this also indicates that the consumer address + /// for that NFT (if any) is set to none + event ConsumerChanged(address indexed owner, address indexed consumer, uint256 indexed tokenId); + + /// @notice Get the consumer address of an NFT + /// @dev The zero address indicates that there is no consumer + /// Throws if `_tokenId` is not a valid NFT + /// @param _tokenId The NFT to get the consumer address for + /// @return The consumer address for this NFT, or the zero address if there is none + function consumerOf(uint256 _tokenId) view external returns (address); + + /// @notice Change or reaffirm the consumer address for an NFT + /// @dev The zero address indicates there is no consumer address + /// Throws unless `msg.sender` is the current NFT owner, an authorised + /// operator of the current owner or approved address + /// Throws if `_tokenId` is not valid NFT + /// @param _consumer The new consumer of the NFT + function changeConsumer(address _consumer, uint256 _tokenId) external; +} +``` + +Every contract implementing the `EIP721Consumable` extension is free to define the permissions of a `consumer` (e.g. what are consumers allowed to do within their system) with only one exception - consumers MUST NOT be considered owners, authorised operators or approved addresses as per the EIP-721 specification. Thus, they MUST NOT be able to execute transfers & approvals. + +The `consumerOf(uint256 _tokenId)` function MAY be implemented as `pure` or `view`. + +The `changeConsumer(address _consumer, uint256 _tokenId)` function MAY be implemented as `public` or `external`. + +The `ConsumerChanged` event MUST be emitted when a consumer is changed. + +On every `transfer`, the consumer MUST be changed to a default address. It is RECOMMENDED for implementors to use `address(0)` as that default address. + +The `supportsInterface` method MUST return `true` when called with `0x953c8dfa`. + +## Rationale + +Key factors influencing the standard: + +- Keeping the number of functions in the interfaces to a minimum to prevent contract bloat +- Simplicity +- Gas Efficiency +- Not reusing or overloading other already existing roles (e.g. owners, operators, approved addresses) + +### Name + +The chosen name resonates with the purpose of its existence. Consumers can be considered entities that utilise the token instances, without necessarily having ownership rights to it. + +The other name for the role that was considered was `operator`, however it is already defined and used within the `EIP-721` standard. + +### Restriction on the Permissions + +There are numerous use-cases where a distinct role for NFTs is required that MUST NOT have owner permissions. A contract that implements the consumer role and grants ownership permissions to the consumer renders this standard pointless. + +## Backwards Compatibility + +This standard is compatible with current EIP-721 standards. There are no other standards that define a similar role for NFTs and the name (`consumer`) is not used by other EIP-721 related standards. + +## Test Cases + +Test cases are available in the reference implementation [here](../assets/eip-4400/test/erc721-consumable.ts). + +## Reference Implementation + +The reference implementation can be found [here](../assets/eip-4400/contracts/ERC721Consumable.sol). + +## Security Considerations + +Implementors of the `EIP721Consumable` standard must consider thoroughly the permissions they give to `consumers`. Even if they implement the standard correctly and do not allow transfer/burning of NFTs, they might still provide permissions to the `consumers` that they might not want to provide otherwise and should be restricted to `owners` only. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4430.md b/EIPS/eip-4430.md new file mode 100644 index 00000000000000..48dc234bc00564 --- /dev/null +++ b/EIPS/eip-4430.md @@ -0,0 +1,145 @@ +--- +eip: 4430 +title: Described Transactions +description: A technique for contracts to provide a human-readable description of a transaction's side-effects. +author: Richard Moore (@ricmoo), Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/discussion-eip-4430-described-transactions/8762 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-11-07 +--- + +## Abstract + +Use a contract method to provide *virtual functions* which can generate +a human-readable description at the same time as the machine-readable +bytecode, allowing the user to agree to the human-readable component +in a UI while the machine can execute the bytecode once accepted. + + +## Motivation + +When using an Ethereum Wallet (e.g. MetaMask, Clef, Hardware Wallets) +users must accept a transaction before it can be submitted (or the user +may decline). + +Due to the complexity of Ethereum transactions, wallets are very limited +in their ability to provide insight into the effects of a transaction +that the user is approving; outside special-cased support for common +transactions such as ERC20 transfers, this often amounts to asking the +user to sign an opaque blob of binary data. + +This EIP presents a method for dapp developers to enable a more comfortable +user experience by providing wallets with a means to generate a better +description about what the contract claims will happen. + +It does not address malicious contracts which wish to lie, it only addresses +honest contracts that want to make their user's life better. We believe +that this is a reasonable security model, as transaction descriptions can be +audited at the same time as contract code, allowing auditors and code +reviewers to check that transaction descriptions are accurate as part of +their review. + + +## Specification + +The **description** (a string) and the matching **execcode** (bytecode) +are generated simultaneously by evaluating the method on a contract: + +```solidity +function eipXXXDescribe(bytes inputs, bytes32 reserved) view returns (string description, bytes execcode) +``` + +The human-readable **description** can be shown in any client which supports +user interaction for approval, while the **execcode** is the data that +should be included in a transaction to the contract to perform that operation. + +The method must be executable in a static context, (i.e. any side effects, +such as logX, sstore, etc.), including through indirect calls may be ignored. + +During evaluation, the `ADDRESS` (i.e. `to`), `CALLER` (i.e. `from`), `VALUE`, +and `GASPRICE` must be the same as the values for the transaction being +described, so that the code generating the description can rely on them. + +When executing the bytecode, best efforts should be made to ensure `BLOCKHASH`, +`NUMBER`, `TIMESTAMP` and `DIFFICULTY` match the `"latest"` block. The +`COINBASE` should be the zero address. + +The method may revert, in which case the signing must be aborted. + + +## Rationale + +### Meta Description + +There have been many attempts to solve this problem, many of which attempt +to examine the encoded transaction data or message data directly. + +In many cases, the information that would be necessary for a meaningful +description is not present in the final encoded transaction data or message +data. + +Instead this EIP uses an indirect description of the data. + +For example, the `commit(bytes32)` method of ENS places a commitment +**hash** on-chain. The hash contains the **blinded** name and address; +since the name is blinded, the encoded data (i.e. the hash) no longer +contains the original values and is insufficient to access the necessary +values to be included in a description. + +By instead describing the commitment indirectly (with the original information +intact: NAME, ADDRESS and SECRET) a meaningful description can be computed +(e.g. "commit to NAME for ADDRESS (with SECRET)") and the matching data can +be computed (i.e. `commit(hash(name, owner, secret))`). + +This technique of blinded data will become much more popular with L2 +solutions, which use blinding not necessarily for privacy, but for +compression. + +### Entangling the Contract Address + +To prevent signed data being used across contracts, the contract address +is entanlged into both the transaction implicitly via the `to` field. + + +### Alternatives + +- NatSpec and company are a class of more complex languages that attempt to describe the encoded data directly. Because of the language complexity they often end up being quite large requiring entire runtime environments with ample processing power and memory, as well as additional sandboxing to reduce security concerns. One goal of this is to reduce the complexity to something that could execute on hardware wallets and other simple wallets. These also describe the data directly, which in many cases (such as blinded data), cannot adequately describe the data at all + +- Custom Languages; due to the complexity of Ethereum transactions, any language used would require a lot of expressiveness and re-inventing the wheel. The EVM already exists (it may not be ideal), but it is there and can handle everything necessary. + +- Format Strings (e.g. Trustless Signing UI Protocol; format strings can only operate on the class of regular languages, which in many cases is insufficient to describe an Ethereum transaction. This was an issue quite often during early attempts at solving this problem. + +- The signTypedData [EIP-712](./eip-712.md) has many parallels to what this EIP aims to solve + + +## Backwards Compatibility + +This does not affect backwards compatibility. + + +## Reference Implementation + +I will add deployed examples by address and chain ID. + + +## Security Considerations + +### Escaping Text + +Wallets must be careful when displaying text provided by contracts and proper +efforts must be taken to sanitize it, for example, be sure to consider: + +- HTML could be embedded to attempt to trick web-based wallets into executing code using the script tag (possibly uploading any private keys to a server) +- In general, extreme care must be used when rendering HTML; consider the ENS names `not-ricmoo.eth` or ` ricmoo.eth`, which if rendered without care would appear as `ricmoo.eth`, which it is not +- Other marks which require escaping could be included, such as quotes (`"`), formatting (`\n` (new line), `\f` (form feed), `\t` (tab), any of many non-standard whitespaces), back-slassh (`\`) +- UTF-8 has had bugs in the past which could allow arbitrary code execution and crashing renderers; consider using the UTF-8 replacement character (or *something*) for code-points outside common planes or common sub-sets within planes +- Homoglyphs attacks +- Right-to-left mark may affect rendering +- Many other things, deplnding on your environment + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4444.md b/EIPS/eip-4444.md new file mode 100644 index 00000000000000..df543d454f1667 --- /dev/null +++ b/EIPS/eip-4444.md @@ -0,0 +1,105 @@ +--- +eip: 4444 +title: Bound Historical Data in Execution Clients +description: Prune historical data in clients older than one year +author: George Kadianakis (@asn-d6), lightclient (@lightclient), Alex Stokes (@ralexstokes) +discussions-to: https://ethereum-magicians.org/t/eip-4444-bound-historical-data-in-execution-clients/7450 +status: Stagnant +type: Standards Track +category: Networking +created: 2021-11-02 +--- + +## Abstract + +Clients must stop serving historical headers, bodies, and receipts older than one year on the p2p layer. Clients may locally prune this historical data. + +## Motivation + +Historical blocks and receipts currently occupy more than 400GB of disk space (and growing!). Therefore, to validate the chain, users must typically have a 1TB disk. + +Historical data is not necessary for validating new blocks, so once a client has synced the tip of the chain, historical data is only retrieved when requested explicitly over the JSON-RPC or when a peer attempts to sync the chain. By pruning the history, this proposal reduces the disk requirements for users. Pruning history also allows clients to remove code that processes historical blocks. This means that execution clients don't need to maintain code paths that deal with each upgrade's compounding changes. + +Finally, this change will result in less bandwidth usage on the network as clients adopt more lightweight sync strategies based on the PoS weak subjectivity assumption. + +## Specification + +| Parameter | Value | Description | +| - | - | - | +| `HISTORY_PRUNE_EPOCHS` | 82125 | A year in beacon chain epochs | + +Clients SHOULD NOT serve headers, block bodies, and receipts that are older than `HISTORY_PRUNE_EPOCHS` epochs on the p2p network. + +Clients MAY locally prune headers, block bodies, and receipts that is older than `HISTORY_PRUNE_EPOCHS` epochs. + +#### Bootstrapping and syncing + +This EIP impacts the way clients bootstrap and sync. Clients will not be able to full sync using devp2p since historical data will no longer be served. + +Clients MUST use a valid Weak Subjectivity Checkpoint to bootstrap from a more recent view of the chain. For the purpose of syncing, clients treat weak subjectivity checkpoints as the genesis block. We call this method "checkpoint sync". + +For the purposes of this proposal, we assume clients always start with a configured and valid weak subjectivity checkpoint. The way this is achieved is out-of-scope for this proposal. + +## Rationale + +This proposal forces clients to stop serving old historical data over p2p. We make this explicit to force clients to seek historical data from other sources, instead of relying on the optional behavior of some clients which would result in quality degradation. + +### Why a year? + +This proposal sets `HISTORY_PRUNE_EPOCHS` to 82125 epochs (one earth year). This constant is large enough to provide sufficient room for the Weak Subjectivity Period to grow, and it's also small enough so as to not occupy too much disk space. + +## Backwards Compatibility + +### Preserving historical data + +This proposal impacts nodes that make use of historical data (e.g. web3 applications that display history of blocks, transactions or accounts). Preserving the history of Ethereum is fundamental and we believe there are various out-of-band ways to achieve this. + +Historical data can be packaged and shared via torrent magnet links or over networks like IPFS. Furthermore, systems like the Portal Network or The Graph can be used to acquire historical data. + +Clients should allow importing and exporting of historical data. Clients can provide scripts that fetch/verify data and automatically import them. + +### Full syncing from genesis + +Full syncing will no longer be possible over the p2p network. However, we do want to allow interested parties to do so on their own. + +We suggest that a specialized "full sync" client is built. The client is a shim that pieces together different releases of execution engines and can import historical blocks to validate the entire Ethereum chain from genesis and generate all other historical data. + +It's important to also note that although archive nodes with "state sync" functionality are in development, full sync is currently the only reliable way to bootstrap them. Clients that wish to continue providing archive support would need the ability to import historical blocks retrieved out-of-band and retain support for each historical upgrade of the network. + +### User experience + +This proposal impacts the UX for setting up applications that use historical data. Hence we suggest that clients introduce this change in two phases: + +In the first phase, clients don't prune historical data by default. They introduce a command line option similar to geth's `--txlookuplimit` that users can use if they want to prune historical data. + +In the second phase, history is pruned by default and the command line option is removed. Clients assume that users will find and import data in an out-of-band way. + +### JSON-RPC changes + +After this proposal is implemented, certain JSON-RPC endpoints (e.g. like `getBlockByHash`) won't be able to tell whether a given hash is invalid or just too old. Other endpoints like `getLogs` will simply no longer have the data the user is requesting. The way this regression should be handled by applications or clients is out-of-scope for this proposal. + +## Security Considerations + +### Relying on weak subjectivity + +With the move to PoS, it's essential for security to use valid weak subjectivity checkpoints because of long-range attacks. + +This proposal relies on the weak subjectivity assumption and assumes that clients will not bootstrap with an invalid or old WS checkpoint. + +This proposal also assumes that the weak subjectivity period will never be longer than `HISTORY_PRUNE_EPOCHS`. If that were to happen, clients with an old weak subjectivity checkpoint would never be able to "checkpoint sync" since the p2p network would not be able to provide the required data. + +### Centralization/censorship risk + +There are censorship/availability risks if there is a lack of incentives to keep historical data. + +It's important that Ethereum historical data are preserved and seeded by independent organizations, and their availability should be checked frequently. We consider these mechanisms as out-of-scope for this proposal. + +Furthermore, there is a risk that more dapps will rely on centralized services for acquiring historical data because it will be harder to setup a full node. + +### Confusion with other proposals + +Because there are a number of alternative proposals for reducing the execution client's footprint on disk, we've decided to enforce a specific pronouncination of the EIP. When pronouncing the EIP number, it **MUST** be pronounced EIP "four fours". All other pronounciations are incorrect. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4488.md b/EIPS/eip-4488.md new file mode 100644 index 00000000000000..0ac03550238d92 --- /dev/null +++ b/EIPS/eip-4488.md @@ -0,0 +1,71 @@ +--- +eip: 4488 +title: Transaction calldata gas cost reduction with total calldata limit +description: Greatly decreases the gas cost of transaction calldata and simultaneously caps total transaction calldata in a block +author: Vitalik Buterin (@vbuterin), Ansgar Dietrichs (@adietrichs) +discussions-to: https://ethereum-magicians.org/t/eip-4488-transaction-calldata-gas-cost-reduction-with-total-calldata-limit/7555 +type: Standards Track +category: Core +status: Stagnant +created: 2021-11-23 +--- + +## Abstract + +Decrease transaction calldata gas cost, and add a limit of how much total transaction calldata can be in a block. + +## Motivation + +Rollups are in the short and medium term, and possibly the long term, the only trustless scaling solution for Ethereum. Transaction fees on L1 have been very high for months and there is greater urgency in doing anything required to help facilitate an ecosystem-wide move to rollups. Rollups are significantly reducing fees for many Ethereum users: Optimism and Arbitrum frequently provide fees that are ~3-8x lower than the Ethereum base layer itself, and ZK rollups, which have better data compression and can avoid including signatures, have fees ~40-100x lower than the base layer. + +However, even these fees are too expensive for many users. The long-term solution to the long-term inadequacy of rollups by themselves has always been [data sharding](https://github.com/ethereum/consensus-specs#sharding), which would add ~1-2 MB/sec of dedicated data space for rollups to the chain. However, data sharding will still take a considerable amount of time to finish implementing and deploying. Hence, a short-term solution to further cut costs for rollups, and to incentivize an ecosystem-wide transition to a rollup-centric Ethereum, is desired. + +This EIP presents a quick-to-implement short-term solution that also mitigates security risks. + +## Specification + +| Parameter | Value | +| - | - | +| `NEW_CALLDATA_GAS_COST` | `3` | +| `BASE_MAX_CALLDATA_PER_BLOCK` | `1,048,576` | +| `CALLDATA_PER_TX_STIPEND` | `300` | + +Reduce the gas cost of transaction calldata to `NEW_CALLDATA_GAS_COST` per byte, regardless of whether the byte is zero or nonzero. + +Add a rule that a block is only valid if `sum(len(tx.calldata) for tx in block.txs) <= BASE_MAX_CALLDATA_PER_BLOCK + len(block.txs) * CALLDATA_PER_TX_STIPEND`. + +## Rationale + +A natural alternative proposal is to decrease `NEW_CALLDATA_GAS_COST` without adding a limit. However, this presents a security concern: today, the average block size [is 60-90 kB](https://etherscan.io/chart/blocksize), but the _maximum_ block size is `30M / 16 = 1,875,000` bytes (plus about a kilobyte of block and tx overhead). Simply decreasing the calldata gas cost from 16 to 3 would increase the maximum block size to 10M bytes. This would push the Ethereum p2p networking layer to unprecedented levels of strain and risk breaking the network; some previous live tests of ~500 kB blocks a few years ago had already taken down a few bootstrap nodes. + +The decrease-cost-and-cap proposal achieves most of the benefits of the decrease, as rollups are unlikely to _dominate_ Ethereum block space in the short term future and so 1.5 MB will be sufficient, while preventing most of the security risk. + +Historically, the Ethereum protocol community has been suspicious of multi-dimensional resource limit rules (in this case, gas and calldata) because such rules greatly increase the complexity of the block packing problem that proposers (today miners, post-merge validators) need to solve. Today, proposers can generate blocks with near-optimal fee revenue by simply choosing transactions in highest-to-lowest order of priority fee. In a multi-dimensional world, proposers would have to deal with multi-dimensional constraints. Multi-dimensional knapsack problems are much more complicated than the single-dimensional equivalent, and well-optimized proprietary implementations built by pools may well outperform default open source implementations. + +Today, there are two key reasons why this is less of a problem than before: + +1. [EIP-1559](./eip-1559.md) means that, at least most of the time, the problem that block proposers are solving is _not_ a knapsack problem. Rather, block proposers are simply including all the transactions they can find with sufficient base fee and priority fee. Hence, naive algorithms will also frequently generate close-to-optimal results. +2. The existence of sophisticated proprietary strategies for miner extractable value (MEV) extraction means that decentralized optimal block production is already in the medium and long term a lost cause. Research is instead going into solutions that separate away the specialization-friendly task of block body generation from the role of a validator ("proposer/builder separation"). Instead of being a fundamental change, two-dimensional knapsack problems today would be merely "yet another" MEV opportunity. + +Hence, it's worth rethinking the historical opposition to multi-dimensional resource limits and considering them as a pragmatic way to simultaneously achieve moderate scalability gains while retaining security. + +Additionally, the stipend mechanism makes the two-dimensional optimization problem even less of an issue in practice. 90% of all transactions ([sample](../assets/eip-4488/gas_and_calldata_sample.csv) taken from blocks `13500000, 13501000 ... 13529000`) have <300 bytes of calldata. Hence, if a naive transaction selection algorithm overfills the calldata of a block that the proposer is creating, the proposer will still be able to keep adding transactions from 90% of their mempool. + +## Backwards Compatibility + +This is a backwards incompatible gas repricing that requires a scheduled network upgrade. + +Users will be able to continue operating with no changes. + +Miners will be able to continue operating with no changes except for a rule to stop adding new transactions into a block when the total calldata size reaches the maximum. However, there are pragmatic heuristics that they could add to achieve closer-to-optimal returns in such cases: for example, if a block fills up to the size limit, they could repeatedly remove the last data-heavy transaction and replace it with as many data-light transactions as possible, until doing so is no longer profitable. + +## Security Considerations + +The _burst_ data capacity of the chain does not increase as a result of this proposal (in fact, it slightly decreases). However, the _average_ data capacity will increase. This means that the storage requirements of history-storing will go up. A worst-case scenario would be a theoretical long-run maximum of ~1,262,861 bytes per 12 sec slot, or ~3.0 TB per year. + +We recommend [EIP-4444](./eip-4444.md) or some similar history expiry proposal be implemented either at the same time or soon after this EIP to mitigate this risk. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4494.md b/EIPS/eip-4494.md new file mode 100644 index 00000000000000..8da81a96865548 --- /dev/null +++ b/EIPS/eip-4494.md @@ -0,0 +1,207 @@ +--- +eip: 4494 +title: Permit for ERC-721 NFTs +description: ERC-712-singed approvals for ERC-721 NFTs +author: Simon Fremaux (@dievardump), William Schwab (@wschwab) +discussions-to: https://ethereum-magicians.org/t/eip-extending-erc2612-style-permits-to-erc721-nfts/7519/2 +status: Draft +type: Standards Track +category: ERC +created: 2021-11-25 +requires: 165, 712, 721 +--- + +## Abstract +The "Permit" approval flow outlined in [ERC-2612](./eip-2612.md) has proven a very valuable advancement in UX by creating gasless approvals for ERC20 tokens. This EIP extends the pattern to ERC-721 NFTs. This EIP borrows heavily from ERC-2612. + +This requires a separate EIP due to the difference in structure between ERC-20 and ERC-721 tokens. While ERC-20 permits use value (the amount of the ERC-20 token being approved) and a nonce based on the owner's address, ERC-721 permits focus on the `tokenId` of the NFT and increment nonce based on the transfers of the NFT. + +## Motivation +The permit structure outlined in [ERC-2612](./eip-2612.md) allows for a signed message (structured as outlined in [ERC-712](./eip-712.md)) to be used in order to create an approval. Whereas the normal approval-based pull flow generally involves two transactions, one to approve a contract and a second for the contract to pull the asset, which is poor UX and often confuses new users, a permit-style flow only requires signing a message and a transaction. Additional information can be found in [ERC-2612](./eip-2612.md). + +[ERC-2612](./eip-2612.md) only outlines a permit architecture for ERC-20 tokens. This ERC proposes an architecture for ERC-721 NFTs, which also contain an approve architecture that would benefit from a signed message-based approval flow. + +## 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. + +Three new functions MUST be added to [ERC-721](./eip-721.md): +```solidity +pragma solidity 0.8.10; + +import "./IERC165.sol"; + +/// +/// @dev Interface for token permits for ERC-721 +/// +interface IERC4494 is IERC165 { + /// ERC165 bytes to add to interface array - set in parent contract + /// + /// _INTERFACE_ID_ERC4494 = 0x5604e225 + + /// @notice Function to approve by way of owner signature + /// @param spender the address to approve + /// @param tokenId the index of the NFT to approve the spender on + /// @param deadline a timestamp expiry for the permit + /// @param sig a traditional or EIP-2098 signature + function permit(address spender, uint256 tokenId, uint256 deadline, bytes memory sig) external; + /// @notice Returns the nonce of an NFT - useful for creating permits + /// @param tokenId the index of the NFT to get the nonce of + /// @return the uint256 representation of the nonce + function nonces(uint256 tokenId) external view returns(uint256); + /// @notice Returns the domain separator used in the encoding of the signature for permits, as defined by EIP-712 + /// @return the bytes32 domain separator + function DOMAIN_SEPARATOR() external view returns(bytes32); +} +``` +The semantics of which are as follows: + +For all addresses `spender`, `uint256`s `tokenId`, `deadline`, and `nonce`, and `bytes` `sig`, a call to `permit(spender, tokenId, deadline, sig)` MUST set `spender` as approved on `tokenId` as long as the owner of `tokenId` remains in possession of it, and MUST emit a corresponding `Approval` event, if and only if the following conditions are met: + +* the current blocktime is less than or equal to `deadline` +* the owner of the `tokenId` is not the zero address +* `nonces[tokenId]` is equal to `nonce` +* `sig` is a valid `secp256k1` or [EIP-2098](./eip-2098.md) signature from owner of the `tokenId`: +``` +keccak256(abi.encodePacked( + hex"1901", + DOMAIN_SEPARATOR, + keccak256(abi.encode( + keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"), + spender, + tokenId, + nonce, + deadline)) +)); +``` +where `DOMAIN_SEPARATOR` MUST be defined according to [EIP-712](./eip-712.md). The `DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, and satisfy the requirements of EIP-712, but is otherwise unconstrained. A common choice for `DOMAIN_SEPARATOR` is: +``` +DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), + keccak256(bytes(name)), + keccak256(bytes(version)), + chainid, + address(this) +)); +``` +In other words, the message is the following ERC-712 typed structure: +```json +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ], + "primaryType": "Permit", + "domain": { + "name": erc721name, + "version": version, + "chainId": chainid, + "verifyingContract": tokenAddress + }, + "message": { + "spender": spender, + "value": value, + "nonce": nonce, + "deadline": deadline + } +}} +``` +In addition: +* the `nonce` of a particular `tokenId` (`nonces[tokenId]`) MUST be incremented upon any transfer of the `tokenId` +* the `permit` function MUST check that the signer is not the zero address + +Note that nowhere in this definition do we refer to `msg.sender`. The caller of the `permit` function can be any address. + +This EIP requires [EIP-165](./eip-165.md). EIP165 is already required in [ERC-721](./eip-721.md), but is further necessary here in order to register the interface of this EIP. Doing so will allow easy verification if an NFT contract has implemented this EIP or not, enabling them to interact accordingly. The interface of this EIP (as defined in EIP-165) is `0x5604e225`. Contracts implementing this EIP MUST have the `supportsInterface` function return `true` when called with `0x5604e225`. + +## Rationale +The `permit` function is sufficient for enabling a `safeTransferFrom` transaction to be made without the need for an additional transaction. + +The format avoids any calls to unknown code. + +The `nonces` mapping is given for replay protection. + +A common use case of permit has a relayer submit a Permit on behalf of the owner. In this scenario, the relaying party is essentially given a free option to submit or withhold the Permit. If this is a cause of concern, the owner can limit the time a Permit is valid for by setting deadline to a value in the near future. The deadline argument can be set to uint(-1) to create Permits that effectively never expire. + +ERC-712 typed messages are included because of its use in [ERC-2612](./eip-2612.md), which in turn cites widespread adoption in many wallet providers. + +While ERC-2612 focuses on the value being approved, this EIP focuses on the `tokenId` of the NFT being approved via `permit`. This enables a flexibility that cannot be achieved with ERC-20 (or even [ERC-1155](./eip-1155.md)) tokens, enabling a single owner to give multiple permits on the same NFT. This is possible since each ERC-721 token is discrete (oftentimes referred to as non-fungible), which allows assertion that this token is still in the possession of the `owner` simply and conclusively. + +Whereas ERC-2612 splits signatures into their `v,r,s` components, this EIP opts to instead take a `bytes` array of variable length in order to support [EIP-2098](./eip-2098) signatures (64 bytes), which cannot be easily separated or reconstructed from `r,s,v` components (65 bytes). + +## Backwards Compatibility +There are already some ERC-721 contracts implementing a `permit`-style architecture, most notably Uniswap v3. + +Their implementation differs from the specification here in that: + * the `permit` architecture is based on `owner` + * the `nonce` is incremented at the time the `permit` is created + * the `permit` function must be called by the NFT owner, who is set as the `owner` + * the signature is split into `r,s,v` instead of `bytes` + + Rationale for differing on design decisions is detailed above. + +## Test Cases + +Basic test cases for the reference implementation can be found [here](https://github.com/dievardump/erc721-with-permits/tree/main/test). + +In general, test suites should assert at least the following about any implementation of this EIP: +* the nonce is incremented after each transfer +* `permit` approves the `spender` on the correct `tokenId` +* the permit cannot be used after the NFT is transferred +* an expired permit cannot be used + +## Reference Implementation + +A reference implementation has been set up [here](https://github.com/dievardump/erc721-with-permits). + +## Security Considerations + +Extra care should be taken when creating transfer functions in which `permit` and a transfer function can be used in one function to make sure that invalid permits cannot be used in any way. This is especially relevant for automated NFT platforms, in which a careless implementation can result in the compromise of a number of user assets. + +The remaining considerations have been copied from [ERC-2612](./eip-2612.md) with minor adaptation, and are equally relevant here: + +Though the signer of a `Permit` may have a certain party in mind to submit their transaction, another party can always front run this transaction and call `permit` before the intended party. The end result is the same for the `Permit` signer, however. + +Since the ecrecover precompile fails silently and just returns the zero address as `signer` when given malformed messages, it is important to ensure `ownerOf(tokenId) != address(0)` to avoid `permit` from creating an approval to any `tokenId` which does not have an approval set. + +Signed `Permit` messages are censorable. The relaying party can always choose to not submit the `Permit` after having received it, withholding the option to submit it. The `deadline` parameter is one mitigation to this. If the signing party holds ETH they can also just submit the `Permit` themselves, which can render previously signed `Permit`s invalid. + +The standard [ERC-20 race condition for approvals](https://swcregistry.io/docs/SWC-114) applies to `permit` as well. + +If the `DOMAIN_SEPARATOR` contains the `chainId` and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a future chain split. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4519.md b/EIPS/eip-4519.md new file mode 100644 index 00000000000000..b1743323f289e4 --- /dev/null +++ b/EIPS/eip-4519.md @@ -0,0 +1,225 @@ +--- +eip: 4519 +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: Final +type: Standards Track +category: ERC +created: 2021-12-03 +requires: 165, 721 +--- + +## Abstract + +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 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 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) + +In the case of defining the attribute `addressAsset` but not the attribute `addressUser`, two states define if the token is waiting for authentication with the owner or if the authentication has finished successfully. `Figure 2` shows these states in a flow chart. When a token is created or transferred to a new owner, then the token changes its state to `waitingForOwner`. In this state, the token is waiting for the mutual authentication between the asset and the owner. Once authentication is finished successfully, the token changes its state to `engagedWithOwner`. + +![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 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. 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 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 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: + +KO = PKA * SKO_A + +Using the function `startOwnerEngagement`, PKO_A is saved as the attribute `dataEngagement` and the hash of KO as the attribute `hashK_OA`. The owner sends request engagement to the asset, and the asset calculates: + +KA = SKA * PKO_A + +If everything is correctly done, KO and KA are the same since: + +KO = PKA * SKO_A = (SKA * P) * SKO_A = SKA * (SKO_A * P) = SKA * PKO_A + +Using the function `ownerEngagement`, the asset sends the hash of KA, 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 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, PKU_A, as the attribute `dataEngagement` and the hash of KU = PKA * SKU_A as the attribute `hashK_UA` in the NFT. + +The user sends request engagement and the asset calculates: + +KA = SKA * PKU_A + +If everything is correctly done, KU and KA are the same since: + +KU = PKA * SKU_A = (SKA * P) * SKU_A = SKA * (SKU_A * P) = SKA * PKU_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. + + ![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, 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 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. + event UserAssigned(uint256 indexed tokenId, address indexed _addressUser); + + /// @dev This emits when user and asset finish mutual authentication process successfully. + /// This event emits when both the user and the asset prove they share a secure communication channel. + event UserEngaged(uint256 indexed tokenId); + + /// @dev This emits when owner and asset finish mutual authentication process successfully. + /// This event emits when both the owner and the asset prove they share a secure communication channel. + 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 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 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 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 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; + + /// @notice This function completes the mutual authentication process between the owner and the asset. + /// @dev Only the asset tied to the token can finish this authentication process provided that the state of the token is + /// "waitingForOwner" and dataEngagement is different from 0. This function compares hashK_OA saved in + /// the token with hashK_A. If they are equal then the state of the token changes to "engagedWithOwner", dataEngagement is set to 0, + /// and the event "OwnerEngaged" is emitted. + /// @param _hashK_A is the hash of the secret generated by the asset to share with the owner. + function ownerEngagement(uint256 _hashK_A) external payable; + + /// @notice This function defines the initialization of the mutual authentication process between the user and the asset. + /// @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 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 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. + function userEngagement(uint256 _hashK_A) external payable; + + /// @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 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 EIP-4519 NFT tied to the asset. + /// @param _timeout is the value to assign to timeout. + function setTimeout(uint256 _tokenId, uint256 _timeout) external; + + /// @notice This function updates the timestamp, thus avoiding the timeout alarm. + /// @dev Only the asset tied to the token can update its own timestamp. + function updateTimestamp() external; + + /// @notice This function lets obtain the tokenId from an address. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressAsset is the address to obtain the tokenId from it. + /// @return tokenId of the token tied to the asset that generates _addressAsset. + function tokenFromBCA(address _addressAsset) external view returns (uint256); + + /// @notice This function lets know the owner of the token from the address of the asset tied to the token. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressAsset is the address to obtain the owner from it. + /// @return owner of the token bound to the asset that generates _addressAsset. + function ownerOfFromBCA(address _addressAsset) external view returns (address); + + /// @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 EIP-4519 NFT tied to the asset. + /// @return user of the token from its _tokenId. + function userOf(uint256 _tokenId) external view returns (address); + + /// @notice This function lets know the user of the token from the address of the asset tied to the token. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressAsset is the address to obtain the user from it. + /// @return user of the token tied to the asset that generates _addressAsset. + function userOfFromBCA(address _addressAsset) external view returns (address); + + /// @notice This function lets know how many tokens are assigned to a user. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressUser is the address of the user. + /// @return number of tokens assigned to a user. + function userBalanceOf(address _addressUser) external view returns (uint256); + + /// @notice This function lets know how many tokens of a particular owner are assigned to a user. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressUser is the address of the user. + /// @param _addressOwner is the address of the owner. + /// @return number of tokens assigned to a user from an owner. + function userBalanceOfAnOwner(address _addressUser, address _addressOwner) external view returns (uint256); +} +``` + +## Rationale + +### Authentication + +This EIP uses smart contracts to verify the mutual authentication process since smart contracts are trustless. + +### 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. + +### 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 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). + +## 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 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-4520.md b/EIPS/eip-4520.md new file mode 100644 index 00000000000000..45b73f24ee7662 --- /dev/null +++ b/EIPS/eip-4520.md @@ -0,0 +1,36 @@ +--- +eip: 4520 +title: Multi-byte opcodes prefixed by EB and EC. +description: Reserve `0xEB` and `0xEC` for usage as extended opcode space. +author: Brayton Goodall (@Spore-Druid-Bray), Mihir Faujdar (@uink45) +discussions-to: https://ethereum-magicians.org/t/multi-byte-opcodes/7681 +status: Stagnant +type: Standards Track +category: Core +created: 2021-12-01 +--- + +## Abstract +Reserve `0xEB` and `0xEC` for usage as extended opcode space. + +## Motivation +It would be convenient to introduce new opcodes that are likely to be infrequently used, whilst also being able to have greater than 256 opcodes in total. As a single byte opcode is half the size of a double byte opcode, the greatest efficiency in code sizes will be one where frequently used opcodes are single bytes. Two prefix bytes are used to accommodate up to 510 double byte opcodes. + +## Specification +For example, a new arithmetic opcode may be allocated to `0xEC 01`(`ADD`), and a novel opcode may be introduced at `0xEB F4`(`DELEGATECALL`). + +Triple byte opcodes may be doubly-prefixed by `0xEB EB`, `0xEC EC`, `0xEB EC` and `0xEC EB`. It is possible to allocate experimental opcodes to this triple-byte space initially, and if they prove safe and useful, they could later be allocated a location in double-byte or single-byte space. + +Only `0xEB EB`, `0xEC EC`, `0xEC EC`, and `0xEB EC` may be interpreted as further extensions of the opcode space. `0xEB` and `0xEC` do not themselves affect the stack or memory, however opcodes specified by further bytes may. If a multi-byte opcode is yet to be defined, it is to be treated as `INVALID` rather than as a `NOP`, as per usual for undefined opcodes. + +## Rationale +It was considered that two prefix bytes rather than one would be adequate for reservation as extension addresses. Both `0xEB` and `0xEC` were chosen to be part of the E-series of opcodes. For example, the `0xEF` byte is reserved for contracts conforming to the Ethereum Object Format. By having unassigned opcodes for extending the opcode space, there will be a lower risk of breaking the functionalities of deployed contracts compared to choosing assigned opcodes. + +## Backwards Compatibility +Previous usage of `0xEB` and `0xEC` may result in unexpected behaviour and broken code. + +## Security Considerations +There are no known security considerations. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4521.md b/EIPS/eip-4521.md new file mode 100644 index 00000000000000..d12bf999bd8cf6 --- /dev/null +++ b/EIPS/eip-4521.md @@ -0,0 +1,62 @@ +--- +eip: 4521 +title: 721/20-compatible transfer +description: Recommends a simple extension to make NFTs compatible with apps and contracts that handle fungibles. +author: Ross Campbell (@z0r0z) +discussions-to: https://ethereum-magicians.org/t/eip-4521-721-20-compatible-transfer/7903 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-12-13 +requires: 721 +--- + +## Abstract +ERC-721, the popular standard for non-fungible tokens (NFTs), includes send functions, such as `transferFrom()` and `safeTransferFrom()`, but does not include a backwards-compatible `transfer()` found in fungible ERC-20 tokens. This standard provides references to add such a `transfer()`. + +## Motivation +This standard proposes a simple extension to allow NFTs to work with contracts designed to manage ERC-20s and many consumer wallets which expect to be able to execute a token `transfer()`. For example, if an NFT is inadvertently sent to a contract that typically handles ERC-20, that NFT will be locked. It should also simplify the task for contract programmers if they can rely on `transfer()` to both handle ERC-20 and 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. + +The interface for ERC-4521 `transfer()` MUST conform to ERC-20 and resulting transfers MUST fire the `Transfer` event as described in ERC-721. + +```sol +function transfer(address to, uint256 tokenId) external returns (bool success); +``` + +## Rationale +Replicating ERC-20 `transfer()` with just a minor change to accurately reflect that a unique `tokenId` rather than fungible sum is being sent is desirable for code simplicity and to make integration easier. + +## Backwards Compatibility +This EIP does not introduce any known backward compatibility issues. + +## Reference Implementation +A reference implementation of an ERC-4521 `transfer()`: + +```sol +function transfer(address to, uint256 tokenId) public virtual returns (bool success) { + require(msg.sender == ownerOf[tokenId], "NOT_OWNER"); + + unchecked { + balanceOf[msg.sender]--; + + balanceOf[to]++; + } + + delete getApproved[tokenId]; + + ownerOf[tokenId] = to; + + emit Transfer(msg.sender, to, tokenId); + + success = true; +} +``` + +## Security Considerations +Implementers must be sure to include the relevant return `bool` value for an ERC-4521 in order to conform with existing contracts that use ERC-20 interfaces, otherwise, NFTs may be locked unless a `safeTransfer` is used in such contracts. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4524.md b/EIPS/eip-4524.md new file mode 100644 index 00000000000000..4475577eddebd2 --- /dev/null +++ b/EIPS/eip-4524.md @@ -0,0 +1,82 @@ +--- +eip: 4524 +title: Safer ERC-20 +description: Extending ERC-20 with ERC165 and adding safeTransfer (like ERC-721 and ERC-1155) +author: William Schwab (@wschwab) +discussions-to: https://ethereum-magicians.org/t/why-isnt-there-an-erc-for-safetransfer-for-erc20/7604 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-12-05 +requires: 20, 165 +--- + +## Abstract + +This standard extends [ERC-20](./eip-20.md) tokens with [EIP-165](./eip-165.md), and adds familiar functions from [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) ensuring receiving contracts have implemented proper functionality. + +## Motivation + +[EIP-165](./eip-165.md) adds (among other things) the ability to tell if a target recipient explicitly signals compatibility with an ERC. This is already used in the EIPs for NFTs, [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md). In addition, EIP-165 is a valuable building block for extensions on popular standards to signal implementation, a trend we've seen in a number of NFT extensions. This EIP aims to bring these innovations back to ERC-20. + +The importance of [EIP-165](./eip-165.md) is perhaps felt most for app developers looking to integrate with a generic standard such as ERC-20 or ERC-721, while integrating newer innovations built atop these standards. An easy example would be token permits, which allow for a one-transaction approval and transfer. This has already been implemented in many popular ERC-20 tokens using the [ERC-2612](./eip-2612.md) standard or similar. A platform integrating ERC-20 tokens has no easy way of telling if a particular token has implemented token permits or not. (As of this writing, ERC-2612 does not require EIP-165.) With EIP-165, the app (or contracts) could query `supportsInterface` to see if the `interfaceId` of a particular EIP is registered (in this case, EIP-2612), allowing for easier and more modular functions interacting with ERC-20 contracts. It is already common in NFT extensions to include an EIP-165 interface with a standard, we would argue this is at least in part due to the underlying [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) standards integrating EIP-165. Our hope is that this extension to ERC-20 would also help future extensions by making them easier to integrate. + +## 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. + +In order to be compliant with this EIP, and ERC-20-compliant contract MUST also implement the following functions: +```solidity +pragma solidity 0.8.10; + +import './IERC20.sol'; +import './IERC165.sol'; + +// the EIP-165 interfaceId for this interface is 0x534f5876 + +interface SaferERC-20 is IERC20, IERC165 { + function safeTransfer(address to, uint256 amount) external returns(bool); + function safeTransfer(address to, uint256 amount, bytes memory data) external returns(bool); + function safeTransferFrom(address from, address to, uint256 amount) external returns(bool); + function safeTransferFrom(address from, address to, uint256 amount, bytes memory data) external returns(bool); +} +``` +`safeTransfer` and `safeTransferFrom` MUST transfer as expected to EOA addresses, and to contracts implementing `ERC20Receiver` and returning the function selector (`0x4fc35859`) when called, and MUST revert when transferring to a contract which either does not have `ERC20Receiver` implemented, or does not return the function selector when called. + +In addition, a contract accepting safe transfers MUST implement the following if it wishes to accept safe transfers, and MUST return the function selector (`0x4fc35859`): +```solidity +pragma solidity 0.8.10; + +import './IERC165.sol'; + +interface ERC20Receiver is IERC165 { + function onERC20Received( + address _operator, + address _from, + uint256 _amount, + bytes _data + ) external returns(bytes4); +} +``` + +## Rationale + +This EIP is meant to be minimal and straightforward. Adding EIP-165 to ERC-20 is useful for a number of applications, and outside of a minimal amount of code increasing contract size, carries no downside. The `safeTransfer` and `safeTransferFrom` functions are well recognized from ERC-721 and ERC-1155, and therefore keeping identical naming conventions is reasonable, and the benefits of being able to check for implementation before transferring are as useful for ERC-20 tokens as they are for ERC-721 and ERC-1155. + +Another easy backport from EIP721 and EIP1155 might be the inclusion of a metadata URI for tokens, allowing them to easily reference logo and other details. This has not been included, both in order to keep this EIP as minimal as possible, and because it is already sufficiently covered by [EIP-1046](./eip-1046.md). + +## Backwards Compatibility + +There are no issues with backwards compatibility in this EIP, as the full suite of ERC-20 functions is unchanged. + +## Test Cases +Test cases have been provided in the implementation repo [here](https://github.com/wschwab/SaferERC-20/blob/main/src/SaferERC-20.t.sol). + +## Reference Implementation +A sample repo demonstrating an implementation of this EIP has been created [here](https://github.com/wschwab/SaferERC-20). It is (as of this writing) in a Dapptools environment, for details on installing and running Dapptools see the Dapptools repo. + +## Security Considerations + +`onERC20Received` is a callback function. Callback functions have been exploited in the past as a reentrancy vector, and care should be taken to make sure implementations are not vulnerable. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4527.md b/EIPS/eip-4527.md new file mode 100644 index 00000000000000..76e1e8ad8d262d --- /dev/null +++ b/EIPS/eip-4527.md @@ -0,0 +1,239 @@ +--- +eip: 4527 +title: QR Code transmission protocol for wallets +description: QR Code data transmission protocol between wallets and offline signers. +author: Aaron Chen (@aaronisme), Sora Lee (@soralit), ligi (@ligi), Dan Miller (@danjm), AndreasGassmann (@andreasgassmann), xardass (@xardass), Lixin Liu (@BitcoinLixin) +discussions-to: https://ethereum-magicians.org/t/add-qr-code-scanning-between-software-wallet-cold-signer-hardware-wallet/6568 +status: Review +type: Standards Track +category: ERC +created: 2021-12-07 +--- + +## Abstract + +The purpose of this EIP is to provide a process and data transmission protocol via QR Code between offline signers and watch-only wallets. + +## Motivation + +There is an increasing number of users whom like to use complete offline signers to manage their private keys, signers like hardware wallets and mobile phones in offline mode. In order to sign transactions or data, these offline signers have to rely on a watch-only wallet since it would prepare the data to be signed. Currently, there are 4 possible data transmission methods between offline signers and watch-only wallets: QR Code, USB, Bluetooth, and file transfer. The QR Code data transmission method have the following advantages when compared to the other three methods mentioned above: + +- Transparency and Security: Compared to USB or Bluetooth, users can easily decode the data via QR Code (with the help of some tools). It can also help users clearly identify what they are going to sign, which improves transparency and thus better security. +- Improved Compatibility: Compared to USB and Bluetooth, QR Code data transmissions has a wider range of compatibility. Normally, it wouldn’t be broken by software changes like browser upgrades, system upgrade, and etc. +- Improved User experience: QR Code data transmissions can provide a better user experience compared to USB, Bluetooth, and file transfer especially when the user is using a mobile device. +- A smaller attack surface: USB and Bluetooth have a bigger attack surface than QR-Codes. + +Due to these advantages, QR Code data transmissions is a better choice. Unfortunately, there is no modern standard for how offline signers should work with watch-only wallets nor how data should be encoded. +This EIP presents a standard process and data transmission protocol for offline signers to work with watch-only wallets. + +## Specification + +**Offline signer**: An offline signer is a device or application which holds the user’s private keys and does not have network access. + +**Watch-only wallet**: A watch-only wallet is a wallet that has network access and can interact with the Ethereum blockchain. + +### Process + +In order to work with offline signers, the watch-only wallet should follow the following process. + +1. The offline signer provides the public key information to the watch-only wallet to generate addresses, sync balances and etc via QR Codes. +2. The watch-only wallet generates the unsigned data and sends it to an offline signer for signing via QR Code, data that can include transactions, typed data, and etc. +3. The offline signer signs the data and provides a signature back to the watch-only wallet via QR Code. +4. The watch-only wallet receives the signature, constructs the signed data (transaction) and performs the following activities like broadcasting the transaction etc. + +### Data Transmission Protocol + +Since a single QR Code can only contain a limited amount of data, animated QR Codes should be utilized for data transmission. The `BlockchainCommons` have published a series of data transmission protocol called Uniform Resources (UR). It provides a basic method to encode data into animated QR Codes. This EIP will use UR and extend its current definition. + +`Concise Binary Object Representation(CBOR)` will be used for binary data encoding. `Concise Data Definition Language(CDDL)` will be used for expressing the CBOR. + +### Setting up the watch-only wallet with the offline signer + +In order to allow a watch-only wallet to collect information from the Ethereum blockchain, the offline signer would need to provide the public keys to the watch-only wallet in which the wallet will use them to query the necessary information from the Ethereum blockchain. + +In such a case, offline signers should provide the extended public keys and derivation path. The UR Type called `crypto-hdkey` will be used to encode this data and the derivation path will be encoded as `crypto-keypath`. + + +#### CDDL for Key Path + +The `crypto-keypath` will be used to specify the key path.The following specification is written in Concise Data Definition Language(CDDL) for `crypto-key-path` + +``` +; Metadata for the derivation path of a key. +; +; `source-fingerprint`, if present, is the fingerprint of the +; ancestor key from which the associated key was derived. +; +; If `components` is empty, then `source-fingerprint` MUST be a fingerprint of +; a master key. +; +; `depth`, if present, represents the number of derivation steps in +; the path of the associated key, even if not present in the `components` element +; of this structure. + crypto-keypath = { + components: [path-component], ; If empty, source-fingerprint MUST be present + ? source-fingerprint: uint32 .ne 0 ; fingerprint of ancestor key, or master key if components is empty + ? depth: uint8 ; 0 if this is a public key derived directly from a master key + } + path-component = ( + child-index / child-index-range / child-index-wildcard-range, + is-hardened + ) + uint32 = uint .size 4 + uint31 = uint32 .lt 2147483648 ;0x80000000 + child-index = uint31 + child-index-range = [child-index, child-index] ; [low, high] where low < high + child-index-wildcard = [] + is-hardened = bool + components = 1 + source-fingerprint = 2 + depth = 3 +``` + +#### CDDL for Extended Public Keys + +Since the purpose is to transfer public key data, the definition of `crypto-hdkey` will be kept only for public key usage purposes. + +The following specification is written in Concise Data Definition Language `CDDL` and includes the crypto-keypath spec above. + +``` +; An hd-key must be a derived key. +hd-key = { + derived-key +} +; A derived key must be public, has an optional chain code, and +; may carry additional metadata about its use and derivation. +; To maintain isomorphism with [BIP32] and allow keys to be derived from +; this key `chain-code`, `origin`, and `parent-fingerprint` must be present. +; If `origin` contains only a single derivation step and also contains `source-fingerprint`, +; then `parent-fingerprint` MUST be identical to `source-fingerprint` or may be omitted. +derived-key = ( + key-data: key-data-bytes, + ? chain-code: chain-code-bytes ; omit if no further keys may be derived from this key + ? origin: #6.304(crypto-keypath), ; How the key was derived + ? name: text, ; A short name for this key. + ? source: text, ; The device info or any other description for this key +) +key-data = 3 +chain-code = 4 +origin = 6 +name = 9 +source = 10 + +uint8 = uint .size 1 +key-data-bytes = bytes .size 33 +chain-code-bytes = bytes .size 32 +``` + +If the chain-code is provided, then it can be used to derive child keys but if it isn’t provided, it is simply a solo key and the origin can be provided to indicate the derivation key path. + +If the signer would like to provide muliple public keys instead of the extended public key for any reason, the signer can use `crypto-account` for that. + +### Sending the unsigned data from the watch-only wallet to the offline signer + +To send the unsigned data from a watch-only wallet to an offline signer, the new UR type `eth-sign-request` will be introduced to encode the signing request. + +#### CDDL for Eth Sign Request. + +The following specification is written in Concise Data Definition Language `CDDL`. +UUIDs in this specification notated UUID are CBOR binary strings tagged with #6.37, per the IANA `CBOR Tags Registry`. + +``` +; Metadata for the signing request for Ethereum. +; +sign-data-type = { + type: int .default 1 transaction data; the unsigned data type +} + +eth-transaction-data = 1; legacy transaction rlp encoding of unsigned transaction data +eth-typed-data = 2; EIP-712 typed signing data +eth-raw-bytes=3; for signing message usage, like EIP-191 personal_sign data +eth-typed-transaction=4; EIP-2718 typed transaction of unsigned transaction data + +; Metadata for the signing request for Ethereum. +; request-id: the identifier for this signing request. +; sign-data: the unsigned data +; data-type: see sign-data-type definition +; chain-id: chain id definition see https://github.com/ethereum-lists/chains for detail +; derivation-path: the key path of the private key to sign the data +; address: Ethereum address of the signing type for verification purposes which is optional + +eth-sign-request = ( + sign-data: sign-data-bytes, ; sign-data is the data to be signed by offline signer, currently it can be unsigned transaction or typed data + data-type: #3.401(sign-data-type), + chain-id: int .default 1, + derivation-path: #5.304(crypto-keypath), ;the key path for signing this request + ?request-id: uuid, ; the uuid for this signing request + ?address: eth-address-bytes, ;verification purpose for the address of the signing key + ?origin: text ;the origin of this sign request, like wallet name +) +request-id = 1 +sign-data = 2 +data-type = 3 +chain-id = 4 ;it will be the chain id of ethereum related blockchain +derivation-path = 5 +address = 6 +origin = 7 +eth-address-bytes = bytes .size 20 +sign-data-bytes = bytes ; for unsigned transactions it will be the rlp encoding for unsigned transaction data and ERC 712 typed data it will be the bytes of json string. +``` + +### The signature provided by offline signers to watch-only wallets + +After the data is signed, the offline signer should send the signature back to the watch-only wallet. The new UR type called `eth-signature` is introduced here to encode this data. + +#### CDDL for Eth Signature. + +The following specification is written in Concise Data Definition Language `CDDL`. + +``` +eth-signature = ( + request-id: uuid, + signature: eth-signature-bytes, + ? origin: text, ; The device info for providing this signature +) + +request-id = 1 +signature = 2 +origin = 3 + +eth-signature-bytes = bytes .size 65; the signature of the signing request (r,s,v) +``` + +## Rationale + +This EIP uses some existing UR types like `crypto-keypath` and `crypto-hdkey` and also introduces some new UR types like `eth-sign-request` and `eth-signature`. Here are the reasons we choose UR for the QR Code data transmission protocol: + +### UR provides a solid foundation for QR Code data transmission + +- Uses the alphanumeric QR code mode for efficiency. +- Includes a CRC32 checksum of the entire message in each part to tie the different parts of the QR code together and ensure the transmitted message has been reconstructed. +- uses `Fountain Code` for the arbitrary amount of data which can be both a minimal, finite sequence of parts and an indefinite sequence of parts. The Fountain Code can ultimately help the receiver to make the data extraction easier. + +### UR provides existing helpful types and scalability for new usages + +Currently, UR has provided some existing types like `crypto-keypath` and `crypto-hdkey` so it is quite easy to add a new type and definitions for new usages. + +### UR has an active air-gapped wallet community. + +Currently, the UR has an active `airgapped wallet community` which continues to improve the UR forward. + +## Backwards Compatibility + +Currently, there is no existing protocol to define data transmissions via QR Codes so there are no backward compatibility issues that needs to be addressed now. + +## Test Cases + +The test cases can be found on the `ur-registry-eth` package released by the Keystone team. + +## Reference Implementation + +The reference implementation can be found on the `ur-registry-eth` package released by the Keystone team. + +## Security Considerations + +The offline signer should decode all the data from `eth-sign-request` and show them to the user for confirmation prior to signing. It is recommended to provide an address field in the `eth-sign-request`. If provided, the offline signer should verify the address being the same one as the address associated with the signing key. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4546.md b/EIPS/eip-4546.md new file mode 100644 index 00000000000000..c27fcabce67b99 --- /dev/null +++ b/EIPS/eip-4546.md @@ -0,0 +1,173 @@ +--- +eip: 4546 +title: Wrapped Deposits +description: A singleton contract for managing asset deposits. +author: Justice Hudson (@jchancehud) +discussions-to: https://ethereum-magicians.org/t/wrapped-deposit-contract-eip/7740 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-12-11 +--- + +## Abstract +The wrapped deposit contract handles deposits of assets (Ether, [ERC-20](./eip-20.md), [ERC-721](./eip-721.md)) on behalf of a user. A user must only approve a spend limit once and then an asset may be deposited to any number of different applications that support deposits from the contract. + +## Motivation +The current user flow for depositing assets in dapps is unnecessarily expensive and insecure. To deposit an ERC-20 asset a user must either: + + - send an approve transaction for the exact amount being sent, before making a deposit, and then repeat this process for every subsequent deposit. + - send an approve transaction for an infinite spend amount before making deposits. + +The first option is inconvenient, and expensive. The second option is insecure. Further, explaining approvals to new or non-technical users is confusing. This has to be done in _every_ dapp that supports ERC20 deposits. + +## Specification +The wrapped deposit contract SHOULD be deployed at an identifiable address (e.g. `0x1111119a9e30bceadf9f939390293ffacef93fe9`). The contract MUST be non-upgradable with no ability for state variables to be changed. + +The wrapped deposit contract MUST have the following public functions: + +```js +depositERC20(address to, address token, uint amount) external; +depositERC721(address to, address token, uint tokenId) external; +safeDepositERC721(address to, address token, uint tokenId, bytes memory data) external; +safeDepositERC1155(address to, address token, uint tokenId, uint value, bytes calldata data) external; +batchDepositERC1155(address to, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) external; +depositEther(address to) external payable; +``` + +Each of these functions MUST revert if `to` is an address with a zero code size. Each function MUST attempt to call a method on the `to` address confirming that it is willing and able to accept the deposit. If this function call does not return a true value execution MUST revert. If the asset transfer is not successful execution MUST revert. + +The following interfaces SHOULD exist for contracts wishing to accept deposits: + +```ts +interface ERC20Receiver { + function acceptERC20Deposit(address depositor, address token, uint amount) external returns (bool); +} + +interface ERC721Receiver { + function acceptERC721Deposit(address depositor, address token, uint tokenId) external returns (bool); +} + +interface ERC1155Receiver { + function acceptERC1155Deposit(address depositor, address token, uint tokenId, uint value, bytes calldata data) external returns (bool); + function acceptERC1155BatchDeposit(address depositor, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) external returns (bool); +} + +interface EtherReceiver { + function acceptEtherDeposit(address depositor, uint amount) external returns (bool); +} +``` + +A receiving contract MAY implement any of these functions as desired. If a given function is not implemented deposits MUST not be sent for that asset type. + +## Rationale +Having a single contract that processes all token transfers allows users to submit a single approval per token to deposit to any number of contracts. The user does not have to trust receiving contracts with token spend approvals and receiving contracts have their complexity reduced by not having to implement token transfers themselves. + +User experience is improved because a simple global dapp can be implemented with the messaging: "enable token for use in other apps". + +## Backwards Compatibility + +This EIP is not backward compatible. Any contract planning to use this deposit system must implement specific functions to accept deposits. Existing contracts that are upgradeable can add support for this EIP retroactively by implementing one or more accept deposit functions. + +Upgraded contracts can allow deposits using both the old system (approving the contract itself) and the proposed deposit system to preserve existing approvals. New users should be prompted to use the proposed deposit system. + +## Reference Implementation +```ts +pragma solidity ^0.7.0; + +interface ERC20Receiver { + function acceptERC20Deposit(address depositor, address token, uint amount) external returns (bool); +} + +interface ERC721Receiver { + function acceptERC721Deposit(address depositor, address token, uint tokenId) external returns (bool); +} + +interface ERC1155Receiver { + function acceptERC1155Deposit(address depositor, address token, uint tokenId, uint value, bytes calldata data) external returns (bool); + function acceptERC1155BatchDeposit(address depositor, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) external returns (bool); +} + +interface EtherReceiver { + function acceptEtherDeposit(address depositor, uint amount) external returns (bool); +} + +interface IERC20 { + function transferFrom(address sender, address recipient, uint amount) external returns (bool); +} + +interface IERC721 { + function transferFrom(address _from, address _to, uint256 _tokenId) external payable; + function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) external payable; +} + +interface IERC1155 { + function safeTransferFrom(address _from, address _to, uint _id, uint _value, bytes calldata _data) external; + function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external; +} + +contract WrappedDeposit { + function depositERC20(address to, address token, uint amount) public { + _assertContract(to); + require(ERC20Receiver(to).acceptERC20Deposit(msg.sender, token, amount)); + bytes memory data = abi.encodeWithSelector( + IERC20(token).transferFrom.selector, + msg.sender, + to, + amount + ); + (bool success, bytes memory returndata) = token.call(data); + require(success); + // backward compat for tokens incorrectly implementing the transfer function + if (returndata.length > 0) { + require(abi.decode(returndata, (bool)), "ERC20 operation did not succeed"); + } + } + + function depositERC721(address to, address token, uint tokenId) public { + _assertContract(to); + require(ERC721Receiver(to).acceptERC721Deposit(msg.sender, token, tokenId)); + IERC721(token).transferFrom(msg.sender, to, tokenId); + } + + function safeDepositERC721(address to, address token, uint tokenId, bytes memory data) public { + _assertContract(to); + require(ERC721Receiver(to).acceptERC721Deposit(msg.sender, token, tokenId)); + IERC721(token).safeTransferFrom(msg.sender, to, tokenId, data); + } + + function safeDepositERC1155(address to, address token, uint tokenId, uint value, bytes calldata data) public { + _assertContract(to); + require(ERC1155Receiver(to).acceptERC1155Deposit(msg.sender, to, tokenId, value, data)); + IERC1155(token).safeTransferFrom(msg.sender, to, tokenId, value, data); + } + + function batchDepositERC1155(address to, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) public { + _assertContract(to); + require(ERC1155Receiver(to).acceptERC1155BatchDeposit(msg.sender, to, tokenIds, values, data)); + IERC1155(token).safeBatchTransferFrom(msg.sender, to, tokenIds, values, data); + } + + function depositEther(address to) public payable { + _assertContract(to); + require(EtherReceiver(to).acceptEtherDeposit(msg.sender, msg.value)); + (bool success, ) = to.call{value: msg.value}(''); + require(success, "nonpayable"); + } + + function _assertContract(address c) private view { + uint size; + assembly { + size := extcodesize(c) + } + require(size > 0, "noncontract"); + } +} +``` +## Security Considerations +The wrapped deposit implementation should be as small as possible to reduce the risk of bugs. The contract should be small enough that an engineer can read and understand it in a few minutes. + +Receiving contracts MUST verify that `msg.sender` is equal to the wrapped deposit contract. Failing to do so allows anyone to simulate deposits. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4573.md b/EIPS/eip-4573.md new file mode 100644 index 00000000000000..9015b073a0a867 --- /dev/null +++ b/EIPS/eip-4573.md @@ -0,0 +1,195 @@ +--- +eip: 4573 +title: Procedures for the EVM +description: Introduces support for EVM Procedures. +status: Stagnant +type: Standards Track +category: Core +author: Greg Colvin (@gcolvin), Greg Colvin +discussions-to: https://ethereum-magicians.org/t/eip-4573-named-procedures-for-evm-code-sections/7776 +created: 2021-12-16 +requires: 2315, 3540, 3670, 3779, 4200 +--- + +## Abstract + +Five EVM instructions are introduced to define, call, and return from named EVM _procedures_ and access their _call frames_ in memory - `ENTERPROC`, `LEAVEPROC`, `CALLPROC`, `RETURNPROC`, and `FRAMEADDRESS`. + +## Motivation + +Currently, Ethereum bytecode has no syntactic structure, and _subroutines_ have no defined interfaces. + +We propose to add _procedures_ -- delimited blocks of code that can be entered only by calling into them via defined interfaces. + +Also, the EVM currently has no automatic management of memory for _procedures_. So we also propose to automatically reserve call frames on an in-memory stack. + +Constraints on the use of _procedures_ must be validated at contract initialization time to maintain the safety properties of [EIP-3779](./eip-3779.md): Valid programs will not halt with an exception unless they run out of gas or recursively overflow stack. + +### Prior Art + +The terminology is not well-defined, but we will follow Intel in calling the low-level concept _subroutines_ and the higher level concept _procedures_. The distinction is that _subroutines_ are little more than a jump that knows where it came from, whereas procedures have a defined interface and manage memory as a stack. [EIP-2315](./eip-2315.md) introduces _subroutines_, and this EIP introduces _procedures_. + +## Specification + +### Instructions + +#### ENTERPROC (0x??) dest_section: uint8, dest_offset: uint8, n_inputs: uint16, n_outputs: uint16, n_locals: uint16 +``` +frame_stack.push(FP) +FP -= n_locals * 32 +PC +- +``` +Marks the entry point to a procedure +* at offset `dest_offset` from the beginning of the `dest_section`. +* taking `n_inputs` arguments from the data stack, +* returning `n_outputs` values on the `data stack`, and +* reserving `n_locals` words of data in memory on the `frame stack`. + +Procedures can only be entered via a `CALLPROC` to their entry point. + +#### LEAVEPROC (0x??) + +``` + FP = frame_stack.pop() + asm RETURNSUB +``` +> Pop the `frame stack` and return to the calling procedure using `RETURNSUB`. + +Marks the end of a procedure. Each `ENTERPROC` requires a closing `LEAVEPROC`. + +*Note: Attempts to jump into a procedure (including its `LEAVEPROC`) from outside of the procedure or to jump or step to `ENTERPROC` at all must be prevented at validation time. `CALLPROC` is the only valid way to enter a procedure.* + +#### CALLPROC (0x??) dest_section: uint16, dest_proc: uint16 + ``` + FP -= n_locals + asm JUMPSUB + +``` +> Allocate a *stack frame* and transfer control and `JUMPSUB` to the Nth (N=*dest_proc*) _procedure_ in the Mth(M=*dest_section*) _section_ of the code. _Section 0_ is the current code section, any other code sections are indexed starting at _1_. + +*Note: That the procedure is defined and the required `n_inputs` words are available on the `data stack` must be shown at validation time.* + +#### RETURNPROC (0x??) +``` + FP += n_locals + asm RETURNSUB +``` +> Pop the `frame stack` and return control to the calling procedure using `RETURNSUB`. + +*Note: That the promised `n_outputs` words are available on the `data stack` must be shown at validation time.* + +#### FRAMEADDRESS (0x??) offset: int16 +``` +asm PUSH2 FP + offset +``` +> Push the address `FP + offset` onto the data stack. + +Call frame data is addressed at an immediate `offset` relative to `FP`. + +Typical usage includes storing data on a call frame +``` +PUSH 0xdada +FRAMEADDRESS 32 +MSTORE +``` +and loading data from a call frame +``` +FRAMEADDRESS 32 +MLOAD +``` + +### Memory Costs + +Presently,`MSTORE` is defined as +``` + memory[stack[0]...stack[0]+31] = stack[1] + memory_size = max(memory_size,floor((stack[0]+32)÷32) +``` +* where `memory_size` is the number of active words of memory above _0_. + +We propose to treat memory addresses as signed, so the formula needs to be +``` + memory[stack[0]...stack[0]+31] = stack[1] + if (stack[0])+32)÷32) < 0 + negative_memory_size = max(negative_memory_size,floor((stack[0]+32)÷32)) + else + positive_memory_size = max(positive_memory_size,floor((stack[0]+32)÷32)) + memory_size = positive_memory_size + negative_memory_size +``` +* where `negative_memory_size` is the number of active words of memory below _0_ and +* where `positive_memory_size` is the number of active words of memory at or above _0_. + +### Call Frame Stack + +These instructions make use of a `frame stack` to allocate and free frames of local data for _procedures_ in memory. Frame memory begins at address 0 in memory and grows downwards, towards more negative addresses. A frame is allocated for each procedure when it is called, and freed when it returns. + +Memory can be addressed relative to the frame pointer `FP` or by absolute address. `FP` starts at 0, and moves downward towards more negative addresses to point to the frame for each `CALLPROC` and moving upward towards less negative addresses to point to the previous frame for the corresponding `RETURNPROC`. + +Equivalently, in the EVM's twos-complement arithmetic, `FP` moves from the highest address down, as is common in many calling conventions. + +For example, after an initial `CALLPROC` to a procedure needing two words of data the `frame stack` might look like this + +``` + 0-> ........ + ........ + FP-> +``` +Then, after a further `CALLPROC` to a procedure needing three words of data the `frame stack` would like this + +``` + 0-> ........ + ........ + -64-> ........ + ........ + ........ + FP-> +``` +After a `RETURNPROC` from that procedure the `frame stack` would look like this +``` + 0-> ........ + ........ + FP-> ........ + ........ + ........ +``` +and after a final `RETURNPROC`, like this +``` + FP-> ........ + ........ + ........ + ........ + ........ +``` + +## Rationale + +There is actually not much new here. It amounts to [EIP-615](./eip-615.md), refined and refactored into bite-sized pieces, along lines common to other machines. + +This proposal uses the [EIP-2315](./eip-2315.md) return stack to manage calls and returns, and steals ideas from [EIP-615](./eip-615.md), [EIP-3336](./eip-3336.md), and [EIP-4200](./eip-4200.md). `ENTERPROC` corresponds to `BEGINSUB` from EIP-615. Like EIP-615 it uses a frame stack to track call-frame addresses with `FP` as _procedures_ are entered and left, but like EIP-3336 and EIP-3337 it moves call frames from the data stack to memory. + +Aliasing call frames with ordinary memory supports addressing call-frame data with ordinary stores and loads. This is generally useful, especially for languages like C that provide pointers to variables on the stack. + +The design model here is the _subroutines_ and _procedures_ of the Intel x86 architecture. +* `JUMPSUB` and `RETURNSUB` (from [EIP-2315](./eip-2315.md) -- like `CALL` and `RET` -- jump to and return from _subroutines_. +* `ENTERPROC` -- like `ENTER` -- sets up the stack frame for a _procedure_. +* `CALLPROC` amounts to a `JUMPSUB` to an `ENTERPROC`. +* `RETURNPROC` amounts to an early `LEAVEPROC`. +* `LEAVEPROC` -- like `LEAVE` -- takes down the stack frame for a _procedure_. It then executes a `RETURNSUB`. + +## Backwards Compatibility + +This proposal adds new EVM opcodes. It doesn't remove or change the semantics of any existing opcodes, so there should be no backwards compatibility issues. + +## Security + +Safe use of these constructs must be checked completely at validation time -- per EIP-3779 -- so there should be no security issues at runtime. + +`ENTERPROC` and `LEAVEPROC` must follow the same safety rules as for `JUMPSUB` and `RETURNSUB` in EIP-2315. In addition, the following constraints must be validated: + +* Every`ENTERPROC` must be followed by a `LEAVEPROC` to delimit the bodies of _procedures_. +* There can be no nested _procedures_. +* There can be no jump into the body of a procedure (including its `LEAVEPROC`) from outside of that body. +* There can be no jump or step to `BEGINPROC` at all -- only `CALLPROC`. +* The specified `n_inputs` and `n_outputs` must be on the stack. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4626.md b/EIPS/eip-4626.md new file mode 100644 index 00000000000000..c28df5cba1ac01 --- /dev/null +++ b/EIPS/eip-4626.md @@ -0,0 +1,612 @@ +--- +eip: 4626 +title: Tokenized Vaults +description: Tokenized Vaults with a single underlying EIP-20 token. +author: Joey Santoro (@joeysantoro), t11s (@transmissions11), Jet Jadeja (@JetJadeja), Alberto Cuesta Cañada (@alcueca), Señor Doggo (@fubuloubu) +discussions-to: https://ethereum-magicians.org/t/eip-4626-yield-bearing-vault-standard/7900 +status: Final +type: Standards Track +category: ERC +created: 2021-12-22 +requires: 20, 2612 +--- + +## Abstract + +The following standard allows for the implementation of a standard API for tokenized Vaults +representing shares of a single underlying [EIP-20](./eip-20.md) token. +This standard is an extension on the EIP-20 token that provides basic functionality for depositing +and withdrawing tokens and reading balances. + +## Motivation + +Tokenized Vaults have a lack of standardization leading to diverse implementation details. +Some various examples include lending markets, aggregators, and intrinsically interest bearing tokens. +This makes integration difficult at the aggregator or plugin layer for protocols which need to conform to many standards, and forces each protocol to implement their own adapters which are error prone and waste development resources. + +A standard for tokenized Vaults will lower the integration effort for yield-bearing vaults, while creating more consistent and robust implementation patterns. + +## Specification + +All [EIP-4626](./eip-4626.md) tokenized Vaults MUST implement EIP-20 to represent shares. +If a Vault is to be non-transferrable, it MAY revert on calls to `transfer` or `transferFrom`. +The EIP-20 operations `balanceOf`, `transfer`, `totalSupply`, etc. operate on the Vault "shares" +which represent a claim to ownership on a fraction of the Vault's underlying holdings. + +All EIP-4626 tokenized Vaults MUST implement EIP-20's optional metadata extensions. +The `name` and `symbol` functions SHOULD reflect the underlying token's `name` and `symbol` in some way. + +EIP-4626 tokenized Vaults MAY implement [EIP-2612](./eip-2612.md) to improve the UX of approving shares on various integrations. + +### Definitions: + +- asset: The underlying token managed by the Vault. + Has units defined by the corresponding EIP-20 contract. +- share: The token of the Vault. Has a ratio of underlying assets + exchanged on mint/deposit/withdraw/redeem (as defined by the Vault). +- fee: An amount of assets or shares charged to the user by the Vault. Fees can exists for + deposits, yield, AUM, withdrawals, or anything else prescribed by the Vault. +- slippage: Any difference between advertised share price and economic realities of + deposit to or withdrawal from the Vault, which is not accounted by fees. + +### Methods + +#### asset + +The address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + +MUST be an EIP-20 token contract. + +MUST _NOT_ revert. + +```yaml +- name: asset + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: assetTokenAddress + type: address +``` + +#### totalAssets + +Total amount of the underlying asset that is "managed" by Vault. + +SHOULD include any compounding that occurs from yield. + +MUST be inclusive of any fees that are charged against assets in the Vault. + +MUST _NOT_ revert. + +```yaml +- name: totalAssets + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: totalManagedAssets + type: uint256 +``` + +#### convertToShares + +The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met. + +MUST NOT be inclusive of any fees that are charged against assets in the Vault. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and from. + +```yaml +- name: convertToShares + type: function + stateMutability: view + + inputs: + - name: assets + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### convertToAssets + +The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met. + +MUST NOT be inclusive of any fees that are charged against assets in the Vault. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and from. + +```yaml +- name: convertToAssets + type: function + stateMutability: view + + inputs: + - name: shares + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### maxDeposit + +Maximum amount of the underlying asset that can be deposited into the Vault for the `receiver`, through a `deposit` call. + +MUST return the maximum amount of assets `deposit` would allow to be deposited for `receiver` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. MUST NOT rely on `balanceOf` of `asset`. + +MUST factor in both global and user-specific limits, like if deposits are entirely disabled (even temporarily) it MUST return 0. + +MUST return `2 ** 256 - 1` if there is no limit on the maximum amount of assets that may be deposited. + +MUST NOT revert. + +```yaml +- name: maxDeposit + type: function + stateMutability: view + + inputs: + - name: receiver + type: address + + outputs: + - name: maxAssets + type: uint256 +``` + +#### previewDeposit + +Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions. + +MUST return as close to and no more than the exact amount of Vault shares that would be minted in a `deposit` call in the same transaction. I.e. `deposit` should return the same or more `shares` as `previewDeposit` if called in the same transaction. + +MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the deposit would be accepted, regardless if the user has enough tokens approved, etc. + +MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `deposit` to revert. + +Note that any unfavorable discrepancy between `convertToShares` and `previewDeposit` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. + +```yaml +- name: previewDeposit + type: function + stateMutability: view + + inputs: + - name: assets + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### deposit + +Mints `shares` Vault shares to `receiver` by depositing exactly `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support EIP-20 `approve` / `transferFrom` on `asset` as a deposit flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `deposit` execution, and are accounted for during `deposit`. + +MUST revert if all of `assets` cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: deposit + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + + outputs: + - name: shares + type: uint256 +``` + +#### maxMint + +Maximum amount of shares that can be minted from the Vault for the `receiver`, through a `mint` call. + +MUST return the maximum amount of shares `mint` would allow to be deposited to `receiver` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. MUST NOT rely on `balanceOf` of `asset`. + +MUST factor in both global and user-specific limits, like if mints are entirely disabled (even temporarily) it MUST return 0. + +MUST return `2 ** 256 - 1` if there is no limit on the maximum amount of shares that may be minted. + +MUST NOT revert. + +```yaml +- name: maxMint + type: function + stateMutability: view + + inputs: + - name: receiver + type: address + + outputs: + - name: maxShares + type: uint256 +``` + +#### previewMint + +Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions. + +MUST return as close to and no fewer than the exact amount of assets that would be deposited in a `mint` call in the same transaction. I.e. `mint` should return the same or fewer `assets` as `previewMint` if called in the same transaction. + +MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint would be accepted, regardless if the user has enough tokens approved, etc. + +MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `mint` to revert. + +Note that any unfavorable discrepancy between `convertToAssets` and `previewMint` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by minting. + +```yaml +- name: previewMint + type: function + stateMutability: view + + inputs: + - name: shares + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### mint + +Mints exactly `shares` Vault shares to `receiver` by depositing `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support EIP-20 `approve` / `transferFrom` on `asset` as a mint flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `mint` execution, and are accounted for during `mint`. + +MUST revert if all of `shares` cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: mint + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + + outputs: + - name: assets + type: uint256 +``` + +#### maxWithdraw + +Maximum amount of the underlying asset that can be withdrawn from the `owner` balance in the Vault, through a `withdraw` call. + +MUST return the maximum amount of assets that could be transferred from `owner` through `withdraw` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxWithdraw + type: function + stateMutability: view + + inputs: + - name: owner + type: address + + outputs: + - name: maxAssets + type: uint256 +``` + +#### previewWithdraw + +Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. + +MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a `withdraw` call in the same transaction. I.e. `withdraw` should return the same or fewer `shares` as `previewWithdraw` if called in the same transaction. + +MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though the withdrawal would be accepted, regardless if the user has enough shares, etc. + +MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `withdraw` to revert. + +Note that any unfavorable discrepancy between `convertToShares` and `previewWithdraw` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. + +```yaml +- name: previewWithdraw + type: function + stateMutability: view + + inputs: + - name: assets + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### withdraw + +Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a withdraw flow where the shares are burned from `owner` directly where `owner` is `msg.sender`. + +MUST support a withdraw flow where the shares are burned from `owner` directly where `msg.sender` has EIP-20 approval over the shares of `owner`. + +MAY support an additional flow in which the shares are transferred to the Vault contract before the `withdraw` execution, and are accounted for during `withdraw`. + +SHOULD check `msg.sender` can spend owner funds, assets needs to be converted to shares and shares should be checked for allowance. + +MUST revert if all of `assets` cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: withdraw + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + - name: owner + type: address + + outputs: + - name: shares + type: uint256 +``` + +#### maxRedeem + +Maximum amount of Vault shares that can be redeemed from the `owner` balance in the Vault, through a `redeem` call. + +MUST return the maximum amount of shares that could be transferred from `owner` through `redeem` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxRedeem + type: function + stateMutability: view + + inputs: + - name: owner + type: address + + outputs: + - name: maxShares + type: uint256 +``` + +#### previewRedeem + +Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. + +MUST return as close to and no more than the exact amount of assets that would be withdrawn in a `redeem` call in the same transaction. I.e. `redeem` should return the same or more `assets` as `previewRedeem` if called in the same transaction. + +MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the redemption would be accepted, regardless if the user has enough shares, etc. + +MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `redeem` to revert. + +Note that any unfavorable discrepancy between `convertToAssets` and `previewRedeem` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by redeeming. + +```yaml +- name: previewRedeem + type: function + stateMutability: view + + inputs: + - name: shares + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### redeem + +Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a redeem flow where the shares are burned from `owner` directly where `owner` is `msg.sender`. + +MUST support a redeem flow where the shares are burned from `owner` directly where `msg.sender` has EIP-20 approval over the shares of `owner`. + +MAY support an additional flow in which the shares are transferred to the Vault contract before the `redeem` execution, and are accounted for during `redeem`. + +SHOULD check `msg.sender` can spend owner funds using allowance. + +MUST revert if all of `shares` cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: redeem + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + - name: owner + type: address + + outputs: + - name: assets + type: uint256 +``` + +### Events + +#### Deposit + +`sender` has exchanged `assets` for `shares`, and transferred those `shares` to `owner`. + +MUST be emitted when tokens are deposited into the Vault via the `mint` and `deposit` methods. + +```yaml +- name: Deposit + type: event + + inputs: + - name: sender + indexed: true + type: address + - name: owner + indexed: true + type: address + - name: assets + indexed: false + type: uint256 + - name: shares + indexed: false + type: uint256 +``` + +#### Withdraw + +`sender` has exchanged `shares`, owned by `owner`, for `assets`, and transferred those `assets` to `receiver`. + +MUST be emitted when shares are withdrawn from the Vault in `EIP-4626.redeem` or `EIP-4626.withdraw` methods. + +```yaml +- name: Withdraw + type: event + + inputs: + - name: sender + indexed: true + type: address + - name: receiver + indexed: true + type: address + - name: owner + indexed: true + type: address + - name: assets + indexed: false + type: uint256 + - name: shares + indexed: false + type: uint256 +``` + +## Rationale + +The Vault interface is designed to be optimized for integrators with a feature complete yet minimal interface. +Details such as accounting and allocation of deposited tokens are intentionally not specified, +as Vaults are expected to be treated as black boxes on-chain and inspected off-chain before use. + +EIP-20 is enforced because implementation details like token approval +and balance calculation directly carry over to the shares accounting. +This standardization makes the Vaults immediately compatible with all EIP-20 use cases in addition to EIP-4626. + +The mint method was included for symmetry and feature completeness. +Most current use cases of share-based Vaults do not ascribe special meaning to the shares such that +a user would optimize for a specific number of shares (`mint`) rather than specific amount of underlying (`deposit`). +However, it is easy to imagine future Vault strategies which would have unique and independently useful share representations. + +The `convertTo` functions serve as rough estimates that do not account for operation specific details like withdrawal fees, etc. +They were included for frontends and applications that need an average value of shares or assets, not an exact value possibly including slippage or other fees. +For applications that need an exact value that attempts to account for fees and slippage we have included a corresponding `preview` function to match each mutable function. These functions must not account for deposit or withdrawal limits, to ensure they are easily composable, the `max` functions are provided for that purpose. + +## Backwards Compatibility + +EIP-4626 is fully backward compatible with the EIP-20 standard and has no known compatibility issues with other standards. +For production implementations of Vaults which do not use EIP-4626, wrapper adapters can be developed and used. + +## Reference Implementation + +See [Solmate EIP-4626](https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol): +a minimal and opinionated implementation of the standard with hooks for developers to easily insert custom logic into deposits and withdrawals. + +See [Vyper EIP-4626](https://github.com/fubuloubu/ERC4626): +a demo implementation of the standard in Vyper, with hooks for share price manipulation and other testing needs. + +## Security Considerations + +Fully permissionless use cases could fall prey to malicious implementations which only conform to the interface but not the specification. +It is recommended that all integrators review the implementation for potential ways of losing user deposits before integrating. + +If implementors intend to support EOA account access directly, they should consider adding an additional function call for `deposit`/`mint`/`withdraw`/`redeem` with the means to accommodate slippage loss or unexpected deposit/withdrawal limits, since they have no other means to revert the transaction if the exact output amount is not achieved. + +The methods `totalAssets`, `convertToShares` and `convertToAssets` are estimates useful for display purposes, +and do _not_ have to confer the _exact_ amount of underlying assets their context suggests. + +The `preview` methods return values that are as close as possible to exact as possible. For that reason, they are manipulable by altering the on-chain conditions and are not always safe to be used as price oracles. This specification includes `convert` methods that are allowed to be inexact and therefore can be implemented as robust price oracles. For example, it would be correct to implement the `convert` methods as using a time-weighted average price in converting between assets and shares. + +Integrators of EIP-4626 Vaults should be aware of the difference between these view methods when integrating with this standard. Additionally, note that the amount of underlying assets a user may receive from redeeming their Vault shares (`previewRedeem`) can be significantly different than the amount that would be taken from them when minting the same quantity of shares (`previewMint`). The differences may be small (like if due to rounding error), or very significant (like if a Vault implements withdrawal or deposit fees, etc). Therefore integrators should always take care to use the preview function most relevant to their use case, and never assume they are interchangeable. + +Finally, EIP-4626 Vault implementers should be aware of the need for specific, opposing rounding directions across the different mutable and view methods, as it is considered most secure to favor the Vault itself during calculations over its users: + +- If (1) it's calculating how many shares to issue to a user for a certain amount of the underlying tokens they provide or (2) it's determining the amount of the underlying tokens to transfer to them for returning a certain amount of shares, it should round _down_. + +- If (1) it's calculating the amount of shares a user has to supply to receive a given amount of the underlying tokens or (2) it's calculating the amount of underlying tokens a user has to provide to receive a certain amount of shares, it should round _up_. + +The only functions where the preferred rounding direction would be ambiguous are the `convertTo` functions. To ensure consistency across all EIP-4626 Vault implementations it is specified that these functions MUST both always round _down_. Integrators may wish to mimic rounding up versions of these functions themselves, like by adding 1 wei to the result. + +Although the `convertTo` functions should eliminate the need for any use of an EIP-4626 Vault's `decimals` variable, it is still strongly recommended to mirror +the underlying token's `decimals` if at all possible, to eliminate possible sources of confusion and simplify integration across front-ends and for other off-chain users. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4671.md b/EIPS/eip-4671.md new file mode 100644 index 00000000000000..f7f55b91e7d710 --- /dev/null +++ b/EIPS/eip-4671.md @@ -0,0 +1,296 @@ +--- +eip: 4671 +title: Non-Tradable Tokens Standard +description: A standard interface for non-tradable tokens, aka badges or souldbound NFTs. +author: Omar Aflak (@omaraflak), Pol-Malo Le Bris, Marvin Martin (@MarvinMartin24) +discussions-to: https://ethereum-magicians.org/t/eip-4671-non-tradable-token/7976 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-01-13 +requires: 165 +--- + +## Abstract + +A non-tradable token, or NTT, represents inherently personal possessions (material or immaterial), such as university diplomas, online training certificates, government issued documents (national id, driving license, visa, wedding, etc.), labels, and so on. + +As the name implies, non-tradable tokens are made to not be traded or transferred, they are "soulbound". They don't have monetary value, they are personally delivered to **you**, and they only serve as a **proof of possession/achievement**. + +In other words, the possession of a token carries a strong meaning in itself depending on **why** it was delivered. + +## Motivation + +We have seen in the past smart contracts being used to deliver university diplomas or driving licenses, for food labeling or attendance to events, and much more. All of these implementations are different, but they have a common ground: the tokens are **non-tradable**. + +The blockchain has been used for too long as a means of speculation, and non-tradable tokens want to be part of the general effort aiming to provide usefulness through the blockchain. + +By providing a common interface for non-tradable tokens, we allow more applications to be developed and we position blockchain technology as a standard gateway for verification of personal possessions and achievements. + +## Specification + +### Non-Tradable Token + +A NTT contract is seen as representing **one type of certificate** delivered by **one authority**. For instance, one NTT contract for the French National Id, another for Ethereum EIP creators, and so on... + +* An address might possess multiple tokens. Each token has a unique identifier: `tokenId`. +* An authority who delivers a certificate should be in position to revoke it. Think of driving licenses or weddings. However, it cannot delete your token, i.e. the record will show that you once owned a token from that contract. +* The most typical usage for third-parties will be to verify if a user has a valid token in a given contract. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC4671 is IERC165 { + /// Event emitted when a token `tokenId` is minted for `owner` + event Minted(address owner, uint256 tokenId); + + /// Event emitted when token `tokenId` of `owner` is revoked + event Revoked(address owner, uint256 tokenId); + + /// @notice Count all tokens assigned to an owner + /// @param owner Address for whom to query the balance + /// @return Number of tokens owned by `owner` + function balanceOf(address owner) external view returns (uint256); + + /// @notice Get owner of a token + /// @param tokenId Identifier of the token + /// @return Address of the owner of `tokenId` + function ownerOf(uint256 tokenId) external view returns (address); + + /// @notice Check if a token hasn't been revoked + /// @param tokenId Identifier of the token + /// @return True if the token is valid, false otherwise + function isValid(uint256 tokenId) external view returns (bool); + + /// @notice Check if an address owns a valid token in the contract + /// @param owner Address for whom to check the ownership + /// @return True if `owner` has a valid token, false otherwise + function hasValid(address owner) external view returns (bool); +} +``` + +#### Extensions + +##### Metadata + +An interface allowing to add metadata linked to each token. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Metadata is IERC4671 { + /// @return Descriptive name of the tokens in this contract + function name() external view returns (string memory); + + /// @return An abbreviated name of the tokens in this contract + function symbol() external view returns (string memory); + + /// @notice URI to query to get the token's metadata + /// @param tokenId Identifier of the token + /// @return URI for the token + function tokenURI(uint256 tokenId) external view returns (string memory); +} +``` + +##### Enumerable + +An interface allowing to enumerate the tokens of an owner. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Enumerable is IERC4671 { + /// @return emittedCount Number of tokens emitted + function emittedCount() external view returns (uint256); + + /// @return holdersCount Number of token holders + function holdersCount() external view returns (uint256); + + /// @notice Get the tokenId of a token using its position in the owner's list + /// @param owner Address for whom to get the token + /// @param index Index of the token + /// @return tokenId of the token + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /// @notice Get a tokenId by it's index, where 0 <= index < total() + /// @param index Index of the token + /// @return tokenId of the token + function tokenByIndex(uint256 index) external view returns (uint256); +} +``` + +##### Delegation + +An interface allowing delegation rights of token minting. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Delegate is IERC4671 { + /// @notice Grant one-time minting right to `operator` for `owner` + /// An allowed operator can call the function to transfer rights. + /// @param operator Address allowed to mint a token + /// @param owner Address for whom `operator` is allowed to mint a token + function delegate(address operator, address owner) external; + + /// @notice Grant one-time minting right to a list of `operators` for a corresponding list of `owners` + /// An allowed operator can call the function to transfer rights. + /// @param operators Addresses allowed to mint + /// @param owners Addresses for whom `operators` are allowed to mint a token + function delegateBatch(address[] memory operators, address[] memory owners) external; + + /// @notice Mint a token. Caller must have the right to mint for the owner. + /// @param owner Address for whom the token is minted + function mint(address owner) external; + + /// @notice Mint tokens to multiple addresses. Caller must have the right to mint for all owners. + /// @param owners Addresses for whom the tokens are minted + function mintBatch(address[] memory owners) external; + + /// @notice Get the issuer of a token + /// @param tokenId Identifier of the token + /// @return Address who minted `tokenId` + function issuerOf(uint256 tokenId) external view returns (address); +} +``` + +##### Consensus + +An interface allowing minting/revocation of tokens based on a consensus of a predefined set of addresses. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Consensus is IERC4671 { + /// @notice Get voters addresses for this consensus contract + /// @return Addresses of the voters + function voters() external view returns (address[] memory); + + /// @notice Cast a vote to mint a token for a specific address + /// @param owner Address for whom to mint the token + function approveMint(address owner) external; + + /// @notice Cast a vote to revoke a specific token + /// @param tokenId Identifier of the token to revoke + function approveRevoke(uint256 tokenId) external; +} +``` + +##### Pull + +An interface allowing a token owner to pull his token to a another of his wallets (here `recipient`). The caller must provide a signature of the tuple `(tokenId, owner, recipient)` using the `owner` wallet. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Pull is IERC4671 { + /// @notice Pull a token from the owner wallet to the caller's wallet + /// @param tokenId Identifier of the token to transfer + /// @param owner Address that owns tokenId + /// @param signature Signed data (tokenId, owner, recipient) by the owner of the token + function pull(uint256 tokenId, address owner, bytes memory signature) external; +} +``` + +### NTT Store + +Non-tradable tokens are meant to be fetched by third-parties, which is why there needs to be a convenient way for users to expose some or all of their tokens. We achieve this result using a store which must implement the following interface. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC4671Store is IERC165 { + // Event emitted when a IERC4671Enumerable contract is added to the owner's records + event Added(address owner, address token); + + // Event emitted when a IERC4671Enumerable contract is removed from the owner's records + event Removed(address owner, address token); + + /// @notice Add a IERC4671Enumerable contract address to the caller's record + /// @param token Address of the IERC4671Enumerable contract to add + function add(address token) external; + + /// @notice Remove a IERC4671Enumerable contract from the caller's record + /// @param token Address of the IERC4671Enumerable contract to remove + function remove(address token) external; + + /// @notice Get all the IERC4671Enumerable contracts for a given owner + /// @param owner Address for which to retrieve the IERC4671Enumerable contracts + function get(address owner) external view returns (address[] memory); +} +``` + +## Rationale + +### On-chain vs Off-chain + +A decision was made to keep the data off-chain (via `tokenURI()`) for two main reasons: +* Non-tradable tokens represent personal possessions. Therefore, there might be cases where the data should be encrypted. The standard should not outline decisions about encryption because there are just so many ways this could be done, and every possibility is specific to the use-case. +* Non-tradable tokens must stay generic. There could have been a possibility to make a `MetadataStore` holding the data of tokens in an elegant way, unfortunately we would have needed a support for generics in solidity (or struct inheritance), which is not available today. + +## Reference Implementation + +You can find an implementation of this standard in [../assets/eip-4671](https://github.com/ethereum/EIPs/tree/master/assets/eip-4671). + +Using this implementation, this is how you would create a token: + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC4671.sol"; + +contract EIPCreatorBadge is ERC4671 { + constructor() ERC4671("EIP Creator Badge", "EIP") {} + + function giveThatManABadge(address owner) external { + require(_isCreator(), "You must be the contract creator"); + _mint(owner); + } + + function _baseURI() internal pure override returns (string memory) { + return "https://eips.ethereum.org/ntt/"; + } +} +``` + +This could be a contract managed by the Ethereum foundation and which allows them to deliver tokens to EIP creators. + +## Security Considerations + +One security aspect is related to the `tokenURI` method which returns the metadata linked to a token. Since the standard represents inherently personal possessions, users might want to encrypt the data in some cases e.g. national id cards. Moreover, it is the responsibility of the contract creator to make sure the URI returned by this method is available at all times. + +The standard does not define any way to transfer a token from one wallet to another. Therefore, users must be very cautious with the wallet they use to receive these tokens. If a wallet is lost, the only way to get the tokens back is for the issuing authorities to deliver the tokens again, akin real life. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4675.md b/EIPS/eip-4675.md new file mode 100644 index 00000000000000..6e04d3f3ef53ed --- /dev/null +++ b/EIPS/eip-4675.md @@ -0,0 +1,213 @@ +--- +eip: 4675 +title: Multi-Fractional Non-Fungible Tokens +description: Fractionalize multiple NFTs using a single contract +author: David Kim (@powerstream3604) +discussions-to: https://ethereum-magicians.org/t/eip-4675-multi-fractional-non-fungible-token-standard/8008 +status: Draft +type: Standards Track +category: ERC +created: 2022-01-13 +requires: 165, 721 +--- + +## Abstract +This standard outlines a smart contract interface eligible to represent any number of fractionalized non-fungible tokens. Existing projects utilizing standards like [EIP-1633](./eip-1633.md) conventionally deploy separate [EIP-20](./eip-20.md) compatible token contracts to fractionalize the non-fungible token into EIP-20 tokens. In contrast, this ERC allows each token ID to represent a token type representing(fractionalizing) the non-fungible token. + +This standard is approximate in terms of using `_id` for distinguishing token types. However, this ERC has a clear difference with [EIP-1155](./eip-1155.md) as each `_id` represents a distinct NFT. + +## Motivation +The conventional fractionalization process of fractionalizing a NFT to FT requires deployment of a FT token contract representing the ownership of NFT. This leads to inefficient bytecode usage on Ethereum Blockchain and limits functionalities since each token contract is separated into its own permissioned address. +With the rise of multiple NFT projects needing to fractionalize NFT to FT, new type of token standard is needed to back up them. + +## Specification + +```solidity +/** + @title Multi-Fractional Non-Fungible Token Standard + @dev Note : The ERC-165 identifier for this interface is 0x83f5d35f. +*/ +interface IMFNFT { + /** + @dev This emits when ownership of any token changes by any mechanism. + The `_from` argument MUST be the address of an account/contract sending the token. + The `_to` argument MUST be the address of an account/contract receiving the token. + The `_id` argument MUST be the token type being transferred. (represents NFT) + The `_value` argument MUST be the number of tokens the holder balance is decrease by and match the recipient balance is increased by. + */ + event Transfer(address indexed _from, address indexed _to, uint256 indexed _id, uint256 _value); + + /** + @dev This emits when the approved address for token is changed or reaffirmed. + The `_owner` argument MUST be the address of account/contract approving to withdraw. + The `_spender` argument MUST be the address of account/contract approved to withdraw from the `_owner` balance. + The `_id` argument MUST be the token type being transferred. (represents NFT) + The `_value` argument MUST be the number of tokens the `_approved` is able to withdraw from `_owner` balance. + */ + event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _value); + + /** + @dev This emits when new token type is added which represents the share of the Non-Fungible Token. + The `_parentToken` argument MUST be the address of the Non-Fungible Token contract. + The `_parentTokenId` argument MUST be the token ID of the Non-Fungible Token. + The `_id` argument MUST be the token type being added. (represents NFT) + The `_totalSupply` argument MUST be the number of total token supply of the token type. + */ + event TokenAddition(address indexed _parentToken, uint256 indexed _parentTokenId, uint256 _id, uint256 _totalSupply); + + /** + @notice Transfers `_value` amount of an `_id` from the msg.sender address to the `_to` address specified + @dev msg.sender must have sufficient balance to handle the tokens being transferred out of the account. + MUST revert if `_to` is the zero address. + MUST revert if balance of msg.sender for token `_id` is lower than the `_value` being transferred. + MUST revert on any other error. + MUST emit the `Transfer` event to reflect the balance change. + @param _to Source address + @param _id ID of the token type + @param _value Transfer amount + @return True if transfer was successful, false if not + */ + function transfer(address _to, uint256 _id, uint256 _value) external returns (bool); + + /** + @notice Approves `_value` amount of an `_id` from the msg.sender to the `_spender` address specified. + @dev msg.sender must have sufficient balance to handle the tokens when the `_spender` wants to transfer the token on behalf. + MUST revert if `_spender` is the zero address. + MUST revert on any other error. + MUST emit the `Approval` event. + @param _spender Spender address(account/contract which can withdraw token on behalf of msg.sender) + @param _id ID of the token type + @param _value Approval amount + @return True if approval was successful, false if not + */ + function approve(address _spender, uint256 _id, uint256 _value) external returns (bool); + + /** + @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified. + @dev Caller must be approved to manage the tokens being transferred out of the `_from` account. + MUST revert if `_to` is the zero address. + MUST revert if balance of holder for token `_id` is lower than the `_value` sent. + MUST revert on any other error. + MUST emit `Transfer` event to reflect the balance change. + @param _from Source address + @param _to Target Address + @param _id ID of the token type + @param _value Transfer amount + @return True if transfer was successful, false if not + + */ + function transferFrom(address _from, address _to, uint256 _id, uint256 _value) external returns (bool); + + /** + @notice Sets the NFT as a new type token + @dev The contract itself should verify if the ownership of NFT is belongs to this contract itself with the `_parentNFTContractAddress` & `_parentNFTTokenId` before adding the token. + MUST revert if the same NFT is already registered. + MUST revert if `_parentNFTContractAddress` is address zero. + MUST revert if `_parentNFTContractAddress` is not ERC-721 compatible. + MUST revert if this contract itself is not the owner of the NFT. + MUST revert on any other error. + MUST emit `TokenAddition` event to reflect the token type addition. + @param _parentNFTContractAddress NFT contract address + @param _parentNFTTokenId NFT tokenID + @param _totalSupply Total token supply + */ + function setParentNFT(address _parentNFTContractAddress, uint256 _parentNFTTokenId, uint256 _totalSupply) external; + + /** + @notice Get the token ID's total token supply. + @param _id ID of the token + @return The total token supply of the specified token type + */ + function totalSupply(uint256 _id) external view returns (uint256); + + /** + @notice Get the balance of an account's tokens. + @param _owner The address of the token holder + @param _id ID of the token + @return The _owner's balance of the token type requested + */ + function balanceOf(address _owner, uint256 _id) external view returns (uint256); + + /** + @notice Get the amount which `_spender` is still allowed to withdraw from `_owner` + @param _owner The address of the token holder + @param _spender The address approved to withdraw token on behalf of `_owner` + @param _id ID of the token + @return The amount which `_spender` is still allowed to withdraw from `_owner` + */ + function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256); + + /** + @notice Get the bool value which represents whether the NFT is already registered and fractionalized by this contract. + @param _parentNFTContractAddress NFT contract address + @param _parentNFTTokenId NFT tokenID + @return The bool value representing the whether the NFT is already registered. + */ + function isRegistered(address _parentNFTContractAddress, uint256 _parentNFTTokenId) external view returns (bool); +} + +interface ERC165 { + /** + @notice Query if a contract implements an interface + @param interfaceID The interface identifier, as specified in ERC-165 + @dev Interface identification is specified in ERC-165. This function + uses less than 30,000 gas. + @return `true` if the contract implements `interfaceID` and + `interfaceID` is not 0xffffffff, `false` otherwise + */ + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +To receive Non-Fungible Token on `safe Transfer` the contract should include `onERC721Received()`. +Including `onERC721Received()` is needed to be compatible with Safe Transfer Rules. +```solidity +/** + @notice Handle the receipt of an NFT + @param _operator The address which called `safeTransferFrom` function + @param _from The address which previously owned the token + @param _tokenId The NFT identifier which is being transferred + @param _data Additional data with no specified format + @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` +*/ +function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external pure returns (bytes4); +``` + +## Rationale + +**Metadata** + +The `symbol()` & `name()` functions were not included since the majority of users can just fetch it from the originating NFT contract. Also, copying the name & symbol every time when token gets added might place a lot of redundant bytecode on the Ethereum blockchain. +However, according to the need and design of the project it could also be added to each token type by fetching the metadata from the NFT contract. + +**Design** + +Most of the decisions made around the design of this ERC were done to keep it as flexible for diverse token design & architecture. +These minimum requirement for this standard allows for each project to determine their own system for minting, governing, burning their MFNFT tokens depending on their programmable architecture. + +## Backwards Compatibility + +To make this standard compatible with existing standards, this standard `event` & `function` names are identical with ERC-20 token standard with some more `events` & `functions` to add token type dynamically. + +Also, the sequence of parameter in use of `_id` for distinguishing token types in `functions` and `events` are very much similar to ERC-1155 Multi-Token Standard. + +Since this standard is intended to interact with the EIP-721 Non-Fungible Token Standard, it is kept purposefully agnostic to extensions beyond the standard in order to allow specific projects to design their own token usage and scenario. + +## Test Cases + +Reference Implementation of MFNFT Token includes test cases written using hardhat. (Test coverage : 100%) + +## Reference Implementation +[MFNFT - Implementation](../assets/eip-4675/README.md) + +## Security Considerations + +To fractionalize an already minted NFT, it is evident that ownership of NFT should be given to token contracts before fractionalization. +In the case of fractionalizing NFT, the token contract should thoroughly verify the ownership of NFT before fractionalizing it to prevent tokens from being a separate tokens with the NFT. + +If an arbitrary account has the right to call `setParentNFT()` there might be a front-running issue. The caller of `setParentNFT()` might be different from the real NFT sender. +To prevent this issue, implementors should just allow **admin** to call, or fractionalize and receive NFT in an atomic transaction similar to flash loan(swap). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4736.md b/EIPS/eip-4736.md new file mode 100644 index 00000000000000..3266787337e5d9 --- /dev/null +++ b/EIPS/eip-4736.md @@ -0,0 +1,157 @@ +--- +eip: 4736 +title: Consensus Layer Withdrawal Protection +description: Additional security for BLSToExecutionChange operation when a consensus layer mnemonic may be compromised, without changing consensus +author: Benjamin Chodroff (@benjaminchodroff), Jim McDonald (@mcdee) +discussions-to: https://ethereum-magicians.org/t/consensus-layer-withdrawal-protection/8161 +status: Final +type: Standards Track +category: Interface +created: 2022-01-30 +--- + +## Abstract + +If a consensus layer mnemonic phrase is compromised, it is impossible for the consensus layer network to differentiate the legitimate holder of the key from an illegitimate holder. However, there are signals that can be considered in a wider sense without changing core Ethereum consensus. This proposal outlines ways in which on chain evidence such as the execution layer deposit address and list of signed messages could create a social consensus that would significantly favor but not guarantee legitimate mnemonic holders would win a race condition against an attacker. + +## Motivation + +The consensus layer `BLSToExecutionChange` message is secure for a single user who has certainty their keys and mnemonic have not been compromised. However, as validator withdrawals on the consensus layer are not possible until the Capella hard fork, no user can have absolute certainty that their keys are not compromised until the `BLSToExecutionChange` is on chain, and by then too late to change. All legitimate mnemonic phrase holders were originally in control of the execution layer deposit address. Beacon node clients and node operators may optionally load a list of verifiable `BLSToExecutionChange` messages to broadcasts that may create a social consensus for legitimate holders to successfully win a race condition against an attacker. If attackers compromise a significant number of consensus layer nodes, it would pose risks to the entire Ethereum community. + +Setting a withdrawal address to an execution layer address was not supported by the eth2.0-deposit-cli until v1.1.1 on March 23, 2021, leaving early adopters wishing they could force set their execution layer address to a deposit address earlier. Forcing this change is not something that can be enforced in-protocol, partly due to lack of information on the beacon chain about the execution layer deposit address and partly due to the fact that this was never listed as a requirement. It is also possible that the execution layer deposit address is no longer under the control of the legitimate holder of the withdrawal private key. + +However, it is possible for individual nodes to locally restrict the changes they wish to include in blocks they propose, and which they propagate around the network, in a way that does not change consensus. It is also possible for client nodes to help broadcast signed `BLSToExecutionChange` requests to ensure as many nodes witness this message as soon as possible in a fair manner. Further, such `BLSToExecutionChange` signed messages can be preloaded into clients in advance to further help nodes filter attacking requests. + +This proposal provides purely optional additional protection. It aims to request nodes set a priority on withdrawal credential claims that favour a verifiable execution layer deposit address in the event of two conflicting `BLSToExecutionChange` messages. It also establishes a list of `BLSToExecutionChange` signed messages to help broadcast "as soon as possible" when the network supports it, and encourage client teams to help use these lists to honour filter and prioritize accepting requests by REST and transmitting them via P2P. This will not change consensus, but may help prevent propagating an attack where a withdrawal key has been knowingly or unknowingly compromised. + +It is critical to understand that this proposal is not a consensus change. Nothing in this proposal restricts the validity of withdrawal credential operations within the protocol. It is a voluntary change by client teams to build this functionality in to their beacon nodes, and a voluntary change by node operators to accept any or all of the restrictions and broadcasting capabilities suggested by end users. + +Because of the above, even if fully implemented, it will be down to chance as to which validators propose blocks, and which voluntary restrictions those validators’ beacon nodes are running. Node operators can do what they will to help the community prevent attacks on any compromised consensus layer keys, but there are no guarantees of success this will prevent a successful attack. + +## Specification + +The Consensus Layer `BLSToExecutionChange` operation has the following fields: + +* Validator index +* Current withdrawal BLS public key +* Proposed execution layer withdrawal address +* Signature by withdrawal private key over the prior fields + +This proposal describes OPTIONAL and RECOMMENDED mechanisms which a client beacon node MAY implement, and end users are RECOMMENDED to use in their beacon node operation. + +### `BLSToExecutionChange` Broadcast File + +Beacon node clients MAY support an OPTIONAL file of lines specifying "validator index" , "current withdrawal BLS public key" , "proposed execution layer withdrawal address", and "signature" which, if implemented and if provided, SHALL instruct nodes to automatically submit a one-time `BLSToExecutionChange` broadcast message for each valid signature at the Capella hard fork. This file SHALL give all node operators an OPTIONAL opportunity to ensure any valid `BLSToExecutionChange` messages are broadcast, heard, and shared by nodes at the Capella hard fork. This OPTIONAL file SHALL also instruct nodes to perpetually prefer accepting and repeating signatures matching the signature in the file, and SHALL reject accepting or rebroadcasting messages which do not match a signature for a given withdrawal credential. + +### `BLSToExecutionChange` Handling + +Beacon node clients are RECOMMENDED to allow accepting "`BLSToExecutionChange` Broadcast" file of verifiable signatures, and then MAY fallback to accept a "first request" via P2P. All of this proposal is OPTIONAL for beacon nodes to implement or use, but all client teams are RECOMMENDED to allow a "`BLSToExecutionChange` Broadcast File" to be loaded locally before the Capella hard fork. This OPTIONAL protection will allow a user to attempt to set a withdrawal address message as soon as the network supports it without any change to consensus. + +## Rationale + +This proposal is intended to protect legitimate validator mnemonic holders where it was knowingly or unknowingly compromised. As there is no safe way to transfer ownership of a validator without exiting, it can safely be assumed that all validator holders intend to set to a withdrawal address they specify. Using the deposit address in the execution layer to determine the legitimate holder is not possible to consider in consensus as it may be far back in history and place an overwhelming burden to maintain such a list. As such, this proposal outlines optional mechanism which protect legitimate original mnemonic holders and does so in a way that does not place any mandatory burden on client node software or operators. + +## Backwards Compatibility + +As there is no existing `BLSToExecutionChange` operation prior to Capella, there is no documented backwards compatibility. As all of the proposal is OPTIONAL in both implementation and operation, it is expected that client beacon nodes that do not implement this functionality would still remain fully backwards compatible with any or all clients that do implement part or all of the functionality described in this proposal. Additionally, while users are RECOMMENDED to enable these OPTIONAL features, if they decide to either disable or ignore some or all of the features, or even purposefully load content contrary to the intended purpose, the beacon node client will continue to execute fully compatible with the rest of the network as none of the proposal will change core Ethereum consensus. + +## Reference Implementation + +### `BLSToExecutionChange` Broadcast File + +A "change-operations.json" file intended to be preloaded with all consensus layer withdrawal credential signatures and verifiable execution layer deposit addresses. This file may be generated by a script and able to be independently verified by community members using the consensus layer node, and intended to be included by all clients, enabled by default. Client nodes are encouraged to enable packaging this independently verifiable list with the client software, and enable it by default to help further protect the community from unsuspected attacks. + +The change-operations.json format is the "`BLSToExecutionChange` File - Claim" combined into a single JSON array. + +### `BLSToExecutionChange` Broadcast File - Claim + +A community collected and independently verifiable list of "`BLSToExecutionChange` Broadcasts" containing verifiable claims will be collected. Node operators may verify these claims independently and are suggested to load claims in compatible beacon node clients. + +To make a verifiable claim, users MAY upload a claim to any public repository in a text file "[chain]/validatorIndex.json" such as "mainnet/123456.json". + +123456.json: + +``` +[{"message":{"validator_index":"123456","from_bls_pubkey":"0x977cc21a067003e848eb3924bcf41bd0e820fbbce026a0ff8e9c3b6b92f1fea968ca2e73b55b3908507b4df89eae6bfb","to_execution_address":"0x08f2e9Ce74d5e787428d261E01b437dC579a5164"},"signature":"0x872935e0724b31b2f0209ac408b673e6fe2f35b640971eb2e3b429a8d46be007c005431ef46e9eb11a3937f920cafe610c797820ca088543c6baa0b33797f0a38f6db3ac68ffc4fd03290e35ffa085f0bfd56b571d7b2f13c03f7b6ce141c283"}] +``` + +#### Claim Acceptance + +In order for a submission to be merged into public repository, the submission must have: + +1. Valid filename in the format validatorIndex.json +2. Valid validator index which is active on the consensus layer +3. Verifiable signature +5. A single change operation for a single validator, with all required fields in the file with no other content present + +All merge requests that fail will be provided a reason from above which must be addressed prior to merge. Any future verifiable amendments to accepted claims must be proposed by the same submitter, or it will be treated as a contention. + +#### `BLSToExecutionChange` Broadcast + +Anyone in the community will be able to independently verify the files from the claims provided using the Capella spec and command line clients such as "ethdo" which support the specification. + +A claim will be considered contested if a claim arrives where the verifiable consensus layer signatures differ between two or more submissions, where neither party has proven ownership of the execution layer deposit address. If a contested but verified "`BLSToExecutionChange` Broadcast" request arrives to a repository, all parties can be notified, and may try to convince the wider community by providing any publicly verifiable on chain evidence or off chain evidence supporting their claim to then include their claim in nodes. Node operators may decide which verifiable claims they wish to include based on social consensus. + +## Security Considerations + +### 1: Attacker lacks EL deposit key, uncontested claim + +* User A: Controls the CL keys and the EL key used for the deposit +* User B: Controls the CL keys, but does not control the EL key for the deposit + +User A signs and submits a claim to the CLWP repository, clients load User A message into the "`BLSToExecutionChange` Broadcast" file. At the time of the first epoch support `BLSToExecutionChange`, many (not all) nodes begin to broadcast the message. User B also tries to submit a different but valid `BLSToExecutionChange` to an address that does not match the signature in the claim. This message is successfully received via REST API, but some (not all) nodes begin to silently drop this message as the signature does not match the signature in the "`BLSToExecutionChange` Broadcast" file. As such, these nodes do not replicate this message via P2P. + +### 2: Attacker has both EL deposit key and CL keys, uncontested claim + +* User A: Controls the CL key/mnemonic and the EL key used for the deposit, and submits a claim to move to a new address +* User B: Controls the CL and EL key/mnemonic used for the EL deposit, but fails to submit a claim + +It is possible/likely that User A would notice that all their funds in the EL deposit address had been stolen. This may signal that their CL key is compromised as well, so they decide to pick a new address for the withdrawal. The story will play out the same as Scenario 1 as the claim is uncontested. + +### 3: Same as #2, but the attacker submits a contested claim + +* User A: Controls the CL keys/mnemonic and the EL key used for the deposit, and submits a claim to move to a new address +* User B: Controls the CL keys/mnemonic and the EL key used for the deposit, and submits a claim to move to a new address + +This is a contested claim and as such there is no way to prove who is in control using on chain data. Instead, either user may try to persuade the community they are the rightful owner (identity verification, social media, etc.) in an attempt to get node operators to load their contested claim into their "`BLSToExecutionChange` Broadcast" file. However, there is no way to fully prove it. + +### 4: A user has lost either their CL key and/or mnemonic (no withdrawal key) + +* User A: Lacks the CL keys and mnemonic + +There is no way to recover this scenario with this proposal as we cannot prove a user has lost their keys, and the mnemonic is required to generate the withdrawal key. + +### 5: End game - attacker + +* User A: Controls EL and CL key/mnemonic, successfully achieves a set address withdrawal +* User B: Controls CL key, decides to attack + +Upon noticing User A has submitted a successful set address withdrawal, User B may run a validator and attempt to get User A slashed. Users who suspect their validator key or seed phrase is compromised should take action to exit their validator as early as possible. + +### 6: Compromised key, but not vulnerable to withdrawal + +* User A: Controls EL and CL key/mnemonic, but has a vulnerability which leaks their CL key but NOT their CL mnemonic +* User B: Controls the CL key, but lacks the CL mnemonic + +User A may generate the withdrawal key (requires the mnemonic). User B can attack User A by getting them slashed, but will be unable to generate the withdrawal key. + +### 7: Attacker loads a malicious `BLSToExecutionChange` Broadcast file into one or multiple nodes, User A submits claim + +* User A: Submits a valid uncontested claim which is broadcast out as soon as possible by many nodes +* User B: Submits no claim, but broadcasts a valid malicious claim out through their `BLSToExecutionChange` Broadcast list, and blocks User A's claim from their node. + +User B's claim will make it into many nodes, but when it hits nodes that have adopted User A's signature they will be dropped and not rebroadcast. Statistically, User B will have a harder time achieving consensus among the entire community, but it will be down to chance. + +### 8: Same as #7, but User A submits no claim + +The attacker will statistically likely win as they will be first to have their message broadcast to many nodes and, unless User A submits a request exactly at the time of support, it is unlikely to be heard by enough nodes to gain consensus. All users are encouraged to submit claims for this reason because nobody can be certain their mnemonic has not been compromised until it is too late. + +### Second Order Effects + +1. A user who participates in the "`BLSToExecutionChange` Broadcast" may cause the attacker to give up early and instead start to slash. For some users, the thought of getting slashed is preferable to giving an adversary any funds. As the proposal is voluntary, users may choose not to participate if they fear this scenario. +2. The attacker may set up their own `BLSToExecutionChange` Broadcast to reject signatures not matching their attack. This is possible with or without this proposal. +3. The attacker may be the one collecting "`BLSToExecutionChange` Broadcast" claims for this proposal and may purposefully reject legitimate requests. Anyone is free to set up their own community claim collection and gather their own community support using the same mechanisms described in this proposal to form an alternative social consensus. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4747.md b/EIPS/eip-4747.md new file mode 100644 index 00000000000000..2a710d36ad68ba --- /dev/null +++ b/EIPS/eip-4747.md @@ -0,0 +1,312 @@ +--- +eip: 4747 +title: Simplify EIP-161 +description: Simplify EIP-161 and retroactively deprecate unused aspects of it +author: Peter Davies (@petertdavies) +discussions-to: https://ethereum-magicians.org/t/eip-4747-simplify-eip-161/8246 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-02 +requires: 161 +--- + + +## Abstract + +Simplify the definition of [EIP-161](./eip-161.md), removing the requirement for implementors to support edge cases that are impossible on Ethereum Mainnet. + +## Motivation + +EIP-161 is overly complex and has a number of edge cases that are poorly documented and tested. This EIP takes advantage of the complete removal of all remaining empty accounts in block 14049881 (tx `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`) to clarify it, and allows implementors to not implement various edge cases that never occurred and are not possible in the future. + +In particular, this EIP permits implementors who do not wish to support historical blocks to not implement state clearing at all. + +## 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. + +Retroactively replace EIP-161, starting from its introduction in block 2675000, with the following rules: + +a. When creating an account, set it's nonce to `1` prior to executing initcode. + +b. When performing EVM execution treat all empty accounts as if they do not exist. Any operation that would create an empty account instead leaves it non-existent. + +c. If one of the following events happens to an empty account `X`, it is deleted: + +1. `X` receives a zero value `CALL`. +2. `X` is the recipient of a zero value transaction. +3. `X` is the beneficiary of a `SELFDESTRUCT` with zero value. + +If the transaction, `CALL` or `SELFDESTRUCT` is reverted, then the state clearing is also reverted. As a special case on Ethereum Mainnet, in block 2675119 (tx `0xcf416c536ec1a19ed1fb89e4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba`), an empty call is made to an empty account at `RIPEMD160` (precompile `0x3`), the call fails out of gas, but the empty account is removed anyway. + +The deletion MAY happen immediately or be processed at the end of the transaction. A client may assume that `STATICCALL`s to empty accounts never occur and that `CALL`s to empty accounts in `STATICCALL` contexts never occur. + +On Ethereum Mainnet, the only state clearing events after the start of Byzantium are the two listed below. Clients MAY implement post Byzantium state clearing by hardcoding those events. + +* In block 4457731 (tx `0x63791f962e13e6b01ec13d38a8ab66c87c2f5a1768276f866300d900cca808fe`), `0xd29DFe5aE95B5C067a91F472Dac0d9be6700A4A9` receives a zero value `SELFDESTRUCT` and is cleared. + +* In block 14049881 (tx `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`), the following accounts receive zero value calls and are cleared. +
+ Accounts cleared + + ``` + 0x01a3dd7d158e3b4c9d8d2af0ddcf3df0f5e14463 + 0x0366c731dd7c095dc08896806765a649c6c0885a + 0x056c68da52395f1d42c5ba15c4fb956146a4f2c1 + 0x070ba92497cd4b88a8a9a60795ca7d7f7de0faa3 + 0x07a1648ce2bed6721a5d25de3c228a296d03fd52 + 0x07b14ba68f474529cc0bd6a9bffee2bc4090d185 + 0x07ea32232e37d44134a3071319d228bdab249a60 + 0x096b7382500fa11c22c54c0422c5e38899a2e933 + 0x09f3200441bd60522bcf28f3666f8e8dbd19fb62 + 0x0ad0f3c60696adece09367a9a11c968fb88560bb + 0x0af6181e1db22071f38fc162e1610e29d288de04 + 0x0cdc7fef8f8d0ee77360060930aada1263b26ff7 + 0x0dac3d571eb5b884a2550db2791d5ac1efca306b + 0x0ec857faba49392080b68dd5074d85f34724d04a + 0x0f5054f9c674b37d15915ca8925f231edb3afa8c + 0x0f78d535e1faad9a982dca2a76d16da4649f7021 + 0x104c5b235166f26df54f52666d5e77d9e03e353e + 0x106b47175965b6d607008544267c91490672a54f + 0x1223d5c03b4d52ebed43f781251098c9138c3dd7 + 0x1251d13cde439378349f039379e83c2641b6269f + 0x12c814cebee6bb08a5d1b9d009332bf8b536d645 + 0x150c63df3da35e590a6d2f7accf2e6f241ea5f1a + 0x15ddf20e4eb8b53b823bc45c9bad2670aad907dd + 0x1712b1c428f89bc695b1871abfff6b5097350150 + 0x178df2e27b781f024e90ab0abe9cff7e2f66a5fc + 0x1c2bd83dc29095173c4bcc14927811f5141c1373 + 0x1d12f2fad3603ea871fcb13ac3e30674f9ad903f + 0x1f7391b6881b6f025aef25cff737ff3fcb9d7660 + 0x219a3d724f596a4b75656e9b1569289c71782804 + 0x21a7fd9228c46ec72f926978f791fc8bfcd277fa + 0x23acb760cebd01fe7c92361274a4077d37b59f4c + 0x23b249eeeeedd86bc40349f8bb8e2df34bd28f78 + 0x28d006b1a2309e957005ee575f422af8034f93df + 0x28ef72d5614b2833d645aecf8ef7add075eb21e2 + 0x292966802ffedb6f34f2c8c59df35c9d8f612c24 + 0x2c2661ddd320017138075aba06999440a902695f + 0x2c632be2dc2f47affd07bfce91bd4a27c02f4563 + 0x2f86de22ced85a7dd0d680fc4266929a72775e27 + 0x2fa04f15123025ab487dce71668f5265649d0598 + 0x30f78fd12c17855453e0db166fecf684bb239b8c + 0x31534e95353323209cd18ad35c22c2528db6d164 + 0x336e0e1a14e0136c02bf8dcf0a9a3fe408548262 + 0x340399588bba5b843883d1ad7afd771a3651447a + 0x341d2b82d0924ef42d75ce053654295d34839459 + 0x34c2b8975b47e13818f496cf80b40566798cf968 + 0x370e67f45db9c18d6551000e6c0918bc8d346ebf + 0x37149dae898296173d309f1de6981922ec1dc495 + 0x377cb0d3427af7f06df47d2ab420458834bed1fc + 0x3d473af3e6ce45183c781b414e8a9edcb8b26f72 + 0x42794c1d807079e16735e47e193825cec80ee28c + 0x45603aa97b67965b42b38ddc8884373edbcf2d56 + 0x465cb9df2f6d3c8a1c1ce3f2338823f0638fefa5 + 0x49fbe69c2897bce0340b5983a0f719213a8c6e6f + 0x4a84cbd3ef642e301aa59bedf4fa4d28e24e6204 + 0x4d4d551bd6244b854e732572902f19f4ccaa6996 + 0x4f62af4ec150ea121859b3368e6a61fb7bcf9002 + 0x4fd1c530f73ddfff5c609a4a8b25af6ca489d1fd + 0x50010a4f0e429b398c66876dea7694d5f8b1a639 + 0x522c9f65bc77ad9eed6bcdc3ec220236451c9583 + 0x52b30ca3c2f8656e2c022e896bef7fad9a0449ca + 0x537a7030ecd9d159e8231ce31b0c2e83b4f9ed75 + 0x5483a4c5583d5ba3db23676a3db346f47ba357e1 + 0x55ec1a78a1187428dc0c67cbb77ae9fbdd61cc2a + 0x56cc1c0aadc2b8beb71f1ac61f03645483abe165 + 0x58bea8cea61fad5c453731aaeed377f3d77a04cc + 0x58f632327fbc4f449bda3bd51e13f590e67a8627 + 0x59d122afcbd68c731de85c2597004c6ddafbc7ed + 0x5da0228024cc084b9475470a7b7ae1d478d51bb7 + 0x5e51d6621883afcbd4e999b93180a96909bdc766 + 0x5e9a0a1bdfdd868706f4554aae21bb2c46da32c2 + 0x5f3f0d3215db85faa693d99acfb03cca66556671 + 0x5f6aa25f22edb2347b464312e2508cbc4c6e0162 + 0x6006f79e4104850ab7c9b0f75918c1e2cf6311df + 0x60f5da58bccb716f58b5759a06fc2167fe237c26 + 0x62d3a444d0af59f9de79f8abeb5c942fcfbfbef5 + 0x630ea66c8c5dc205d45a978573fa86df5af1fe7a + 0x6464f0f96a29934087a955c67a6b53d5ed852e49 + 0x6653cedb0b7f51c4b0c44079eb45c514df24ecfd + 0x66d69ac12b573299f36b108792be75a1e2ccdfdc + 0x690ed837d25b46dbf46727fcda7392d997c2bc97 + 0x696eecbc97189c5b2a8245a8e32517db9960c171 + 0x69aaff0b7babe85e0a95adfc540e689399db7f24 + 0x6b71d2ceab5678b607aa1e69b6781f5c7abc9aaf + 0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd + 0x6e278cfecfe96fa5e6d5411ba6eeb765dff4f118 + 0x6e557f01c9dcb573b03909c9a5b3528aec263472 + 0x6ec268f8bef9c685d7e04d5cdb61fbb544869a9f + 0x6f2ba051b3ce06a90705c22e0241c2b7e32c1af0 + 0x7063732ced55cfa08aea520f3fe200c39b3df0f5 + 0x7073a17a0172dfb1e46a62f054d11a775aeac32e + 0x71d3718cfa0f9ee8173688fe52bb499e1f36534b + 0x74e20aec156674945894d404f8dea602570e62f5 + 0x783e45c2989e675ffc9d067914d7de3ff68aee58 + 0x7a5f843f884bb15d070806e1ff59b6c6f74bbe2d + 0x7c6b1706c86ea76a0e232324f249e1508ca2dfda + 0x7d23a23584c83c1f6636124255cfd8e9cfc0e529 + 0x7e8b5df0dec9168741c93d52d7045aca7ea632d3 + 0x7ec5da0f1036750688084252b802befe41551205 + 0x82c9fcef4dd2d374b000063d4899a38a7219cdc7 + 0x82fa2ab30a566ceeac987eb5510485be9382f130 + 0x83d927aca3266f94e8163eaa32700c70e9b76e6e + 0x8476f7e193c930f21e88dae84888e0d8bfaf3ed8 + 0x85ec166cb81f5010b4a8d365821473dac0c0aa88 + 0x8883c55943d5caf06b6484de9c1d73da8307cd82 + 0x8c07456cffd4254c89aaaa9d4e95c8b3e36c2a3b + 0x8fef965e5db6f7f1a165008499e8b7901cd766b2 + 0x9018e2967c15e1faed9b5d6439522f075535a683 + 0x903f1d8a086c6af1afe24648b6409aade83c4340 + 0x9127c398827d8db6b6d5f17b71f5db69d06e8b74 + 0x917b5be6e3acd96d40a33c13e6748e4a88576c6d + 0x91edfd05112f0bc9d6cd43b65361713a50e9eb7f + 0x93026a2c4a0bc69de31515070bf086e0c1f789e5 + 0x94863bbbc12ec5be148f60a7020fd49236fc1937 + 0x94befc001e203f141462f16bde60873bcefae401 + 0x94c408cf5934f241d4fdd55ff3825131635c6af2 + 0x94cfdec548de92301735dc0b82d8e1f79404ff94 + 0x96527f3311f44340887c926acc16f0997eb3b955 + 0x974117faf194885c01513e8d87b38a2291083ed5 + 0x993424827a5fb2fa97818814ea4027e28150f187 + 0x9a6f30a5cb46840076edd780da2dbb4bc7c39f24 + 0x9a74a096b0bb82adfd28494107f2c07f4545723e + 0x9af82ec46185641c0ea44679aac8a7e7570be202 + 0x9e2287a60ed85f6bd80c62c1b7b4130ea1b521dd + 0x9fee5b81ee0cbf34c18c52061f1b257d4ccb2702 + 0xa017226377e775af8e56450301cc035ae72267f8 + 0xa1b423e024daf925f25296ea2efcf009cc328873 + 0xa23c0cbfe59e8650277ffa635c59f287cece9087 + 0xa340b7625eec76b372f2c317fe08a7733f05d09c + 0xa4cb6be13c2eace6c0f1157553e3c446f7b38b10 + 0xa54326267784fae3ffd6800af38099753bb7f470 + 0xa580086125d040fddd3af9d563285bd0ec4d13e3 + 0xa88fc7a34ca36b952aa45d94c1e13155042b5e7d + 0xac8f4ce2e4eff39c738bf1941350b3b57e8eec4f + 0xacb17dca110db022b1aceb5399acba1e9bf577e3 + 0xae0b03c8d8bf9cf71eda758e9e8b59c70a3b4580 + 0xae365ff4b0c64413baf6f7dfdb5cd3fb65ad1376 + 0xaf7e60d02b425b54730b7281a97d1640233704b0 + 0xaf9846f8098656e7c2f0e53e9ff7d38ec7b7f679 + 0xb2784c0a95e9b6b865aca13556fb32e2f37cb775 + 0xb385fa211cd08326ff84b0d4f37cc8c3735aa3aa + 0xb3fb883cbbccb0551daf1507f87426fd38da087e + 0xb6515cfb82fa877fbadae5a87006a8d3deeeb7c9 + 0xb78c4f0b8c9ec0b3058724eca65292d0d65586b9 + 0xba25f341e16ee81ab80ea246d45bdead7cc339e5 + 0xbab14024437285c2e3b3c521abff96b0ef2e919f + 0xbaf0996297cc70fca1bee30162eabcd892f0574a + 0xbb01ea95321a94242c89479995b7e3f264cb46a0 + 0xc1b37a3b7f76947e24cc2470e0e948aab0181346 + 0xc24431c1a1147456414355b1f1769de450e524da + 0xc467b893e29277f9b62b4ed6c9ba054bd8225bff + 0xc4bc101a168ea2228973a65564a7d40a68528dd2 + 0xc784626571c2c25cd2cfe24192a149cad86d40d8 + 0xc7acf90a9f442855b8f291288bb5fb612536ed9b + 0xc9956593dbfb46cfd24686a365b34051a55abce6 + 0xca2eb2af7dd7a90777c8c6456efcc00fe56dbd6f + 0xcb4bb078edaae9393c8da27b809aa9c0f4c920b7 + 0xcc8f68e8e2d8196e2ecd0caf2f35b1611739a21f + 0xcd67903318a805d63fe79bf9b8401c1b79c6babf + 0xcd7a2fe9cb80c95b03950daf5b6d476bec9ac24d + 0xd09476f5ee7979becca8ffe6dc22a72565fc3cea + 0xd1c4bd2b583f445354d1b644ea4b8353f2d23048 + 0xd32bb8bceafc89ff59ba43ce8b6cd65bb06dd7b0 + 0xd49e9fa792db9d9398c57eabf94ba1b2c709ace7 + 0xd6b862cf0d009bde0f020ab9d8f96e475069c5c6 + 0xd747c05d9c8057db608ef7aedabf07e4db0bbe97 + 0xdb9b40d1b691ced3680e539261b6bc195388b3c0 + 0xdbcc502093cadd0feb709708c633e2427aeb9c2d + 0xdc53001181ddc6a279deea6419443ea0ac0aec9c + 0xde3b38cb1050e7b5db39b4cbb2b2b63a1e32cbf6 + 0xdf1b687a99216ad4ebf9176983bf165be7b25bbe + 0xe000662c02a02d8b40aabfcd661594312992311d + 0xe30c59e4dc19d7c9ed6eb10d734d4d7ef28403ac + 0xe415114089b4b4933e542a5c79af4b6e6cd7abc9 + 0xe47f0a0e93241d390fe9b99de852682522e847bc + 0xe54abbd51e324bf8cf349b6b31c01b043d1ee0e4 + 0xe57838f777b11fdc428d9e7e67f1187d6251ba1f + 0xe5e4b26325d0fbf551367f2cf3b5d01caed6abcf + 0xe6655208bd812d833238b560e847014b0aab3b51 + 0xe6e16a1023af4a8fe54669f3fce7c406801bb333 + 0xe727bba699fbe82a731dad9476b5234d0038cfa1 + 0xec361d34a55e24e2f77de7121ae2b7bf11ed0d65 + 0xed3bf94976eb11d55b955d1369a478620872b57c + 0xee93ad447fe6a0e2bbac4952e651b21c0175acad + 0xefc5d9cabc0bda8124e1b821e8c86c7e7bf1e4bc + 0xf272f72a00f166f491d994642c8243099b72d2cd + 0xf45f642034bbce869e31b05d1da919125c7331ee + 0xf4883b21724405b19e240f3309a64d16dd89adc7 + 0xf5cb2a87ff1095f6d93e7b4bfc1bc47542380550 + 0xf6ddd386c4f7f0b460032c8055d7f9c3503d7140 + 0xf72093096c81b3e9e991f5b737baec9570a56927 + 0xf7412232a7a731bca2e5554c8ee051274373c17c + 0xfc2321dc32c2e6e96a0e41c911fb73a7b278d5c8 + 0xfc4dc782bf7e81a2ed5cc0519f80de36e7931bd9 + 0xfcde1c261eb257e14491b4e7cb1949a7623c00c5 + 0xfd17a22fd80075f2716e93268aa01bcdd7d70b22 + ``` +
+ + +## Rationale + +EIP-161 provides that empty accounts (accounts that have zero nonce, zero balance and no code, but that might have storage) can no longer be created and provides mechanism to remove old empty accounts. The last empty accounts were removed in block 14049881 (tx `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`). + +The complete removal of all empty accounts ensures that certain edgecases of EIP-161 can never occur on Ethereum Mainnet. Continuing to define and test those cases as part of the Ethereum Specification burdens future client implementors with unnecessary technical debt. This EIP declares those cases undefined and leaves clients free to assume they will not occur. + +## Backwards Compatibility + +This EIP is identical to EIP-161 except for the following differences, none of which affect Ethereum Mainnet. The differences are: + +### "Potentially state-changing operations" + +EIP-161 specifies 11 "potentially state-changing operations" that trigger state clearing. All but the 3 listed in this EIP are irrelevant, for the following reasons: + +#### Impossible + +* Receiving zero value mining reward/fees (this would become possible after the merge). + +#### Cannot happen to an empty account + +* Being the source of a `CREATE`. +* Being the source of a `CALL`. +* Being refunded by a `SELFDESTRUCT` + +#### Causes the account to become non-empty + +* Being the sender of a message call transaction. +* Being the sender of a contract creation transaction. +* Being created by a `CREATE`. +* Being created by a contract creation transaction. + +### Interaction with `STATICCALL` + +The interaction between `STATICCALL` and account clearing has never been specified in an EIP. The Ethereum currently testsuite requires that `STATICCALL` triggers state clearing. + +This EIP formally undefines all interactions between `STATICCALL` and state clearing as it has never happened on Ethereum Mainnet and cannot happen in future. + +### "At the end of the transaction" + +This only makes a difference if an account is deleted and later recreated in the same transaction. This never happens on Ethereum Mainnet. + +### Test Cases + +All test cases involving empty accounts in the Ethereum execution layer test suite shall be removed unless they relate to the Spurious Dragon Hardfork. If a Spurious Dragon test relates involved deprecated edgecase the test must be removed or reworked. + +### Other networks + +Ropsten had empty accounts seeded at genesis. They appear to have been cleared early in Ropsten's history before the Byzantium hardfork. Ropsten has never been checked for edgecases occurring. All other Ethereum testnets have had EIP-161 from genesis. + +As a security precaution all empty accounts on Ethereum Classic have been cleared, but no checks for edgecases occurring have been done. Due to EIP-161's age the vast majority of EVM compatible networks have supported it from genesis. + +## Security considerations + +This EIP is only equivalent to EIP-161 on Ethereum Mainnet if the following facts are true: + +1. No empty accounts are ever touched and then reinstated in the same transaction. +2. The transactions in the Appendix are the only state clearing transactions on Ethereum Mainnet after block 4370000 (start of Byzantium). +3. All empty accounts have been removed on Ethereum Mainnet. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4750.md b/EIPS/eip-4750.md new file mode 100644 index 00000000000000..ce8345ee7f9e18 --- /dev/null +++ b/EIPS/eip-4750.md @@ -0,0 +1,156 @@ +--- +eip: 4750 +title: EOF - Functions +description: Individual sections for functions with `CALLF` and `RETF` instructions +author: Andrei Maiboroda (@gumb0), Alex Beregszaszi (@axic), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-4750-eof-functions/8195 +status: Review +type: Standards Track +category: Core +created: 2022-01-10 +requires: 3540, 3670, 5450 +--- + +## Abstract + +Introduce the ability to have several code sections in EOF-formatted ([EIP-3540](./eip-3540.md)) bytecode, each one representing a separate subroutine/function. Two new opcodes,`CALLF` and `RETF`, are introduced to call and return from such a function. Dynamic jump instructions are disallowed. + +## Motivation + +Currently, in the EVM everything is a dynamic jump. Languages like Solidity generate most jumps in a static manner (i.e. the destination is pushed to the stack right before, `PUSHn .. JUMP`). Unfortunately however this cannot be used by most EVM interpreters, because of added requirement of validation/analysis. This also restricts them from making optimisations and potentially reducing the cost of jumps. + +[EIP-4200](./eip-4200.md) introduces static jump instructions, which remove the need for *most* dynamic jump use cases, but not everything can be solved with them. + +This EIP aims to remove the need and disallow dynamic jumps as it offers the most important feature those are used for: calling into and returning from functions. + +Furthermore, it aims to improve analysis opportunities by encoding the number of inputs and outputs for each given function, and isolating the stack of each function (i.e. a function cannot read the stack of the caller/callee). + +## Specification + +### Type Section + +The type section of EOF containers must adhere to following requirements: + +1. The section is comprised of a list of metadata where the metadata index in the type section corresponds to a code section index. Therefore, the type section size MUST be `n * 4` bytes, where `n` is the number of code sections. +2. Each metadata item has 3 attributes: a uint8 `inputs`, a uint8 `outputs`, and a uint16 `max_stack_height`. *Note:* This implies that there is a limit of 255 stack for the input and in the output. This is further restricted to 127 stack items, because the upper bit of both the input and output bytes are reserved for future use. `max_stack_height` is further defined in [EIP-5450](./eip-5450.md). +3. The first code section MUST have 0 inputs and 0 outputs. + +Refer to [EIP-3540](./eip-3540.md) to see the full structure of a well-formed EOF bytecode. + +### New execution state in EVM + +A return stack is introduced, separate from the operand stack. It is a stack of items representing execution state to return to after function execution is finished. Each item is comprised of: code section index, offset in the code section (PC value), calling function stack height. + +Note: Implementations are free to choose particular encoding for a stack item. In the specification below we assume that representation is three unsigned integers: `code_section_index`, `offset`, `stack_height`. + +The return stack is limited to a maximum 1024 items. + +Additionally, EVM keeps track of the index of currently executing section - `current_section_index`. + +### New instructions + +We introduce two new instructions: + +1. `CALLF` (`0xe3`) - call a function +2. `RETF` (`0xe4`) - return from a function + +If the code is legacy bytecode, any of these instructions results in an *exceptional halt*. (*Note: This means no change to behaviour.*) + +First we define several helper values: + +- `caller_stack_height = return_stack.top().stack_height` - stack height value saved in the top item of return stack +- `type[i].inputs = type_section_contents[i * 4]` - number of inputs of ith section +- `type[i].outputs = type_section_contents[i * 4 + 1]` - number of outputs of ith section + +If the code is valid EOF1, the following execution rules apply: + +#### `CALLF` + +1. Has one immediate argument,`code_section_index`, encoded as a 16-bit unsigned big-endian value. +2. EOF validation guarantees that operand stack has at least `caller_stack_height + type[code_section_index].inputs` items. +3. If operand stack size exceeds `1024 - type[code_section_index].max_stack_height` (i.e. if the called function may exceed the global stack height limit), execution results in exceptional halt. This also guarantees that the stack height after the call is within the limits. +4. If return stack already has `1024` items, execution results in exceptional halt. +5. Charges 5 gas. +6. Pops nothing and pushes nothing to operand stack. +7. Pushes to return stack an item: + + ``` + (code_section_index = current_section_index, + offset = PC_post_instruction, + stack_height = data_stack.height - types[code_section_index].inputs) + ``` + + Under `PC_post_instruction` we mean the PC position after the entire immediate argument of `CALLF`. Operand stack height is saved as it was before function inputs were pushed. + + *Note:* Code validation rules of [EIP-5450](./eip-5450.md) guarantee there is always an instruction following `CALLF` (since terminating instruction or unconditional jump is required to be final one in the section), therefore `PC_post_instruction` always points to an instruction inside section bounds. +8. Sets `current_section_index` to `code_section_index` and `PC` to `0`, and execution continues in the called section. + +#### `RETF` + +1. Does not have immediate arguments. +2. EOF validation guarantees that operand stack has exactly `caller_stack_height + type[current_section_index].outputs` items. +3. Charges 3 gas. +4. Pops nothing and pushes nothing to operand stack. +5. Pops an item from return stack and sets `current_section_index` and `PC` to values from this item. + 1. If return stack is empty after this, execution halts with success. + +### Code Validation + +In addition to container format validation rules above, we extend code section validation rules (as defined in [EIP-3670](./eip-3670.md)). + +1. Code validation rules of EIP-3670 are applied to every code section. +2. Code section is invalid in case an immediate argument of any `CALLF` is greater than or equal to the total number of code sections. +3. `RJUMP`, `RJUMPI` and `RJUMPV` immediate argument value (jump destination relative offset) validation: + 1. Code section is invalid in case offset points to a position outside of section bounds. + 2. Code section is invalid in case offset points to one of two bytes directly following `CALLF` instruction. + +### Disallowed instructions + +Dynamic jump instructions `JUMP` (`0x56`) and `JUMPI` (`0x57`) are invalid and their opcodes are undefined. + +`JUMPDEST` (`0x5b`) instruction is renamed to `NOP` ("no operation") without the change in behaviour: it pops nothing and pushes nothing to operand stack and has no other effects except for `PC` increment and charging 1 gas. + +`PC` (0x58) instruction becomes invalid and its opcode is undefined. + +*Note:* This change implies that JUMPDEST analysis is no longer required for EOF code. + +### Execution + +1. Execution starts at the first byte of the first code section, and PC is set to 0. +2. Return stack is initialized to contain one item: `(code_section_index = 0, offset = 0, stack_height = 0)` +3. If any instruction access the operand stack item below `caller_stack_height`, execution results in exceptional halt. This rule replaces the old stack underflow check. +4. No change in stack overflow check: if any instruction causes the operand stack height to exceed `1024`, execution results in exceptional halt. + +## Rationale + +### `RETF` in the top frame ends execution vs exceptionally halts + +Alternative logic for executing `RETF` in the top frame could be to exceptionally halt execution, because there is arguably no caller for the starting function. This would mean that return stack is initialized as empty, and `RETF` exceptionally aborts when return stack is empty. + +We have decided in favor of always having at least one item in the return stack, because it allows to avoid having a special case for empty stack in the interpreter loop stack underflow check. We keep the stack underflow rule general by having `caller_stack_height = 0` in the top frame. + +### Code section limit and instruction size + +The number of code sections is limited to 1024. This requires 2-byte immediate for `CALLF` and leaves room for increasing the limit in the future. The 256 limit (1-byte immediate) was discussed and concerns were raised that it might not be sufficient. + +### `NOP` instruction + +Instead of deprecating `JUMPDEST` we repurpose it as `NOP` instruction, because `JUMPDEST` effectively was a "no-operation" instruction and was already used as such in various contexts. It can be useful for some off-chain tooling, e.g. benchmarking EVM implementations (performance of `NOP` instruction is performance of EVM interpreter loop), as a padding to force code alignment, as a placeholder in dynamic code composition. + +### Deprecating `JUMPDEST` analysis + +The purpose of `JUMPDEST` analysis was to find in code the valid `JUMPDEST` bytes that do not happen to be inside `PUSH` immediate data. Only dynamic jump instructions (`JUMP`, `JUMPI`) required destination to be `JUMPDEST` instruction. Relative static jumps (`RJUMP` and `RJUMPI`) do not have this requirement and are validated once at deploy-time EOF instruction validation. Therefore, without dynamic jump instructions, `JUMPDEST` analysis is not required. + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced only for EOF1 contracts, for which deploying undefined instructions is not allowed, therefore there are no existing contracts using these instructions. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted). + +The new execution state and multi-section control flow pose no risk to backwards compatibility, because it is a generalization of executing a single code section. Executing existing contracts (both legacy and EOF1) has no user-observable changes. + +## Security Considerations + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4758.md b/EIPS/eip-4758.md new file mode 100644 index 00000000000000..e5d569214cb57f --- /dev/null +++ b/EIPS/eip-4758.md @@ -0,0 +1,50 @@ +--- +eip: 4758 +title: Deactivate SELFDESTRUCT +description: Deactivate SELFDESTRUCT by changing it to SENDALL, which does recover all funds to the caller but does not delete any code or storage. +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/eip-4758-deactivate-selfdestruct/8710 +status: Review +type: Standards Track +category: Core +created: 2022-02-03 +--- + +## Abstract + +This EIP renames the `SELFDESTRUCT` opcode to `SENDALL`, and replaces its functionality. The new functionality will be only to send all Ether in the account to the caller. + +## Motivation + +The `SELFDESTRUCT` opcode requires large changes to the state of an account, in particular removing all code and storage. This will not be possible in the future with Verkle trees: Each account will be stored in many different account keys, which will not be obviously connected to the root account. + +This EIP implements this change. Applications that only use `SELFDESTRUCT` to retrieve funds will still work. + +## Specification + + * The `SELFDESTRUCT` opcode is renamed to `SENDALL`, and now only immediately moves all ETH in the account to the target; it no longer destroys code or storage or alters the nonce + * All refunds related to `SELFDESTRUCT` are removed + +## Rationale + +Getting rid of the `SELFDESTRUCT` opcode has been considered in the past, and there are currently no strong reasons to use it. Disabling it will be a requirement for statelessness. + +## Backwards Compatibility + +This EIP requires a hard fork, since it modifies consensus rules. + +Few applications are affected by this change. The only use that breaks is where a contract is re-created at the same address using `CREATE2` (after a `SELFDESTRUCT`). + +## Security Considerations + +The following applications of `SELFDESTRUCT` will be broken and applications that use it in this way are not safe anymore: + +1. Any use where `SELFDESTRUCT` is used to burn non-ETH token balances, such as [EIP-20](./eip-20.md)), inside a contract. We do not know of any such use (since it can easily be done by sending to a burn address this seems an unlikely way to use `SELFDESTRUCT`) +2. Where `CREATE2` is used to redeploy a contract in the same place. There are two ways in which this can fail: + * The destruction prevents the contract from being used outside of a certain context. For example, the contract allows anyone to withdraw funds, but `SELFDESTRUCT` is used at the end of an operation to prevent others from doing this. This type of operation can easily be modified to not depend on `SELFDESTRUCT`. + * The `SELFDESTRUCT` operation is used in order to make a contract upgradable. This is not supported anymore and delegates should be used. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4760.md b/EIPS/eip-4760.md new file mode 100644 index 00000000000000..aafcbfd365d511 --- /dev/null +++ b/EIPS/eip-4760.md @@ -0,0 +1,63 @@ +--- +eip: 4760 +title: SELFDESTRUCT bomb +description: Deactivate SELFDESTRUCT by changing it to SENDALL and stage this via a stage of exponential gas cost increases. +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/eip-4760-selfdestruct-bomb/8713 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-03 +--- +## Abstract + +This EIP renames the `SELFDESCRUCT` opcode to `SENDALL`, and replaces its functionality. The new functionality will be only to send all Ether in the account to the caller. + +In order to give apps more warning even if their developers are completely unaware of the EIP process, this version will exponentially increase the gas costs of the opcode, so any developer has time to see this change and react by implementing a version of their contract that does not rely on `SELFDESTRUCT` . + +## Motivation + +The `SELFDESTRUCT` opcode requires large changes to the state of an account, in particular removing all code and storage. This will not be possible in the future with Verkle trees: Each account will be stored in many different account keys, which will not be obviously connected to the root account. + +This EIP implements this change. Applications that only use `SELFDESTRUCT` to retrieve funds will still work. + +## Specification + +### Constants + +| Name | Value | Comment | +|------|-------|---------| +| `OLD_SELFDESTRUCT_COST` | 5000 | Current gas cost of `SELFDESTRUCT` opcode | +| `HARD_FORK_BLOCK` | TBD | (Shanghai HF block height) | +| `DOUBLING_SLOTS` | `2**16` | (Time for gas price to double, ca. 9 days) | +| `DOUBLINGS_BEFORE_SENDALL` | `13` | `SELFDESTRUCT` will be converted to `SENDALL` at `HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL` | + + * If `HARD_FORK_BLOCK <= slot < HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL` + * `SELFDESTRUCT` functionality remains unchanged + * `SELFDESTRUCT` gas cost is now `OLD_SELFDESTRUCT_COST * 2 ** ((slot - HARD_FORK_BLOCK) // DOUBLING_SLOTS)` + * For `slot >= HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL` + * The cost reverts back to `OLD_SELFDESTRUCT_COST` + * The `SELFDESTRUCT` opcode is renamed to `SENDALL`, and now only immediately moves all ETH in the account to the target; it no longer destroys code or storage or alters the nonce + * All refunds related to `SELFDESTRUCT` are removed + +## Rationale + +The idea behind this EIP is to disable `SELFDESTRUCT` in a way that gives ample warning to Dapp developers. Many developers do not watch the EIP process closely and can therefore be caught by surprise when an opcode is deactivated and does not fulfill its original purpose anymore. However, at least if the smart contract has regular use, then users will notice the price of the operation going up tremendously. The period over which this is happening (`HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL`) is chosen to be long enough (ca. 4 months) such that it gives developers time to react to this change and prepare their application. + +## Backward Compatibility + +This EIP requires a hard fork, since it modifies consensus rules. + +Few applications are affected by this change. The only use that breaks is where a contract is re-created at the same address using `CREATE2` (after a `SELFDESTRUCT`). The only application that is significantly affected (and where code can be analyzed) is able to switch to a different model, and should have ample time to do so. + +## Security Considerations + +The following applications of `SELFDESTRUCT` will be broken and applications that use it in this way are not safe anymore: +1. Any use where `SELFDESTRUCT` is used to burn non-ETH token balances, such as ERC20, inside a contract. We do not know of any such use (since it can easily be done by sending to a burn address this seems an unlikely way to use `SELFDESTRUCT`) +2. Where `CREATE2` is used to redeploy a contract in the same place. There are two ways in which this can fail: + - The destruction prevents the contract from being used outside of a certain context. For example, the contract allows anyone to withdraw funds, but `SELFDESTRUCT` is used at the end of an operation to prevent others from doing this. This type of operation can easily be modified to not depend on `SELFDESTRUCT`. + - The `SELFDESTRUCT` operation is used in order to make a contract upgradable. This is not supported anymore and delegates should be used. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-4762.md b/EIPS/eip-4762.md new file mode 100644 index 00000000000000..03bcdc5c4f2633 --- /dev/null +++ b/EIPS/eip-4762.md @@ -0,0 +1,308 @@ +--- +eip: 4762 +title: Statelessness gas cost changes +description: Changes the gas schedule to reflect the costs of creating a witness by requiring clients update their database layout to match. +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/eip-4762-statelessness-gas-cost-changes/8714 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-03 +--- +## Abstract + +This EIP introduces changes in the gas schedule to reflect the costs of creating a witness. It requires clients to update their database layout to match this, so as to avoid potential DoS attacks. + +## Motivation + +The introduction of Verkle trees into Ethereum requires fundamental changes and as a preparation, this EIP is targeting the fork coming right before the verkle tree fork, in order to incentivize Dapp developers to adopt the new storage model, and ample time to adjust to it. It also incentivizes client developers to migrate their database format ahead of the verkle fork. + +## Specification + +### Access events + +We define access events as follows. When an access event takes place, the accessed data is saved to the Verkle tree (even if it was not modified). An access event is of the form`(address, sub_key, leaf_key)`, determining what data is being accessed. + +#### Access events for account headers + +When a non-precompile address is the target of a `CALL`, `CALLCODE`, `DELEGATECALL`, `SELFDESTRUCT`, `EXTCODESIZE`, or `EXTCODECOPY` opcode, or is the target address of a contract creation whose initcode starts execution, process these access events: + +``` +(address, 0, VERSION_LEAF_KEY) +(address, 0, CODE_SIZE_LEAF_KEY) +``` + +If a call is value-bearing (ie. it transfers nonzero wei), whether or not the callee is a precompile, process these two access events: + +``` +(caller_address, 0, BALANCE_LEAF_KEY) +(callee_address, 0, BALANCE_LEAF_KEY) +``` + +When a contract is created, process these access events: + +``` +(contract_address, 0, VERSION_LEAF_KEY) +(contract_address, 0, NONCE_LEAF_KEY) +(contract_address, 0, BALANCE_LEAF_KEY) +(contract_address, 0, CODE_KECCAK_LEAF_KEY) +(contract_address, 0, CODE_SIZE_LEAF_KEY) +``` + +If the `BALANCE` opcode is called targeting some address, process this access event: + +``` +(address, 0, BALANCE_LEAF_KEY) +``` + +If the `SELFDESTRUCT` opcode is called by some caller_address targeting some target_address (regardless of whether it’s value-bearing or not), process access events of the form: + +``` +(caller_address, 0, BALANCE_LEAF_KEY) +(target_address, 0, BALANCE_LEAF_KEY) +``` + +If the `EXTCODEHASH` opcode is called targeting some address, process an access event of the form: + +``` +(address, 0, CODEHASH_LEAF_KEY) +``` + +#### Access events for storage + +`SLOAD` and `SSTORE` opcodes with a given address and key process an access event of the form + +``` +(address, tree_key, sub_key) +``` + +Where tree_key and sub_key are computed as follows: + +```python +def get_storage_slot_tree_keys(storage_key: int) -> [int, int]: + if storage_key < (CODE_OFFSET - HEADER_STORAGE_OFFSET): + pos = HEADER_STORAGE_OFFSET + storage_key + else: + pos = MAIN_STORAGE_OFFSET + storage_key + return ( + pos // 256, + pos % 256 + ) +``` + +#### Access events for code + +In the conditions below, “chunk chunk_id is accessed” is understood to mean an access event of the form + +``` +(address, (chunk_id + 128) // 256, (chunk_id + 128) % 256) +``` + + * At each step of EVM execution, if and only if PC < len(code), chunk PC // CHUNK_SIZE (where PC is the current program counter) of the callee is accessed. In particular, note the following corner cases: + * The destination of a `JUMP` (or positively evaluated JUMPI) is considered to be accessed, even if the destination is not a jumpdest or is inside pushdata + * The destination of a `JUMPI` is not considered to be accessed if the jump conditional is false. + * The destination of a jump is not considered to be accessed if the execution gets to the jump opcode but does not have enough gas to pay for the gas cost of executing the `JUMP` opcode (including chunk access cost if the `JUMP` is the first opcode in a not-yet-accessed chunk) + * The destination of a jump is not considered to be accessed if it is beyond the code (`destination >= len(code)`) + * If code stops execution by walking past the end of the code, `PC = len(code)` is not considered to be accessed + * If the current step of EVM execution is a `PUSH{n}`, all chunks `(PC // CHUNK_SIZE) <= chunk_index <= ((PC + n) // CHUNK_SIZE)`` of the callee are accessed. + * If a nonzero-read-size `CODECOPY` or `EXTCODECOPY` read bytes `x...y` inclusive, all chunks ``(x // CHUNK_SIZE) <= chunk_index <= (min(y, code_size - 1) // CHUNK_SIZE)`` of the accessed contract are accessed. + * Example 1: for a `CODECOPY` with start position 100, read size 50, `code_size = 200`, `x = 100` and `y = 149` + * Example 2: for a `CODECOPY` with start position 600, read size 0, no chunks are accessed + * Example 3: for a `CODECOPY` with start position 1500, read size 2000, `code_size = 3100`, `x = 1500` and `y = 3099` + * `CODESIZE`, `EXTCODESIZE` and `EXTCODEHASH` do NOT access any chunks. + When a contract is created, access chunks `0 ... (len(code)+30)//31` + +### Write Events + +We define **write events** as follows. Note that when a write takes place, an access event also takes place (so the definition below should be a subset of the definition of access lists) A write event is of the form `(address, sub_key, leaf_key)`, determining what data is being written to. + +#### Write events for account headers + +When a nonzero-balance-sending CALL or SELFDESTRUCT with a given sender and recipient takes place, process these write events: + +``` +(sender, 0, BALANCE_LEAF_KEY) +(recipient, 0, BALANCE_LEAF_KEY) +``` + +When a contract creation is initialized, process these write events: + +``` +(contract_address, 0, VERSION_LEAF_KEY) +(contract_address, 0, NONCE_LEAF_KEY) +``` + +Only if the value sent with the creation is nonzero, also process: + +``` +(contract_address, 0, BALANCE_LEAF_KEY) +``` + +When a contract is created, process these write events: + +``` +(contract_address, 0, VERSION_LEAF_KEY) +(contract_address, 0, NONCE_LEAF_KEY) +(contract_address, 0, BALANCE_LEAF_KEY) +(contract_address, 0, CODE_KECCAK_LEAF_KEY) +(contract_address, 0, CODE_SIZE_LEAF_KEY) +``` + +#### Write events for storage + +SSTORE opcodes with a given `address` and `key` process a write event of the form + +``` +(address, tree_key, sub_key) +``` + +Where `tree_key` and `sub_key` are computed as follows: + +```python +def get_storage_slot_tree_keys(storage_key: int) -> [int, int]: + if storage_key < (CODE_OFFSET - HEADER_STORAGE_OFFSET): + pos = HEADER_STORAGE_OFFSET + storage_key + else: + pos = MAIN_STORAGE_OFFSET + storage_key + return ( + pos // 256, + pos % 256 + ) +``` + +#### Write events for code + +When a contract is created, make write events: + +```python +( + address, + (CODE_OFFSET + i) // VERKLE_NODE_WIDTH, + (CODE_OFFSET + i) % VERKLE_NODE_WIDTH +) +``` + +For `i` in `0 ... (len(code)+30)//31`. + +### Transactions + +#### Access events +For a transaction, make these access events: + +``` +(tx.origin, 0, VERSION_LEAF_KEY) +(tx.origin, 0, BALANCE_LEAF_KEY) +(tx.origin, 0, NONCE_LEAF_KEY) +(tx.origin, 0, CODE_SIZE_LEAF_KEY) +(tx.origin, 0, CODE_KECCAK_LEAF_KEY) +(tx.target, 0, VERSION_LEAF_KEY) +(tx.target, 0, BALANCE_LEAF_KEY) +(tx.target, 0, NONCE_LEAF_KEY) +(tx.target, 0, CODE_SIZE_LEAF_KEY) +(tx.target, 0, CODE_KECCAK_LEAF_KEY) +``` + +#### Write events + +``` +(tx.origin, 0, NONCE_LEAF_KEY) +``` + +if `value` is non-zero: + +``` +(tx.origin, 0, BALANCE_LEAF_KEY) +(tx.target, 0, BALANCE_LEAF_KEY) +``` + +### Witness gas costs + +Remove the following gas costs: + + * Increased gas cost of `CALL` if it is nonzero-value-sending + * EIP-2200 `SSTORE` gas costs except for the `SLOAD_GAS` + * 200 per byte contract code cost + +Reduce gas cost: + + * `CREATE` to 1000 + +|Constant |Value| +|-|-| +|WITNESS_BRANCH_COST |1900| +|WITNESS_CHUNK_COST |200| +|SUBTREE_EDIT_COST |3000| +|CHUNK_EDIT_COST |500| +|CHUNK_FILL_COST |6200| + +When executing a transaction, maintain four sets: + + * `accessed_subtrees: Set[Tuple[address, int]]` + * `accessed_leaves: Set[Tuple[address, int, int]]` + * `edited_subtrees`: `Set[Tuple[address, int]]` + * `edited_leaves`: `Set[Tuple[address, int, int]]` + + +When an **access** event of `(address, sub_key, leaf_key)` occurs, perform the following checks: + + * If ``(address, sub_key)`` is not in accessed_subtrees, charge WITNESS_BRANCH_COST gas and add that tuple to accessed_subtrees. + * If `leaf_key` is not `None` and ``(address, sub_key, leaf_key)`` is not in `accessed_leaves`, charge `WITNESS_CHUNK_COST` gas and add it to `accessed_leaves` + +When a **write** event of `(address, sub_key, leaf_key)` occurs, perform the following checks: + + * If (address, sub_key) is not in edited_subtrees, charge `SUBTREE_EDIT_COST` gas and add that tuple to edited_subtrees. + * If leaf_key is not None and `(address, sub_key, leaf_key)` is not in `edited_leaves`, charge `CHUNK_EDIT_COST` gas and add it to `edited_leaves` + * Additionally, if there was no value stored at `(address, sub_key, leaf_key)` (ie. the state held None at that position), charge `CHUNK_FILL_COST` + +Note that tree keys can no longer be emptied: only the values `0...2**256-1` can be written to a tree key, and 0 is distinct from None. Once a tree key is changed from `None` to not-`None`, it can never go back to `None`. + +### Replacement for access lists + +We replace EIP 2930 access lists with an SSZ structure of the form: + +```python +class AccessList(Container): + addresses: List[AccountAccessList, ACCESS_LIST_MAX_ELEMENTS] + +class AccountAccessList(Container): + address: Address32 + subtrees: List[AccessSubtree, ACCESS_LIST_MAX_ELEMENTS] + +class AccessSubtree(Container): + subtree_key: uint256 + elements: BitVector[256] +``` + +## Rationale + +### Gas reform + +Gas costs for reading storage and code are reformed to more closely reflect the gas costs under the new Verkle tree design. `WITNESS_CHUNK_COST` is set to charge 6.25 gas per byte for chunks, and `WITNESS_BRANCH_COST` is set to charge ~13,2 gas per byte for branches on average (assuming 144 byte branch length) and ~2.5 gas per byte in the worst case if an attacker fills the tree with keys deliberately computed to maximize proof length. + +The main differences from gas costs in Berlin are: + + * 200 gas charged per 31 byte chunk of code. This has been estimated to increase average gas usage by ~6-12% suggesting 10-20% gas usage increases at a 350 gas per chunk level). + * Cost for accessing adjacent storage slots (`key1 // 256 == key2 // 256`) decreases from 2100 to 200 for all slots after the first in the group, + * Cost for accessing storage slots 0…63 decreases from 2100 to 200, including the first storage slot. This is likely to significantly improve performance of many existing contracts, which use those storage slots for single persistent variables. + +Gains from the latter two properties have not yet been analyzed, but are likely to significantly offset the losses from the first property. It’s likely that once compilers adapt to these rules, efficiency will increase further. + +The precise specification of when access events take place, which makes up most of the complexity of the gas repricing, is necessary to clearly specify when data needs to be saved to the period 1 tree. + +## Backward Compatibility + +This EIP requires a hard fork, since it modifies consensus rules. + +The main backwards-compatibility-breaking changes is the gas costs for code chunk access making some applications less economically viable. It can be mitigated by increasing the gas limit at the same time as implementing this EIP, reducing the risk that applications will no longer work at all due to transaction gas usage rising above the block gas limit. + +## Security Considerations + +This EIP will mean that certain operations, mostly reading and writing several elements in the same suffix tree, become cheaper. If clients retain the same database structure as they have now, this would result in a DOS vector. + +So some adaptation of the database is required in order to make this work. + * In all possible futures, it is important to logically separate the commitment scheme from data storage. In particular, no traversal of the commitment scheme tree should be necessary to find any given state element + * In order to make accesses to the same stem cheap as required for this EIP, the best way is probably to store each stem in the same location in the database. Basically the 256 leaves of 32 bytes each would be stored in an 8kB BLOB. The overhead of reading/writing this BLOB is small because most of the cost of disk access is seeking and not the amount transferred. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4788.md b/EIPS/eip-4788.md new file mode 100644 index 00000000000000..153cf439079c56 --- /dev/null +++ b/EIPS/eip-4788.md @@ -0,0 +1,307 @@ +--- +eip: 4788 +title: Beacon block root in the EVM +description: Expose beacon chain roots in the EVM +author: Alex Stokes (@ralexstokes), Ansgar Dietrichs (@adietrichs), Danny Ryan (@djrtwo), Martin Holst Swende (@holiman), lightclient (@lightclient) +discussions-to: https://ethereum-magicians.org/t/eip-4788-beacon-root-in-evm/8281 +status: Draft +type: Standards Track +category: Core +created: 2022-02-10 +requires: 1559 +--- + +## Abstract + +Commit to the hash tree root of each beacon chain block in the corresponding execution payload header. + +Store each of these roots in a smart contract. + +## Motivation + +Roots of the beacon chain blocks are cryptographic accumulators that allow proofs of arbitrary consensus state. +Exposing these roots inside the EVM allows for trust-minimized access to the consensus layer. +This functionality supports a wide variety of use cases that improve trust assumptions of staking pools, +restaking constructions, smart contract bridges, MEV mitigations and more. + +## Specification + +| constants | value | +|--- |--- | +| `FORK_TIMESTAMP` | TBD | +| `HISTORY_BUFFER_LENGTH` | `8191` | +| `SYSTEM_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe` | +| `BEACON_ROOTS_ADDRESS` | `0xBEaC020001c6C8B69E5257f4754e46e25f5dc9cB` | + +### Background + +The high-level idea is that each execution block contains the parent beacon block's root. Even in the event of missed slots since the previous block root does not change, +we only need a constant amount of space to represent this "oracle" in each execution block. To improve the usability of this oracle, a small history of block roots +are stored in the contract. + +To bound the amount of storage this construction consumes, a ring buffer is used that mirrors a block root accumulator on the consensus layer. + +### Block structure and validity + +Beginning at the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST** extend the header schema with an additional field: the `parent_beacon_block_root`. +This root consumes 32 bytes and is exactly the [hash tree root](https://github.com/ethereum/consensus-specs/blob/fa09d896484bbe240334fa21ffaa454bafe5842e/ssz/simple-serialize.md#merkleization) of the parent beacon block for the given execution block. + +The resulting RLP encoding of the header is therefore: + +```python +rlp([ + parent_hash, + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash + coinbase, + state_root, + txs_root, + receipts_root, + logs_bloom, + 0, # difficulty + number, + gas_limit, + gas_used, + timestamp, + extradata, + prev_randao, + 0x0000000000000000, # nonce + base_fee_per_gas, + withdrawals_root, + data_gas_used, + excess_data_gas, + parent_beacon_block_root, +]) +``` + +Validity of the parent beacon block root is guaranteed from the consensus layer, much like how withdrawals are handled. + +When verifying a block, execution clients **MUST** ensure the root value in the block header matches the one provided by the consensus client. + +For a genesis block with no existing parent beacon block root the 32 zero bytes are used as a root placeholder. + +#### Beacon roots contract + +The beacon roots contract has two operations: `get` and `set`. The input itself is not used to determine which function to execute, for that the result of `caller` is used. If `caller` is equal to `SYSTEM_ADDRESS` then the operation to perform is `set`. Otherwise, `get`. + +##### `get` + +* Callers provide the `timestamp` they are querying encoded as 32 bytes in big-endian format. +* If the input is not exactly 32 bytes, the contract must revert. +* If the input is equal to 0, the contract must revert. +* Given `timestamp`, the contract computes the storage index in which the timestamp is stored by computing the modulo `timestamp % HISTORY_BUFFER_LENGTH` and reads the value. +* If the `timestamp` does not match, the contract must revert. +* Finally, the beacon root associated with the timestamp is returned to the user. It is stored at `timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH`. + +##### `set` + +* Caller provides the parent beacon block root as calldata to the contract. +* Set the storage value at `header.timestamp % HISTORY_BUFFER_LENGTH` to be `header.timestamp` +* Set the storage value at `header.timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH` to be `calldata[0:32]` + +##### Pseudocode + +```python +if evm.caller == SYSTEM_ADDRESS: + set() +else: + get() + +def get(): + if len(evm.calldata) != 32: + evm.revert() + + if to_uint256_be(evm.calldata) == 0: + evm.revert() + + timestamp_idx = to_uint256_be(evm.calldata) % HISTORY_BUFFER_LENGTH + timestamp = storage.get(timestamp_idx) + + if timestamp != evm.calldata: + evm.revert() + + root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH + root = storage.get(root_idx) + + evm.return(root) + +def set(): + timestamp_idx = to_uint256_be(evm.timestamp) % HISTORY_BUFFER_LENGTH + root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH + + storage.set(timestamp_idx, evm.timestamp) + storage.set(root_idx, evm.calldata) +``` + +##### Bytecode + +The exact initcode to deploy is shared below. + +```asm +push1 0x61 +dup1 +push1 0x09 +push0 +codecopy +push0 +return + +caller +push20 0xfffffffffffffffffffffffffffffffffffffffe +eq +push1 0x4d +jumpi + +push1 0x20 +calldatasize +eq +push1 0x24 +jumpi + +push0 +push0 +revert + +jumpdest +push0 +calldataload +dup1 +iszero +push1 0x49 +jumpi + +push3 0x016da0 +dup2 +mod +swap1 +dup2 +sload +eq +push1 0x3c +jumpi + +push0 +push0 +revert + +jumpdest +push3 0x016da0 +add +sload +push0 +mstore +push1 0x20 +push0 +return + +jumpdest +push0 +push0 +revert + +jumpdest +push3 0x016da0 +timestamp +mod +timestamp +dup2 +sstore +push0 +calldataload +swap1 +push3 0x016da0 +add +sstore +stop +``` + +#### Deployment + +The beacon roots contract is deployed like any other smart contract. A special synthetic address is generated +by working backwards from the desired deployment transaction: + +```json +{ + "type": "0x0", + "nonce": "0x0", + "to": null, + "gas": "0x3d090", + "gasPrice": "0xe8d4a51000", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "value": "0x0", + "input": "0x60618060095f395ff33373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762016da0810690815414603c575f5ffd5b62016da001545f5260205ff35b5f5ffd5b62 +016da042064281555f359062016da0015500", + "v": "0x1b", + "r": "0x539", + "s": "0x133700f399526625760859", + "hash": "0x3bd7320b837cfe4efba41d333a1a6514b2ed691c4ffd63db84e39852df4a3292" +} +``` + +The sender of the transaction can be calculated as `0x6ccA914845EB5413c1b7D600dC4D47f6CE85601a`. The address of the first contract deployed from the account is `rlp([sender, 0])` which equals `0xBEaC020001c6C8B69E5257f4754e46e25f5dc9cB`. This is how `BEACON_ROOTS_ADDRESS` is determined. Although this style of contract creation is not tied to any specific initcode like create2 is, the synthetic address is cryptographically bound to the input data of the transaction (e.g. the initcode). + +### Block processing + +At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. before processing any transactions), call `BEACON_ROOTS_ADDRESS` as `SYSTEM_ADDRESS` with the 32-byte input of `header.parent_beacon_block_root`, a gas limit of `30_000_000`, and `0` value. This will trigger the `set()` routine of the beacon roots contract. This is a system operation and therefore: + +* the call must execute to completion +* the call does not count against the block's gas limit +* the call does not follow the [EIP-1559](./eip-1559.md) burn semantics - no value should be transferred as part of the call +* if no code exists at `BEACON_ROOTS_ADDRESS`, the call must fail silently + +Clients may decide to omit an explicit EVM call and directly set the storage values. Note: While this is a valid optimization for Ethereum mainnet, it could be problematic on non-mainnet situations in case a different contract is used. + +If this EIP is active in a genesis block, the genesis header's `parent_beacon_block_root` must be `0x0` and no system transaction may occur. + +## Rationale + +### Why not repurpose `BLOCKHASH`? + +The `BLOCKHASH` opcode could be repurposed to provide the beacon root instead of some execution block hash. +To minimize code change, avoid breaking changes to smart contracts, and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding new +functionality with the desired semantics. + +### Beacon block root instead of state root + +Block roots are preferred over state roots so there is a constant amount of work to do with each new execution block. Otherwise, skipped slots would require +a linear amount of work with each new payload. While skipped slots are quite rare on mainnet, it is best to not add additional load under what would already +be nonfavorable conditions. + +Use of block root over state root does mean proofs will require a few additional nodes but this cost is negligible (and could be amortized across all consumers, +e.g. with a singleton state root contract that caches the proof per slot). + +### Why two ring buffers? + +The first ring buffer only tracks `HISTORY_BUFFER_LENGTH` worth of roots and so for all possible timestamp values would consume a constant amount of storage. +However, this design opens the contract to an attack where a skipped slot that has the same value modulo the ring buffer length would return an old root value, +rather than the most recent one. + +To nullify this attack while retaining a fixed memory footprint, this EIP keeps track of the pair of data `(parent_beacon_block_root, timestamp)` for each index into the +ring buffer and verifies the timestamp matches the one originally used to write the root data when being read. Given the fixed size of storage slots (only 32 bytes), the requirement +to store a pair of values necessitates two ring buffers, rather than just one. + +### Size of ring buffers + +The ring buffer data structures are sized to hold 8192 roots from the consensus layer at current slot timings (`SECONDS_PER_SLOT` is 12 seconds on mainnet, and `8192 * 12 == 98304`). +At mainnet values, 8192 roots provides about a day of coverage of the chain which gives users plenty of time to make a transaction with a verification against a given root in +the chain and get the transaction included. + +## Backwards Compatibility + +No issues. + +## Test Cases + +TODO + +## Reference Implementation + +TODO + +## Security Considerations + +TODO + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4799.md b/EIPS/eip-4799.md new file mode 100644 index 00000000000000..922d6828122fb0 --- /dev/null +++ b/EIPS/eip-4799.md @@ -0,0 +1,200 @@ +--- +eip: 4799 +title: Non-Fungible Token Ownership Designation Standard +description: A standardized interface for designating ownership of an NFT +author: David Buckman (@davidbuckman), Isaac Buckman (@isaacbuckman) +discussions-to: https://ethereum-magicians.org/t/erc-4799-non-fungible-token-wrapping-standard/8396 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-02-13 +requires: 165 +--- + +## Abstract + +The following defines a standard interface for designating ownership of an NFT to someone while the NFT is held in escrow by a smart contract. The standard allows for the construction of a directed acyclic graph of NFTs, where the designated owner of every NFT in a given chain is the terminal address of that chain. This enables the introduction of additional functionality to pre-existing NFTs, without having to give up the authenticity of the original. In effect, this means that all NFTs are composable and can be rented, used as collateral, fractionalized, and more. + +## Motivation + +Many NFTs aim to provide their holders with some utility - utility that can come in many forms. This can be the right to inhabit an apartment, access to tickets to an event, an airdrop of tokens, or one of the infinitely many other potential applications. However, in their current form, NFTs are limited by the fact that the only verifiable wallet associated with an NFT is the owner, so clients that want to distribute utility are forced to do so to an NFT's listed owner. This means that any complex ownership agreements must be encoded into the original NFT contract - there is no mechanism by which an owner can link the authenticity of their original NFT to any external contract. + +The goal of this standard is to allow users and developers the ability to define arbitrarily complex ownership agreements on NFTs that have already been minted. This way, new contracts with innovative ownership structures can be deployed, but they can still leverage the authenticity afforded by established NFT contracts - in the past a wrapping contract meant brand new NFTs with no established authenticity. + +Prior to this standard, wrapping an NFT inside another contract was the only way to add functionality after the NFT contract had been deployed, but this meant losing access to the utility of holding the original NFT. Any application querying for the owner of that NFT would determine the wrapping smart contract to be the owner. Using this standard, applications will have a standardized method of interacting with wrapping contracts so that they can continue to direct their utility to users even when the NFT has been wrapped. + +## 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 +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +interface IERC4799NFT is IERC165 { + /// @dev This emits when ownership of any NFT changes by any mechanism. + /// This event emits when NFTs are created (`from` == 0) and destroyed + /// (`to` == 0). Exception: during contract creation, any number of NFTs + /// may be created and assigned without emitting Transfer. At the time of + /// any transfer, the approved address for that NFT (if any) is reset to none. + event Transfer( + address indexed from, + address indexed to, + uint256 indexed tokenId + ); + + /// @notice Find the owner of an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries + /// about them throw + /// @param tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 tokenId) external view returns (address); +} +``` +```solidity +/// @title ERC-4799 Non-Fungible Token Ownership Designation Standard +/// @dev See https://eips.ethereum.org/EIPS/eip-4799 +/// Note: the ERC-165 identifier for this interface is [TODO]. + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "./IERC4799NFT.sol"; + +interface IERC4799 is IERC165 { + /// @dev Emitted when a source token designates its ownership to the owner of the target token + event OwnershipDesignation( + IERC4799NFT indexed sourceContract, + uint256 sourceTokenId, + IERC4799NFT indexed targetContract, + uint256 targetTokenId + ); + + /// @notice Find the designated NFT + /// @param sourceContract The contract address of the source NFT + /// @param sourceTokenId The tokenId of the source NFT + /// @return (targetContract, targetTokenId) contract address and tokenId of the parent NFT + function designatedTokenOf(IERC4799NFT sourceContract, uint256 sourceTokenId) + external + view + returns (IERC4799NFT, uint256); +} +``` + +The authenticity of designated ownership of an NFT is conferred by the designating ERC-4799 contract’s ownership of the original NFT according to the source contract. This MUST be verified by clients by querying the source contract. + +Clients respecting this specification SHALL NOT distribute any utility to the address of the ERC-4799 contract. Instead, they MUST distribute it to the owner of the designated token that the ERC-4799 contract points them to. + +## Rationale + +To maximize the future compatibility of the wrapping contract, we first defined a canonical NFT interface. We created `IERC4799NFT`, an interface implicitly implemented by virtually all popular NFT contracts, including all deployed contracts that are [ERC-721](./eip-721.md) compliant. This interface represents the essence of an NFT: a mapping from a token identifier to the address of a singular owner, represented by the function `ownerOf`. + +The core of our proposal is the `IERC4799` interface, an interface for a standard NFT ownership designation contract (ODC). ERC4799 requires the implementation of a `designatedTokenOf` function, which maps a source NFT to exactly one target NFT. Through this function, the ODC expresses its belief of designated ownership. This designated ownership is only authentic if the ODC is listed as the owner of the original NFT, thus maintaining the invariant that every NFT has exactly one designated owner. + +## Backwards Compatibility + +The `IERC4799NFT` interface is backwards compatible with `IERC721`, as `IERC721` implicitly extends `IERC4799NFT`. This means that the ERC-4799 standard, which wraps NFTs that implement `ERC4799NFT`, is fully backwards compatible with ERC-721. + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0 <0.9.0; + +import "./IERC4799.sol"; +import "./IERC4799NFT.sol"; +import "./ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; + +contract ERC721Composable is IERC4799, IERC721Receiver { + mapping(IERC4799NFT => mapping(uint256 => IERC4799NFT)) private _targetContracts; + mapping(IERC4799NFT => mapping(uint256 => uint256)) private _targetTokenIds; + + function designatedTokenOf(IERC4799NFT sourceContract, uint256 sourceTokenId) + external + view + override + returns (IERC4799NFT, uint256) + { + return ( + IERC4799NFT(_targetContracts[sourceContract][sourceTokenId]), + _targetTokenIds[sourceContract][sourceTokenId] + ); + } + + function designateToken( + IERC4799NFT sourceContract, + uint256 sourceTokenId, + IERC4799NFT targetContract, + uint256 targetTokenId + ) external { + require( + ERC721(address(sourceContract)).ownerOf(sourceTokenId) == msg.sender || + ERC721(address(sourceContract)).getApproved(sourceTokenId) == msg.sender, + "ERC721Composable: Only owner or approved address can set a designate ownership"); + _targetContracts[sourceContract][sourceTokenId] = targetContract; + _targetTokenIds[sourceContract][sourceTokenId] = targetTokenId; + emit OwnershipDesignation( + sourceContract, + sourceTokenId, + targetContract, + targetTokenId + ); + } + + function onERC721Received( + address, + address from, + uint256 sourceTokenId, + bytes calldata + ) external override returns (bytes4) { + ERC721(msg.sender).approve(from, sourceTokenId); + return IERC721Receiver.onERC721Received.selector; + } + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override + returns (bool) + { + return + (interfaceId == type(IERC4799).interfaceId || + interfaceId == type(IERC721Receiver).interfaceId); + } +} +``` +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0 <0.9.0; + +import "./IERC4799.sol"; +import "./IERC4799NFT.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + +contract DesignatedOwner { + function designatedOwnerOf( + IERC4799NFT tokenContract, + uint256 tokenId, + uint256 maxDepth + ) public view returns (address owner) { + owner = tokenContract.ownerOf(tokenId); + if (ERC165Checker.supportsInterface(owner, type(IERC4799).interfaceId)) { + require(maxDepth > 0, "designatedOwnerOf: depth limit exceeded"); + (tokenContract, tokenId) = IERC4799(owner).designatedTokenOf( + tokenContract, + tokenId + ); + return designatedOwnerOf(tokenContract, tokenId, maxDepth - 1); + } + } +} +``` + +## Security Considerations + +### Long/Cyclical Chains of Ownership + +The primary security concern is that of malicious actors creating excessively long or cyclical chains of ownership, leading applications that attempt to query for the designated owner of a given token to run out of gas and be unable to function. To address this, clients are expected to always query considering a `maxDepth` parameter, cutting off computation after a certain number of chain traversals. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4803.md b/EIPS/eip-4803.md new file mode 100644 index 00000000000000..f5f81a81ad4018 --- /dev/null +++ b/EIPS/eip-4803.md @@ -0,0 +1,51 @@ +--- +eip: 4803 +title: Limit transaction gas to a maximum of 2^63-1 +description: Valid transactions must have a reasonable gas limit +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-4803-limit-transaction-gas-to-a-maximum-of-2-63-1/8296 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-02 +--- + +## Abstract + +Limit transaction gas to be between `0` and `2^63-1`. + +## Motivation + +The gas limit field in the transaction is specified to be an arbitrary long unsigned integer, but various clients put limits on this value. This EIP brings a reasonable limit into consensus. + +## Specification + +Introduce one new restrictions retroactively from genesis: any transaction is invalid and not includeable in a block, where the gas limit exceeds `2^63-1`. + +## Rationale + +### `2^63-1` vs `2^64-1` + +`2^63-1` is chosen because it allows representing the gas value as a signed integer, and so the out of gas check can be done as a simple "less than zero" check after subtraction. + +### Current limit + +Due to the nature of RLP encoding, there is no fixed upper bound for the value, but most implementations limit it to 256-bits. Furthermore, most client implementations (such as geth) internally handle gas as a 64-bit value. + +## Backwards Compatibility + +While this is a breaking change, no actual effect should be visible. + +Before [EIP-1559](./eip-1559.md) it was possible to include transactions with `gasPrice = 0` and thus the `gasLimit * gasPrice <= accountBalance` calculation could have allowed for arbitrarily large values of `gasLimit`. However, the rule that the transaction list cannot exceed the block gas limit, and the strict rules about how the block gas limit can change, prevented arbitrarily large values of `gasLimit` to be in the historical state. + +## Security Considerations + +None. + +## Test Cases + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4804.md b/EIPS/eip-4804.md new file mode 100644 index 00000000000000..4a86e99ee83a78 --- /dev/null +++ b/EIPS/eip-4804.md @@ -0,0 +1,147 @@ +--- +eip: 4804 +title: Web3 URL to EVM Call Message Translation +description: A translation of an HTTP-style Web3 URL to an EVM call message +author: Qi Zhou (@qizhou), Chao Pi (@pichaoqkc), Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-4804-web3-url-to-evm-call-message-translation/8300 +status: Final +type: Standards Track +category: ERC +created: 2022-02-14 +requires: 137 +--- + +## Abstract + +This standard translates an RFC 2396 URI like `web3://uniswap.eth/` to an EVM message such as: + +``` +EVMMessage { + To: 0xaabbccddee.... // where uniswap.eth's address registered at ENS + Calldata: 0x + ... +} +``` + +## Motivation + +Currently, reading data from Web3 generally relies on a translation done by a Web2 proxy to Web3 blockchain. The translation is mostly done by the proxies such as dApp websites/node service provider/etherscan, which are out of the control of users. The standard here aims to provide a simple way for Web2 users to directly access the content of Web3, especially on-chain Web contents such as SVG/HTML. Moreover, this standard enables interoperability with other standards already compatible with URIs, like SVG/HTML. + +## Specification + +This specification only defines read-only (i.e. Solidity's `view` functions) semantics. State modifying functions may be defined as a future extension. + +A Web3 URL is in the following form + +``` +web3URL = web3Schema [userinfo "@"] contractName [":" chainid] path ["?" query] +web3Schema = [ "ethereum-web3://" | "eth-web3://" | "web3://" ] +contractName = address | [name "." [ subDomain0 "." ... ]] nsProviderSuffix +path = ["/" method ["/" argument_0 ["/" argument_1 ... ]]] +argument = [type "!"] value +query = "attribute_1=value_1 [ "&" attribute_2=value_2 ... ] +attribute = "returns" | "returnTypes" | other_attribute +``` + +where + +- **web3Schema** indicates the schema of the URL, which is `web3://` or `w3://` for short. +- **userinfo** indicates which user is calling the EVM, i.e., "From" field in EVM call message. If not specified, the protocol will use 0x0 as the sender address. +- **contractName** indicates the contract to be called, i.e., "To" field in the EVM call message. If the **contractName** is an **address**, i.e., 0x + 20-byte-data hex, then "To" will be the address. Otherwise, the name is from a name service. In the second case, **nsProviderSuffix** will be the suffix from name service providers such as "eth", etc. The way to translate the name from a name service to an address will be discussed in later EIPs. +- **chainid** indicates which chain to resolve **contractName** and call the message. If not specified, the protocol will use the same chain as the name service provider, e.g., 1 for eth. If no name service provider is available, the default chainid is 1. +- **query** is an optional component containing a sequence of attribute-value pairs separated by "&". + +### Resolve Mode + +Once the "To" address and chainid are determined, the protocol will check the resolver mode of contract by calling "resolveMode" method. The protocol currently supports two resolve modes: + +#### Manual Mode + +The manual mode will not do any interpretation of **path** and **query**, and put **path** [ "?" **query** ] as the calldata of the message directly. + +#### Auto Mode + +The auto mode is the default mode to resolve (also applies when the "resolveMode" method is unavailable in the target contract). In the auto mode, if **path** is empty, then the protocol will call the target contract with empty calldata. Otherwise, the calldata of the EVM message will use standard Solidity contract ABI, where + +- **method** is a string of function method be called +- **argument_i** is the ith argument of the method. If **type** is specified, the value will be translated to the corresponding type. The protocol currently supports the basic types such as uint256, bytes32, address, bytes, and string. If **type** is not specified, then the type will be automatically detected using the following rule in a sequential way: + +1. **type**="uint256", if **value** is numeric; or +2. **type**="bytes32", if **value** is in the form of 0x+32-byte-data hex; or +3. **type**="address", if **value** is in the form of 0x+20-byte-data hex; or +4. **type**="bytes", if **value** is in the form of 0x followed by any number of bytes besides 20 or 32; or +5. else **type**="address" and parse the argument as a domain name in the form of `[name "." [ subDomain0 "." ... ]] nsProviderSuffix`. In this case, the actual value of the argument will be obtained from **nsProviderSuffix**, e.g., eth. If **nsProviderSuffix** is not supported, an unsupported NS provider error will be returned. + +Note that if **method** does not exist, i.e., **path** is empty or "/", then the contract will be called with empty calldata. + +- **returns** attribute in **query** tells the format of the returned data. If not specified, the returned message data will be parsed in "(bytes32)" and MIME will be set based on the suffix of the last argument. If **returns** is "()", the returned data will be parsed in raw bytes in JSON. Otherwise, the returned message will be parsed in the specified **returns** attribute in JSON. If multiple **returns** attributes are present, the value of the last **returns** attribute will be applied. Note that **returnTypes** is the alias of **returns**, but it is not recommended to use and is mainly for backward-compatible purpose. + +### Examples + +#### Example 1 + +``` +web3://w3url.eth/ +``` + +The protocol will find the address of **w3url.eth** from ENS in chainid 1 (Mainnet), and then the protocol will call the address with "From" = "0x..." and "Calldata" = "0x2F". + +#### Example 2 + +``` +web3://cyberbrokers-meta.eth/renderBroker/9999 +``` + +The protocol will find the address of **cyberbrokers-meta.eth** from ENS on chainid 1 (Mainnet), and then call the address with "To" = "0x..." and "Calldata" = "0x" + `keccak("view(uint256)")[0:4] + abi.encode(uint256(9999))`. + +#### Example 3 + +``` +web3://vitalikblog.eth:5/ +``` + +The protocol will find the address of **vitalikblog.eth** from ENS on chainid 5 (Goerli), and then call the address with "From" = "0x..." and "Calldata" = "0x2F" with chainid = 5. + +#### Example 4 + +``` +web3://0xe4ba0e245436b737468c206ab5c8f4950597ab7f:42170/ +``` + +The protocol will call the address with "To" = "0x9e081Df45E0D167636DB9C61C7ce719A58d82E3b" and "Calldata" = "0x" with chainid = 42170 (Arbitrum Nova). + +#### Example 5 + +``` +web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=(uint256) +``` + +The protocol will find the addresses of **vitalik.eth** from ENS on chainid 1 (Mainnet) and then call the method "balanceOf(address)" of the contract with the **charles.eth**'s address. The returned data will be parsed as uint256 like `[ "10000000000000" ]`. + +#### Example 6 + +``` +web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=() +``` + +The protocol will find the address of **vitalik.eth** from ENS on chainid 1 (Mainnet) and then call the method "balanceOf(address)" of the address. The returned data will be parsed as raw bytes like `["0x000000000000000000000000000000000000000000000000000009184e72a000"]`. + +## Rationale + +The purpose of the proposal is to add a decentralized presentation layer for Ethereum. With the layer, we are able to render any web content (including HTML/CSS/JPG/PNG/SVG, etc) on-chain using human-readable URLs, and thus EVM can be served as decentralized Backend. The design of the standard is based on the following principles: + +- **Human-readable**. The Web3 URL should be easily recognized by human similar to Web2 URL (`http://`). As a result, we support names from name services to replace address for better readability. In addition, instead of using calldata in hex, we use human-readable method + arguments and translate them to calldata for better readability. + +- **Maximum-Compatible with HTTP-URL standard**. The Web3 URL should be compatible with HTTP-URL standard including relative pathing, query, fragment, etc so that the support of existing HTTP-URL (e.g., by browser) can be easily extended to Web3 URL with minimal modification. This also means that existing Web2 users can easily migrate to Web3 with minimal extra knowledge of this standard. + +- **Simple**. Instead of providing explicit types in arguments, we use a "maximum likelihood" principle of auto-detecting the types of the arguments such as address, bytes32, and uint256. This could greatly minimize the length of URL, while avoiding confusion. In addition, explicit types are also supported to clear the confusion if necessary. + +- **Flexible**. The contract is able to override the encoding rule so that the contract has fine-control of understanding the actual Web resources that the users want to locate. + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4824.md b/EIPS/eip-4824.md new file mode 100644 index 00000000000000..c9dd0459c40d6b --- /dev/null +++ b/EIPS/eip-4824.md @@ -0,0 +1,473 @@ +--- +eip: 4824 +title: Common Interfaces for DAOs +description: An API for decentralized autonomous organizations (DAOs). +author: Joshua Tan (@thelastjosh), Isaac Patka (@ipatka), Ido Gershtein , Eyal Eithcowich , Michael Zargham (@mzargham), Sam Furter (@nivida) +discussions-to: https://ethereum-magicians.org/t/eip-4824-decentralized-autonomous-organizations/8362 +status: Draft +type: Standards Track +category: ERC +created: 2022-02-17 +--- + +## Abstract + +An API standard for decentralized autonomous organizations (DAOs), focused on relating on-chain and off-chain representations of membership and proposals. + +## Motivation + +DAOs, since being invoked in the Ethereum whitepaper, have been vaguely defined. This has led to a wide range of patterns but little standardization or interoperability between the frameworks and tools that have emerged. Standardization and interoperability are necessary to support a variety of use-cases. In particular, a standard daoURI, similar to tokenURI in [ERC-721](./eip-721), will enhance DAO discoverability, legibility, proposal simulation, and interoperability between tools. More consistent data across the ecosystem is also a prerequisite for future DAO 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. + +Every contract implementing this EIP MUST implement the [ERC-4824](./eip-4824) interface below: + +```solidity +pragma solidity ^0.8.1; + +/// @title ERC-4824 DAOs +/// @dev See +interface IERC-4824 { + event DAOURIUpdate(address daoAddress, string daoURI); + + /// @notice A distinct Uniform Resource Identifier (URI) pointing to a JSON object following the "ERC-4824 DAO JSON-LD Schema". This JSON file splits into four URIs: membersURI, proposalsURI, activityLogURI, and governanceURI. The membersURI should point to a JSON file that conforms to the "ERC-4824 Members JSON-LD Schema". The proposalsURI should point to a JSON file that conforms to the "ERC-4824 Proposals JSON-LD Schema". The activityLogURI should point to a JSON file that conforms to the "ERC-4824 Activity Log JSON-LD Schema". The governanceURI should point to a flatfile, normatively a .md file. Each of the JSON files named above can be statically-hosted or dynamically-generated. + function daoURI() external view returns (string memory _daoURI); +} +``` + +The DAO JSON-LD Schema mentioned above: + +```json +{ + "@context": "http://www.daostar.org/schemas", + "type": "DAO", + "name": "", + "description": "", + "membersURI": "", + "proposalsURI": "", + "activityLogURI": "", + "governanceURI": "", + "contractsURI": "" +} +``` + +A DAO MAY inherit the above interface above or it MAY create an external registration contract that is compliant with this EIP. If a DAO creates an external registration contract, the registration contract MUST store the DAO’s primary address. + +If the DAO inherits the above interface, it SHOULD define a method for updating daoURI. If the DAO uses an external registration contract, the registration contract SHOULD contain some access control logic to enable efficient updating for daoURI. + +```solidity +pragma solidity ^0.8.1; + +/// @title ERC-4824 Common Interfaces for DAOs +/// @dev See +/// @title ERC-4824: DAO Registration +contract ERC-4824Registration is IERC-4824, AccessControl { + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); + + string private _daoURI; + + address daoAddress; + + constructor() { + daoAddress = address(0xdead); + } + + /// @notice Set the initial DAO URI and offer manager role to an address + /// @dev Throws if initialized already + /// @param _daoAddress The primary address for a DAO + /// @param _manager The address of the URI manager + /// @param daoURI_ The URI which will resolve to the governance docs + function initialize( + address _daoAddress, + address _manager, + string memory daoURI_, + address _ERC-4824Index + ) external { + initialize(_daoAddress, daoURI_, _ERC-4824Index); + _grantRole(MANAGER_ROLE, _manager); + } + + /// @notice Set the initial DAO URI + /// @dev Throws if initialized already + /// @param _daoAddress The primary address for a DAO + /// @param daoURI_ The URI which will resolve to the governance docs + function initialize( + address _daoAddress, + string memory daoURI_, + address _ERC-4824Index + ) public { + if (daoAddress != address(0)) revert AlreadyInitialized(); + daoAddress = _daoAddress; + _setURI(daoURI_); + + _grantRole(DEFAULT_ADMIN_ROLE, _daoAddress); + _grantRole(MANAGER_ROLE, _daoAddress); + + ERC-4824Index(_ERC-4824Index).logRegistration(address(this)); + } + + /// @notice Update the URI for a DAO + /// @dev Throws if not called by dao or manager + /// @param daoURI_ The URI which will resolve to the governance docs + function setURI(string memory daoURI_) public onlyRole(MANAGER_ROLE) { + _setURI(daoURI_); + } + + function _setURI(string memory daoURI_) internal { + _daoURI = daoURI_; + emit DAOURIUpdate(daoAddress, daoURI_); + } + + function daoURI() external view returns (string memory daoURI_) { + return _daoURI; + } + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override returns (bool) { + return + interfaceId == type(IERC-4824).interfaceId || + super.supportsInterface(interfaceId); + } +} +``` + +### Indexing + +If a DAO inherits the ERC-4824 interface from a 4824-compliant DAO factory, then the DAO factory SHOULD incorporate a call to an indexer contract as part of the DAO's initialization to enable efficient network indexing. If the DAO is [ERC-165](./eip-165)-compliant, the factory can do this without additional permissions. If the DAO is _not_ compliant with ERC-165, the factory SHOULD first obtain access control rights to the indexer contract and then call logRegistration directly with the address of the new DAO and the daoURI of the new DAO. Note that any user, including the DAO itself, MAY call logRegistration and submit a registration for a DAO which inherits the ERC-4824 interface and which is also ERC-165-compliant. + +```solidity +pragma solidity ^0.8.1; + +error ERC-4824InterfaceNotSupported(); + +contract ERC-4824Index is AccessControl { + using ERC165Checker for address; + + bytes32 public constant REGISTRATION_ROLE = keccak256("REGISTRATION_ROLE"); + + event DAOURIRegistered(address daoAddress); + + constructor() { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(REGISTRATION_ROLE, msg.sender); + } + + function logRegistrationPermissioned( + address daoAddress + ) external onlyRole(REGISTRATION_ROLE) { + emit DAOURIRegistered(daoAddress); + } + + function logRegistration(address daoAddress) external { + if (!daoAddress.supportsInterface(type(IERC-4824).interfaceId)) + revert ERC-4824InterfaceNotSupported(); + emit DAOURIRegistered(daoAddress); + } +} +``` + +If a DAO uses an external registration contract, the DAO SHOULD use a common registration factory contract linked to a common indexer to enable efficient network indexing. + +```solidity +pragma solidity ^0.8.1; + +/// @title ERC-4824 Common Interfaces for DAOs +/// @dev See + +contract CloneFactory { + // implementation of eip-1167 - see https://eips.ethereum.org/EIPS/eip-1167 + function createClone(address target) internal returns (address result) { + bytes20 targetBytes = bytes20(target); + assembly { + let clone := mload(0x40) + mstore( + clone, + 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 + ) + mstore(add(clone, 0x14), targetBytes) + mstore( + add(clone, 0x28), + 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 + ) + result := create(0, clone, 0x37) + } + } +} + +contract ERC-4824RegistrationSummoner { + event NewRegistration( + address indexed daoAddress, + string daoURI, + address registration + ); + + address public ERC-4824Index; + address public template; /*Template contract to clone*/ + + constructor(address _template, address _ERC-4824Index) { + template = _template; + ERC-4824Index = _ERC-4824Index; + } + + function registrationAddress( + address by, + bytes32 salt + ) external view returns (address addr, bool exists) { + addr = Clones.predictDeterministicAddress( + template, + _saltedSalt(by, salt), + address(this) + ); + exists = addr.code.length > 0; + } + + function summonRegistration( + bytes32 salt, + string calldata daoURI_, + address manager, + address[] calldata contracts, + bytes[] calldata data + ) external returns (address registration, bytes[] memory results) { + registration = Clones.cloneDeterministic( + template, + _saltedSalt(msg.sender, salt) + ); + + if (manager == address(0)) { + ERC-4824Registration(registration).initialize( + msg.sender, + daoURI_, + ERC-4824Index + ); + } else { + ERC-4824Registration(registration).initialize( + msg.sender, + manager, + daoURI_, + ERC-4824Index + ); + } + + results = _callContracts(contracts, data); + + emit NewRegistration(msg.sender, daoURI_, registration); + } +``` + +### Members + +Members JSON-LD Schema. Every contract implementing this EIP SHOULD implement a membersuRI pointing to a JSON object satisfying this schema. + +```json +{ + "@context": "", + "type": "DAO", + "name": "", + "members": [ + { + "type": "EthereumAddress", + "id": "
" + }, + { + "type": "EthereumAddress", + "id": "
" + } + ] +} +``` + +### Proposals + +Proposals JSON-LD Schema. Every contract implementing this EIP SHOULD implement a proposalsURI pointing to a JSON object satisfying this schema. + +In particular, any on-chain proposal MUST be associated to an id of the form CAIP10_ADDRESS + “?proposalId=” + PROPOSAL_COUNTER, where CAIP10_ADDRESS is an address following the CAIP-10 standard and PROPOSAL_COUNTER is an arbitrary identifier such as a uint256 counter or a hash that is locally unique per CAIP-10 address. Off-chain proposals MAY use a similar id format where CAIP10_ADDRESS is replaced with an appropriate URI or URL. + +```json +{ + "@context": "http://www.daostar.org/schemas", + "type": "DAO", + "name": "", + "proposals": [ + { + "type": "proposal", + "id": "", + "name": "", + "contentURI": "", + "status": "", + "calls": [ + { + "type": "CallDataEVM", + "operation": "", + "from": "", + "to": "", + "value": "", + "data": "" + } + ] + } + ] +} +``` + +### Activity Log + +Activity Log JSON-LD Schema. Every contract implementing this EIP SHOULD implement a activityLogURI pointing to a JSON object satisfying this schema. + +```json +{ + "@context": "", + "type": "DAO", + "name": "", + "activities": [ + { + "id": "", + "type": "activity", + "proposal": { + "type": "proposal" + "id": "", + }, + "member": { + "type": "EthereumAddress", + "id": "
" + } + }, + ], + "activities": [ + { + "id": "", + "type": "activity", + "proposal": { + "type": "proposal" + "id": "", + }, + "member": { + "type": "EthereumAddress", + "id": "
" + } + } + ] +} +``` + +### Contracts + +Contracts JSON-LD Schema. Every contract implementing this EIP SHOULD implement a contractsURI pointing to a JSON object satisfying this schema. + +Further, every contractsURI SHOULD include at least the contract inheriting the ERC-4824 interface. + +``` +{ + "@context": "", + "type": "DAO", + "name": "", + "contracts": [ + { + "type": "EthereumAddress", + "id": "
", + "name": "", + "description": "" + }, + { + "type": "EthereumAddress", + "id": "
", + "name": "", + "description": "" + } + { + "type": "EthereumAddress", + "id": "
", + "name": "", + "description": "" + } + ] +} +``` + +## Rationale + +In this standard, we assume that all DAOs possess at least two primitives: _membership_ and _behavior_. _Membership_ is defined by a set of addresses. _Behavior_ is defined by a set of possible contract actions, including calls to external contracts and calls to internal functions. _Proposals_ relate membership and behavior; they are objects that members can interact with and which, if and when executed, become behaviors of the DAO. + +### APIs, URIs, and off-chain data + +DAOs themselves have a number of existing and emerging use-cases. But almost all DAOs need to publish data off-chain for a number of reasons: communicating to and recruiting members, coordinating activities, powering user interfaces and governance applications such as Snapshot or Tally, or enabling search and discovery via platforms like DeepDAO, Messari, and Etherscan. Having a standardized schema for this data organized across multiple URIs, i.e. an API specification, would strengthen existing use-cases for DAOs, help scale tooling and frameworks across the ecosystem, and build support for additional forms of interoperability. + +While we considered standardizing on-chain aspects of DAOs in this standard, particularly on-chain proposal objects and proposal IDs, we felt that this level of standardization was premature given (1) the relative immaturity of use-cases, such as multi-DAO proposals or master-minion contracts, that would benefit from such standardization, (2) the close linkage between proposal systems and governance, which we did not want to standardize (see “governanceURI”, below), and (3) the prevalence of off-chain and L2 voting and proposal systems in DAOs (see “proposalsURI”, below). Further, a standard URI interface is relatively easy to adopt and has been actively demanded by frameworks (see “Community Consensus”, below). + +### membersURI + +Approaches to membership vary widely in DAOs. Some DAOs and DAO frameworks (e.g. Gnosis Safe, Tribute), maintain an explicit, on-chain set of members, sometimes called owners or stewards. But many DAOs are structured so that membership status is based on the ownership of a token or tokens (e.g. Moloch, Compound, DAOstack, 1Hive Gardens). In these DAOs, computing the list of current members typically requires some form of off-chain indexing of events. + +In choosing to ask only for an (off-chain) JSON schema of members, we are trading off some on-chain functionality for more flexibility and efficiency. We expect different DAOs to use membersURI in different ways: to serve a static copy of on-chain membership data, to contextualize the on-chain data (e.g. many Gnosis Safe stewards would not say that they are the only members of the DAO), to serve consistent membership for a DAO composed of multiple contracts, or to point at an external service that computes the list, among many other possibilities. We also expect many DAO frameworks to offer a standard endpoint that computes this JSON file, and we provide a few examples of such endpoints in the implementation section. + +We encourage extensions of the Membership JSON-LD Schema, e.g. for DAOs that wish to create a state variable that captures active/inactive status or different membership levels. + +### proposalsURI + +Proposals have become a standard way for the members of a DAO to trigger on-chain actions, e.g. sending out tokens as part of grant or executing arbitrary code in an external contract. In practice, however, many DAOs are governed by off-chain decision-making systems on platforms such as Discourse, Discord, or Snapshot, where off-chain proposals may function as signaling mechanisms for an administrator or as a prerequisite for a later on-chain vote. (To be clear, on-chain votes may also serve as non-binding signaling mechanisms or as “binding” signals leading to some sort of off-chain execution.) The schema we propose is intended to support both on-chain and off-chain proposals, though DAOs themselves may choose to report only on-chain, only off-chain, or some custom mix of proposal types. + +**Proposal ID**. Every unique on-chain proposal MUST be associated to a proposal ID of the form CAIP10_ADDRESS + “?proposalId=” + PROPOSAL_COUNTER, where PROPOSAL_COUNTER is an arbitrary string which is unique per CAIP10_ADDRESS. Note that PROPOSAL_COUNTER may not be the same as the on-chain representation of the proposal; however, each PROPOSAL_COUNTER should be unique per CAIP10_ADDRESS, such that the proposal ID is a globally unique identifier. We endorse the CAIP-10 standard to support multi-chain / layer 2 proposals and the “?proposalId=” query syntax to suggest off-chain usage. + +**ContentURI**. In many cases, a proposal will have some (off-chain) content such as a forum post or a description on a voting platform which predates or accompanies the actual proposal. + +**Status**. Almost all proposals have a status or state, but the actual status is tied to the governance system, and there is no clear consensus between existing DAOs about what those statuses should be (see table below). Therefore, we have defined a “status” property with a generic, free text description field. + +| Project | Proposal Statuses | +| --- | --- | +| Aragon | Not specified | +| Colony | [‘Null’, ‘Staking’, ‘Submit’, ‘Reveal’, ‘Closed’, ‘Finalizable’, ‘Finalized’, ‘Failed’] | +| Compound | [‘Pending’, ‘Active’, ‘Canceled’, ‘Defeated’, ‘Succeeded’, ‘Queued’, ‘Expired’, ‘Executed’] | +| DAOstack/ Alchemy | [‘None’, ‘ExpiredInQueue’, ‘Executed’, ‘Queued’, ‘PreBoosted’, ‘Boosted’, ‘QuietEndingPeriod’] | +| Moloch v2 | [sponsored, processed, didPass, cancelled, whitelist, guildkick] | +| Tribute | [‘EXISTS’, ‘SPONSORED’, ‘PROCESSED’] | + +**ExecutionData**. For on-chain proposals with non-empty execution, we include an array field to expose the call data. The main use-case for this data is execution simulation of proposals. + +### activityLogURI + +The activity log JSON is intended to capture the interplay between a member of a DAO and a given proposal. Examples of activities include the creation/submission of a proposal, voting on a proposal, disputing a proposal, and so on. + +_Alternatives we considered: history, interactions_ + +### governanceURI + +Membership, to be meaningful, usually implies rights and affordances of some sort, e.g. the right to vote on proposals, the right to ragequit, the right to veto proposals, and so on. But many rights and affordances of membership are realized off-chain (e.g. right to vote on a Snapshot, gated access to a Discord). Instead of trying to standardize these wide-ranging practices or forcing DAOs to locate descriptions of those rights on-chain, we believe that a flatfile represents the easiest and most widely-acceptable mechanism for communicating what membership means and how proposals work. These flatfiles can then be consumed by services such as Etherscan, supporting DAO discoverability and legibility. + +We chose the word “governance” as an appropriate word that reflects (1) the widespread use of the word in the DAO ecosystem and (2) the common practice of emitting a governance.md file in open-source software projects. + +_Alternative names considered: description, readme, constitution_ + +### contractsURI + +Over the course of community conversations, multiple parties raised the need to report on, audit, and index the different contracts belonging to a given DAO. Some of these contracts are deployed as part of the modular design of a single DAO framework, e.g. the core, voting, and timelock contracts within Open Zeppelin / Compound Governor. In other cases, a DAO might deploy multiple multsigs as treasuries and/or multiple subDAOs that are effectively controlled by the DAO. ContractsURI offers a generic way of declaring these many instruments. + +_Alternative names considered: contractsRegistry, contractsList_ + +### Why JSON-LD + +We chose to use JSON-LD rather than the more widespread and simpler JSON standard because (1) we want to support use-cases where a DAO wants to include members using some other form of identification than their Ethereum address and (2) we want this standard to be compatible with future multi-chain standards. Either use-case would require us to implement a context and type for addresses, which is already implemented in JSON-LD. + +Further, given the emergence of patterns such as subDAOs and DAOs of DAOs in large organizations such as Synthetix, as well as L2 and multi-chain use-cases, we expect some organizations will point multiple DAOs to the same URI, which would then serve as a gateway to data from multiple contracts and services. The choice of JSON-LD allows for easier extension and management of that data. + +### **Community Consensus** + +The initial draft standard was developed as part of the DAOstar One roundtable series, which included representatives from all major EVM-based DAO frameworks (Aragon, Compound, DAOstack, Gnosis, Moloch, OpenZeppelin, and Tribute), a wide selection of DAO tooling developers, as well as several major DAOs. Thank you to all the participants of the roundtable. We would especially like to thank Auryn Macmillan, Fabien of Snapshot, Selim Imoberdorf, Lucia Korpas, and Mehdi Salehi for their contributions. + +In-person events will be held at Schelling Point 2022 and at ETHDenver 2022, where we hope to receive more comments from the community. We also plan to schedule a series of community calls through early 2022. + +## Backwards Compatibility + +Existing contracts that do not wish to use this specification are unaffected. DAOs that wish to adopt the standard without updating or migrating contracts can do so via an external registration contract. + +## Security Considerations + +This standard defines the interfaces for the DAO URIs but does not specify the rules under which the URIs are set, or how the data is prepared. Developers implementing this standard should consider how to update this data in a way aligned with the DAO’s governance model, and keep the data fresh in a way that minimizes reliance on centralized service providers. + +Indexers that rely on the data returned by the URI should take caution if DAOs return executable code from the URIs. This executable code might be intended to get the freshest information on membership, proposals, and activity log, but it could also be used to run unrelated tasks. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4834.md b/EIPS/eip-4834.md new file mode 100644 index 00000000000000..2f86485726c0bd --- /dev/null +++ b/EIPS/eip-4834.md @@ -0,0 +1,229 @@ +--- +eip: 4834 +title: Hierarchical Domains +description: Extremely generic name resolution +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/erc-4834-hierarchical-domains-standard/8388 +status: Final +type: Standards Track +category: ERC +created: 2022-02-22 +--- + +## Abstract + +This is a standard for generic name resolution with arbitrarily complex access control and resolution. It permits a contract that implements this EIP (referred to as a "domain" hereafter) to be addressable with a more human-friendly name, with a similar purpose to [ERC-137](./eip-137.md) (also known as "ENS"). + +## Motivation + +The advantage of this EIP over existing standards is that it provides a minimal interface that supports name resolution, adds standardized access control, and has a simple architecture. ENS, although useful, has a comparatively complex architecture and does not have standard access control. + +In addition, all domains (including subdomains, TLDs, and even the root itself) are actually implemented as domains, meaning that name resolution is a simple iterative algorithm, not unlike DNS itself. + +## 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. + +### Contract Interface + +```solidity +interface IDomain { + /// @notice Query if a domain has a subdomain with a given name + /// @param name The subdomain to query, in right to left order + /// @return `true` if the domain has a subdomain with the given name, `false` otherwise + function hasDomain(string[] memory name) external view returns (bool); + + /// @notice Fetch the subdomain with a given name + /// @dev This should revert if `hasDomain(name)` is `false` + /// @param name The subdomain to fetch, in right to left order + /// @return The subdomain with the given name + function getDomain(string[] memory name) external view returns (address); +} +``` + +### Name Resolution + +To resolve a name (like `"a.b.c"`), split it by the delimiter (resulting in something like `["a", "b", "c"]`). Set `domain` initially to the root domain, and `path` to be an empty list. + +Pop off the last element of the array (`"c"`) and add it to the path, then call `domain.hasDomain(path)`. If it's `false`, then the domain resolution fails. Otherwise, set the domain to `domain.getDomain(path)`. Repeat until the list of split segments is empty. + +There is no limit to the amount of nesting that is possible. For example, `0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z` would be valid if the root contains `z`, and `z` contains `y`, and so on. + +Here is a solidity function that resolves a name: + +```solidity +function resolve(string[] calldata splitName, IDomain root) public view returns (address) { + IDomain current = root; + string[] memory path = []; + for (uint i = splitName.length - 1; i >= 0; i--) { + // Append to back of list + path.push(splitName[i]); + // Require that the current domain has a domain + require(current.hasDomain(path), "Name resolution failed"); + // Resolve subdomain + current = current.getDomain(path); + } + return current; +} +``` + +### Optional Extension: Registerable + +```solidity +interface IDomainRegisterable is IDomain { + //// Events + + /// @notice Must be emitted when a new subdomain is created (e.g. through `createDomain`) + /// @param sender msg.sender for createDomain + /// @param name name for createDomain + /// @param subdomain subdomain in createDomain + event SubdomainCreate(address indexed sender, string name, address subdomain); + + /// @notice Must be emitted when the resolved address for a domain is changed (e.g. with `setDomain`) + /// @param sender msg.sender for setDomain + /// @param name name for setDomain + /// @param subdomain subdomain in setDomain + /// @param oldSubdomain the old subdomain + event SubdomainUpdate(address indexed sender, string name, address subdomain, address oldSubdomain); + + /// @notice Must be emitted when a domain is unmapped (e.g. with `deleteDomain`) + /// @param sender msg.sender for deleteDomain + /// @param name name for deleteDomain + /// @param subdomain the old subdomain + event SubdomainDelete(address indexed sender, string name, address subdomain); + + //// CRUD + + /// @notice Create a subdomain with a given name + /// @dev This should revert if `canCreateDomain(msg.sender, name, pointer)` is `false` or if the domain exists + /// @param name The subdomain name to be created + /// @param subdomain The subdomain to create + function createDomain(string memory name, address subdomain) external payable; + + /// @notice Update a subdomain with a given name + /// @dev This should revert if `canSetDomain(msg.sender, name, pointer)` is `false` of if the domain doesn't exist + /// @param name The subdomain name to be updated + /// @param subdomain The subdomain to set + function setDomain(string memory name, address subdomain) external; + + /// @notice Delete the subdomain with a given name + /// @dev This should revert if the domain doesn't exist or if `canDeleteDomain(msg.sender, name)` is `false` + /// @param name The subdomain to delete + function deleteDomain(string memory name) external; + + + //// Parent Domain Access Control + + /// @notice Get if an account can create a subdomain with a given name + /// @dev This must return `false` if `hasDomain(name)` is `true`. + /// @param updater The account that may or may not be able to create/update a subdomain + /// @param name The subdomain name that would be created/updated + /// @param subdomain The subdomain that would be set + /// @return Whether an account can update or create the subdomain + function canCreateDomain(address updater, string memory name, address subdomain) external view returns (bool); + + /// @notice Get if an account can update or create a subdomain with a given name + /// @dev This must return `false` if `hasDomain(name)` is `false`. + /// If `getDomain(name)` is also a domain implementing the subdomain access control extension, this should return `false` if `getDomain(name).canMoveSubdomain(msg.sender, this, subdomain)` is `false`. + /// @param updater The account that may or may not be able to create/update a subdomain + /// @param name The subdomain name that would be created/updated + /// @param subdomain The subdomain that would be set + /// @return Whether an account can update or create the subdomain + function canSetDomain(address updater, string memory name, address subdomain) external view returns (bool); + + /// @notice Get if an account can delete the subdomain with a given name + /// @dev This must return `false` if `hasDomain(name)` is `false`. + /// If `getDomain(name)` is a domain implementing the subdomain access control extension, this should return `false` if `getDomain(name).canDeleteSubdomain(msg.sender, this, subdomain)` is `false`. + /// @param updater The account that may or may not be able to delete a subdomain + /// @param name The subdomain to delete + /// @return Whether an account can delete the subdomain + function canDeleteDomain(address updater, string memory name) external view returns (bool); +} +``` + +### Optional Extension: Enumerable + +```solidity +interface IDomainEnumerable is IDomain { + /// @notice Query all subdomains. Must revert if the number of domains is unknown or infinite. + /// @return The subdomain with the given index. + function subdomainByIndex(uint256 index) external view returns (string memory); + + /// @notice Get the total number of subdomains. Must revert if the number of domains is unknown or infinite. + /// @return The total number of subdomains. + function totalSubdomains() external view returns (uint256); +} +``` + +### Optional Extension: Access Control + +```solidity +interface IDomainAccessControl is IDomain { + /// @notice Get if an account can move the subdomain away from the current domain + /// @dev May be called by `canSetDomain` of the parent domain - implement access control here!!! + /// @param updater The account that may be moving the subdomain + /// @param name The subdomain name + /// @param parent The parent domain + /// @param newSubdomain The domain that will be set next + /// @return Whether an account can update the subdomain + function canMoveSubdomain(address updater, string memory name, IDomain parent, address newSubdomain) external view returns (bool); + + /// @notice Get if an account can unset this domain as a subdomain + /// @dev May be called by `canDeleteDomain` of the parent domain - implement access control here!!! + /// @param updater The account that may or may not be able to delete a subdomain + /// @param name The subdomain to delete + /// @param parent The parent domain + /// @return Whether an account can delete the subdomain + function canDeleteSubdomain(address updater, string memory name, IDomain parent) external view returns (bool); +} +``` + +## Rationale + +This EIP's goal, as mentioned in the abstract, is to have a simple interface for resolving names. Here are a few design decisions and why they were made: + +- Name resolution algorithm + - Unlike ENS's resolution algorithm, this EIP's name resolution is fully under the control of the contracts along the resolution path. + - This behavior is more intuitive to users. + - This behavior allows for greater flexibility - e.g. a contract that changes what it resolves to based on the time of day. +- Parent domain access control + - A simple "ownable" interface was not used because this specification was designed to be as generic as possible. If an ownable implementation is desired, it can be implemented. + - This also gives parent domains the ability to call subdomains' access control methods so that subdomains, too, can choose whatever access control mechanism they desire +- Subdomain access control + - These methods are included so that subdomains aren't always limited to their parent domain's access control + - The root domain can be controlled by a DAO with a non-transferable token with equal shares, a TLD can be controlled by a DAO with a token representing stake, a domain of that TLD can be controlled by a single owner, a subdomain of that domain can be controlled by a single owner linked to an NFT, and so on. + - Subdomain access control functions are suggestions: an ownable domain might implement an owner override, so that perhaps subdomains might be recovered if the keys are lost. + +## Backwards Compatibility + +This EIP is general enough to support ENS, but ENS is not general enough to support this EIP. + +## Security Considerations + +### Malicious canMoveSubdomain (Black Hole) + +#### Description: Malicious `canMoveSubdomain` + +Moving a subdomain using `setDomain` is a potentially dangerous operation. + +Depending on the parent domain's implementation, if a malicious new subdomain unexpectedly returns `false` on `canMoveSubdomain`, that subdomain can effectively lock the ownership of the domain. + +Alternatively, it might return `true` when it isn't expected (i.e. a backdoor), allowing the contract owner to take over the domain. + +#### Mitigation: Malicious `canMoveSubdomain` + +Clients should help by warning if `canMoveSubdomain` or `canDeleteSubdomain` for the new subdomain changes to `false`. It is important to note, however, that since these are functions, it is possible for the value to change depending on whether or not it has already been linked. It is also still possible for it to unexpectedly return true. It is therefore recommended to **always** audit the new subdomain's source code before calling `setDomain`. + +### Parent Domain Resolution + +#### Description: Parent Domain Resolution + +Parent domains have full control of name resolution for their subdomains. If a particular domain is linked to `a.b.c`, then `b.c` can, depending on its code, set `a.b.c` to any domain, and `c` can set `b.c` itself to any domain. + +#### Mitigation: Parent Domain Resolution + +Before acquiring a domain that has been pre-linked, it is recommended to always have the contract **and** all the parents up to the root audited. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4844.md b/EIPS/eip-4844.md new file mode 100644 index 00000000000000..5f3d4f416bcc6c --- /dev/null +++ b/EIPS/eip-4844.md @@ -0,0 +1,438 @@ +--- +eip: 4844 +title: Shard Blob Transactions +description: Shard Blob Transactions scale data-availability of Ethereum in a simple, forwards-compatible manner. +author: Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad), Diederik Loerakker (@protolambda), George Kadianakis (@asn-d6), Matt Garnett (@lightclient), Mofi Taiwo (@Inphi), Ansgar Dietrichs (@adietrichs) +discussions-to: https://ethereum-magicians.org/t/eip-4844-shard-blob-transactions/8430 +status: Review +type: Standards Track +category: Core +created: 2022-02-25 +requires: 1559, 2718, 2930, 4895 +--- + +## Abstract + +Introduce a new transaction format for "blob-carrying transactions" which contain a large amount of data that cannot be +accessed by EVM execution, but whose commitment can be accessed. +The format is intended to be fully compatible with the format that will be used in full sharding. + +## Motivation + +Rollups are in the short and medium term, and possibly in the long term, the only trustless scaling solution for Ethereum. +Transaction fees on L1 have been very high for months and there is greater urgency in doing anything required to help facilitate an ecosystem-wide move to rollups. +Rollups are significantly reducing fees for many Ethereum users: Optimism and Arbitrum frequently provide fees that are ~3-8x lower than the Ethereum base layer itself, +and ZK rollups, which have better data compression and can avoid including signatures, have fees ~40-100x lower than the base layer. + +However, even these fees are too expensive for many users. The long-term solution to the long-term inadequacy of rollups +by themselves has always been data sharding, which would add ~16 MB per block of dedicated data space to the chain that rollups could use. +However, data sharding will still take a considerable amount of time to finish implementing and deploying. + +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 ~0.375 MB per block and a limit of ~0.75 MB. + +## Specification + +### Parameters + +| Constant | Value | +| - | - | +| `BLOB_TX_TYPE` | `Bytes1(0x03)` | +| `BYTES_PER_FIELD_ELEMENT` | `32` | +| `FIELD_ELEMENTS_PER_BLOB` | `4096` | +| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | +| `VERSIONED_HASH_VERSION_KZG` | `Bytes1(0x01)` | +| `POINT_EVALUATION_PRECOMPILE_ADDRESS` | `Bytes20(0x0A)` | +| `POINT_EVALUATION_PRECOMPILE_GAS` | `50000` | +| `MAX_BLOB_GAS_PER_BLOCK` | `786432` | +| `TARGET_BLOB_GAS_PER_BLOCK` | `393216` | +| `MIN_BLOB_GASPRICE` | `1` | +| `BLOB_GASPRICE_UPDATE_FRACTION` | `3338477` | +| `LIMIT_BLOBS_PER_TX` | `2**12` | +| `GAS_PER_BLOB` | `2**17` | +| `HASH_OPCODE_BYTE` | `Bytes1(0x49)` | +| `HASH_OPCODE_GAS` | `3` | + +### Type aliases + +| Type | Base type | Additional checks | +| - | - | - | +| `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | | +| `VersionedHash` | `Bytes32` | | +| `KZGCommitment` | `Bytes48` | Perform IETF BLS signature "KeyValidate" check but do allow the identity point | +| `KZGProof` | `Bytes48` | Same as for `KZGCommitment` | + +### Cryptographic Helpers + +Throughout this proposal we use cryptographic methods and classes defined in the corresponding [consensus 4844 specs](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb). + +Specifically, we use the following methods from [`polynomial-commitments.md`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md): + +- [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md#verify_kzg_proof) +- [`verify_blob_kzg_proof_batch()`](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md#verify_blob_kzg_proof_batch) + +### Helpers + +```python +def kzg_to_versioned_hash(commitment: KZGCommitment) -> VersionedHash: + return VERSIONED_HASH_VERSION_KZG + sha256(commitment)[1:] +``` + +Approximates `factor * e ** (numerator / denominator)` using Taylor expansion: + +```python +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 +``` + +### Blob transaction + +We introduce a new [EIP-2718](./eip-2718.md) transaction, "blob transaction", where the `TransactionType` is `BLOB_TX_TYPE` and the `TransactionPayload` is the RLP serialization of the following `TransactionPayloadBody`: + +``` +[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s] +``` + +The fields `chain_id`, `nonce`, `max_priority_fee_per_gas`, `max_fee_per_gas`, `gas_limit`, `value`, `data`, and `access_list` follow the same semantics as [EIP-1559](./eip-1559.md). + +The field `to` deviates slightly from the semantics with the exception that it MUST NOT be `nil` and therefore must always represent a 20-byte address. This means that blob transactions cannot have the form of a create transaction. + +The field `max_fee_per_blob_gas` is a `uint256` and the field `blob_versioned_hashes` represents a list of hash outputs from `kzg_to_versioned_hash`. + +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`. + +#### Signature + +The signature values `y_parity`, `r`, and `s` are calculated by constructing a secp256k1 signature over the following digest: + +`keccak256(BLOB_TX_TYPE || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes]))`. + +### Header extension + +The current header encoding is extended with two new 64-bit unsigned integer fields: + +- `blob_gas_used` is the total amount of blob gas consumed by the transactions within the block. +- `excess_blob_gas` is a running total of blob gas consumed in excess of the target, prior to the block. Blocks with above-target blob gas consumption increase this value, blocks with below-target blob gas consumption decrease it (bounded at 0). + +The resulting RLP encoding of the header is therefore: + +``` +rlp([ + parent_hash, + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash + coinbase, + state_root, + txs_root, + receipts_root, + logs_bloom, + 0, # difficulty + number, + gas_limit, + gas_used, + timestamp, + extradata, + prev_randao, + 0x0000000000000000, # nonce + base_fee_per_gas, + withdrawals_root, + blob_gas_used, + excess_blob_gas, +]) +``` + +The value of `excess_blob_gas` can be calculated using the parent header. + +```python +def calc_excess_blob_gas(parent: Header) -> int: + if parent.excess_blob_gas + parent.blob_gas_used < TARGET_BLOB_GAS_PER_BLOCK: + return 0 + else: + return parent.excess_blob_gas + parent.blob_gas_used - TARGET_BLOB_GAS_PER_BLOCK +``` + +For the first post-fork block, both `parent.blob_gas_used` and `parent.excess_blob_gas` are evaluated as `0`. + +### Gas accounting + +We introduce blob 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_blob_gas` header field to store persistent data needed to compute the blob gas price. For now, only blobs are priced in blob gas. + +```python +def calc_data_fee(header: Header, tx: SignedBlobTransaction) -> int: + return get_total_blob_gas(tx) * get_blob_gasprice(header) + +def get_total_blob_gas(tx: SignedBlobTransaction) -> int: + return GAS_PER_BLOB * len(tx.blob_versioned_hashes) + +def get_blob_gasprice(header: Header) -> int: + return fake_exponential( + MIN_BLOB_GASPRICE, + header.excess_blob_gas, + BLOB_GASPRICE_UPDATE_FRACTION + ) +``` + +The block validity conditions are modified to include blob gas checks (see the [Execution layer validation](#execution-layer-validation) section below). + +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. + +### Opcode to get versioned hashes + +We add an instruction `BLOBHASH` (with opcode `HASH_OPCODE_BYTE`) which reads `index` from the top of the stack +as big-endian `uint256`, and replaces it on the stack with `tx.blob_versioned_hashes[index]` +if `index < len(tx.blob_versioned_hashes)`, and otherwise with a zeroed `bytes32` value. +The opcode has a gas cost of `HASH_OPCODE_GAS`. + +### Point evaluation precompile + +Add a precompile at `POINT_EVALUATION_PRECOMPILE_ADDRESS` that verifies a KZG proof which claims that a blob +(represented by a commitment) evaluates to a given value at a given point. + +The precompile costs `POINT_EVALUATION_PRECOMPILE_GAS` and executes the following logic: + +```python +def point_evaluation_precompile(input: Bytes) -> Bytes: + """ + Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof. + Also verify that the provided commitment matches the provided versioned_hash. + """ + # The data is encoded as follows: versioned_hash | z | y | commitment | proof | with z and y being padded 32 byte big endian values + assert len(input) == 192 + versioned_hash = input[:32] + z = input[32:64] + y = input[64:96] + commitment = input[96:144] + proof = input[144:192] + + # Verify commitment matches versioned_hash + assert kzg_to_versioned_hash(commitment) == versioned_hash + + # Verify KZG proof with z and y in big endian format + assert verify_kzg_proof(commitment, z, y, proof) + + # Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values + return Bytes(U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes32() + U256(BLS_MODULUS).to_be_bytes32()) +``` + +The precompile MUST reject non-canonical field elements (i.e. provided field elements MUST be strictly less than `BLS_MODULUS`). + +### Consensus layer validation + +On the consensus layer the blobs are referenced, but not fully encoded, in the beacon block body. +Instead of embedding the full contents in the body, the blobs are propagated separately, as "sidecars". + +This "sidecar" design provides forward compatibility for further data increases by black-boxing `is_data_available()`: +with full sharding `is_data_available()` can be replaced by data-availability-sampling (DAS) thus avoiding all blobs being downloaded by all beacon nodes on the network. + +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 consensus layer 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 blob sidecars. +- Honest validator: produce beacon blocks with blobs; sign and publish the associated blob sidecars. + +### Execution layer validation + +On the execution layer, the block validity conditions are extended as follows: + +```python +def validate_block(block: Block) -> None: + ... + + # check that the excess blob gas was updated correctly + assert block.header.excess_blob_gas == calc_excess_blob_gas(block.parent.header) + + blob_gas_used = 0 + + for tx in block.transactions: + ... + + # modify the check for sufficient balance + max_total_fee = tx.gas * tx.max_fee_per_gas + if type(tx) is SignedBlobTransaction: + max_total_fee += get_total_blob_gas(tx) * tx.max_fee_per_blob_gas + assert signer(tx).balance >= max_total_fee + + ... + + # add validity logic specific to blob txs + if type(tx) is SignedBlobTransaction: + # there must be at least one blob + assert len(tx.blob_versioned_hashes) > 0 + + # all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG + for h in tx.blob_versioned_hashes: + assert h[0] == VERSIONED_HASH_VERSION_KZG + + # ensure that the user was willing to at least pay the current blob gasprice + assert tx.max_fee_per_blob_gas >= get_blob_gasprice(block.header) + + # keep track of total blob gas spent in the block + blob_gas_used += get_total_blob_gas(tx) + + # ensure the total blob gas spent is at most equal to the limit + assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK + + # ensure blob_gas_used matches header + assert block.header.blob_gas_used == blob_gas_used + +``` + +### Networking + +Blob transactions have two network representations. During transaction gossip responses (`PooledTransactions`), the EIP-2718 `TransactionPayload` of the blob transaction is wrapped to become: + +``` +rlp([tx_payload_body, blobs, commitments, proofs]) +``` + +Each of these elements are defined as follows: + +- `tx_payload_body` - is the `TransactionPayloadBody` of standard EIP-2718 [blob transaction](#blob-transaction) +- `blobs` - list of `Blob` items +- `commitments` - list of `KZGCommitment` of the corresponding `blobs` +- `proofs` - list of `KZGProof` of the corresponding `blobs` and `commitments` + +The node MUST validate `tx_payload_body` and verify the wrapped data against it. To do so, ensure that: + +- There are an equal number of `tx_payload_body.blob_versioned_hashes`, `blobs`, `commitments`, and `proofs`. +- The KZG `commitments` hash to the versioned hashes, i.e. `kzg_to_versioned_hash(commitments[i]) == tx_payload_body.blob_versioned_hashes[i]` +- The KZG `commitments` match the corresponding `blobs` and `proofs`. (Note: this can be optimized using `blob_kzg_proofs`, with a proof for a + random evaluation at a point derived from the commitment and blob data for each blob) + +For body retrieval responses (`BlockBodies`), the standard EIP-2718 blob transaction `TransactionPayload` is used. + +Nodes MUST NOT automatically broadcast blob transactions to their peers. +Instead, those transactions are only announced using `NewPooledTransactionHashes` messages, and can then be manually requested via `GetPooledTransactions`. + +## Rationale + +### 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 initially scale to 0.375 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, +without imposing extra development burdens on rollups to take advantage of this relief. +Today, rollups use calldata. In the future, rollups will have no choice but to use sharded data (also called "blobs") +because sharded data will be much cheaper. +Hence, rollups cannot avoid making a large upgrade to how they process data at least once along the way. +But what we _can_ do is ensure that rollups need to _only_ upgrade once. +This immediately implies that there are exactly two possibilities for a stopgap: (i) reducing the gas costs of existing calldata, +and (ii) bringing forward the format that will be used for sharded data, but not yet actually sharding it. +Previous EIPs were all a solution of category (i); this EIP is a solution of category (ii). + +The main tradeoff in designing this EIP is that of implementing more now versus having to implement more later: +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 + +The work that remains to be done to get to full sharding includes: + +- A low-degree extension of the `commitments` 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. For example, its (cleaner) gas price update rule could be applied to the primary basefee calculation. + +### How rollups would function + +Instead of putting rollup block data in transaction calldata, rollups would expect rollup block submitters +to put the data into blobs. This guarantees availability (which is what rollups need) but would be much cheaper than calldata. +Rollups need data to be available once, long enough to ensure honest actors can construct the rollup state, but not forever. + +Optimistic rollups only need to actually provide the underlying data when fraud proofs are being submitted. +The fraud proof can verify the transition in smaller steps, loading at most a few values of the blob at a time through calldata. +For each value it would provide a KZG proof and use the point evaluation precompile to verify the value against the versioned hash that was submitted before, +and then perform the fraud proof verification on that data as is done today. + +ZK rollups would provide two commitments to their transaction or state delta data: +the blob commitment (which the protocol ensures points to available data) and the ZK rollup's own commitment using whatever proof system the rollup uses internally. +They would use a proof of equivalence protocol, using the point evaluation precompile, +to prove that the two commitments refer to the same data. + +### Versioned hashes & precompile return data + +We use versioned hashes (rather than commitments) as references to blobs in the execution layer to ensure forward compatibility with future changes. +For example, if we need to switch to Merkle trees + STARKs for quantum-safety reasons, then we would add a new version, +allowing the point evaluation 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. + +However, the point evaluation happens inside a finite field, and it is only well defined if the field modulus is known. Smart contracts could contain a table mapping the commitment version to a modulus, but this would not allow smart contract to take into account future upgrades to a modulus that is not known yet. By allowing access to the modulus inside the EVM, the smart contract can be built so that it can use future commitments and proofs, without ever needing an upgrade. + +In the interest of not adding another precompile, we return the modulus and the polynomial degree directly from the point evaluation precompile. It can then be used by the caller. It is also "free" in that the caller can just ignore this part of the return value without incurring an extra cost -- systems that remain upgradable for the foreseeable future will likely use this route for now. + +### Blob gasprice update rule + +The blob gasprice update rule is intended to approximate the formula `blob_gasprice = MIN_BLOB_GASPRICE * e**(excess_blob_gas / BLOB_GASPRICE_UPDATE_FRACTION)`, +where `excess_blob_gas` is the total "extra" amount of blob gas that the chain has consumed relative to the "targeted" number (`TARGET_BLOB_GAS_PER_BLOCK` per block). +Like EIP-1559, it's a self-correcting formula: as the excess goes higher, the `blob_gasprice` increases exponentially, reducing usage and eventually forcing the excess back down. + +The block-by-block behavior is roughly as follows. +If block `N` consumes `X` blob gas, then in block `N+1` `excess_blob_gas` increases by `X - TARGET_BLOB_GAS_PER_BLOCK`, +and so the `blob_gasprice` of block `N+1` increases by a factor of `e**((X - TARGET_BLOB_GAS_PER_BLOCK) / BLOB_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 `BLOB_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_BLOB_GAS_PER_BLOCK / BLOB_GASPRICE_UPDATE_FRACTION) ≈ 1.125` per block. + +### Throughput + +The values for `TARGET_BLOB_GAS_PER_BLOCK` and `MAX_BLOB_GAS_PER_BLOCK` are chosen to correspond to a target of 3 blobs (0.375 MB) and maximum of 6 blobs (0.75 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 + +This EIP introduces a transaction type that has a distinct mempool version and execution-payload version, +with only one-way convertibility between the two. The blobs are in the network representation and not in the consensus representation; +instead, they are coupled with the beacon block. This means that there is now a part of a transaction that will not be accessible from the web3 API. + +### Mempool issues + +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. + +By only broadcasting announcements for blob transactions, receiving nodes will have control over which and how many transactions to receive, +allowing them to throttle throughput to an acceptable level. +[EIP-5793](./eip-5793.md) will give further fine-grained control to nodes by extending the `NewPooledTransactionHashes` announcement messages to include the transaction type and size. + +In addition, we recommend including a 1.1x blob gasprice bump requirement to the mempool transaction replacement rules. + +## Test Cases + +TBD + +## Security Considerations + +This EIP increases the bandwidth requirements per beacon block by a maximum of ~0.75 MB. +This is 40% 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 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, +because there is no existing software that stores the blobs indefinitely and there is no expectation that they need to be stored for as long as an execution payload. +This makes it easier to implement a policy that these blobs should be deleted after e.g. 30-60 days, +a much shorter delay compared to proposed (but yet to be implemented) one-year rotation times for execution payload history. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4863.md b/EIPS/eip-4863.md new file mode 100644 index 00000000000000..869b9a9dde4d00 --- /dev/null +++ b/EIPS/eip-4863.md @@ -0,0 +1,107 @@ +--- +eip: 4863 +title: Beacon chain push withdrawals +description: Support validator withdrawals from the beacon chain to the EVM via a new "push-style" transaction type. +author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4863-beacon-chain-push-withdrawals/8465 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-28 +--- + +## Abstract + +Introduce a new [EIP-2718 transaction type](./eip-2718.md) to support validator withdrawals that are "pushed" from the beacon chain to the EVM. + +Add block validations to ensure the withdrawal transactions are sound with respect to withdrawal processing on the beacon chain. + +## Motivation + +This EIP provides a way for validator withdrawals made on the beacon chain to enter into the EVM. +The architecture is "push"-based, rather than "pull"-based, where withdrawals are required to be processed in the execution block as soon as they are dequeued from the beacon chain. + +This approach is more involved than "pull"-based alternatives (e.g. [EIP-4788](./eip-4788.md) + user-space withdrawal contract) with respect to the core protocol (by providing a new transaction type with special semantics) but does provide tighter integration of a critical feature into the protocol itself. + +## Specification + +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | TBD | +| `WITHDRAWAL_TX_TYPE` | `0x3` | byte + +Beginning with the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST** introduce the following extensions to transaction processing and block validation: + +### New transaction type + +Define a new [EIP-2718](./eip-2718.md) transaction type with `TransactionType` `WITHDRAWAL_TX_TYPE`. + +The `TransactionPayload` is an RLP-encoded list `RLP([index, address, amount])` where the `index` is a 64-bit value uniquely labeling a specific withdrawal, the `address` refers to an execution layer account and the `amount` refers to an ether value given in units of wei. + +These values are provided by the consensus layer. + +### Block validity + +If a block contains *any* transactions with `WITHDRAWAL_TX_TYPE` type, they **MUST** come after **ALL** other transactions in the block. + +If the execution client receives a block where this is not the case, it **MUST** consider the block invalid. + +### Transaction processing + +When processing a transaction with `WITHDRAWAL_TX_TYPE` type, the implementation should increase the balance of the `address` specified by +the `WithdrawalTransaction` by the `amount` of wei specified. + +This balance change is unconditional and **MUST** not fail. + +This transaction type has no associated gas costs. + +TODO: add logs? + +## Rationale + +### Push vs pull approach + +This push approach gives validators a small subsidy with respect to processing, in lieu of needing to buy gas via normal EVM processing that would be required for a pull-based approach. + +This style also happens automatically when the requisite conditions are met on the beacon chain which is nicer UX for validators. + +### Why a new transaction type? + +This EIP suggests a new transaction type as it has special semantics different from other existing types of EVM transactions. + +An entirely new transaction type firewalls off generic EVM execution from this type of processing to simplify testing and security review of withdrawals. + +### Why no (gas) costs for new transaction type? + +The maximum number of this transaction type that can reach the execution layer at a given time is bounded (enforced by the consensus layer) and this limit is kept small so that +any execution layer operational costs are negligible in the context of the broader block execution. + +### Why only balance updates? No general EVM execution? + +More general processing introduces the risk of failures, which complicates accounting on the beacon chain. + +This EIP suggests a route for withdrawals that provides most of the benefits for a minimum of the (complexity) cost. + +### Why new block validations? + +The beacon chain must be able to efficiently validate that the withdrawal transactions in a given execution block are +the ones expected based on its own internal scheduling logic to maintain the soundness of the withdrawal mechanism. + +By requiring all withdrawal transactions to be at the back of every block where they are applicable, the algorithm to +check consistency becomes a straightforward linear walk from the start of the set until a known, bounded (small) number. + +Having a simple ordering scheme like this facilitates optimizations clients may do with respect to withdrawal processing, which +would be hampered if withdrawal transactions could be placed in the block freely. + +## Backwards Compatibility + +No issues. + +## Security Considerations + +Consensus-layer validation of withdrawal transactions is critical to ensure that the proper amount of ETH is withdrawn back into the execution layer. +This consensus-layer to execution-layer ETH transfer does not have a current analog in the EVM and thus deserves very high security scrutiny. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4881.md b/EIPS/eip-4881.md new file mode 100644 index 00000000000000..4b6fcb4409e34f --- /dev/null +++ b/EIPS/eip-4881.md @@ -0,0 +1,321 @@ +--- +eip: 4881 +title: Deposit Contract Snapshot Interface +description: Establishing the format and endpoint for transmitting a snapshot of the deposit Merkle tree +author: Mark Mackey (@ethDreamer) +discussions-to: https://ethereum-magicians.org/t/eip-4881-deposit-contract-snapshot-interface/8554 +status: Last Call +last-call-deadline: 2023-08-31 +type: Standards Track +category: Interface +created: 2021-01-29 +--- + +## Abstract + +This EIP defines a standard format for transmitting the deposit contract Merkle tree in a compressed form during weak subjectivity sync. This allows newly syncing consensus clients to reconstruct the deposit tree much faster than downloading all historical deposits. The format proposed also allows clients to prune deposits that are no longer needed to participate fully in consensus (see [Deposit Finalization Flow](#deposit-finalization-flow)). + +## Motivation + +To reconstruct the deposit Merkle tree, most client implementations require beacon nodes to download and store every deposit log since the launch of the deposit contract. However, this approach requires beacon nodes to store far more deposits than necessary to participate in consensus. Additionally, this leads to increased sync times for new nodes, which is particularly evident during weak subjectivity sync. This simplistic approach also prevents historical contract logs from being pruned from full nodes, a prospect frequently discussed in the context of limiting state growth. + +## Specification + +Consensus clients MAY continue to implement the deposit Merkle tree however they choose. However, when transmitting the tree to newly syncing nodes, clients MUST use the following format: + +```python +class DepositTreeSnapshot: + finalized: List[Hash32, DEPOSIT_CONTRACT_DEPTH] + deposit_root: Hash32 + deposit_count: uint64 + execution_block_hash: Hash32 + execution_block_height: uint64 +``` + +Where `finalized` is a variable-length list (of maximum size `DEPOSIT_CONTRACT_DEPTH`) containing the hashes defined in the [Deposit Finalization Flow](#deposit-finalization-flow) section below. The fields `deposit_root`, `deposit_count`, and `execution_block_hash` store the same information as the [`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#eth1data) object that corresponds to the snapshot, and `execution_block_height` is the height of the execution block with hash `execution_block_hash`. Consensus clients MUST make this structure available via the Beacon Node API endpoint: + +``` +/eth/v1/beacon/deposit_snapshot +``` + +### Deposit Finalization Flow + +During deposit processing, the beacon chain requires deposits to be submitted along with a Merkle path to the deposit root. This is required exactly once for each deposit. When a deposit has been processed by the beacon chain and the [deposit finalization conditions](#deposit-finalization-conditions) have been met, many of the hashes along the path to the deposit root will never be required again to construct Merkle proofs on chain. These unnecessary hashes MAY be pruned to save space. The image below illustrates the evolution of the deposit Merkle tree under this process alongside the corresponding `DepositTreeSnapshot` as new deposits are added and older deposits become finalized: + +![deposit tree evolution](../assets/eip-4881/deposit_tree_evolution.svg) + +## Rationale + +The format in this specification was chosen to achieve several goals simultaneously: + +1. Enable reconstruction of the deposit contract Merkle tree without requiring full nodes to store all historical contract logs +2. Avoid requiring consensus nodes to retain more deposits than necessary to fully participate in consensus +3. Simplicity of implementation (see [Reference Implementation](#reference-implementation) section) +4. Increase speed of weak subjectivity sync +5. Compatibility with existing implementations of this mechanism (see discussion) + +The proposed `DepositTreeSnapshot` structure includes both `execution_block_hash` and `execution_block_height` for convenience to consensus node implementors. While only one of these fields is strictly necessary, different clients may have already designed their block cache logic around one or the other. Sending only one of these would force some consensus clients to query the execution engine for the other information, but as this is happening in the context of a newly syncing consensus node, it is very likely that the execution engine will not be synced, especially post-merge. The `deposit_root` field is also not strictly necessary, but by including it, newly syncing consensus nodes can cheaply validate any received snapshot against itself (see the `calculate_root()` method in the [Reference Implementation](#reference-implementation)). + +### Why not Reconstruct the Tree Directly from the Deposit Contract? + +The deposit contract can only provide the tree at the head of the chain. Because the beacon chain's view of the deposit contract lags behind the execution chain by `ETH1_FOLLOW_DISTANCE`, there are almost always deposits which haven't yet been included in the chain that need proofs constructed from an earlier version of the tree than exists at the head. + +### Why not Reconstruct the Tree from a Deposit in the Beacon Chain? + +In principle, a node could scan backwards through the chain starting from the weak subjectivity checkpoint to locate a suitable [`Deposit`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#deposit), and then extract the rightmost branch of the tree from that. The node would also need to extract the `execution_block_hash` from which to start syncing new deposits from the `Eth1Data` in the corresponding `BeaconState`. This approach is less desirable for a few reasons: + +* More difficult to implement due to the edge cases involved in finding a suitable deposit to anchor to (the rightmost branch of the latest not-yet-included deposit is required) +* This would make backfilling beacon blocks a requirement for reconstructing the deposit tree and therefore a requirement for block production +* This is inherently slower than getting this information from the weak subjectivity checkpoint + +## Backwards Compatibility + +This proposal is fully backwards compatible. + +## Test Cases + +Test cases are included in [test_cases.yaml](../assets/eip-4881/test_cases.yaml). Each case is structured as follows: + +```python +class DepositTestCase: + deposit_data: DepositData # These are all the inputs to the deposit contract's deposit() function + deposit_data_root: Hash32 # The tree hash root of this deposit (calculated for convenience) + eth1_data: Eth1Data # An Eth1Data object that can be used to finalize the tree after pushing this deposit + block_height: uint64 # The height of the execution block with this Eth1Data + snapshot: DepositTreeSnapshot # The resulting DepositTreeSnapshot object if the tree were finalized after this deposit +``` + +This EIP also includes other files for testing: + +* [deposit_snapshot.py](../assets/eip-4881/deposit_snapshot.py) contains the same code as the [Reference Implementation](#reference-implementation) +* [eip_4881.py](../assets/eip-4881/eip_4881.py) contains boilerplate declarations +* [test_deposit_snapshot.py](../assets/eip-4881/test_deposit_snapshot.py) includes code for running test cases against the reference implementation + +If these files are downloaded to the same directory, the test cases can be run by executing `pytest` in that directory. + +## Reference Implementation + +This implementation lacks full error checking and is optimized for readability over efficiency. If `tree` is a `DepositTree`, then the `DepositTreeSnapshot` can be obtained by calling `tree.get_snapshot()` and a new instance of the tree can be recovered from the snapshot by calling `DepositTree.from_snapshot()`. See the [Deposit Finalization Conditions](#deposit-finalization-conditions) section for discussion on when the tree can be pruned by calling `tree.finalize()`. + +Generating proofs for deposits against an earlier version of the tree is relatively fast in this implementation; just create a copy of the finalized tree with `copy = DepositTree.from_snapshot(tree.get_snapshot())` and then append the remaining deposits to the desired count with `copy.push_leaf(deposit)`. Proofs can then be obtained with `copy.get_proof(index)`. + +```python +from __future__ import annotations +from typing import List, Optional, Tuple +from dataclasses import dataclass +from abc import ABC,abstractmethod +from eip_4881 import DEPOSIT_CONTRACT_DEPTH,Hash32,sha256,to_le_bytes,zerohashes + +@dataclass +class DepositTreeSnapshot: + finalized: List[Hash32, DEPOSIT_CONTRACT_DEPTH] + deposit_root: Hash32 + deposit_count: uint64 + execution_block_hash: Hash32 + execution_block_height: uint64 + + def calculate_root(self) -> Hash32: + size = self.deposit_count + index = len(self.finalized) + root = zerohashes[0] + for level in range(0, DEPOSIT_CONTRACT_DEPTH): + if (size & 1) == 1: + index -= 1 + root = sha256(self.finalized[index] + root) + else: + root = sha256(root + zerohashes[level]) + size >>= 1 + return sha256(root + to_le_bytes(self.deposit_count)) + def from_tree_parts(finalized: List[Hash32], + deposit_count: uint64, + execution_block: Tuple[Hash32, uint64]) -> DepositTreeSnapshot: + snapshot = DepositTreeSnapshot( + finalized, zerohashes[0], deposit_count, execution_block[0], execution_block[1]) + # A real implementation should store the deposit_root from the eth1_data passed to + # DepositTree.finalize() instead of relying on calculate_root() here. This allows + # the snapshot to be validated using calculate_root(). + snapshot.deposit_root = snapshot.calculate_root() + return snapshot + +@dataclass +class DepositTree: + tree: MerkleTree + mix_in_length: uint + finalized_execution_block: Optional[Tuple[Hash32, uint64]] + def new() -> DepositTree: + merkle = MerkleTree.create([], DEPOSIT_CONTRACT_DEPTH) + return DepositTree(merkle, 0, None) + def get_snapshot(self) -> DepositTreeSnapshot: + assert(self.finalized_execution_block is not None) + finalized = [] + deposit_count = self.tree.get_finalized(finalized) + return DepositTreeSnapshot.from_tree_parts( + finalized, deposit_count, self.finalized_execution_block) + def from_snapshot(snapshot: DepositTreeSnapshot) -> DepositTree: + # decent validation check on the snapshot + assert(snapshot.deposit_root == snapshot.calculate_root()) + finalized_execution_block = (snapshot.execution_block_hash, snapshot.execution_block_height) + tree = MerkleTree.from_snapshot_parts( + snapshot.finalized, snapshot.deposit_count, DEPOSIT_CONTRACT_DEPTH) + return DepositTree(tree, snapshot.deposit_count, finalized_execution_block) + def finalize(self, eth1_data: Eth1Data, execution_block_height: uint64): + self.finalized_execution_block = (eth1_data.block_hash, execution_block_height) + self.tree.finalize(eth1_data.deposit_count, DEPOSIT_CONTRACT_DEPTH) + def get_proof(self, index: uint) -> Tuple[Hash32, List[Hash32]]: + assert(self.mix_in_length > 0) + # ensure index > finalized deposit index + assert(index > self.tree.get_finalized([]) - 1) + leaf, proof = self.tree.generate_proof(index, DEPOSIT_CONTRACT_DEPTH) + proof.append(to_le_bytes(self.mix_in_length)) + return leaf, proof + def get_root(self) -> Hash32: + return sha256(self.tree.get_root() + to_le_bytes(self.mix_in_length)) + def push_leaf(self, leaf: Hash32): + self.mix_in_length += 1 + self.tree = self.tree.push_leaf(leaf, DEPOSIT_CONTRACT_DEPTH) + +class MerkleTree(): + @abstractmethod + def get_root(self) -> Hash32: + pass + @abstractmethod + def is_full(self) -> bool: + pass + @abstractmethod + def push_leaf(self, leaf: Hash32, level: uint) -> MerkleTree: + pass + @abstractmethod + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + pass + @abstractmethod + def get_finalized(self, result: List[Hash32]) -> uint: + # returns the number of finalized deposits in the tree + # while populating result with the finalized hashes + pass + def create(leaves: List[Hash32], depth: uint) -> MerkleTree: + if not(leaves): + return Zero(depth) + if not(depth): + return Leaf(leaves[0]) + split = min(2**(depth - 1), len(leaves)) + left = MerkleTree.create(leaves[0:split], depth - 1) + right = MerkleTree.create(leaves[split:], depth - 1) + return Node(left, right) + def from_snapshot_parts(finalized: List[Hash32], deposits: uint, level: uint) -> MerkleTree: + if not(finalized) or not(deposits): + # empty tree + return Zero(level) + if deposits == 2**level: + return Finalized(deposits, finalized[0]) + left_subtree = 2**(level - 1) + if deposits <= left_subtree: + left = MerkleTree.from_snapshot_parts(finalized, deposits, level - 1) + right = Zero(level - 1) + return Node(left, right) + else: + left = Finalized(left_subtree, finalized[0]) + right = MerkleTree.from_snapshot_parts(finalized[1:], deposits - left_subtree, level - 1) + return Node(left, right) + def generate_proof(self, index: uint, depth: uint) -> Tuple[Hash32, List[Hash32]]: + proof = [] + node = self + while depth > 0: + ith_bit = (index >> (depth - 1)) & 0x1 + if ith_bit == 1: + proof.append(node.left.get_root()) + node = node.right + else: + proof.append(node.right.get_root()) + node = node.left + depth -= 1 + proof.reverse() + return node.get_root(), proof + +@dataclass +class Finalized(MerkleTree): + deposit_count: uint + hash: Hash32 + def get_root(self) -> Hash32: + return self.hash + def is_full(self) -> bool: + return True + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + return self + def get_finalized(self, result: List[Hash32]) -> uint: + result.append(self.hash) + return self.deposit_count + +@dataclass +class Leaf(MerkleTree): + hash: Hash32 + def get_root(self) -> Hash32: + return self.hash + def is_full(self) -> bool: + return True + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + return Finalized(1, self.hash) + def get_finalized(self, result: List[Hash32]) -> uint: + return 0 + +@dataclass +class Node(MerkleTree): + left: MerkleTree + right: MerkleTree + def get_root(self) -> Hash32: + return sha256(self.left.get_root() + self.right.get_root()) + def is_full(self) -> bool: + return self.right.is_full() + def push_leaf(self, leaf: Hash32, level: uint) -> MerkleTree: + if not(self.left.is_full()): + self.left = self.left.push_leaf(leaf, level - 1) + else: + self.right = self.right.push_leaf(leaf, level - 1) + return self + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + deposits = 2**level + if deposits <= deposits_to_finalize: + return Finalized(deposits, self.get_root()) + self.left = self.left.finalize(deposits_to_finalize, level - 1) + if deposits_to_finalize > deposits / 2: + remaining = deposits_to_finalize - deposits / 2 + self.right = self.right.finalize(remaining, level - 1) + return self + def get_finalized(self, result: List[Hash32]) -> uint: + return self.left.get_finalized(result) + self.right.get_finalized(result) + +@dataclass +class Zero(MerkleTree): + n: uint64 + def get_root(self) -> Hash32: + if self.n == DEPOSIT_CONTRACT_DEPTH: + # Handle the entirely empty tree case. This is included for + # consistency/clarity as the zerohashes array is typically + # only defined from 0 to DEPOSIT_CONTRACT_DEPTH - 1. + return sha256(zerohashes[self.n - 1] + zerohashes[self.n - 1]) + return zerohashes[self.n] + def is_full(self) -> bool: + return False + def push_leaf(self, leaf: Hash32, level: uint) -> MerkleTree: + return MerkleTree.create([leaf], level) + def get_finalized(self, result: List[Hash32]) -> uint: + return 0 +``` + +## Security Considerations + +### Relying on Weak Subjectivity Sync + +The upcoming switch to PoS will require newly synced nodes to rely on valid weak subjectivity checkpoints because of long-range attacks. This proposal relies on the weak subjectivity assumption that clients will not bootstrap with an invalid WS checkpoint. + +### Deposit Finalization Conditions + +Care must be taken not to send a snapshot which includes deposits that haven't been fully included in the finalized checkpoint. Let `state` be the [`BeaconState`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#beaconstate) at a given block in the chain. Under normal operation, the [`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#eth1data) stored in `state.eth1_data` is replaced every `EPOCHS_PER_ETH1_VOTING_PERIOD` epochs. Thus, finalization of the deposit tree proceeds with increments of `state.eth1_data`. Let `eth1data` be some `Eth1Data`. Both of the following conditions MUST be met to consider `eth1data` finalized: + +1. A finalized checkpoint exists where the corresponding `state` has `state.eth1_data == eth1data` +2. A finalized checkpoint exists where the corresponding `state` has `state.eth1_deposit_index >= eth1data.deposit_count` + +When these conditions are met, the tree can be pruned in the [reference implementation](#reference-implementation) by calling `tree.finalize(eth1data, execution_block_height)` + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4883.md b/EIPS/eip-4883.md new file mode 100644 index 00000000000000..8f3d999d114d4c --- /dev/null +++ b/EIPS/eip-4883.md @@ -0,0 +1,71 @@ +--- +eip: 4883 +title: Composable SVG NFT +description: Compose an SVG NFT by concatenating the SVG with the rendered SVG of another NFT. +author: Andrew B Coathup (@abcoathup), Alex (@AlexPartyPanda), Damian Martinelli (@damianmarti), blockdev (@0xbok), Austin Griffith (@austintgriffith) +discussions-to: https://ethereum-magicians.org/t/eip-4883-composable-svg-nft/8765 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-08 +requires: 165, 721 +--- + +## Abstract + +Compose an SVG (Scalable Vector Graphics) NFT by concatenating the SVG with the SVG of another NFT rendered as a string for a specific token ID. + +## Motivation + +Onchain SVG NFTs allow for NFTs to be entirely onchain by returning artwork as SVG in a data URI of the `tokenUri` function. Composability allows onchain SVG NFTs to be crafted. e.g. adding glasses & hat NFTs to a profile pic NFT or a fish NFT to a fish tank NFT. + +## 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 +/// @title EIP-4883 Non-Fungible Token Standard +interface IERC4883 is IERC165 { + function renderTokenById(uint256 id) external view returns (string memory); +} +``` + +`renderTokenById` must return the SVG body for the specified token `id` and must either be an empty string or valid SVG element(s). + +## Rationale + +SVG elements can be string concatenated to compose an SVG. + +### Ordering of concatenation + +SVG uses a "painters model" of rendering. + +**Scalable Vector Graphics (SVG) 1.1 (Second Edition)**, section: **3.3 Rendering Order** +>Elements in an SVG document fragment have an implicit drawing order, with the first elements in the SVG document fragment getting "painted" first. Subsequent elements are painted on top of previously painted elements. + +The ordering of the SVG concatenation determines the drawing order rather than any concept of a z-index. + +This EIP only specifies the rendering of the rendered SVG NFT and does not require any specific ordering when composing. This allows the SVG NFT to use a rendered SVG NFT as a foreground or a background as required. + +### Alternatives to concatenation + +SVG specifies a `link` tag. Linking could allow for complex SVGs to be composed but would require creating a URI format and then getting ecosystem adoption. As string concatenation of SVG's is already supported, the simpler approach of concatenation is used. + +### Sizing + +This EIP doesn't specify any requirements on the size of the rendered SVG. Any scaling based on sizing can be performed by the SVG NFT as required. + +### Render function name + +The render function is named `renderTokenById` as this function name was first used by Loogies and allows existing deployed NFTs to be compatible with this EIP. + +## Backwards Compatibility +This EIP has no backwards compatibility concerns + + +## Security Considerations + +- SVG uses a "painters model" of rendering. A rendered SVG body could be added and completely obscure the existing SVG NFT artwork. +- SVG is XML and can contain malicious content, and while it won't impact the contract, it could impact the use of the SVG. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4885.md b/EIPS/eip-4885.md new file mode 100644 index 00000000000000..ce7959ecb5d8b5 --- /dev/null +++ b/EIPS/eip-4885.md @@ -0,0 +1,201 @@ +--- +eip: 4885 +title: Subscription NFTs and Multi Tokens +description: An interface for subscription tokens that gives holders subscriptions to NFTs and multi tokens +author: Jules Lai (@julesl23) +discussions-to: https://ethereum-magicians.org/t/eip-subscription-token-standard/8531 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-03-08 +requires: 165, 721, 1155 +--- + +## Abstract + +The following standard allows for the implementation of a standard API for subscribing to non-fungible and multi tokens. [EIP-20](./eip-20.md) tokens are deposited in exchange for subscription tokens that give the right to use said non-fungible and multi tokens for a specified time limited or unlimited period. + +## Motivation + +This standard offers a flexible, general purpose way to subscribe to the use of assets or services offered by [EIP-721](./eip-721.md) or [EIP-1155](./eip-1155.md) contracts. From here on in, for the sake of simplicity, these contracts will be known as NFTs; the provider is the issuer of said NFTs and the subscriber(s) uses them. + +This proposal was originally conceived from the want to give creators of music and film, back control. The distribution and delivery of digital content is currently the purview of centralised tech corporations who offer homogeneous subscription models to their customers. This proposal specifies a standard for dapp developers to give creators the ability to set their own custom subscription models and hence, open up new revenue streams that can lead to decentralised distribution and delivery models. + +Use cases include any sort of periodic (e.g. daily, weekly, monthly, quarterly, yearly/annual, or seasonal) use of or access to assets or services such as: + +- Subscriptions for streaming music, video, e-learning or book/news services +- Sharing of digital assets among subscribers +- Club memberships such as health clubs +- Season tickets for sports and e-sports +- Agreement between parties to exchange fixed rate subscription stream with variable income in DeFi +- Renting in-game assets +- Etc. + +The subscription token borrows a few functions from the EIP-20 specification. An implementer is free to implement the rest of the standard; allowing for example subscription tokens to be transferred in secondary markets, sent as gifts or for refunds etc. + +## Specification + +The subscriber deposits EIP-20 to receive an NFT and subscription. Subscription tokens balance automatically decreases linearly over the lifetime of usage of the NFT, and use of the NFT is disabled once the subscription token balance falls to zero. The subscriber can top up the balance to extend the lifetime of the subscription by depositing EIP-20 tokens in exchange for more subscription tokens. + +Smart contracts implementing this EIP standard MUST implement the [EIP-165](./eip-165.md) supportsInterface function and MUST return the constant value true if 0xC1A48422 is passed through the interfaceID argument. Note that revert in this document MAY mean a require, throw (not recommended as depreciated) or revert solidity statement with or without error messages. + +```solidity +interface ISubscriptionToken { + /** + @dev This emits when the subscription token constructor or initialize method is + executed. + @param name The name of the subscription token + @param symbol The symbol of the subscription token + @param provider The provider of the subscription whom receives the deposits + @param subscriptionToken The subscription token contract address + @param baseToken The ERC-20 compatible token to use for the deposits. + @param nft Address of the `nft` contract that the provider mints/transfers from. + All tokenIds referred to in this interface MUST be token instances of this `nft` contract. + */ + event InitializeSubscriptionToken( + string name, + string symbol, + address provider, + address indexed subscriptionToken, + address indexed baseToken, + address indexed nft, + string uri + ); + + /** + @dev This emits for every new subscriber to `nft` contract of token `tokenId`. + `subscriber` MUST have received `nft` of token `tokenId` in their account. + @param subscriber The subscriber account + @param tokenId MUST be token id of `nft` sent to `subscriber` + @param uri MUST be uri of the `nft` that was sent to `subscriber` or empty string + */ + event SubscribeToNFT( + address indexed subscriber, + uint256 indexed tokenId, + string uri + ); + + /** + @dev Emits when `subscriber` deposits ERC-20 of token type `baseToken` via the `deposit method. + This tops up `subscriber` balance of subscription tokens + @param depositAmount The amount of ERC-20 of type `baseToken` deposited + @param subscriptionTokenAmount The amount of subscription tokens sent in exchange to `subscriber` + @param subscriptionPeriod Amount of additional time in seconds subscription is extended + */ + event Deposit( + address indexed subscriber, + uint256 indexed tokenId, + uint256 depositAmount, + uint256 subscriptionTokenAmount, + uint256 subscriptionPeriod + ); + + /** + @return The name of the subscription token + */ + function name() external view returns (string memory); + + /** + @return The symbol of the subscription token + */ + function symbol() external view returns (string memory); + + /** + @notice Subscribes `subscriber` to `nft` of 'tokenId'. `subscriber` MUST receive `nft` + of token `tokenId` in their account. + @dev MUST revert if `subscriber` is already subscribed to `nft` of 'tokenId' + MUST revert if 'nft' has not approved the `subscriptionToken` contract address as operator. + @param subscriber The subscriber account. MUST revert if zero address. + @param tokenId MUST be token id of `nft` contract sent to `subscriber` + `tokenId` emitted from event `SubscribeToNFT` MUST be the same as tokenId except when + tokenId is zero; allows OPTIONAL tokenid that is then set internally and minted by + `nft` contract + @param uri The OPTIONAL uri of the `nft`. + `uri` emitted from event `SubscribeToNFT` MUST be the same as uri except when uri is empty. + */ + function subscribeToNFT( + address subscriber, + uint256 tokenId, + string memory uri + ) external; + + /** + @notice Top up balance of subscription tokens held by `subscriber` + @dev MUST revert if `subscriber` is not subscribed to `nft` of 'tokenId' + MUST revert if 'nft' has not approved the `subscriptionToken` contract address as operator. + @param subscriber The subscriber account. MUST revert if zero address. + @param tokenId The token id of `nft` contract to subscribe to + @param depositAmount The amount of ERC-20 token of contract address `baseToken` to deposit + in exchange for subscription tokens of contract address `subscriptionToken` + */ + function deposit( + address subscriber, + uint256 tokenId, + uint256 depositAmount + ) external payable; + + /** + @return The balance of subscription tokens held by `subscriber`. + RECOMMENDED that the balance decreases linearly to zero for time limited subscriptions + RECOMMENDED that the balance remains the same for life long subscriptions + MUST return zero balance if the `subscriber` does not hold `nft` of 'tokenId' + MUST revert if subscription has not yet started via the `deposit` function + When the balance is zero, the use of `nft` of `tokenId` MUST NOT be allowed for `subscriber` + */ + function balanceOf(address subscriber) external view returns (uint256); +} +``` + +### Subscription token balances + +An example implementation mints an amount of subscription token that totals to one subscription token per day of the subscription period length paid for by the subscriber; for example a week would be for seven subscription tokens. The subscription token balance then decreases automatically at a rate of one token per day continuously and linearly over time until zero. The `balanceOf` function can be implemented lazily by calculating the amount of subscription tokens left only when it is called as a view function, thus has no gas cost. + +### Subscription token price + +Subscription token price paid per token per second can be calculated from the `Deposit` event parameters as +`depositAmount` / (`subscriptionTokenAmount` \* `subscriptionPeriod`) + +### NFT metadata + +The NFT's metadata can store information of the asset/service offered to the subscriber by the provider for the duration of the subscription. This MAY be the terms and conditions of the agreed subscription service offered by the provider to the subscriber. It MAY also be the metadata of the NFT asset if this is offered directly. This standard is kept purposely general to cater for many different use cases of NFTs. + +### Subscription expiry + +When the subscription token balance falls to zero for a subscriber (signifying that the subscription has expired) then it is up to the implementer on how to handle this for their particular use case. For example, a provider may stop streaming media service to a subscriber. For an NFT that represents an image stored off-chain, perhaps the NFT's `uri` function no longer returns back a link to its metadata. + +### Caveats + +With some traditional subscription models based on fiat currencies, the subscribers' saved payment credentials are used to automatically purchase to extend the subscription period, at or just before expiry. This feature is not possible in this proposal specification as recurring payments will have to have allowance approved for signed by a subscriber for each payment when using purely cryptocurrencies. + +This proposal does not deal with pausing subscriptions directly, implementers can write their own or inherit off 3rd party smart contract abstractions such as OpenZeppelin's Pausable. In that case, `balanceOf` method would need extra logic and storage to account for the length of time the subscription tokens were paused. + +## Rationale + +### Tokenisation of subscriptions + +The subscription itself has value when it is exchanged for a deposit. This proposal enables subscriptions to be 'tokenised' thus secondary markets can exist where the subscription tokens can be bought and sold. For example, a fan might want to sell their season ticket, that gives access to live sporting events, on to another fan. This would not be as easily possible if there was only a date expiry extension feature added to NFTs. +An implementer can simply implement the rest of the EIP-20 functions for subscription tokens to be traded. It is left to the implementer to decide if the subscription service offered is non-fungible or fungible. If non-fungible then buying the subscription tokens would simply give the same period left to expiration. If fungible and the purchaser already had an existing subscription for the same service then their total subscription period can be extended by the amount of subscription tokens bought. + +### Cater for current and future uses of NFTs + +This proposal purposely keeps `tokenId` and `uri` optional in the `subcribeToNFT` method to keep the specification general purpose. Some use cases such as pre-computed image NFT collections don't require a different 'uri', just a different `tokenId` for each NFT. However, in other use cases such as those that require legal contracts between both parties, individual `uri` links are probably required as the NFT's metadata may require information from both parties to be stored on immutable storage. + +### Giving back users control + +Traditional subscription models, particularly with streaming services, control of the subscription model is totally with that of the central service provider. This proposal gives decentralised services a standard way to give control back to their users. Hence each user is able to develop their own subscription eco system and administer it towards one that suits theirs and their subscribers' needs. + +## Backwards Compatibility + +A subscription token contract can be fully compatible with EIP-20 specification to allow, for example, transfers from one subscriber to another subscriber or user. EIP-20 methods `name`, `symbol` and `balanceOf` are already part of the specification of this proposal, and it is left to the implementer to choose whether to implement the rest of EIP-20's interface by considering their own use case. + +Use of subscription tokens is in effect an indirect way to control the lifetime of an NFT. As such it is assumed that this arrangement would work best when the NFTs and subscription token contracts subscribing to the NFTs, are deployed by the same platform or decentralised app. It MUST NOT have an impact or dependencies to existing NFTs that have not approved the subscription token as an operator. Indeed in this case, any other parties wouldn't be aware of and any NFT lifetime dependencies will be ignored, hence should not work anyway. To this end, this proposal specifies that the 'nft' MUST have approved the `subscriptionToken` contract address as operator. + +## Security Considerations + +It is normal for service providers to receive subscriber payments upfront before the subscriber gets to use the service. Indeed this proposal via the `deposit` method follows this remit. It would therefore be possible that a service provider sets up, receives the deposits and then does not provide or provides the service poorly to its subscribers. This happens in the traditional world too and this proposal does not cover how to resolve this. + +The `subscribeToNFT` method takes a parameter `uri` link to the `nft` metadata. It is possible if stored on centralised storage that the owners can change the metadata, or perhaps the metadata is hacked which is an issue with vanilla NFT contracts too. But because the `uri` is provided at the time of subscription rather then deployment, it is RECOMMENDED that where the use case requires, implementers ensure that the `uri` link is to immutable storage. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4886.md b/EIPS/eip-4886.md new file mode 100644 index 00000000000000..c9800feaf3b5f7 --- /dev/null +++ b/EIPS/eip-4886.md @@ -0,0 +1,295 @@ +--- +eip: 4886 +title: Proxy Ownership Register +description: A proxy ownership register allowing trustless proof of ownership between Ethereum addresses, with delegated asset delivery +author: Omnus Sunmo (@omnus) +discussions-to: https://ethereum-magicians.org/t/eip-4886-a-proxy-ownership-and-asset-delivery-register/8559 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-09-03 +--- + +## Abstract + +A proxy protocol that allows users to nominate a proxy address to act on behalf of another wallet address, together with a delivery address for new assets. Smart contracts and applications making use of the protocol can take a proxy address and lookup holding information for the nominator address. This has a number of practical applications, including allowing users to store valuable assets safely in a cold wallet and interact with smart contracts using a proxy address of low value. The assets in the nominator are protected as all contract interactions take place with the proxy address. This eliminates a number of exploits seen recently where users' assets are drained through a malicious contract interaction. In addition, the register holds a delivery address, allowing new assets to be delivered directly to a cold wallet address. + +## Motivation + +To make full use of Ethereum users often need to prove their ownership of existing assets. For example: + * Discord communities require users to sign a message with their wallet to prove they hold the tokens or NFTs of that community. + * Whitelist events (for example recent airdrops, or NFT mints), require the user to interact using a given address to prove eligibility. + * Voting in DAOs and other protocols require the user to sign using the address that holds the relevant assets. + + There are more examples, with the unifying theme being that the user must make use of the address with the assets to derive the platform benefit. This means the addresses holding these assets cannot be truly 'cold', and is a gift to malicious developers seeking to steal valuable assets. For example, a new project can offer free NFTs to holders of an existing NFT asset. The existing holders have to prove ownership by minting from the wallet with the asset that determined eligibility. This presents numerous possible attack vectors for a malicious developer who knows that all users interacting with the contract have an asset of that type. + + Possibly even more damaging is the effect on user confidence across the whole ecosystem. Users become reluctant to interact with apps and smart contracts for fear of putting their assets at risk. They may also decide not to store assets in cold wallet addresses as they need to prove they own them on a regular basis. A pertinent example is the user trying to decide whether to 'vault' their NFT and lose access to a discord channel, or keep their NFT in another wallet, or even to connect their 'vault' to discord. + + Ethereum is amazing at providing trustless proofs. The *only* time a user should need to interact using the wallet that holds an asset is if they intend to sell or transfer that asset. If a user merely wishes to prove ownership (to access a resource, get an airdrop, mint an NFT, or vote in a DAO), they should do this through a trustless proof stored on-chain. + + Furthermore, users should be able to decide where new assets are delivered, rather than them being delivered to the wallet providing the interaction. This allows hot wallets to acquire assets sent directly to a cold wallet 'vault', possibly even the one they are representing in terms of asset ownership. + + The aim of this EIP is to provide a convenient method to avoid this security concern and empower more people to feel confident leveraging the full scope of Ethereum functionality. Our vision is an Ethereum where users setup a new hardware wallet for assets they wish to hold long-term, then make one single contract interaction with that wallet: to nominate a hot wallet proxy. That user can always prove they own assets on that address, and they can specify it as a delivery address for new asset delivery. + +## 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. + +### Definitions + + * Delivery address: The address that assets will be delivered to for the current Proxy Record, i.e. a new NFT minted by the Proxy address, representing the Nominator address, should be delivered to the Delivery address. + * Nomination: Where a Nominator has nominated a Proxy address. Will only be active when the Proxy has accepted the nomination. + * Nominator address: The address that proposes a proxy relationship. This address nominates another address to act as its proxy, representing it and its holdings in all interactions. + * Proxy address: The address that will represent a Nominator on-chain. + * Proxy Record: An active proxy relationship encompassing a Nominator, Proxy and Delivery. + * Register: The main EPS contract, which holds details of both Nominations and Proxy Records. + +### EPS Specification + +There are two main parts to the register - a nomination and a proxy record: + + Contract / Dapp Register + + Nominator: 0x1234.. Nominator: 0x1234.. + Proxy: 0x5678.. ---------> Proxy: 0x4567.. + Delivery: 0x9876.. + +The first step to creating a proxy record is for an address to nominate another address as its proxy. This creates a nomination that maps the nominator (the address making the nomination) to the proposed proxy address. + +This is not a proxy record on the register at this stage, as the proxy address needs to first accept the nomination. Until the nomination is accepted it can be considered to be pending. Once the proxy address has accepted the nomination a proxy record is added to the register. + +When accepting a nomination the proxy address sets the delivery address for that proxy record. The proxy address remains in control of updating that delivery address as required. Both the nominator and proxy can delete the proxy record and nomination at any time. The proxy will continue forever if not deleted - it is eternal. + +The register is a single smart contract that stores all nomination and register records. The information held for each is as follows: + * Nomination: + * The address of the Nominator + * The address of the Proposed Proxy + +* Proxy Record: + * The address of the Nominator + * The address of the Proxy + * The delivery address for proxied deliveries + +Any address can act as a Nominator or a Proxy. A Nomination must have been made first in order for an address to accept acting as a Proxy. + +A Nomination cannot be made to an address that is already active as either a Proxy or a Nominator, i.e. that address is already in an active proxy relationship. + +The information for both Nominations and Proxy records is held as a mapping. For the Nomination this is address => address for the Nominator to the Proxy address. For the Proxy Record the mapping is from address => struct for the Proxy Address to a struct containing the Nominator and Delivery address. + +Mapping between an address and its Nominator and Delivery address is a simple process as shown below: + + Contract / Dapp Register + + | | + |------------- 0x4567..---------------> | + | | + | <-------nominator: 0x1234..---------- | + | delivery: 0x9876.. | + | | + +The protocol is fully backwards compatible. If it is passed an address that does not have an active mapping it will pass back the received address as both the Nominator and Delivery address, thereby preserving functionality as the address is acting on its own behalf. + + Contract / Dapp Register + + | | + |------------- 0x0222..---------------> | + | | + | <-------nominator: 0x0222..---------- | + | delivery: 0x0222.. | + | | + +If the EPS register is passed the address of a Nominator it will revert. This is of vital importance. The purpose of the proxy is that the Proxy address is operating on behalf of the Nominator. The Proxy address therefore can derive the same benefits as the Nominator (for example discord roles based on the Nominator's holdings, or mint NFTs that require another NFT to be held). It is therefore imperative that the Nominator in an active proxy cannot also interact and derive these benefits, otherwise two addresses represent the same holding. A Nominator can of course delete the Proxy Record at any time and interact on it's own behalf, with the Proxy address instantly losing any benefits associated with the proxy relationship. + +### Solidity Interface Definition + +**Nomination Exists** + + function nominationExists(address _nominator) external view returns (bool); + +Returns true if a Nomination exists for the address specified. + +**Nomination Exists for Caller** + + function nominationExistsForCaller() external view returns (bool); + +Returns true if a Nomination exists for the msg.sender. + +**Proxy Record Exists** + + function proxyRecordExists(address _proxy) external view returns (bool); + +Returns true if a Proxy Record exists for the passed Proxy address. + +**Proxy Record Exists for Caller** + + function proxyRecordExistsForCaller() external view returns (bool); + +Returns true if a Proxy Record exists for the msg.sender. + +**Nominator Record Exists** + + function nominatorRecordExists(address _nominator) external view returns (bool); + +Returns true if a Proxy Record exists for the passed Nominator address. + +**Nominator Record Exists for Caller** + + function nominatorRecordExistsForCaller() external view returns (bool); + +Returns true if a Proxy Record exists for the msg.sender. + +**Get Proxy Record** + + function getProxyRecord(address _proxy) external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for a passed Proxy address. + +**Get Proxy Record for Caller** + + function getProxyRecordForCaller() external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for msg.sender as Proxy address. + +**Get Nominator Record** + + function getNominatorRecord(address _nominator) external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for a passed Nominator address. + +**Get Nominator Record for Caller** + + function getNominatorRecordForCaller() external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for msg.sender address as Nominator. + +**Address Is Active** + + function addressIsActive(address _receivedAddress) external view returns (bool); + +Returns true if the passed address is Nominator or Proxy address on an active Proxy Record. + +**Address Is Active for Caller** + + function addressIsActiveForCaller() external view returns (bool); + +Returns true if msg.sender is Nominator or Proxy address on an active Proxy Record. + +**Get Nomination** + +function getNomination(address _nominator) external view returns (address proxy); + +Returns the proxy address for a Nomination when passed a Nominator. + +**Get Nomination for Caller** + +function getNominationForCaller() external view returns (address proxy); + +Returns the proxy address for a Nomination if msg.sender is a Nominator + +**Get Addresses** + + function getAddresses(address _receivedAddress) external view returns (address nominator, address delivery, bool isProxied); + +Returns the Nominator, Proxy, Delivery and a boolean isProxied for the passed address. If you pass an address that is not a Proxy address it will return address(0) for the Nominator, Proxy and Delivery address and isProxied of false. If you pass an address that is a Proxy address it will return the relvant addresses and isProxied of true. + +**Get Addresses for Caller** + + function getAddressesForCaller() external view returns (address nominator, address delivery, bool isProxied); + +Returns the Nominator, Proxy, Delivery and a boolean isProxied for msg.sender. If msg.sender is not a Proxy address it will return address(0) for the Nominator, Proxy and Delivery address and isProxied of false. If msg.sender is a Proxy address it will return the relvant addresses and isProxied of true. + +**Get Role** + + function getRole(address _roleAddress) external view returns (string memory currentRole); + +Returns a string value with a role for the passed address. Possible roles are: + +None The address does not appear on the Register as either a Record or a Nomination. + +Nominator - Pending The address is the Nominator on a Nomination which has yet to be accepted by the nominated Proxy address. + +Nominator - Active The address is a Nominator on an active Proxy Record (i.e. the Nomination has been accepted). + +Proxy - Active The address is a Proxy on an active Proxy Record. + +**Get Role for Caller** + + function getRoleForCaller() external view returns (string memory currentRole); + +Returns a string value with a role for msg.sender. Possible roles are: + +None The msg.sender does not appear on the Register as either a Record or a Nomination. + +Nominator - Pending The msg.sender is the Nominator on a Nomination which has yet to be accepted by the nominated Proxy address. + +Nominator - Active The msg.sender is a Nominator on an active Proxy Record (i.e. the Nomination has been accepted). + +Proxy - Active The msg.sender is a Proxy on an active Proxy Record. + +**Make Nomination** + + function makeNomination(address _proxy, uint256 _provider) external payable; + +Can be passed a Proxy address to create a Nomination for the msg.sender. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Accept Nomination** + + function acceptNomination(address _nominator, address _delivery, uint256 _provider) external; + +Can be passed a Nominator and Delivery address to accept a Nomination for a msg.sender. Note that to accept a Nomination the Nomination needs to exists with the msg.sender as the Proxy. The Nominator passed to the function and that on the Nomination must match. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Update Delivery Record** + + function updateDeliveryAddress(address _delivery, uint256 _provider) external; + +Can be passed a new Delivery address where the msg.sender is the Proxy on a Proxy Record. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Delete Record by Nominator** + + function deleteRecordByNominator(uint256 _provider) external; + +Can be called to delete a Record and Nomination when the msg.sender is a Nominator. + +Note that when both a Record and Nomination exist both are deleted. If no Record exists (i.e. the Nomination hasn't been accepted by the Proxy address) the Nomination is deleted. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Delete Record by Proxy** + + function deleteRecordByProxy(uint256 _provider) external; + +Can be called to delete a Record and Nomination when the msg.sender is a Proxy. + +## Rationale + +The rationale for this EIP was to provide a way for all existing and future Ethereum assets to be have a 'beneficial owner' (the proxy) that is different to the address custodying the asset. The use of a register to achieve this ensures that changes to existing tokens are not required. The register stores a trustless proof, signed by both the nominator and proxy, that can be relied upon as a true representation of asset ownership. + +## Backwards Compatibility + +The EIP is fully backwards compatible. + +## Test Cases + +The full SDLC for this proposal has been completed and it is operation at 0xfa3D2d059E9c0d348dB185B32581ded8E8243924 on mainnet, ropsten and rinkeby. The contract source code is validated and available on etherscan. The full unit test suite is available in `../assets/eip-4886/`, as is the source code and example implementations. + +## Reference Implementation + +Please see `../assets/eip-4886/contracts` + +## Security Considerations + +The core intention of the EIP is to improve user security by better safeguarding assets and allowing greater use of cold wallet storage. + +Potential negative security implications have been considered and none are envisaged. The proxy record can only become operational when a nomination has been confirmed by a proxy address, both addresses therefore having provided signed proof. + +From a usability perspective the key risk is in users specifying the incorrect asset delivery address, though it is noted that this burden of accuracy is no different to that currently on the network. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4895.md b/EIPS/eip-4895.md new file mode 100644 index 00000000000000..81af3b60b632bc --- /dev/null +++ b/EIPS/eip-4895.md @@ -0,0 +1,173 @@ +--- +eip: 4895 +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: Final +type: Standards Track +category: Core +created: 2022-03-10 +--- + +## Abstract + +Introduce a system-level "operation" to support validator withdrawals that are "pushed" from the beacon chain to the EVM. + +These operations create unconditional balance increases to the specified recipients. + +## Motivation + +This EIP provides a way for validator withdrawals made on the beacon chain to enter into the EVM. +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 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 with respect to the core protocol but does provide tighter integration of a critical feature into the protocol itself. + +## Specification + +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | 1681338455 | + +Beginning with the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST** introduce the following extensions to payload validation and processing: + +### System-level operation: withdrawal + +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 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, as a `uint64` value, on the consensus layer the withdrawal corresponds to +3. a recipient for the withdrawn ether `address` as a 20-byte value +4. a nonzero `amount` of ether given in Gwei (1e9 wei) as a `uint64` value. + +*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 + +The execution payload gains a new field for the `withdrawals` which is an RLP list of `Withdrawal` data. + +For example: + +```python +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] +``` + +This new field is encoded after the existing fields in the execution payload structure and is considered part of the execution payload's body. + +```python +execution_payload_rlp = RLP([header, transactions, [], withdrawals]) + +execution_payload_body_rlp = RLP([transactions, [], withdrawals]) +``` + +NOTE: the empty list in this schema is due to [EIP-3675](./eip-3675.md) that sets the `ommers` value to a fixed constant. + +### New field in the execution payload header: withdrawals root + +The execution payload header gains a new field committing to the `withdrawals` in the execution payload. + +This commitment is constructed identically to the transactions root in the existing execution payload header by inserting +each withdrawal into a Merkle-Patricia trie keyed by index in the list of `withdrawals`. + +```python +def compute_trie_root_from_indexed_data(data): + trie = Trie.from([(i, obj) for i, obj in enumerate(data)]) + return trie.root + +execution_payload_header.withdrawals_root = compute_trie_root_from_indexed_data(execution_payload.withdrawals) +``` + +The execution payload header is extended with a new field containing the 32 byte root of the trie committing to the list of withdrawals provided in a given execution payload. + +To illustrate: + +```python +execution_payload_header_rlp = RLP([ + parent_hash, + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash + coinbase, + state_root, + txs_root, + receipts_root, + logs_bloom, + 0, # difficulty + number, + gas_limit, + gas_used, + timestamp, + extradata, + prev_randao, + 0x0000000000000000, # nonce + base_fee_per_gas, + withdrawals_root, +]) +``` + +NOTE: field names and constant value in this example reflect [EIP-3675](./eip-3675.md) and [EIP-4399](./eip-4399.md). Refer to those EIPs for further information. + +### Execution payload validity + +Assuming the execution payload is well-formatted, the execution client has an additional payload validation to ensure that the `withdrawals_root` matches the expected value given the list in the payload. + +```python +assert execution_payload_header.withdrawals_root == compute_trie_root_from_indexed_data(execution_payload.withdrawals) +``` + +### State transition + +The `withdrawals` in an execution payload are processed **after** any user-level transactions are applied. + +For each `withdrawal` in the list of `execution_payload.withdrawals`, the implementation increases the balance of the `address` specified by the `amount` given. + +Recall that the `amount` is given in units of Gwei so a conversion to units of wei must be performed when working with account balances in the execution state. + +This balance change is unconditional and **MUST** not fail. + +This operation has no associated gas costs. + +## Rationale + +### Why not a new transaction type? + +This EIP suggests a new type of object -- the "withdrawal operation" -- as it has special semantics different from other existing types of EVM transactions. + +Operations are initiated by the overall system, rather than originating from end users like typical transactions. + +An entirely new type of object firewalls off generic EVM execution from this type of processing to simplify testing and security review of withdrawals. + +### Why no (gas) costs for the withdrawal type? + +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). + +### Why only balance updates? No general EVM execution? + +More general processing introduces the risk of failures, which complicates accounting on the beacon chain. + +This EIP suggests a route for withdrawals that provides most of the benefits for a minimum of the (complexity) cost. + +## Backwards Compatibility + +No issues. + +## Security Considerations + +Consensus-layer validation of withdrawal transactions is critical to ensure that the proper amount of ETH is withdrawn back into the execution layer. +This consensus-layer to execution-layer ETH transfer does not have a current analog in the EVM and thus deserves very high security scrutiny. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4906.md b/EIPS/eip-4906.md new file mode 100644 index 00000000000000..93e6492058a451 --- /dev/null +++ b/EIPS/eip-4906.md @@ -0,0 +1,93 @@ +--- +eip: 4906 +title: EIP-721 Metadata Update Extension +description: Add a MetadataUpdate event to EIP-721. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug , Nathan +discussions-to: https://ethereum-magicians.org/t/eip4906-erc-721-erc-1155-metadata-update-extension/8588 +status: Final +type: Standards Track +category: ERC +created: 2022-03-13 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It adds a `MetadataUpdate` event to EIP-721 tokens. + +## Motivation + +Many [EIP-721](./eip-721.md) contracts emit an event when one of its tokens' metadata are changed. While tracking changes based on these different events is possible, it is an extra effort for third-party platforms, such as an NFT marketplace, to build individualized solutions for each NFT collection. + +Having a standard `MetadataUpdate` event will make it easy for third-party platforms to timely update the metadata of many NFTs. + +## 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. + +The **metadata update extension** is OPTIONAL for EIP-721 contracts. + + +```solidity +/// @title EIP-721 Metadata Update Extension +interface IERC4906 is IERC165, IERC721 { + /// @dev This event emits when the metadata of a token is changed. + /// So that the third-party platforms such as NFT market could + /// timely update the images and related attributes of the NFT. + event MetadataUpdate(uint256 _tokenId); + + /// @dev This event emits when the metadata of a range of tokens is changed. + /// So that the third-party platforms such as NFT market could + /// timely update the images and related attributes of the NFTs. + event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); +} +``` + +The `MetadataUpdate` or `BatchMetadataUpdate` event MUST be emitted when the JSON metadata of a token, or a consecutive range of tokens, is changed. + +Not emitting `MetadataUpdate` event is RECOMMENDED when a token is minted. + +Not emitting `MetadataUpdate` event is RECOMMENDED when a token is burned. + +Not emitting `MetadataUpdate` event is RECOMMENDED when the tokenURI changes but the JSON metadata does not. + +The `supportsInterface` method MUST return `true` when called with `0x49064906`. + +## Rationale + +Different NFTs have different metadata, and metadata generally has multiple fields. `bytes data` could be used to represents the modified value of metadata. It is difficult for third-party platforms to identify various types of `bytes data`, so as to avoid unnecessary complexity, arbitrary metadata is not included in the `MetadataUpdate` event. + +After capturing the `MetadataUpdate` event, a third party can update the metadata with information returned from the `tokenURI(uint256 _tokenId)` of EIP-721. When a range of token ids is specified, the third party can query each token URI individually. + +## Backwards Compatibility + +No backwards compatibility issues were found + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC4906.sol"; + +contract ERC4906 is ERC721, IERC4906 { + + constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) { + } + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == bytes4(0x49064906) || super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +If there is an off-chain modification of metadata, a method that triggers `MetadataUpdate` can be added, but ensure that the function's permission controls are correct. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4907.md b/EIPS/eip-4907.md new file mode 100644 index 00000000000000..ed02d6cc76169a --- /dev/null +++ b/EIPS/eip-4907.md @@ -0,0 +1,249 @@ +--- +eip: 4907 +title: Rental NFT, an Extension of EIP-721 +description: Add a time-limited role with restricted permissions to EIP-721 tokens. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug +discussions-to: https://ethereum-magicians.org/t/idea-erc-721-user-and-expires-extension/8572 +status: Final +type: Standards Track +category: ERC +created: 2022-03-11 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It proposes an additional role (`user`) which can be granted to addresses, and a time where the role is automatically revoked (`expires`). The `user` role represents permission to "use" the NFT, but not the ability to transfer it or set users. + +## Motivation + +Some NFTs have certain utilities. For example, virtual land can be "used" to build scenes, and NFTs representing game assets can be "used" in-game. In some cases, the owner and user may not always be the same. There may be an owner of the NFT that rents it out to a “user”. The actions that a “user” should be able to take with an NFT would be different from the “owner” (for instance, “users” usually shouldn’t be able to sell ownership of the NFT).  In these situations, it makes sense to have separate roles that identify whether an address represents an “owner” or a “user” and manage permissions to perform actions accordingly. + +Some projects already use this design scheme under different names such as “operator” or “controller” but as it becomes more and more prevalent, we need a unified standard to facilitate collaboration amongst all applications. + +Furthermore, applications of this model (such as renting) often demand that user addresses have only temporary access to using the NFT. Normally, this means the owner needs to submit two on-chain transactions, one to list a new address as the new user role at the start of the duration and one to reclaim the user role at the end. This is inefficient in both labor and gas and so an “expires” function is introduced that would facilitate the automatic end of a usage term without the need of a second transaction. + +## 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 with NatSpec & OpenZeppelin v4 Interfaces (also available at [`IERC4907.sol`](../assets/eip-4907/contracts/IERC4907.sol)): + +```solidity +interface IERC4907 { + + // Logged when the user of an NFT is changed or expires is changed + /// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed + /// The zero address for user indicates that there is no user address + event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires); + + /// @notice set the user and expires of an NFT + /// @dev The zero address indicates there is no user + /// Throws if `tokenId` is not valid NFT + /// @param user The new user of the NFT + /// @param expires UNIX timestamp, The new user could use the NFT before expires + function setUser(uint256 tokenId, address user, uint64 expires) external; + + /// @notice Get the user address of an NFT + /// @dev The zero address indicates that there is no user or the user is expired + /// @param tokenId The NFT to get the user address for + /// @return The user address for this NFT + function userOf(uint256 tokenId) external view returns(address); + + /// @notice Get the user expires of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user expires for + /// @return The user expires for this NFT + function userExpires(uint256 tokenId) external view returns(uint256); +} +``` + +The `userOf(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `userExpires(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `setUser(uint256 tokenId, address user, uint64 expires)` function MAY be implemented as `public` or `external`. + +The `UpdateUser` event MUST be emitted when a user address is changed or the user expires is changed. + +The `supportsInterface` method MUST return `true` when called with `0xad092b5c`. + +## Rationale + +This model is intended to facilitate easy implementation. Here are some of the problems that are solved by this standard: + +### Clear Rights Assignment + +With Dual “owner” and “user” roles, it becomes significantly easier to manage what lenders and borrowers can and cannot do with the NFT (in other words, their rights). Additionally, owners can control who the user is and it’s easy for other projects to assign their own rights to either the owners or the users. + +### Simple On-chain Time Management + +Once a rental period is over, the user role needs to be reset and the “user” has to lose access to the right to use the NFT. This is usually accomplished with a second on-chain transaction but that is gas inefficient and can lead to complications because it’s imprecise. With the `expires` function, there is no need for another transaction because the “user” is invalidated automatically after the duration is over. + +### Easy Third-Party Integration + +In the spirit of permission less interoperability, this standard makes it easier for third-party protocols to manage NFT usage rights without permission from the NFT issuer or the NFT application. Once a project has adopted the additional `user` role and `expires`, any other project can directly interact with these features and implement their own type of transaction. For example, a PFP NFT using this standard can be integrated into both a rental platform where users can rent the NFT for 30 days AND, at the same time, a mortgage platform where users can use the NFT while eventually buying ownership of the NFT with installment payments. This would all be done without needing the permission of the original PFP project. + +## 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. + +## Test Cases + +### Test Contract +`ERC4907Demo` Implementation: [`ERC4907Demo.sol`](../assets/eip-4907/contracts/ERC4907Demo.sol) + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./ERC4907.sol"; + +contract ERC4907Demo is ERC4907 { + + constructor(string memory name, string memory symbol) + ERC4907(name,symbol) + { + } + + function mint(uint256 tokenId, address to) public { + _mint(to, tokenId); + } + +} +``` + +### Test Code +[test.js](../assets/eip-4907/test/test.js) + +```JavaScript +const { assert } = require("chai"); + +const ERC4907Demo = artifacts.require("ERC4907Demo"); + +contract("test", async accounts => { + + it("should set user to Bob", async () => { + // Get initial balances of first and second account. + const Alice = accounts[0]; + const Bob = accounts[1]; + + const instance = await ERC4907Demo.deployed("T", "T"); + const demo = instance; + + await demo.mint(1, Alice); + let expires = Math.floor(new Date().getTime()/1000) + 1000; + await demo.setUser(1, Bob, BigInt(expires)); + + let user_1 = await demo.userOf(1); + + assert.equal( + user_1, + Bob, + "User of NFT 1 should be Bob" + ); + + let owner_1 = await demo.ownerOf(1); + assert.equal( + owner_1, + Alice , + "Owner of NFT 1 should be Alice" + ); + }); +}); + + +``` + +run in Terminal: +``` +truffle test ./test/test.js +``` + +## Reference Implementation +Implementation: [`ERC4907.sol`](../assets/eip-4907/contracts/ERC4907.sol) +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC4907.sol"; + +contract ERC4907 is ERC721, IERC4907 { + struct UserInfo + { + address user; // address of user role + uint64 expires; // unix timestamp, user expires + } + + mapping (uint256 => UserInfo) internal _users; + + constructor(string memory name_, string memory symbol_) + ERC721(name_, symbol_) + { + } + + /// @notice set the user and expires of an NFT + /// @dev The zero address indicates there is no user + /// Throws if `tokenId` is not valid NFT + /// @param user The new user of the NFT + /// @param expires UNIX timestamp, The new user could use the NFT before expires + function setUser(uint256 tokenId, address user, uint64 expires) public virtual{ + require(_isApprovedOrOwner(msg.sender, tokenId), "ERC4907: transfer caller is not owner nor approved"); + UserInfo storage info = _users[tokenId]; + info.user = user; + info.expires = expires; + emit UpdateUser(tokenId, user, expires); + } + + /// @notice Get the user address of an NFT + /// @dev The zero address indicates that there is no user or the user is expired + /// @param tokenId The NFT to get the user address for + /// @return The user address for this NFT + function userOf(uint256 tokenId) public view virtual returns(address){ + if( uint256(_users[tokenId].expires) >= block.timestamp){ + return _users[tokenId].user; + } + else{ + return address(0); + } + } + + /// @notice Get the user expires of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user expires for + /// @return The user expires for this NFT + function userExpires(uint256 tokenId) public view virtual returns(uint256){ + return _users[tokenId].expires; + } + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override{ + super._beforeTokenTransfer(from, to, tokenId); + + if (from != to && _users[tokenId].user != address(0)) { + delete _users[tokenId]; + emit UpdateUser(tokenId, address(0), 0); + } + } +} +``` + +## Security Considerations + +This EIP standard can completely protect the rights of the owner, the owner can change the NFT user and expires at any time. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4910.md b/EIPS/eip-4910.md new file mode 100644 index 00000000000000..bd02bf70f7a21d --- /dev/null +++ b/EIPS/eip-4910.md @@ -0,0 +1,746 @@ +--- +eip: 4910 +title: Royalty Bearing NFTs +description: Extension of ERC-721 to correctly define, process, and pay (hierarchical) onchain NFT royalties. +author: Andreas Freund (@Therecanbeonlyone1969) +discussions-to: https://ethereum-magicians.org/t/royalty-bearing-nfts/8453 +status: Final +type: Standards Track +category: ERC +created: 2022-03-14 +requires: 165, 721 +--- + +## Abstract + +The proposal directly connects NFTs and royalties in a smart contract architecture extending the [ERC-721](./eip-721.md) standard, with the aim of precluding central authorities from manipulating or circumventing payments to those who are legally entitled to them. + +The proposal builds upon the OpenZeppelin Smart Contract Toolbox architecture, and extends it to include royalty account management (CRUD), royalty balance and payments management, simple trading capabilities -- Listing/De-Listing/Buying -- and capabilities to trace trading on exchanges. The royalty management capabilities allow for hierarchical royalty structures, referred to herein as royalty trees, to be established by logically connecting a "parent" NFT to its "children", and recursively enabling NFT "children" to have more children. + +## Motivation + +The management of royalties is an age-old problem characterized by complex contracts, opaque management, plenty of cheating and fraud. + +The above is especially true for a hierarchy of royalties, where one or more assets is derived from an original asset such as a print from an original painting, or a song is used in the creation of another song, or distribution rights and compensation are managed through a series of affiliates. + +In the example below, the artist who created the original is eligible to receive proceeds from every sale, and resale, of a print. + +![Fig1](../assets/eip-4910/eip-4910-print-families.png) + +The basic concept for hierarchical royalties utilizing the above "ancestry concept" is demonstrated in the figure below. + +![Fig2](../assets/eip-4910/eip-4910-royalties.png) + + +In order to solve for the complicated inheritance problem, this proposal breaks down the recursive problem of the hierarchy tree of depth N into N separate problems, one for each layer. This allows us to traverse the tree from its lowest level upwards to its root most efficiently. + +This affords creators, and the distributors of art derived from the original, the opportunity to achieve passive income from the creative process, enhancing the value of an NFT, since it now not only has intrinsic value but also comes with an attached cash flow. + +## 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. + +### Outline + +This proposal introduces several new concepts as extensions to the ERC-721 standard that warrant explanation: + +* **Royalty Account (RA)** + * A Royalty Account is attached to each NFT through its `tokenId` and consists of several sub-accounts which can be accounts of individuals or other RAs. A Royalty Account is identified by an account identifier. +* **Account Type** + * This specifies if an RA Sub Account belongs to an individual (user) or is another RA. If there is another RA as an RA Sub Account, the allocated balance needs to be reallocated to the Sub Accounts making up the referenced RA. +* **Royalty Split** + * The percentage each Sub Account receives based on a sale of an NFT that is associated with an RA +* **Royalty Balance** + * The royalty balance associated with an RA +* **Sub Account Royalty Balance** + * The royalty balance associated to each RA Sub Account. Note that only individual accounts can carry a balance that can be paid out. That means that if an RA Sub Account is an RA, its final Sub Account balance must be zero, since all RA balances must be allocated to individual accounts. +* **Token Type** + * Token Type is given as either ETH or the symbol of the supported utility tokens such as `DAI` +* **Asset ID** + * This is the `tokenId` the RA belongs to. +* **Parent** + * This indicates which `tokenId` is the immediate parent of the `tokenId` to which an RA belongs. + +Below a non-normative overview is given of the data structures and functionality that are covered by the requirements in this document. + +#### Data Structures + +In order to create an interconnected data structure linking NFTs to RAs certain global data structures are required: + +* A Royalty Account and associated Royalty Sub Accounts to establish the concept of a Royalty Account with sub accounts. +* Connecting a `tokenId` to a Royalty Account identifier. +* A structure mapping parent-to-child NFT relationships. +* A listing of token types and last validated balance (for trading and royalty payment purposes) +* A listing of registered payments to be made in the `executePayment` function and validated in `safeTransferFrom`. This is sufficient, because a payment once received and distributed in the `safeTransferFrom` function will be removed from the listing. +* A listing of NFTs to be sold + +#### Royalty Account Functions + +Definitions and interfaces for the Royalty Account RUD (Read-Update-Delete) functions. Because the RA is created in the minting function, there is no need to have a function to create a royalty account separately. + +#### Minting of a Royalty Bearing NFT + +When an NFT is minted, an RA must be created and associated with the NFT and the NFT owner, and, if there is an ancestor, with the ancestor's RA. To this end the specification utilizes the `_safemint` function in a newly defined `mint` function and applies various business rules on the input variables. + +#### Listing NFTs for Sale and removing a Listing + +Authorized user addresses can list NFTs for sale for non-exchange mediated NFT purchases. + +#### Payment Function from Buyer to Seller + +To avoid royalty circumvention, a buyer will always pay the NFT contract directly and not the seller. The seller is paid through the royalty distribution and can later request a payout. + +The payment process depends on whether the payment is received in ETH or an [ERC-20](./eip-20.md) token: + +* ERC-20 Token + 1. The Buyer must `approve` the NFT contract for the purchase price, `payment` for the selected payment token (ERC-20 contract address). + 2. For an ERC-20 payment token, the Buyer must then call the `executePayment` in the NFT contract -- the ERC-20 is not directly involved. +* For a non-ERC-20 payment, the Buyer must send a protocol token (ETH) to the NFT contract, and is required to send `msg.data` encoded as an array of purchased NFTs `uint256[] tokenId`. + +#### Modified NFT Transfer Function including required Trade data to allocate Royalties + +The input parameters must satisfy several requirements for the NFT to be transferred AFTER the royalties have been properly distributed. Furthermore, the ability to transfer more than one token at a time is also considered. + +The proposal defines: + +* Input parameter validation +* Payment Parameter Validation +* Distributing Royalties +* Update Royalty Account ownership with payout +* Transferring Ownership of the NFT +* Removing the Payment entry in `registeredPayment` after successful transfer + +Lastly, the approach to distributing royalties is to break down the hierarchical structure of interconnected Royalty Accounts into layers and then process one layer at time, where each relationship between a token and its ancestor is utilized to traverse the Royalty Account chain until the root ancestor and associated RA is reached. + +#### Paying out Royalties to the NFT Owner -- `from` address in `safeTransferFrom` Function + +This is the final part of the proposal. + +There are two versions of the payout function -- a `public` function and an `internal` function. + +The public function has the following interface: + +``` +function royaltyPayOut (uint256 tokenId, address RAsubaccount, address payable payoutAccount, uint256 amount) public virtual nonReentrant returns (bool) +``` + +where we only need the `tokenId`, the RA Sub Account address, `_RAsubaccount` which is the `owner`, and the amount to be paid out, `_amount`. Note that the function has `nonReentrant` modifier protection, because funds are being payed out. + +To finally send a Payout payment, the following steps need to be taken: + +* find the RA Sub Account based on `RAaccount` and the `subaccountPos` and extract the balance +* extract `tokenType` from the Sub Account +* based on the token type, send the payout payment (not exceeding the available balance) + +### Data Structures + +#### Royalty Account and Royalty Sub Accounts + +In order to create an interconnected data structure linking NFTs to RAs that is search optimized requires to make the following additions to the global data structures of an ERC-721. + +Note, a Royalty Account is defined as a collection of Royalty Sub Accounts linked to a meta account. This meta account is comprised of general account identifiers particular to the NFT it is linked to such as asset identifier, parent identifier etc. + +**[R1]** *One or more Royalty Sub-Account MUST be linked to a Royalty Account.* + +**[R2]** *The account identifier of a Royalty Account, `raAccountId`, MUST be unique.* + +**[R3]** *The `tokenId` of a NFT MUST be linked to a `raAccountID` in order to connect an `raAccountId` to a `tokenId`.* + + +#### Print (Child) NFTs + +The set of requirement to manage Parent-Child NFT Relationships and constraints at each level of the NFT (family) tree e.g. number of children permitted, NFT parents have to be linked to their immediate NFT children are as follows. + +**[R4]** *There MUST be a link for direct parent-child relationships* + +#### NFT Payment Tokens + +In order to capture royalties, an NFT contract must be involved in NFT trading. Therefore, the NFT contract needs to be aware of NFT payments, which in turn requires the NFT contract to be aware which tokens can be used for trading. + +**[R5]** *There MUST be a listing of supported token types* + +Since the NFT contract is managing royalty distributions and payouts as well as sales, it needs to track the last available balances of the allowed token types owned by the contract. + +**[R6]** *There MUST be a link of the last validated balance of an allowed token type in the contract to the respective allowed token contract.* + +#### NFT Listings and Payments + +Since the contract is directly involved in the sales process, a capability to list one or more NFTs for sale is required. + +**[R7]** *There MUST be a list of NFTs for sale.* + +**[R8]** *A sales listing MUST have a unique identifier.* + +Besides listings, the contract is required to manage sales as well. This requires the capability to register a payment, either for immediate execution or for later payment such as in an auction situation. + +**[R9]** *There MUST be a listing for registered payments* + +**[R10]** *A registered payment MUST have a unique identifier.* + +#### Contract Constructor and Global Variables and their update functions + +This standard extends the current ERC-721 constructor, and adds several global variables to recognize the special role of the creator of an NFT, and the fact that the contract is now directly involved in managing sales and royalties. + +**[R11]** *The minimal contract constructor MUST contain the following input elements.* + +``` +/// +/// @dev Definition of the contract constructor +/// +/// @param name as in ERC-721 +/// @param symbol as in ERC-721 +/// @param baseTokenURI as in ERC-721 +/// @param allowedTokenTypes is the array of allowed tokens for payment + +constructor( + string memory name, + string memory symbol, + string memory baseTokenURI, + address[] memory allowedTokenTypes + ) ERC721(name, symbol) {...} +``` + + +### Royalty Account Management + +Below are the definitions and interfaces for the Royalty Account RUD (Read-Update-Delete) functions. Since a Royalty Account is created in the NFT minting function, there is no need to have a separate function to create a royalty account. + +#### Get a Royalty Account + +There is only one get function required because a Royalty Account and its sub accounts can be retrieved through the `tokenId` in the `ancestry` field of the Royalty Account. + +**[R12]** *The `getRoyaltyAccount` function interface MUST adhere to the definition below:* + +``` +/// @dev Function to fetch a Royalty Account for a given tokenId +/// @param tokenId is the identifier of the NFT to which a Royalty Account is attached +/// @param RoyaltyAccount is a data structure containing the royalty account information +/// @param RASubAccount[] is an array of data structures containing the information of the royalty sub accounts associated with the royalty account + +function getRoyaltyAccount (uint256 tokenId) public view virtual returns (address, + RoyaltyAccount memory, + RASubAccount[] memory); +``` + + +**[R13]** *The following business rules MUST be enforced in the `getRoyaltyAccount` function:* + +* *`tokenId` exists and is not burned* + +#### Update a Royalty Account + +In order to update a Royalty Account, the caller must have both the 'tokenId' and the `RoyaltyAccount` itself which can be obtained from the Royalty Account getter function. + + +**[R14]** *The `updateRoyaltyAccount` function interface MUST adhere to the definition below:* + +``` +/// @dev Function to update a Royalty Account and its Sub Accounts +/// @param tokenId is the identifier of the NFT to which the Royalty Account to be updated is attached +/// @param RoyaltyAccount is the Royalty Account and associated Royalty Sub Accounts with updated values + +function updateRoyaltyAccount (uint256 _tokenId, `RoyaltyAccount memory _raAccount) public virtual returns (bool) +``` + +The update functionality of a Royalty Account, while straightforward, is also highly nuanced. To avoid complicated change control rules such as multi-signature rules, Royalty Account changes are kept simple. + +**[R15]** *The business rules for the update function are as follows:* + +1. *An NFTs asset identifier MUST NOT be changed.* +2. *An NFTs ancestor MUST NOT be updated.* +3. *An NFTs token type accepted for payment MUST NOT be updated.* +4. *The royalty balance in a Royalty Sub Account MUST NOT be changed.* +5. *The royalty split inherited by the children from the NFT parent MUST NOT be changed.* +6. *New royalty split values MUST be larger than, or less than, or equal to any established boundary value for royalty splits, if it exists.* +7. *The number of existing Royalty Sub Account plus the number of new Royalty Sub Accounts to be added MUST be smaller or equal to an established boundary value, if it exists.* +8. *The sum of all royalty splits across all existing and new Royalty Sub Accounts MUST equal to 1 or its equivalent numerical value at all times.* +9. *'msg.sender` MUST be equal to an account identifier in the Royalty Sub Account of the Royalty Account to be modified and that royalty sub account must be identified as not belonging to the parent NFT* + + 9.1 *the Sub Account belonging to the account identifier MUST NOT be removed* + + 9.2 *A royalty split MUST only be decreased, and either the existing sub account's royalty split MUST be increased accordingly such that the sum of all royalty splits remains equal to 1 or its numerical equivalent, or one or more new Royalty Sub Accounts MUST be added according to rule 10.* + + 9.3 *a royalty balance MUST NOT be changed* + + 9.4 *an account identifier MUST NOT be NULL* + +10. *If `msg.sender` is equal to the account identifier of one of the Sub Account owners which is not the parent NFT, an additional Royalty Sub Accounts MAY be added* + + 10.1 *if the royalty split of the Royalty Sub Account belonging to `msg.sender` is reduced* + + * then the royalty balance in each new Royalty Sub Account MUST be zero + + * and the sum of the new royalty splits data MUST be equal to the royalty split of the Royalty Sub Account of `msg.sender` before it was modified + + 10.2 *new account identifier MUST not be NULL* + +11. *If the Royalty Account update is correct, the function returns `true`, otherwise `false`.* + +#### Deleting a Royalty Account + +While sometimes deleting a Royalty Account is necessary, even convenient, it is a very costly function in terms of gas, and should not be used unless one is absolutely sure that the conditions enumerated below are met. + +**[R16]** *The `deleteRoyaltyAccount` function interface MUST adhere to the definition below:* + +``` +/// @dev Function to delete a Royalty Account +/// @param tokenId is the identifier of the NFT to which the Royalty Account to be updated is attached + +function deleteRoyaltyAccount (uint256 _tokenId) public virtual returns (bool) +``` + +**[R17]** *The business rules for this function are as follows:* + +* *`_tokenId` MUST be burned, i.e., have owner `address(0)`.* +* *all `tokenId` numbers genealogically related to `_tokenId` either as ancestors or offspring MUST also be burnt.* +* *all balances in the Royalty Sub Accounts MUST be zero.* + +### NFT Minting + +In extension to the ERC-721 minting capability, a Royalty Account with Royalty Sub Accounts are required to be added during the minting, besides establishing the NFT token specific data structures supporting constraints such as the maximum number of children an NFT can have. + +**[R18]** *When a new NFT is minted a Royalty Account with one or more Royalty Sub Accounts MUST be created and associated with the NFT and the NFT owner, and, if there is an ancestor, with the ancestor's Royalty Account.* + +To this end the specification utilizes the ERC-721 `_safemint` function in a newly defined `mint` function, and applies various business rules on the function's input variables. + +**[D1]** *Note, that the `mint` function SHOULD have the ability to mint more than one NFT at a time.* + +**[R19]** *Also, note that the `owner` of a new NFT MUST be the NFT contract itself.* + +**[R20]** *The non-contract owner of the NFT MUST be set as `isApproved` which allows the non-contract owner to operate just like the `owner`.* + +This strange choice in the two requirements above is necessary, because the NFT contract functions as an escrow for payments and royalties, and, hence, needs to be able to track payments received from buyers and royalties due to recipients, and to associate them with a valid `tokenId`. + +**[R21]** *For compactness of the input, and since the token meta data might vary from token to token the MUST be a minimal data structure containing:* + +``` +/// @param parent is the parent tokenId of the (child) token, and if set to 0 then there is no parent. +/// @param canBeParent indicates if a tokenId can have children or not. +/// @param maxChildren defines how many children an NFT can have. +/// @param royaltySplitForItsChildren is the royalty percentage split that a child has to pay to its parent. +/// @param uri is the unique token URI of the NFT +``` + +**[R22]** *The `mint` function interface MUST adhere to the definition below:* + +``` +/// @dev Function creates one or more new NFTs with its relevant meta data necessary for royalties, and a Royalty Account with its associated met data for `to` address. The tokenId(s) will be automatically assigned (and available on the emitted {IERC-721-Transfer} event). +/// @param to is the address to which the NFT(s) are minted +/// @param nfttoken is an array of struct type NFTToken for the meta data of the minted NFT(s) +/// @param tokenType is the type of allowed payment token for the NFT + +function mint(address to, NFTToken[] memory nfttoken, address tokenType) public virtual +``` + +**[R23]** *The following business rules for the `mint` function's input data MUST be fulfilled:* + +* *The number of tokens to be minted MUST NOT be zero.* +* *`msg.sender` MUST have either the `MINTER_ROLE` or the `CREATOR_Role` identifying the creator of the first NFT.* +* *`to` address MUST NOT be the zero address.* +* *`to` address MUST NOT be a contract, unless it has been whitelisted -- see [Security Considerations](#security-considerations) for more details.* +* *`tokenType` MUST be a token type supported by the contract.* +* *`royaltySplitForItsChildren` MUST be less or equal to 100% or numerical equivalent thereof less any constraints such as platform fees* +* *If the new NFT(s) cannot have children, `royaltySplitForItsChildren` MUST be zero.* +* *If the new NFT(s) has a parent, the parent NFT `tokenId` MUST exist.* +* *The ancestry level of the parent MUST be less than the maximum number of allowed NFT generations, if specified.* +* *The number of allowed children for an NFT to be minted MUST be less than the maximum number of allowed children, if specified.* + +### Listing and De-Listing of NFTs for Direct Sales + +In the sales process, we need to minimally distinguish two types of transactions + +* Exchange-mediated sales +* Direct sales + +The first type of transaction does not require that the smart contract is aware of a sales listing since the exchange contract will trigger payment and transfer transactions directly with the NFT contract as the owner. However, for the latter transaction type it is essential, since direct sales are required to be mediated at every step by the smart contract. + +**[R24]** *For direct sales, NFT listing, und de-listing, transactions MUST be executed through the NFT smart contract.* + +Exchange-mediated sales will be discussed when this document discusses payments. + +In direct sales, authorized user addresses can list NFTs for sale, see the business rules below. + +**[R25]** *The `listNFT` function interface MUST adhere to the definition below:* + +``` +/// @dev Function to list one or more NFTs for direct sales +/// @param tokenIds is the array of tokenIds to be included in the listing +/// @param price is the price set by the owner for the listed NFT(s) +/// @param tokenType is the payment token type allowed for the listing + +function listNFT (uint256[] calldata tokenIds, uint256 price, address tokenType) public virtual returns (bool) +``` + +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +**[R26]** *The business rules of the `listNFT` function are as follows:* + +* there MUST NOT already be a listing for one or more NFTs in the `listedNFT` mapping of the proposed listing. +* `seller` MUST be equal to `getApproved(tokenId[i])` for all NFTs in the proposed listing. +* `tokenType` MUST be supported by the smart contract. +* `price` MUST be larger than `0`. + +**[R27]** *If the conditions in [**[R26]**](#r26) are met, then the NFT sales list MUST be updated.* + +Authorized user addresses can also remove a direct sale listing of NFTs. + +**[R28]** *The `removeNFTListing` function interface MUST adhere to the definition below:* + +``` +/// @dev Function to de-list one or more NFTs for direct sales +/// @param listingId is the identifier of the NFT listing + +function removeNFTListing (uint256 listingId) public virtual returns (bool) +``` + +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +**[R29]** *The business rules of the `removeNFTListing` function below MUST be adhered to:* + +* *the registered payment entry MUST be NULL* +* *`msg.sender = getApproved(tokenId)` for the NFT listing* + +**[R30]** *If the conditions in [**[R29]**](#r29) are met, then the NFT sales listing MUST be removed.* + +### Payments for NFT Sales + +As noted before, a buyer will always pay the NFT contract directly and not the seller. The seller is paid through the royalty distribution and can later request a payout to their wallet. + +**[R31]** *The payment process requires either one or two steps:* + +1. *For an ERC-20 token* + * *The buyer MUST `approve` the NFT contract for the purchase price, `payment`, for the selected payment token type.* + * *The buyer MUST call the `executePayment` function.* +2. *For a protocol token* + * *The buyer MUST call a payment fallback function with `msg.data` not NULL.* + +**[R32]** *For an ERC-20 token type, the required `executePayment` function interface MUST adhere to the definition below*: + +``` +/// @dev Function to make a NFT direct sales or exchange-mediate sales payment +/// @param receiver is the address of the receiver of the payment +/// @param seller is the address of the NFT seller +/// @param tokenIds are the tokenIds of the NFT to be bought +/// @param payment is the amount of that payment to be made +/// @param tokenType is the type of payment token +/// @param trxnType is the type of payment transaction -- minimally direct sales or exchange-mediated + +function executePayment (address receiver, address seller, uint 256[] tokenIds, uint256 payment, string tokenType, int256 trxnType) public virtual nonReentrant returns (bool) +``` + +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +**[R33]** *Independent of `trxnType`, the business rules for the input data are as follows:* + +* *All purchased NFTs in the `tokenIds` array MUST exist and MUST NOT be burned.* +* *`tokenType` MUST be a supported token.* +* *`trxnType` MUST be set to either `0` (direct sale) or `1` (exchange-mediate sale), or another supported type.* +* *`receiver` MAY be NULL but MUST NOT be the Zero Address.* +* *`seller` MUST be the address in the corresponding listing.* +* *`msg.sender` MUST not be a contract, unless it is whitelisted in the NFT contract.* + +In the following, this document will only discuss the differences between the two minimally required transaction types. + +**[R34]** *For `trxnType = 0`, the payment data MUST to be validated against the listing, based on the following rules:* + +* *NFT(s) MUST be listed* +* *`payment` MUST be larger or equal to the listing price.* +* *The listed NFT(s) MUST match the NFT(s) in the payment data.* +* *The listed NFT(s) MUST be controlled by `seller`.* + +**[R35]** *If all checks in [**[R33]**](#r33), and in [**[R34]**](#r34) for `trxnType = 0`, are passed, the `executePayment` function MUST call the `transfer` function in the ERC-20 contract identified by `tokenType` with `recipient = address(this)` and `amount = payment`.* + +Note the NFT contract pays itself from the available allowance set in the `approve` transaction from the buyer. + +**[R36]** *For `trxnType = 1`, and for a successful payment, the `registeredPayment` mapping MUST updated with the payment, such that it can be validated when the NFT is transferred in a separate `safeTransferFrom` call, and `true` MUST be returned as the return value of the function, if successful, `false` otherwise.* + +**[R37]** *For `trxnType = 0`, an `internal` version of the `safeTransferFrom` function with message data MUST be called to transfer the NFTs to the buyer, and upon success, the buyer MUST be given the `MINTER_ROLE`, unless the buyer already has that role.* + +Note, the `_safeTransferFrom` function has the same structure as `safeTransferFrom` but skips the input data validation. + +**[R38]** *For `trxnType = 0`, and if the NFT transfer is successful, the listing of the NFT MUST be removed.* + +**[R39]** *For a protocol token as a payment token, and independent of `trxnType`, the buyer MUST send protocol tokens to the NFT contract as the escrow, and `msg.data` MUST encode the array of paid for NFTs `uint256[] tokenIds`.* + +**[R40]** *For the NFT contract to receive a protocol token, a payable fallback function (`fallback() external payable`) MUST be implemented.* + +Note that since the information for which NFTs the payment was for must be passed, a simple `receive()` fallback function cannot be allowed since it does not allow for `msg.data` to be sent with the transaction. + +**[R41]** *`msg.data` for the fallback function MUST minimally contain the following data: +`address memory seller, uint256[] memory _tokenId, address memory receiver, int256 memory trxnType`* + +**[R42]** *If `trxnType` is not equal to either '0' or '1', or another supported type, then the fallback function MUST `revert`.* + +**[R43]** *For `trxnType` equal to either '0' or '1', the requirements [**[R33]**](#r33) through [**[R38]**](#r38) MUST be satisfied for the fallback function to successfully execute, otherwise the fallback function MUST `revert`.* + +**[R44]** *In case of a transaction failure (for direct sales, `trxnType = 0`), or the buyer of the NFT listing changing their mind (for exchange-mediated sales, `trxnType = 1`), the submitted payment MUST be able to revert using the `reversePayment` function where the function interface is defined below:* + +``` +/// @dev Definition of the function enabling the reversal of a payment before the sale is complete +/// @param paymentId is the unique identifier for which a payment was made +/// @param tokenType is the type of payment token used in the payment +function reversePayment(uint256 paymentId, string memory tokenType) public virtual returns (bool) +``` + +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +Note, `reentrancy` protection through e.g. `nonReentrant` from the Open Zeppelin library is strongly advised since funds are being paid out. + +**[R45]** *The business rules for the `reversePayment` function are as follows:* + +* *There MUST be registered payment for a given `paymentId` and `tokenType`.* +* *`msg.sender` MUST be the buyer address in the registered payment.* +* *The payment amount must be larger than `0`.* +* *The registered payment MUST be removed when the payment has been successfully reverted, otherwise the function must fail.* + + +### Modified NFT Transfer function + +This document adheres to the ERC-721 interface format for the `safeTransferFrom` function as given below: + +``` +function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) external virtual override +``` + +Note, that the input parameters must satisfy several requirements for the NFT(s) to be transferred AFTER royalties have been properly distributed. Note also, that the ability to transfer more than one token at a time is required. However, the standard interface only allows one token to be transferred at a time. In order to remain compliant with the ERC-721 standard, this document uses `tokenId` only for the first NFT to be transferred. All other transfer relevant data is encoded in `_data`. + +The high-level requirements are as follows: + +* The payment parameters of the trade encoded in `_data` must be validated. +* The seller and the sold NFT token(s) must exist, and the seller must be the owner of the token. +* `msg.sender` must be the seller address or an approved address. +* the payment of the trade received by the NFT smart contract is correctly disbursed to all Royalty Sub Account owners. +* the NFT token is transferred after all Royalty Sub Accounts and their holders associated with the NFT token(s) have been properly credited. + +Also, note that in order to avoid royalty circumvention attacks, there is only one NFT transfer function. + +**[R46]** *Therefore, `transferFrom` and `safeTransferFrom` without `data` MUST be disabled.* + +This can be achieved through for example a `revert` statement in an `override` function. + +**[R47]** *The requirements on input parameters of the function are as follows*: + +* *`from` MUST not be `address(0)`.* +* *`from` MUST be the owner or `approved` for `tokenId` and the other tokens included in `_data`.* +* *`from` MUST not be a smart contract unless whitelisted.* +* *a Royalty Account MUST be associated to `tokenId` and the other tokens included in `_data`.* +* *`_data` MUST NOT be NULL.* +* *`msg.sender` MUST be equal to `from` or an `approved` address, or a whitelisted contract.* + +Note, that in the context of this document only the scenario where the calling contract is still being created, i.e., the constructor being executed is a possible attack vector, and should to be carefully treated in the transfer scenario. + +Turning to the `_data` object. + +**[R48]** *The `_data` object MUST minimally contain the following payment parameters:* + +* *Seller Address as `address`.* +* *Buyer Address as `address`.* +* *Receiver Address as `address.* +* *Token identifiers as `uint256[]`.* +* *Token type used for payment.* +* *Payment amount paid to NFT contract as `uint256`.* +* *a registered payment identifier.* +* *blockchain ID, `block.chainid`, of the underlying blockchain.* + +**[R49]** *The following business rules MUST be met for the payment data in '_data':* + +* *`seller == from`.* +* *`tokenId[0] == tokenId`.* +* *Each token in `_tokenId` has an associated Royalty Account.* +* *`chainid == block.chainid`.* +* *`buyer` is equal to the buyer address in the registered payment for the given ``paymentId.* +* *`receiver == to`.* +* *the receiver of the token is not the seller.* +* *the receiver of the token is not a contract or is a whitelisted contract* +* *For all NFTs in the payment, `tokenId[i] = registeredPayment[paymentId].boughtTokens[i]`.* +* *`tokenType` is supported in the contract.* +* *`allowedToken[tokenType]` is not NULL.* +* *`tokenType = registeredPayment[paymentId].tokenType`.* +* *`payment > lastBalanceAllowedToken[allowedToken[listingId]]`.* +* *`payment = registeredPayment[paymentId].payment`.* + +### Distributing Royalties in the Transfer Function + +The approach to distributing royalties is to break down the hierarchical structure of interconnected Royalty Accounts into layers, and then process one layer at time, where each relationship between a NFT and its ancestor is utilized to traverse the Royalty Account chain until the root ancestor and its associated Royalty Account. + +Note, that the distribution function assumes that the payment made is for ALL tokens in the requested transfer. That means, that `payment` for the distribution function is equally divided between all NFTs included in the payment. + +**[R50]** *The `distributePayment` function interface MUST adhere to the definition below: + +``` +/// @dev Function to distribute a payment as royalties to a chain of Royalty Accounts +/// @param tokenId is a tokenId included in the sale and used to look up the associated Royalty Account +/// @param payment is the payment (portion) to be distributed as royalties + +function distributePayment (uint256 tokenId, uint265 payment) internal virtual returns (bool) +``` + +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +As mentioned before, the internal `distributePayment` function is called within the modified `safeTransferFrom` function. + +Note, that it is necessary to multiply two `uint256` numbers with each other -- the payment amount with the royalty split percentage expressed as a whole number e.g. `10000 = 100%`. And then divide the result by the whole number representing `100%` in order to arrive at the correct application of the royalty split percentage to the payment amount. This requires careful treatment of numbers in the implementation to prevent issues such as buffer over or under runs. + +**[R51]** *The processing logic of `distributePayment` function MUST be as follows:* + +* *Load the Royalty Account (`RA`) and associated Royalty Sub Accounts using the passed `tokenId`.* +* *For each Royalty Sub Account in `RA` apply the following rules:* + * *If a Royalty Sub Account in `RA` has `isIndividual` set to `true` then* + * *apply the royalty percentage of that Royalty Sub Account to `payment` and add the calculated amount, e.g. `royaltyAmountTemp`, to the `royaltybalance` of that Royalty Sub Account.* + * *emit an event as a notification of payment to the `accountId` of the Royalty Sub Account containing: assetId, accountId, tokenType, royaltybalance.* + * *in the RA add `royaltyamountTemp` amount to `balance`* + * *If a Royalty Sub Account in `RA` has `isIndividual` set to `false` then* + * *apply the royalty percentage of that Royalty Sub Account to `payment` and store temporarily in a new variable e.g. `RApaymenttemp`, but do not update the `royaltybalance` of the Royalty Sub Account which remains `0`.* + * *then use `ancestor` to obtain the `RA` connected to `ancestor` e.g. via a look up through a Royalty Account mapping.* + * *load the new RA* + * *if `isIndividual` of the Royalty Sub Account is set to `true`, pass through the Royalty Sub Accounts of the next `RA`, and apply the rule for `isIndividual = true`.* + * *if `isIndividual` of the Royalty Sub Account is set to `false`, pass through the Royalty Sub Accounts of the next `RA`, and apply the rule for `isIndividual = false`.* + * *Repeat the procedures for `isIndividual` equal to `true` and `false` until a `RA` is reached that does not have an `ancestor`, and where all Royalty Sub Accounts have`isIndividual` set to `true`, and apply the rule for a Royalty Sub Account that has `isIndividual` set to `true` to all Royalty Sub Accounts in that `RA`.* + +### Update Royalty Sub Account Ownership with Payout to approved Address (`from`) + +In order to simplify the ownership transfer, first the approved address -- the non-contract NFT owner --, `from`, is paid out its share of the royalties. And then the Royalty Sub Account is updated with the new owner, `to`. This step repeats for each token to be transferred. + +**[R52]** *The business rules are as follows:* + +* *the internal version of the`royaltyPayOut` function MUST pay out the entire royalty balance of the Royalty Sub Account owned by the `from` address to the `from` address.* +* *the Royalty Sub Account MUST only be updated with the new owner only once the payout function has successfully completed and the `royaltybalance = 0`.* + +The last step in the process chain is transferring the NFTs in the purchase to the `to` address. + +**[R53]** *For every NFT (in the batch) the 'to' address MUST be `approved' (ERC-721 function) to complete the ownership transfer:* + +``` +_approve(to, tokenId[i]); +``` + +The technical NFT owner remains the NFT contract. + +### Removing the Payment Entry after successful Transfer + +Only after the real ownership of the NFT, the approved address, has been updated, the payment registry entry can be removed to allow the transferred NFTs to be sold again. + +**[R54]** *After the `approve` relationship has been successfully updated to the `to` address, the registered payment MUST be removed.* + +### Paying out Royalties to the `from` Address in `safeTransferFrom` Function + +There are two versions of the payout function -- a `public` and an `internal` function -- depending on whether there is a payout during a purchase, or a payout is requested by a Royalty Sub Account owner. + +**[R55]** *The public `royaltyPayOut` function interface MUST adhere to the definition below:* + +``` +/// @dev Function to payout a royalty payment +/// @param tokenId is the identifier of the NFT token +/// @param RAsubaccount is the address of the Royalty Sub Account from which the payout should happen +/// @param receiver is the address to receive the payout +/// @param amount is the amount to be paid out + +function royaltyPayOut (uint256 tokenId, address RAsubaccount, address payable payoutAccount, uint256 amount) public virtual nonReentrant returns (bool) +``` + +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +Note, that the function has `reentrancy` protection through `nonReentrant` from the Open Zeppelin library since funds are being paid out. + +**[R56]** *The input parameters of the `royaltyPayOut` function MUST satisfy the following requirements:* + +* *`msg.sender == RAsubaccount`.* +* *`tokenId` must exist and must not be burned.* +* *`tokenId` must be associated with a Royalty Account.* +* *`RAsubaccount` must be a valid `accountId` in a Royalty Sub Account of the Royalty Account of the `tokenId'.* +* *`isIndividual == true` for the Royalty Sub Account, `RAsubaccount`.* +* *`amount <= royaltybalance` of the Royalty Sub Account, `RAsubaccount.*` + +**[R57]** *The internal `_royaltyPayOut` function interface MUST adhere to the definition below*: + +``` +function _royaltyPayOut (uint256 tokenId, address RAsubaccount, address payable payoutAccount, uint256 amount) public virtual returns (bool) +``` + +**[R58]** *The internal `_royaltyPayOut` function MUST perform the following actions: + +* *send the payment to the `payoutaccount`.* +* *update the `royaltybalance` of the `RAsubaccount` of the Royalty Account upon successful transfer.* + +**[R59]** *The following steps MUST be taken to send out a royalty payment to its recipient:* + +* *find the Royalty Sub Account.* +* *extract `tokenType` from the Royalty Sub Account.* +* *based on the token type send to the `payoutAccount` either* + * *'ETH' / relevant protocol token or* + * *another token based on token type* +* *and only if the payout transaction is successful, deduct `amount` from `royaltybalance` of the Royalty Sub Account,`RAsubaccount`, and then return `true` as the function return parameter, otherwise return `false`.* + +## Rationale + +Royalties for NFTs is at its core a distribution licensing problem. A buyer obtains the right to an asset/content which might or might not be reproducible, alterable etc. by the buyer or agents of the buyer. Therefore, a comprehensive specification must address a hierarchy of royalties, where one or more assets are derived from an original asset as described in the Motivation section in detail. Consequently, a design must solve for a multi-level inheritance, and thus, recursion problem. + +In order to solve for the complicated inheritance problem, this proposal design breaks down the recursive problem of the hierarchy first into a tree of depth N. And the further breaks down the tree structure into N separate problems, one for each layer. This design allows one to traverse the tree from its lowest level upwards to its root most efficiently. This is achieved with the design for the `distributePayment` function and the NFT data structures allowing for the tree structure e.g. `ancestry`,`royaltyAccount`, `RAsubaccount`. + +In order to avoid massive gas costs during the payout of royalties, possibly exceeding block gas limits for large royalty trees, the design needed to create a royalty accounting system to maintain royalty balances for recipients as done with the `royaltyAccount`, 'RAsubaccount' data structures and the associated CRUD operations, as well as require that royalty payouts are done by individual and by request, only, as is achieved with the `royaltyPayout` function design. + +Furthermore, the design had to ensure that in order to account for and payout royalties the smart contract must be in the "know" of all buying and selling of an NFT including the exchange of monies. This buying and selling can be either direct through the NFT contract or can be exchange-mediated as is most often the case today -- which is a centralizing factor! The chosen design for purchasing is accounting for those two modes. + +Keeping the NFT contract in the "know" at the beginning of the purchase process requires that authorized user addresses can list NFTs for sale for direct sales , whereas for exchange-mediated purchases, a payment must be registered with the NFT contract before the purchase can be completed. + +The design needed to avoid royalty circumvention during the purchase process, therefore, the NFT must be kept in the "know", a buyer will always have to pay the NFT contract directly and not the seller for both purchasing modes. The seller is subsequently paid through the royalty distribution function in the NFT contract. As a consequence, and a key design choice, and to stay compliant with ERC-721, the NFT contract must be the owner of the NFT, and the actual owner is an `approved` address. + +The specification design also needed to account for that the payment process depends on whether the payment is received in ETH or an ERC-20 token: + +* ERC-20 Token + 1. The Buyer must `approve` the NFT contract for the purchase price, `payment` for the selected payment token (ERC-20 contract address). + 2. For an ERC-20 payment token, the Buyer must then call the `executePayment` in the NFT contract -- the ERC-20 is not directly involved. +* For a non-ERC-20 payment, the Buyer must send a protocol token (ETH) to the NFT contract, and is required to send encoded listing and payment information. + +In addition, the `executePayment` function had to be designed to handle both direct sales (through the NFT contract) and exchange-mediated sales which required the introduction of an indicator whether the purchase is direct or exchange-mediated. + +The `executePayment` function also has to handle the NFT transfer and purchase clean up -- removal of a listing, or removal of a registered payment, distribution of royalties, payment to the seller, and finally transfer to the seller. + +To stay compliant with the ERC-721 design but avoid royalty circumvention, all transfer functions must be disabled save the one that allows for additional information to be submitted with the function in order to manage the complicated purchase cleanup process -- `safeTransferFrom`. To ensure safety, the design enforces that input parameters must satisfy several requirements for the NFT to be transferred AFTER the royalties have been properly distributed, not before. The design accounts for the fact that we need to treat transfer somewhat differently for direct sales versus exchange mediated sales. + +Finally the specification needed to take into account that NFTs must be able to be `minted` and `burned` to maintain compliance with the ERC-721 specification while also having to set up all the data structures for the tree. + +The design enforces that when an NFT is minted, a royalty account for that NFT must be created and associated with the NFT and the NFT owner, and, if there is an ancestor of the NFT with the ancestor's royalty account to enforces the tree structure. To this end the specification utilizes the ERC-721 `_safemint` function in a newly defined `mint` function and applies various business rules on the input variables required to ensure proper set-up. + +An NFT with a royalty account can be burned. However, several things have to be true to avoid locking funds not only for the royalty account of the NFT but also its descendants, if they exist. That means that all royalties for the NFT and its descendants, if they exists, must be paid out. Furthermore, if descendants exist, they must have been burned before an ancestor can be burned. If those rules are not enforced the cleanly, the hierarchical royalty structure in part of the tree can break down and lead to lost funds, not paid out royalties etc. + + +## Backwards Compatibility + +This EIP is backwards compatible to the ERC-721 standard introducing new interfaces and functionality but retaining the core interfaces and functionality of the ERC-721 standard. + +## Test Cases + +A full test suite is part of the reference implementation. + +## Reference Implementation + +The Treetrunk reference implementation of the standard can be found in the public treetrunkio Github repo under treetrunk-nft-reference-implementation. + +## Security Considerations + +Given that this EIP introduces royalty collection, distribution, and payouts to the ERC-721 standard, the number of attack vectors increases. The most important attack vector categories and their mitigation are discussed below: + +* **Payments and Payouts**: + * Reentrancy attacks are mitigated through a reentrancy protection on all payment functions. See for example the Open Zeppelin reference implementation . + * Payouts from unauthorized accounts. Mitigation: Royalty Sub Accounts require at least that `msg.sender` is the Royalty Sub Account owner. + * Payments could get stuck in the NFT contract if the `executePayment` function fails. Mitigation: For exchange-mediated sales, a buyer can always reverse a payment with `reversePayment` if the `executePayment` function fails. For direct sales, `reversePayment` will be directly triggered in the `executePayment` function. +* **Circumventing Royalties**: + * Offchain Key exchanges + * Exchanging a private key for money off chain can not be prevented in any scenario. + * Smart Contract Wallets as NFT owners + * A Smart Contract Wallet controlled by multiple addresses could own an NFT and the owners could transfer the asset within the wallet with an off chain money exchange. Mitigation: Prohibit that Smart Contracts can own an NFT unless explicitly allowed to accommodate special scenarios such as collections. + * Denial of Royalty Disbursement + * An attacker who has purchased one or more NFTs in a given generation of an NFT family can cause out of gas errors or run time errors for the contract, if they add many spurious royalty sub-accounts with very low royalty split percentages, and then mint more prints of those purchased NFTs, and then repeat that step until the set `maxGeneration` limit is reached. An NFT trade at the bottom of the hierarchy will then require a lot of code cycles because of the recursive nature of the royalty distribution function. Mitigation: Limit the number of royalty sub-accounts per NFT and impose a royalty split percentage limit. + * Following the same approach as above but now targeting the `addListNFT` function, an attacker can force an out of gas error or run time errors in the `executePayment` function by listing many NFTs at a low price, and then performing a purchase from another account. Mitigation: Limit the number of NFTs that can be included in one listing. + * The creator of the NFT family could set the number of generations too high such that the royalty distribution function could incur and out of gas or run time error because of the recursive nature of the function. Mitigation: Limiting the `maxNumberGeneration` by the creator. + * General Considerations: The creator of an NFT family must carefully consider the business model for the NFT family and then set the parameters such as maximum number of generations, royalty sub-accounts, number of prints per print, number of NFTs in a listing, and the maximum and minimum royalty split percentage allowed. +* **Phishing Attacks** + * NFT phishing attacks often target the `approve` and `setApprovalForAll` functions by tricking owners of NFTs to sign transactions adding the attacker account as approved for one or all NFTs of the victim. Mitigation: This contract is not vulnerable to these type of phishing attacks because all NFT transfers are sales, and the NFT contract itself is the owner of all NFTs. This means that transfers after a purchase are achieved by setting the new owner in the `_approve` function. Calling the public `approve` function will cause the function call to error out because `msg.sender` of the malicious transaction cannot be the NFT owner. + * NFT phishing attack targeting the `addListNFT` function to trick victim to list one or more NFTs at a very low price and the attacker immediately registering a payment, and executing that payment right away. Mitigation: Implement a waiting period for a purchase can be affected giving the victim time to call the `removeListNFT` function. In addition, an implementer could require Two-Factor-Authentication either built into the contract or by utilizing an authenticator app such as Google Authenticator built into a wallet software. + +Besides the usage of professional security analysis tools, it is also recommended that each implementation performs a security audit of its implementation. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4931.md b/EIPS/eip-4931.md new file mode 100644 index 00000000000000..5fbf719b18072a --- /dev/null +++ b/EIPS/eip-4931.md @@ -0,0 +1,350 @@ +--- +eip: 4931 +title: Generic Token Upgrade Standard +description: Create a standard interface for upgrading ERC20 token contracts. +author: John Peterson (@John-peterson-coinbase), Roberto Bayardo (@roberto-bayardo), David Núñez (@cygnusv) +discussions-to: https://ethereum-magicians.org/t/eip-4931-generic-token-upgrade-standard/8687 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-11-02 +requires: 20 +--- + + +## Abstract + +The following standard allows for the implementation of a standard API for [ERC-20](./eip-20.md) token upgrades. This standard specifies an interface that supports the conversion of tokens from one contract (called the "source token") to those from another (called the "destination token"), as well as several helper methods to provide basic information about the token upgrade (i.e. the address of the source and destination token contracts, the ratio that source will be upgraded to destination, etc.). + +## Motivation + +Token contract upgrades typically require each asset holder to exchange their old tokens for new ones using a bespoke interface provided by the developers. This standard interface will allow asset holders as well as centralized and decentralized exchanges to conduct token upgrades more efficiently since token contract upgrade scripts will be essentially reusable. Standardization will reduce the security overhead involved in verifying the functionality of the upgrade contracts. It will also provide asset issuers clear guidance on how to effectively implement a token upgrade. + +## 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. + +Please Note: Methods marked with (Optional Ext.) are a part of the optional extension for downgrade functionality and may remain unimplemented if downgrade functionality is not required. +### Token Upgrade Interface Contract +``` solidity +interface IEIP4931 { +``` +#### Methods + +##### upgradeSource + +Returns the address of the original (source) token that will be upgraded. + +``` solidity +/// @dev A getter to determine the contract that is being upgraded from ("source contract") +/// @return The address of the source token contract +function upgradeSource() external view returns(address) +``` + +##### upgradeDestination + +Returns the address of the token contract that is being upgraded to. + +``` solidity +/// @dev A getter to determine the contract that is being upgraded to ("destination contract") +/// @return The address of the destination token contract +function upgradeDestination() external view returns(address) +``` + +##### isUpgradeActive + +Returns the current status of the upgrade functionality. Status MUST return `true` when the upgrade contract is functional and serving upgrades. It MUST return `false` when the upgrade contract is not currently serving upgrades. + +``` solidity +/// @dev The method will return true when the contract is serving upgrades and otherwise false +/// @return The status of the upgrade as a boolean +function isUpgradeActive() external view returns(bool) +``` +##### isDowngradeActive + +Returns the current status of the downgrade functionality. Status MUST return `true` when the upgrade contract is functional and serving downgrades. It MUST return `false` when the upgrade contract is not currently serving downgrades. When the downgrade Optional Ext. is not implemented, this method will always return `false` to signify downgrades are not available. + +``` solidity +/// @dev The method will return true when the contract is serving downgrades and otherwise false +/// @return The status of the downgrade as a boolean +function isDowngradeActive() external view returns(bool) +``` +##### ratio + +Returns the ratio of destination token to source token, expressed as a 2-tuple, that the upgrade will use. E.g. `(3, 1)` means the upgrade will provide 3 destination tokens for every 1 source token being upgraded. + +``` solidity +/// @dev A getter for the ratio of destination tokens to source tokens received when conducting an upgrade +/// @return Two uint256, the first represents the numerator while the second represents +/// the denominator of the ratio of destination tokens to source tokens allotted during the upgrade +function ratio() external view returns(uint256, uint256) +``` + +##### totalUpgraded + +Returns the total number of tokens that have been upgraded from source to destination. If the downgrade Optional Ext. is implemented, calls to `downgrade` will reduce the `totalUpgraded` return value making it possible for the value to decrease between calls. The return value will be strictly increasing if downgrades are not implemented. + +``` solidity +/// @dev A getter for the total amount of source tokens that have been upgraded to destination tokens. +/// The value may not be strictly increasing if the downgrade Optional Ext. is implemented. +/// @return The number of source tokens that have been upgraded to destination tokens +function totalUpgraded() external view returns(uint256) +``` +##### computeUpgrade + +Computes the `destinationAmount` of destination tokens that correspond to a given `sourceAmount` of source tokens, according to the predefined conversion ratio, as well as the `sourceRemainder` amount of source tokens that can't be upgraded. For example, let's consider a (3, 2) ratio, which means that 3 destination tokens are provided for every 2 source tokens; then, for a source amount of 5 tokens, `computeUpgrade(5)` must return `(6, 1)`, meaning that 6 destination tokens are expected (in this case, from 4 source tokens) and 1 source token is left as remainder. +``` solidity +/// @dev A method to mock the upgrade call determining the amount of destination tokens received from an upgrade +/// as well as the amount of source tokens that are left over as remainder +/// @param sourceAmount The amount of source tokens that will be upgraded +/// @return destinationAmount A uint256 representing the amount of destination tokens received if upgrade is called +/// @return sourceRemainder A uint256 representing the amount of source tokens left over as remainder if upgrade is called +function computeUpgrade(uint256 sourceAmount) external view + returns (uint256 destinationAmount, uint256 sourceRemainder) +``` + +##### computeDowngrade (Optional Ext.) + +Computes the `sourceAmount` of source tokens that correspond to a given `destinationAmount` of destination tokens, according to the predefined conversion ratio, as well as the `destinationRemainder` amount of destination tokens that can't be downgraded. For example, let's consider a (3, 2) ratio, which means that 3 destination tokens are provided for every 2 source tokens; for a destination amount of 13 tokens, `computeDowngrade(13)` must return `(4, 1)`, meaning that 4 source tokens are expected (in this case, from 12 destination tokens) and 1 destination token is left as remainder. +``` solidity +/// @dev A method to mock the downgrade call determining the amount of source tokens received from a downgrade +/// as well as the amount of destination tokens that are left over as remainder +/// @param destinationAmount The amount of destination tokens that will be downgraded +/// @return sourceAmount A uint256 representing the amount of source tokens received if downgrade is called +/// @return destinationRemainder A uint256 representing the amount of destination tokens left over as remainder if upgrade is called +function computeDowngrade(uint256 destinationAmount) external view + returns (uint256 sourceAmount, uint256 destinationRemainder) +``` + + +##### upgrade + +Upgrades the `amount` of source token to the destination token in the specified ratio. The destination tokens will be sent to the `_to` address. The function MUST lock the source tokens in the upgrade contract or burn them. If the downgrade Optional Ext. is implemented, the source tokens MUST be locked instead of burning. The function MUST `throw` if the caller's address does not have enough source token to upgrade or if `isUpgradeActive` is returning `false`. The function MUST also fire the `Upgrade` event. `approve` MUST be called first on the source contract. +``` solidity +/// @dev A method to conduct an upgrade from source token to destination token. +/// The call will fail if upgrade status is not true, if approve has not been called +/// on the source contract, or if sourceAmount is larger than the amount of source tokens at the msg.sender address. +/// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the upgrade call will +/// only upgrade the nearest whole amount of source tokens returning the excess to the msg.sender address. +/// Emits the Upgrade event +/// @param _to The address the destination tokens will be sent to upon completion of the upgrade +/// @param sourceAmount The amount of source tokens that will be upgraded +function upgrade(address _to, uint256 sourceAmount) external +``` + + +##### downgrade (Optional Ext.) +Downgrades the `amount` of destination token to the source token in the specified ratio. The source tokens will be sent to the `_to` address. The function MUST unwrap the destination tokens back to the source tokens. The function MUST `throw` if the caller's address does not have enough destination token to downgrade or if `isDowngradeActive` is returning `false`. The function MUST also fire the `Downgrade` event. `approve` MUST be called first on the destination contract. +``` solidity +/// @dev A method to conduct a downgrade from destination token to source token. +/// The call will fail if downgrade status is not true, if approve has not been called +/// on the destination contract, or if destinationAmount is larger than the amount of destination tokens at the msg.sender address. +/// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the downgrade call will only downgrade +/// the nearest whole amount of destination tokens returning the excess to the msg.sender address. +/// Emits the Downgrade event +/// @param _to The address the source tokens will be sent to upon completion of the downgrade +/// @param destinationAmount The amount of destination tokens that will be downgraded +function downgrade(address _to, uint256 destinationAmount) external +``` + +#### Events + +##### Upgrade + +MUST trigger when tokens are upgraded. + +``` solidity +/// @param _from Address that called upgrade +/// @param _to Address that destination tokens were sent to upon completion of the upgrade +/// @param sourceAmount Amount of source tokens that were upgraded +/// @param destinationAmount Amount of destination tokens sent to the _to address +event Upgrade(address indexed _from, address indexed _to, uint256 sourceAmount, uint256 destinationAmount) +``` + +##### Downgrade (Optional Ext.) + +MUST trigger when tokens are downgraded. + +``` solidity +/// @param _from Address that called downgrade +/// @param _to Address that source tokens were sent to upon completion of the downgrade +/// @param sourceAmount Amount of source tokens sent to the _to address +/// @param destinationAmount Amount of destination tokens that were downgraded +event Downgrade(address indexed _from, address indexed _to, uint256 sourceAmount, uint256 destinationAmount) +} +``` + +## Rationale +There have been several notable ERC20 upgrades (Ex. Golem: GNT -> GLM) where the upgrade functionality is written directly into the token contracts. We view this as a suboptimal approach to upgrades since it tightly couples the upgrade with the existing tokens. This EIP promotes the use of a third contract to facilitate the token upgrade to decouple the functionality of the upgrade from the functionality of the token contracts. Standardizing the upgrade functionality will allow asset holders and exchanges to write simplified reusable scripts to conduct upgrades which will reduce the overhead of conducting upgrades in the future. The interface aims to be intentionally broad leaving much of the specifics of the upgrade to the implementer, so that the token contract implementations do not interfere with the upgrade process. Finally, we hope to create a greater sense of security and validity for token upgrades by enforcing strict means of disposing of the source tokens during the upgrade. This is achieved by the specification of the `upgrade` method. The agreed upon norm is that burnable tokens shall be burned. Otherwise, tokens shall be effectively burned by being sent to the `0x00` address. When downgrade Optional Ext. is implemented, the default is instead to lock source tokens in the upgrade contract to avoid a series of consecutive calls to `upgrade` and `downgrade` from artificially inflating the supply of either token (source or destination). + +## Backwards Compatibility +There are no breaking backwards compatibility issues. There are previously implemented token upgrades that likely do not adhere to this standard. In these cases, it may be relevant for the asset issuers to communicate that their upgrade is not EIP-4931 compliant. + +## Reference Implementation +``` solidity +//SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.9; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "./IEIP4931.sol"; + +contract SourceUpgrade is IEIP4931 { + using SafeERC20 for IERC20; + + uint256 constant RATIO_SCALE = 10**18; + + IERC20 private source; + IERC20 private destination; + bool private upgradeStatus; + bool private downgradeStatus; + uint256 private numeratorRatio; + uint256 private denominatorRatio; + uint256 private sourceUpgradedTotal; + + mapping(address => uint256) public upgradedBalance; + + constructor(address _source, address _destination, bool _upgradeStatus, bool _downgradeStatus, uint256 _numeratorRatio, uint256 _denominatorRatio) { + require(_source != _destination, "SourceUpgrade: source and destination addresses are the same"); + require(_source != address(0), "SourceUpgrade: source address cannot be zero address"); + require(_destination != address(0), "SourceUpgrade: destination address cannot be zero address"); + require(_numeratorRatio > 0, "SourceUpgrade: numerator of ratio cannot be zero"); + require(_denominatorRatio > 0, "SourceUpgrade: denominator of ratio cannot be zero"); + + source = IERC20(_source); + destination = IERC20(_destination); + upgradeStatus = _upgradeStatus; + downgradeStatus = _downgradeStatus; + numeratorRatio = _numeratorRatio; + denominatorRatio = _denominatorRatio; + } + + /// @dev A getter to determine the contract that is being upgraded from ("source contract") + /// @return The address of the source token contract + function upgradeSource() external view returns(address) { + return address(source); + } + + /// @dev A getter to determine the contract that is being upgraded to ("destination contract") + /// @return The address of the destination token contract + function upgradeDestination() external view returns(address) { + return address(destination); + } + + /// @dev The method will return true when the contract is serving upgrades and otherwise false + /// @return The status of the upgrade as a boolean + function isUpgradeActive() external view returns(bool) { + return upgradeStatus; + } + + /// @dev The method will return true when the contract is serving downgrades and otherwise false + /// @return The status of the downgrade as a boolean + function isDowngradeActive() external view returns(bool) { + return downgradeStatus; + } + + /// @dev A getter for the ratio of destination tokens to source tokens received when conducting an upgrade + /// @return Two uint256, the first represents the numerator while the second represents + /// the denominator of the ratio of destination tokens to source tokens allotted during the upgrade + function ratio() external view returns(uint256, uint256) { + return (numeratorRatio, denominatorRatio); + } + + /// @dev A getter for the total amount of source tokens that have been upgraded to destination tokens. + /// The value may not be strictly increasing if the downgrade Optional Ext. is implemented. + /// @return The number of source tokens that have been upgraded to destination tokens + function totalUpgraded() external view returns(uint256) { + return sourceUpgradedTotal; + } + + /// @dev A method to mock the upgrade call determining the amount of destination tokens received from an upgrade + /// as well as the amount of source tokens that are left over as remainder + /// @param sourceAmount The amount of source tokens that will be upgraded + /// @return destinationAmount A uint256 representing the amount of destination tokens received if upgrade is called + /// @return sourceRemainder A uint256 representing the amount of source tokens left over as remainder if upgrade is called + function computeUpgrade(uint256 sourceAmount) + public + view + returns (uint256 destinationAmount, uint256 sourceRemainder) + { + sourceRemainder = sourceAmount % (numeratorRatio / denominatorRatio); + uint256 upgradeableAmount = sourceAmount - (sourceRemainder * RATIO_SCALE); + destinationAmount = upgradeableAmount * (numeratorRatio / denominatorRatio); + } + + /// @dev A method to mock the downgrade call determining the amount of source tokens received from a downgrade + /// as well as the amount of destination tokens that are left over as remainder + /// @param destinationAmount The amount of destination tokens that will be downgraded + /// @return sourceAmount A uint256 representing the amount of source tokens received if downgrade is called + /// @return destinationRemainder A uint256 representing the amount of destination tokens left over as remainder if upgrade is called + function computeDowngrade(uint256 destinationAmount) + public + view + returns (uint256 sourceAmount, uint256 destinationRemainder) + { + destinationRemainder = destinationAmount % (denominatorRatio / numeratorRatio); + uint256 upgradeableAmount = destinationAmount - (destinationRemainder * RATIO_SCALE); + sourceAmount = upgradeableAmount / (denominatorRatio / numeratorRatio); + } + + /// @dev A method to conduct an upgrade from source token to destination token. + /// The call will fail if upgrade status is not true, if approve has not been called + /// on the source contract, or if sourceAmount is larger than the amount of source tokens at the msg.sender address. + /// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the upgrade call will + /// only upgrade the nearest whole amount of source tokens returning the excess to the msg.sender address. + /// Emits the Upgrade event + /// @param _to The address the destination tokens will be sent to upon completion of the upgrade + /// @param sourceAmount The amount of source tokens that will be upgraded + function upgrade(address _to, uint256 sourceAmount) external { + require(upgradeStatus == true, "SourceUpgrade: upgrade status is not active"); + (uint256 destinationAmount, uint256 sourceRemainder) = computeUpgrade(sourceAmount); + sourceAmount -= sourceRemainder; + require(sourceAmount > 0, "SourceUpgrade: disallow conversions of zero value"); + + upgradedBalance[msg.sender] += sourceAmount; + source.safeTransferFrom( + msg.sender, + address(this), + sourceAmount + ); + destination.safeTransfer(_to, destinationAmount); + sourceUpgradedTotal += sourceAmount; + emit Upgrade(msg.sender, _to, sourceAmount, destinationAmount); + } + + /// @dev A method to conduct a downgrade from destination token to source token. + /// The call will fail if downgrade status is not true, if approve has not been called + /// on the destination contract, or if destinationAmount is larger than the amount of destination tokens at the msg.sender address. + /// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the downgrade call will only downgrade + /// the nearest whole amount of destination tokens returning the excess to the msg.sender address. + /// Emits the Downgrade event + /// @param _to The address the source tokens will be sent to upon completion of the downgrade + /// @param destinationAmount The amount of destination tokens that will be downgraded + function downgrade(address _to, uint256 destinationAmount) external { + require(upgradeStatus == true, "SourceUpgrade: upgrade status is not active"); + (uint256 sourceAmount, uint256 destinationRemainder) = computeDowngrade(destinationAmount); + destinationAmount -= destinationRemainder; + require(destinationAmount > 0, "SourceUpgrade: disallow conversions of zero value"); + require(upgradedBalance[msg.sender] >= sourceAmount, + "SourceUpgrade: can not downgrade more than previously upgraded" + ); + + upgradedBalance[msg.sender] -= sourceAmount; + destination.safeTransferFrom( + msg.sender, + address(this), + destinationAmount + ); + source.safeTransfer(_to, sourceAmount); + sourceUpgradedTotal -= sourceAmount; + emit Downgrade(msg.sender, _to, sourceAmount, destinationAmount); + } +} +``` + + +## Security Considerations +The main security consideration is ensuring the implementation of the interface handles the source tokens during the upgrade in such a way that they are no longer accessible. Without careful handling, the validity of the upgrade may come into question since source tokens could potentially be upgraded multiple times. This is why EIP-4931 will strictly enforce the use of `burn` for source tokens that are burnable. For non-burnable tokens, the accepted method is to send the source tokens to the `0x00` address. When the downgrade Optional Ext. is implemented, the constraint will be relaxed, so that the source tokens can be held by the upgrade contract. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + diff --git a/EIPS/eip-4938.md b/EIPS/eip-4938.md new file mode 100644 index 00000000000000..2879fcad8ff78f --- /dev/null +++ b/EIPS/eip-4938.md @@ -0,0 +1,52 @@ +--- +eip: 4938 +title: "eth/67 - Removal of GetNodeData" +description: "Remove GetNodeData and NodeData messages from the wire protocol" +author: Marius van der Wijden (@MariusVanDerWijden), Felix Lange , Gary Rong +discussions-to: https://ethereum-magicians.org/t/eip-4938-removal-of-getnodedata/8893 +status: Review +type: Standards Track +category: Networking +created: 2022-03-23 +requires: 2464, 2481 +--- + +## Abstract + +The [Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md) defines request and response messages for exchanging data between clients. The `GetNodeData` request retrieves a set of trie nodes or contract code from the state trie by hash. We propose to remove the `GetNodeData` and `NodeData` messages from the wire protocol. + +## Motivation + +`GetNodeData` and `NodeData` were introduced in protocol version `eth/63` to allow for a sync mode called "fast sync", which downloads the Ethereum state without executing all blocks. The sync algorithm works by requesting all state trie nodes and contract codes by their hash. + +Serving `GetNodeData` requests requires clients to store state as a mapping of hashes to trie nodes. Avoiding the need to store such a mapping in the database is the main motivation for removing this request type. + +At this time, some client implementations cannot serve `GetNodeData` requests because they do not store the state in a compatible way. The Ethereum Wire Protocol should accurately reflect the capabilities of clients, and should not contain messages which are impossible to implement in some clients. + +## Specification + +Remove the following message types from the `eth` protocol: + +* `GetNodeData (0x0d)` + * **(eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` +* `NodeData (0x0e)` + * **(eth/66)**: `[request_id: P, [value_0: B, value_1: B, ...]]` + +## Rationale + +A replacement for `GetNodeData` is available in the [snap protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/snap.md). Specifically, clients can use the [GetByteCodes](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/snap.md#getbytecodes-0x04) and [GetTrieNodes](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/snap.md#gettrienodes-0x06) messages instead of `GetNodeData`. The snap protocol can be used to implement the "fast sync" algorithm, though it is recommended to use it for "snap sync". + +## Backwards Compatibility + +This EIP changes the `eth` protocol and requires rolling out a new version, `eth/67`. Supporting multiple versions of a wire protocol is possible. Rolling out a new version does not break older clients immediately, since they can keep using protocol version `eth/66`. + +This EIP does not change consensus rules of the EVM and does not require a hard fork. + +## Security Considerations + +None + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4944.md b/EIPS/eip-4944.md new file mode 100644 index 00000000000000..9a6c4518eaf350 --- /dev/null +++ b/EIPS/eip-4944.md @@ -0,0 +1,82 @@ +--- +eip: 4944 +title: Contract with Exactly One Non-fungible Token +description: An ERC-721 compatible single-token NFT +author: Víctor Muñoz (@victormunoz), Josep Lluis de la Rosa (@peplluis7), Andres El-Fakdi (@Bluezfish) +discussions-to: https://ethereum-magicians.org/t/erc721-minting-only-one-token/8602/2 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-25 +requires: 721 +--- + +## Abstract + +The following describes standard functions for an [ERC-721](./eip-721.md) compatible contract with a total supply of one. +This allows an NFT to be associated uniquely with a single contract address. + +## Motivation + +If the ERC-721 was modified to mint only 1 token (per contract), then the contract address could be identified uniquely with that minted token (instead of the tuple contract address + token id, as ERC-721 requires). +This change would enable automatically all the capabilities of composable tokens [ERC-998](./eip-998.md) (own other ERC-721 or [ERC-20](./eip-20.md)) natively without adding any extra code, just forbidding to mint more than one token per deployed contract. +Then the NFT minted with this contract could operate with his "budget" (the ERC-20 he owned) and also trade with the other NFTs he could own. Just like an autonomous agent, that could decide what to do with his properties (sell his NFTs, buy other NFTs, etc). + +The first use case that is devised is for value preservation. Digital assets, as NFTs, have value that has to be preserved in order to not be lost. If the asset has its own budget (in other ERC-20 coins), could use it to autopreserve itself. + +## Specification + +The constructor should mint the unique token of the contract, and then the mint function should add a restriction to avoid further minting. + +Also, a `tokenTransfer` function should be added in order to allow the contract owner to transact with the ERC-20 tokens owned by the contract/NFT itself. So that if the contract receives a transfer of ERC-20 tokens, the owner of the NFT could spend it from the contract wallet. + +## Rationale + +The main motivation is to keep the contract compatible with current ERC-721 platforms. + +## Backwards Compatibility + +There are no backwards compatibility issues. + +## Reference Implementation + +Add the variable `_minted` in the contract: + +``` solidity + bool private _minted; +``` + +In the constructor, automint the first token and set the variable to true: + +``` solidity + constructor(string memory name, string memory symbol, string memory base_uri) ERC721(name, symbol) { + baseUri = base_uri; + mint(msg.sender,0); + _minted = true; + } +``` + +Add additional functions to interact with the NFT properties (for instance, ERC-20): + +``` solidity + modifier onlyOwner() { + require(balanceOf(msg.sender) > 0, "Caller is not the owner of the NFT"); + _; + } + + function transferTokens(IERC20 token, address recipient, uint256 amount) public virtual onlyOwner { + token.transfer(recipient, amount); + } + + function balanceTokens(IERC20 token) public view virtual returns (uint256) { + return token.balanceOf(address(this)); + } +``` + +## Security Considerations + +No security issues found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4950.md b/EIPS/eip-4950.md new file mode 100644 index 00000000000000..40882212cf75d1 --- /dev/null +++ b/EIPS/eip-4950.md @@ -0,0 +1,93 @@ +--- +eip: 4950 +title: Entangled Tokens +description: ERC-721 extension with two tokens minted that are tied together +author: Víctor Muñoz (@victormunoz), Josep Lluis de la Rosa (@peplluis7), Easy Innova (@easyinnova) +discussions-to: https://ethereum-magicians.org/t/entangled-tokens/8702 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-28 +requires: 20, 721, 1155 +--- + +## Abstract + +This EIP defines an interface for delegating control of a smart contract wallet to pairs of users using entangled [ERC-721](./eip-721.md) non-fungible tokens. + +## Motivation + +The motivation is to provide an easy way to share a wallet through NFTs, so that the act of buying an NFT (in a marketplace) gives the buyer the privilege to have access to a given wallet. This wallet could have budget in many tokens, or even be the owner of other NFTs. + +A use case is to keep contact between an artist and an buyer of its NFTs. If an artist T has created a digital piece of art P with an NFT, then T creates 2 entangled tokens A and B so that he keeps A and transfer B to P. By construction of entangled tokens, only one transfer is possible for them, thus the artist proofs he’s been the creator of P by sending a transaction to A that is visible from B. Otherwise, the owner of P might check the authenticity of the artist by sending a transaction to B so that the artist might proof by showing the outcome out of A. + +A version of this use case is when one user U mints his piece of art directly in the form of an entangled token A; then the user U sells/transfers it while keeping the entangled token B in the U's wallet. The piece of art and the artists will be entangled whoever is the A's owner. + +These applications of entangled tokens are envisaged to be useful for: + +1. NFT authorship / art creation +2. Distribution of royalties by the creator. +3. Authenticity of a work of art: creation limited to the author (e.g. only 1000 copies if there are 1000 1000 entangled tokens in that NFT). +4. Usowners (users that consume an NFT also become -partial- owners of the NFT) +5. Reformulation of property rights: the one who owns the property receives it without having to follow in the footsteps of the owners. +6. Identity: Only those credentials that have an entangled token with you are related to you. +7. Vreservers (value-reservers). + +## Specification + +An entangled token contract implements [ERC-721](./eip-721.md) with the additional restriction that it only ever mints exactly two tokens at contract deployment: one with a `tokenId` of `0`, the other with a `tokenId` of `1`. The entangled token contract also implements a smart contract wallet that can be operated by the owners of those two tokens. + +Also, a `tokenTransfer` function is to be be added in order to allow the token owners to transact with the [ERC-20](./eip-20.md) tokens owned by the contract/NFT itself. The function signature is as follows: + +```solidity + function tokenTransfer(IERC20 token, address recipient, uint256 amount) public onlyOwners; +``` + +## Rationale + +We decide to extend [ERC-721](./eip-721.md) ([ERC-1155](./eip-1155.md) could be also possible) because the main purpose of this is to be compatible with current marketplaces platforms. This entangled NFTs will be listed in a marketplace, and the user who buys it will have then the possibility to transact with the wallet properties (fungible and non fungible tokens). + +## Backwards Compatibility + +No backwards compatibility issues. + +## Reference Implementation + +Mint two tokens, and only two, at the contract constructor, and set the `minted` property to true: + +```solidity +bool private _minted; + +constructor(string memory name, string memory symbol, string memory base_uri) ERC721(name, symbol) { + baseUri = base_uri; + _mint(msg.sender,0); + _mint(msg.sender,1); + _minted = true; + } + +function _mint(address to, uint256 tokenId) internal virtual override { + require(!_minted, "ERC4950: already minted"); + super._mint(to, tokenId); +} +``` + +Add additional functions to allow both NFT user owners to operate with other ERC-20 tokens owned by the contract: + +```solidity + modifier onlyOwners() { + require(balanceOf(msg.sender) > 0, "Caller does not own any of the tokens"); + _; + } + +function tokenTransfer(IERC20 token, address recipient, uint256 amount) public onlyOwners { + token.transfer(recipient, amount); + } +``` + +## Security Considerations + +There are no security considerations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4955.md b/EIPS/eip-4955.md new file mode 100644 index 00000000000000..9a3b98b74f53d3 --- /dev/null +++ b/EIPS/eip-4955.md @@ -0,0 +1,198 @@ +--- +eip: 4955 +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: Final +type: Standards Track +category: ERC +created: 2022-03-29 +requires: 721, 1155 +--- + +## Abstract + +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 standardized NFT metadata schema allows wallets, marketplaces, metaverses, and similar 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. + +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. + +### 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. + +```jsonc +{ + "title": "Asset Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset that this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset that this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset that this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "namespaces": { + "type": "object", + "description": "Application-specific NFT properties" + } + } +} +``` + +### Example + +```jsonc +{ + "name": "My NFT", + "description": "NFT description", + "image": "ipfs://QmZfmRZHuawJDtDVMaEaPWfgWFV9iXoS9SzLvwX76wm6pa", + "namespaces": { + "myAwesomeCompany": { + "prop1": "value1", + "prop2": "value2", + }, + "myAwesomeCompany2": { + "prop3": "value3", + "prop4": "value4", + }, + } +} + +// Or by simply using a `URI` to reduce the size of the JSON response. + +{ + "name": "My NFT", + "description": "NFT description", + "image": "ipfs://QmZfmRZHuawJDtDVMaEaPWfgWFV9iXoS9SzLvwX76wm6pa", + "namespaces": { + "myAwesomeCompany": "URI", + "myAwesomeCompany2": "URI", + } +} +``` + +## Rationale + +There are many projects which need custom properties in order to display a current NFT. Each project may have its own way to render the NFTs and therefore they need different values. An example of this is the metaverses like Decentraland or TheSandbox where they need different 3d models to render the NFT based on the visual/engine of each. NFTs projects like Cryptopunks, Bored Apes, etc. can create the 3d models needed for each project and therefore be supported out of the box. + +The main differences between the projects that are rendering 3d NFTs (models) are: + +### 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) + +![](../assets/eip-4955/different-renders.jpeg) + +### 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 the JSON config of a wearable item in Decentraland: + +```jsonc +"data": { + "replaces": [], + "hides": [], + "tags": [], + "category": "upper_body", + "representations": [ + { + "bodyShapes": [ + "urn:decentraland:off-chain:base-avatars:BaseMale" + ], + "mainFile": "male/Look6_Tshirt_A.glb", + "contents": [ + { + "key": "male/Look6_Tshirt_A.glb", + "url": "https://peer-ec2.decentraland.org/content/contents/QmX3yMhmx4AvGmyF3CM5ycSQB4F99zXh9rL5GvdxTTcoCR" + } + ], + "overrideHides": [], + "overrideReplaces": [] + }, + { + "bodyShapes": [ + "urn:decentraland:off-chain:base-avatars:BaseFemale" + ], + "mainFile": "female/Look6_Tshirt_B (1).glb", + "contents": [ + { + "key": "female/Look6_Tshirt_B (1).glb", + "url": "https://peer-ec2.decentraland.org/content/contents/QmcgddP4L8CEKfpJ4cSZhswKownnYnpwEP4eYgTxmFdav8" + } + ], + "overrideHides": [], + "overrideReplaces": [] + } + ] +}, +"image": "https://peer-ec2.decentraland.org/content/contents/QmPnzQZWAMP4Grnq6phVteLzHeNxdmbRhKuFKqhHyVMqrK", +"thumbnail": "https://peer-ec2.decentraland.org/content/contents/QmcnBFjhyFShGo9gWk2ETbMRDudiX7yjn282djYCAjoMuL", +"metrics": { + "triangles": 3400, + "materials": 2, + "textures": 2, + "meshes": 2, + "bodies": 2, + "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 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. + +```jsonc +{ + "id": "model", + "type": "model/gltf+json", + "style": "Decentraland", + "uri": "..." +}, + +// Or + +{ + "id": "model", + "type": "model/gltf+json", + "style": "humanoide", + "uri": "..." +}, +``` + +With `namespaces`, each vendor will know how to render an asset by doing: + +```ts +fetch(metadata.namespaces["PROJECT_NAME"].uri).then(res => render(res)) +``` + +The idea behind extending the [EIP-721](./eip-721.md) metadata schema is for backward compatibility. Most projects on Ethereum use non-upgradeable contracts. If this EIP required new implementations of those contracts, they would have to be re-deployed. This is time-consuming and wastes money. Leveraging EIP-721's existing metadata field minimizes the number of changes necessary. Finally, the JSON metadata is already used to store representations using the `image` field. 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). diff --git a/EIPS/eip-4972.md b/EIPS/eip-4972.md new file mode 100644 index 00000000000000..a0a57dba7a3a7c --- /dev/null +++ b/EIPS/eip-4972.md @@ -0,0 +1,86 @@ +--- +eip: 4972 +title: Name-Owned Account +description: Name-Owned Account for Social Identity +author: Shu Dong (@dongshu2013), Qi Zhou (@qizhou), Zihao Chen (@zihaoccc) +discussions-to: https://ethereum-magicians.org/t/eip-4972-name-owned-account/8822 +status: Draft +type: Standards Track +category: ERC +created: 2022-04-04 +requires: 137 +--- + +## Abstract + +The ERC suggests expanding the capabilities of the name service, such as ENS, by enabling each human-readable identity to be linked to a single smart contract account that can be controlled by the owner of the name identity. + +## Motivation + +Name itself cannot hold any context. We want to build an extension of name service to give name rich context by offering each name owner an extra ready to use smart contract account, which may help the general smart contract account adoption. With NOA, it is possible to hold assets and information for its name node, opening up new use cases such as name node transfers, which involve transferring ownership of the name node as well as the NOA, including any assets and information it holds. + +## Specification + +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. + +### Name-Owned Account + +An NOA has + +- a human readable name defined by [ERC-137](./eip-137.md); and +- an owned account(NOA), which is an smart contract account whose address is derived from the name; and +- owner(s) of the name that can deploy and manipulate the owned account. + +The following diagram illustrates the relationship between NOA, name node, and name owner, with the ownership being guaranteed by the name service. + + ┌───────────────┐ ┌───────────┐ ┌───────────────┐ + │ Owned Account ◄──own───┤ Name Node ◄───own───┤ Name Owner │ + └───────────────┘ └───────────┘ └───────────────┘ + +### Interface + +The core interface required for a name service to have is: + + interface INameServiceRegistry { + /// @notice get account address owned by the name node + /// @params node represents a name node + /// @return the address of an account + function ownedAccount( + bytes32 node + ) external view returns(address); + } + +The core interface required for the name owned account is: + + interface INameOwnedAccount { + /// @notice get the name node is mapped to this account address + /// @return return a name node + function name() external view returns(bytes32); + + /// @notice get the name service contract address where + /// the name is registered + /// @return return the name service the name registered at + function nameService() external view returns(address); + } + +## Rationale + +To achieve a one-to-one mapping from the name to the NOA, where each NOA's address is derived from the name node, we must include the name node information in each NOA to reflect its name node ownership. The "name()" function can be used to retrieve this property of each NOA and enable reverse tracking to its name node. The "nameService()" function can get the name service contract address where the name is registered, to perform behaviors such as validation checks. Through these two methods, the NOA has the ability to track back to its actual owner who owns the name node. + +## Backwards Compatibility + +The name registry interface is compatible with ERC-137. + +## Reference Implementation + +### Name Owned Account Creation + +The NOA creation is done by a “factory” contract. The factory could be the name service itself and is expected to use CREATE2 (not CREATE) to create the NOA. NOAs should have identical initcode and factory contract in order to achieve deterministic preservation of address. The name node can be used as the salt to guarantee the bijection from name to its owned account. + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4973.md b/EIPS/eip-4973.md new file mode 100644 index 00000000000000..9ddb3bc5efddc6 --- /dev/null +++ b/EIPS/eip-4973.md @@ -0,0 +1,203 @@ +--- +eip: 4973 +title: Account-bound Tokens +description: An interface for non-transferrable NFTs binding to an Ethereum account like a legendary World of Warcraft item binds to a character. +author: Tim Daubenschütz (@TimDaub) +discussions-to: https://ethereum-magicians.org/t/eip-4973-non-transferrable-non-fungible-tokens-soulbound-tokens-or-badges/8825 +status: Review +type: Standards Track +category: ERC +created: 2022-04-01 +requires: 165, 712, 721, 1271 +--- + +## Abstract + +Proposes a standard API for account-bound Tokens (ABT) within smart contracts. An ABT is a non-fungible token bound to a single account. ABTs don't implement a canonical interface for transfers. This EIP defines basic functionality to mint, assign, revoke and track ABTs. + +## Motivation + +In the popular MMORPG World of Warcraft, its game designers intentionally took some items out of the world's auction house market system to prevent them from having a publicly-discovered price and limit their accessibility. + +Vanilla WoW's "Thunderfury, Blessed Blade of the Windseeker" was one such legendary item, and it required a forty-person raid, among other sub-tasks, to slay the firelord "Ragnaros" to gain the "Essence of the Firelord," a material needed to craft the sword once. + +Upon voluntary pickup, the sword permanently **binds** to a character's "soul," making it impossible to trade, sell or even swap it between a player's characters. + +In other words, "Thunderfury"'s price was the aggregate of all social costs related to completing the difficult quest line with friends and guild members. Other players spotting Thunderfuries could be sure their owner had slain "Ragnaros," the blistering firelord. + +World of Warcraft players could **trash** legendary and soulbound items like the Thunderfury to permanently remove them from their account. It was their choice to visibly **equip** or **unequip** an item and hence show their achievements to everyone. + +The Ethereum community has expressed a need for non-transferrable, non-fungible, and socially-priced tokens similar to WoW's soulbound items. Popular contracts implicitly implement account-bound interaction rights today. A principled standardization helps interoperability and improves on-chain data indexing. + +The purpose of this document is to make ABTs a reality on Ethereum by creating consensus around a **maximally backward-compatible** but otherwise **minimal** interface definition. + +## Specification + +### Solidity Interface + +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. + +ABTs _must_ implement the interfaces: + +- [ERC-165](./eip-165.md)'s `ERC165` (`0x01ffc9a7`) +- [ERC-721](./eip-721.md)'s `ERC721Metadata` (`0x5b5e139f`) + +ABTs _must not_ implement the interfaces: + +- [ERC-721](./eip-721.md)'s `ERC721` (`0x80ac58cd`) + +An ABT receiver must be able to always call `function unequip(address _tokenId)` to take their ABT off-chain. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +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 0xeb72bb7c +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 + /// (`to` == 0). + event Transfer( + address indexed from, address indexed to, uint256 indexed tokenId + ); + + /// @notice Count all ABTs assigned to an owner + /// @dev ABTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param owner An address for whom to query the balance + /// @return The number of ABTs owned by `address owner`, possibly zero + function balanceOf(address owner) external view returns (uint256); + + /// @notice Find the address bound to an ERC4973 account-bound token + /// @dev ABTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param tokenId The identifier for an ABT. + /// @return The address of the owner bound to the ABT. + function ownerOf(uint256 tokenId) external view returns (address); + + /// @notice Removes the `uint256 tokenId` from an account. At any time, an + /// ABT receiver must be able to disassociate themselves from an ABT + /// publicly through calling this function. After successfully executing this + /// function, given the parameters for calling `function give` or + /// `function take` a token must be re-equipable. + /// @dev Must emit a `event Transfer` with the `address to` field pointing to + /// the zero address. + /// @param tokenId The identifier for an ABT. + function unequip(uint256 tokenId) external; + + /// @notice Creates and transfers the ownership of an ABT from the + /// transaction's `msg.sender` to `address to`. + /// @dev Throws unless `bytes signature` represents a signature of the + // EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` expressing + /// `address to`'s explicit agreement to be publicly associated with + /// `msg.sender` and `bytes metadata`. A unique `uint256 tokenId` must be + /// generated by type-casting the `bytes32` EIP-712 structured data hash to a + /// `uint256`. If `bytes signature` is empty or `address to` is a contract, + /// an EIP-1271-compatible call to `function isValidSignatureNow(...)` must + /// be made to `address to`. A successful execution must result in the + /// `event Transfer(msg.sender, to, tokenId)`. Once an ABT exists as an + /// `uint256 tokenId` in the contract, `function give(...)` must throw. + /// @param to The receiver of the ABT. + /// @param metadata The metadata that will be associated to the ABT. + /// @param signature A signature of the EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` signed by + /// `address to`. + /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32` + /// EIP-712 structured data hash to a `uint256`. + function give(address to, bytes calldata metadata, bytes calldata signature) + external + returns (uint256); + + /// @notice Creates and transfers the ownership of an ABT from an + /// `address from` to the transaction's `msg.sender`. + /// @dev Throws unless `bytes signature` represents a signature of the + /// EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` expressing + /// `address from`'s explicit agreement to be publicly associated with + /// `msg.sender` and `bytes metadata`. A unique `uint256 tokenId` must be + /// generated by type-casting the `bytes32` EIP-712 structured data hash to a + /// `uint256`. If `bytes signature` is empty or `address from` is a contract, + /// an EIP-1271-compatible call to `function isValidSignatureNow(...)` must + /// be made to `address from`. A successful execution must result in the + /// emission of an `event Transfer(from, msg.sender, tokenId)`. Once an ABT + /// exists as an `uint256 tokenId` in the contract, `function take(...)` must + /// throw. + /// @param from The origin of the ABT. + /// @param metadata The metadata that will be associated to the ABT. + /// @param signature A signature of the EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` signed by + /// `address from`. + + /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32` + /// EIP-712 structured data hash to a `uint256`. + function take(address from, bytes calldata metadata, bytes calldata signature) + external + returns (uint256); + + /// @notice Decodes the opaque metadata bytestring of an ABT into the token + /// URI that will be associated with it once it is created on chain. + /// @param metadata The metadata that will be associated to an ABT. + /// @return A URI that represents the metadata. + function decodeURI(bytes calldata metadata) external returns (string memory); +} +``` + +See [ERC-721](./eip-721.md) for a definition of its metadata JSON Schema. + +### [EIP-712](./eip-712.md) Typed Structured Data Hashing and Bytearray Signature Creation + +To invoke `function give(...)` and `function take(...)` a bytearray signature must be created using [EIP-712](./eip-712.md). A tested reference implementation in Node.js is attached at [index.mjs](../assets/eip-4973/sdk/src/index.mjs), [index_test.mjs](../assets/eip-4973/sdk/test/index_test.mjs) and [package.json](../assets/eip-4973/package.json). In Solidity, this bytearray signature can be created as follows: + +```solidity +bytes32 r = 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90; +bytes32 s = 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064; +uint8 v = 27; +bytes memory signature = abi.encodePacked(r, s, v); +``` + +## Rationale + +### Interface + +ABTs shall be maximally backward-compatible but still only expose a minimal and simple to implement interface definition. + +As [ERC-721](./eip-721.md) tokens have seen widespread adoption with wallet providers and marketplaces, using its `ERC721Metadata` interface with [ERC-165](./eip-165.md) for feature-detection potentially allows implementers to support ABTs out of the box. + +If an implementer of [ERC-721](./eip-721.md) properly built [ERC-165](./eip-165.md)'s `function supportsInterface(bytes4 interfaceID)` function, already by recognizing that [ERC-721](./eip-721.md)'s track and transfer interface component with the identifier `0x80ac58cd` is not implemented, transferring of a token should not be suggested as a user interface option. + +Still, since ABTs support [ERC-721](./eip-721.md)'s `ERC721Metadata` extension, wallets and marketplaces should display an account-bound token with no changes needed. + +Although other implementations of account-bound tokens are possible, e.g., by having all transfer functions revert, ABTs are superior as it supports feature detection through [ERC-165](./eip-165.md). + +We expose `function unequip(address _tokenId)` and require it to be callable at any time by an ABT's owner as it ensures an owner's right to publicly disassociate themselves from what has been issued towards their account. + +### Exception handling + +Given the non-transferable between accounts property of ABTs, if a user's keys to an account or a contract get compromised or rotated, a user may lose the ability to associate themselves with the token. In some cases, this can be the desired effect. Therefore, ABT implementers should build re-issuance and revocation processes to enable recourse. We recommend implementing strictly decentralized, permissionless, and censorship-resistant re-issuance processes. + +But this document is deliberately abstaining from offering a standardized form of exception handling in cases where user keys are compromised or rotated. + +In cases where implementers want to make account-bound tokens shareable among different accounts, e.g., to avoid losing access when keys get compromised, we suggest issuing the account-bound token towards a contract's account that implements a multi-signature functionality. + +### Provenance Indexing + +ABTs can be indexed by tracking the emission of `event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)`. As with [ERC-721](./eip-721.md), transfers between two accounts are represented by `address from` and `address to` being non-zero addresses. Unequipping a token is represented through emitting a transfer with `address to` being set to the zero address. Mint operations where `address from` is set to zero don't exist. To avoid being spoofed by maliciously-implemented `event Transfer` emitting contracts, an indexer should ensure that the transaction's sender is equal to `event Transfer`'s `from` value. + +## Backwards Compatibility + +We have adopted the [ERC-165](./eip-165.md) and `ERC721Metadata` functions purposefully to create a high degree of backward compatibility with [ERC-721](./eip-721.md). We have deliberately used [ERC-721](./eip-721.md) terminology such as `function ownerOf(...)`, `function balanceOf(...)` to minimize the effort of familiarization for ABT implementers already familiar with, e.g., [ERC-20](./eip-20.md) or [ERC-721](./eip-721.md). For indexers, we've re-used the widely-implemented `event Transfer` event signature. + +## Reference Implementation + +You can find an implementation of this standard in [ERC-4973-flat.sol](../assets/eip-4973/ERC4973-flat.sol). + +## Security Considerations + +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-4974.md b/EIPS/eip-4974.md new file mode 100644 index 00000000000000..304bbebb25a0fe --- /dev/null +++ b/EIPS/eip-4974.md @@ -0,0 +1,161 @@ +--- +eip: 4974 +title: Ratings +description: An interface for assigning and managing numerical ratings +author: Daniel Tedesco (@dtedesco1) +discussions-to: https://ethereum-magicians.org/t/8805 +status: Review +type: Standards Track +category: ERC +created: 2022-04-02 +requires: 165 +--- + +## Abstract + +This standard defines a standardized interface for assigning and managing numerical ratings on the Ethereum blockchain. This allows ratings to be codified within smart contracts and recognized by other applications, enabling a wide range of new use cases for tokens. + +## Motivation + +Traditionally, blockchain applications have focused on buying and selling digital assets. However, the asset-centric model has often been detrimental to community-based blockchain projects, as seen in the pay-to-play dynamics of many EVM-based games and DAOs in 2021. + +This proposal addresses this issue by allowing ratings to be assigned to contracts and wallets, providing a new composable primitive for blockchain applications. This allows for a diverse array of new use cases, such as: + +- Voting weight in a DAO: Ratings assigned using this standard can be used to determine the voting weight of members in a decentralized autonomous organization (DAO). For example, a DAO may assign higher ratings to members who have demonstrated a strong track record of contributing to the community, and use these ratings to determine the relative influence of each member in decision-making processes. + +- Experience points in a decentralized game ecosystem: Ratings can be used to track the progress of players in a decentralized game ecosystem, and to reward them for achieving specific milestones or objectives. For example, a game may use ratings to assign experience points to players, which can be used to unlock new content or abilities within the game. + +- Loyalty points for customers of a business: Ratings can be used to track the loyalty of customers to a particular business or service, and to reward them for their continued support. For example, a business may use ratings to assign loyalty points to customers, which can be redeemed for special offers or discounts. + +- Asset ratings for a decentralized insurance company: Ratings can be used to evaluate the risk profile of assets in a decentralized insurance company, and to determine the premiums and coverage offered to policyholders. For example, a decentralized insurance company may use ratings to assess the risk of different types of assets, and to provide lower premiums and higher coverage to assets with lower risk ratings. + +This standard is influenced by the [EIP-20](./eip-20.md) and [EIP-721](./eip-721.md) token standards and takes cues from each in its structure, style, and semantics. + +## 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. + +Every compliant contract MUST implement the following interfaces: + +``` +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +/// @title EIP-4974 Ratings +/// @dev See https://eips.ethereum.org/EIPS/EIP-4974 +/// Note: the EIP-165 identifier for this interface is #######. +/// Must initialize contracts with an `operator` address that is not `address(0)`. +interface IERC4974 /* is ERC165 */ { + + /// @dev Emits when operator changes. + /// MUST emit when `operator` changes by any mechanism. + /// MUST ONLY emit by `setOperator`. + event NewOperator(address indexed _operator); + + /// @dev Emits when operator issues a rating. + /// MUST emit when rating is assigned by any mechanism. + /// MUST ONLY emit by `rate`. + event Rating(address _rated, int8 _rating); + + /// @dev Emits when operator removes a rating. + /// MUST emit when rating is removed by any mechanism. + /// MUST ONLY emit by `remove`. + event Removal(address _removed); + + /// @notice Appoint operator authority. + /// @dev MUST throw unless `msg.sender` is `operator`. + /// MUST throw if `operator` address is either already current `operator` + /// or is the zero address. + /// MUST emit an `Appointment` event. + /// @param _operator New operator of the smart contract. + function setOperator(address _operator) external; + + /// @notice Rate an address. + /// MUST emit a Rating event with each successful call. + /// @param _rated Address to be rated. + /// @param _rating Total EXP tokens to reallocate. + function rate(address _rated, int8 _rating) external; + + /// @notice Remove a rating from an address. + /// MUST emit a Remove event with each successful call. + /// @param _removed Address to be removed. + function removeRating(address _removed) external; + + /// @notice Return a rated address' rating. + /// @dev MUST register each time `Rating` emits. + /// SHOULD throw for queries about the zero address. + /// @param _rated An address for whom to query rating. + /// @return int8 The rating assigned. + function ratingOf(address _rated) external view returns (int8); +} + +interface IERC165 { + /// @notice Query if a contract implements an interface. + /// @dev Interface identification is specified in EIP-165. This function + /// uses less than 30,000 gas. + /// @param interfaceID The interface identifier, as specified in EIP-165. + /// @return bool `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise. + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +## Rationale + +### Rating Assignment + +Ratings SHALL be at the sole discretion of the contract operator. This party may be a sports team coach or a multisig DAO wallet. We decide not to specify how governance occurs, but only *that* governance occurs. This allows for a wider range of potential use cases than optimizing for particular decision-making forms. + +This proposal standardizes a control mechanism to allocate community reputation without encouraging financialization of that recognition. While it does not ensure meritocracy, it opens the door. + +### Choice of int8 + +It's signed: Reviewers should be able to give neutral and negative ratings for the wallets and contracts they interact with. This is especially important for decentralized applications that may be subject to malicious actors. + +It's 8bit: The objective here is to keep ratings within some fathomably comparable range. Longer term, this could encourage easy aggregation of ratings, versus using larger numbers where users might employ a great variety of scales. + +### Rating Changes + +Ratings SHOULD allow rating updates by contract operators. If Bob has contributed greatly to the community, but then is caught stealing from Alice, the community may decide this should lower Bob's standing and influence in the community. Again, while this does not ensure an ethical standard within the community, it opens the door. + +Relatedly, ratings SHOULD allow removal of ratings to rescind a rating if the rater does not have confidence in their ability to rate effectively. + +### Interface Detection + +We chose Standard Interface Detection ([EIP-165](./eip-165.md)) to expose the interfaces that a compliant smart contract supports. + +### Metadata Choices + +We have required `name` and `description` functions in the metadata extension. `name` common among major standards for blockchain-based primitives. We included a `description` function that may be helpful for games or other applications with multiple ratings systems. + +We remind implementation authors that the empty string is a valid response to `name` and `description` if you protest to the usage of this mechanism. We also remind everyone that any smart contract can use the same name and description as your contract. How a client may determine which ratings smart contracts are well-known (canonical) is outside the scope of this standard. + +### Drawbacks + +One potential drawback of using this standard is that ratings are subjective and may not always accurately reflect the true value or quality of a contract or wallet. However, the standard provides mechanisms for updating and removing ratings, allowing for flexibility and evolution over time. + +Users identified in the motivation section have a strong need to identify how a contract or community evaluates another. While some users may be proud of ratings they receive, others may rightly or wrongly receive negative ratings from certain contracts. Negative ratings may allow for nefarious activities such as bullying and discrimination. We implore all implementers to be mindful of the consequences of any ratings systems they create with this standard. + +## Backwards Compatibility + +We have adopted the `name` semantics from the EIP-20 and EIP-721 specifications. + +## Reference Implementation + +A reference implementation of this standard can be found in the assets folder. + + +## Security Considerations + +One potential security concern with this standard is the risk of malicious actors assigning false or misleading ratings to contracts or wallets. This could be used to manipulate voting weights in a DAO, or to deceive users into making poor decisions based on inaccurate ratings. + +To address this concern, the standard includes mechanisms for updating and removing ratings, allowing for corrections to be made in cases of false or misleading ratings. Additionally, the use of a single operator address to assign and update ratings provides a single point of control, which can be used to enforce rules and regulations around the assignment of ratings. + +Another potential security concern is the potential for an attacker to gain control of the operator address and use it to manipulate ratings for their own benefit. To mitigate this risk, it is recommended that the operator address be carefully managed and protected, and that multiple parties be involved in its control and oversight. + +Overall, the security of compliant contracts will depend on the careful management and protection of the operator address, as well as the development of clear rules and regulations around the assignment of ratings. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4987.md b/EIPS/eip-4987.md new file mode 100644 index 00000000000000..3b44d099a50ede --- /dev/null +++ b/EIPS/eip-4987.md @@ -0,0 +1,269 @@ +--- +eip: 4987 +title: Held token interface +description: Interface to query ownership and balance of held tokens +author: Devin Conley (@devinaconley) +discussions-to: https://ethereum-magicians.org/t/eip-4987-held-token-standard-nfts-defi/7117 +status: Review +type: Standards Track +category: ERC +created: 2021-09-21 +requires: 20, 165, 721, 1155 +--- + +## Abstract + +The proposed standard defines a lightweight interface to expose functional ownership and balances of held tokens. A held token is a token owned by a contract. This standard may be implemented by smart contracts which hold [EIP-20](./eip-20.md), [EIP-721](./eip-721.md), or [EIP-1155](./eip-1155.md) tokens and is intended to be consumed by both on-chain and off-chain systems that rely on ownership and balance verification. + +## Motivation + +As different areas of crypto (DeFi, NFTs, etc.) converge and composability improves, there will more commonly be a distinction between the actual owner (likely a contract) and the functional owner (likely a user) of a token. Currently, this results in a conflict between mechanisms that require token deposits and systems that rely on those tokens for ownership or balance verification. + +This proposal aims to address that conflict by providing a standard interface for token holders to expose ownership and balance information. This will allow users to participate in these DeFi mechanisms without giving up existing token utility. Overall, this would greatly increase interoperability across systems, benefiting both users and protocol developers. + +Example implementers of this ERC standard include + +- staking or farming contracts +- lending pools +- time lock or vesting vaults +- fractionalized NFT contracts +- smart contract wallets + +Example consumers of this ERC standard include + +- governance systems +- gaming +- PFP verification +- art galleries or showcases +- token based membership programs + +## Specification + +Smart contracts implementing the `ERC20` held token standard MUST implement all of the functions in the `IERC20Holder` interface. + +Smart contracts implementing the `ERC20` held token standard MUST also implement `ERC165` and return true when the interface ID `0x74c89d54` is passed. + +```solidity +/** + * @notice the ERC20 holder standard provides a common interface to query + * token balance information + */ +interface IERC20Holder is IERC165 { + /** + * @notice emitted when the token is transferred to the contract + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenAmount held token amount + */ + event Hold( + address indexed owner, + address indexed tokenAddress, + uint256 tokenAmount + ); + + /** + * @notice emitted when the token is released back to the user + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenAmount held token amount + */ + event Release( + address indexed owner, + address indexed tokenAddress, + uint256 tokenAmount + ); + + /** + * @notice get the held balance of the token owner + * @dev should throw for invalid queries and return zero for no balance + * @param tokenAddress held token address + * @param owner functional token owner + * @return held token balance + */ + function heldBalanceOf(address tokenAddress, address owner) + external + view + returns (uint256); +} + +``` + +Smart contracts implementing the `ERC721` held token standard MUST implement all of the functions in the `IERC721Holder` interface. + +Smart contracts implementing the `ERC721` held token standard MUST also implement `ERC165` and return true when the interface ID `0x16b900ff` is passed. + +```solidity +/** + * @notice the ERC721 holder standard provides a common interface to query + * token ownership and balance information + */ +interface IERC721Holder is IERC165 { + /** + * @notice emitted when the token is transferred to the contract + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + */ + event Hold( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId + ); + + /** + * @notice emitted when the token is released back to the user + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + */ + event Release( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId + ); + + /** + * @notice get the functional owner of a held token + * @dev should throw for invalid queries and return zero for a token ID that is not held + * @param tokenAddress held token address + * @param tokenId held token ID + * @return functional token owner + */ + function heldOwnerOf(address tokenAddress, uint256 tokenId) + external + view + returns (address); + + /** + * @notice get the held balance of the token owner + * @dev should throw for invalid queries and return zero for no balance + * @param tokenAddress held token address + * @param owner functional token owner + * @return held token balance + */ + function heldBalanceOf(address tokenAddress, address owner) + external + view + returns (uint256); +} +``` + +Smart contracts implementing the `ERC1155` held token standard MUST implement all of the functions in the `IERC1155Holder` interface. + +Smart contracts implementing the `ERC1155` held token standard MUST also implement `ERC165` and return true when the interface ID `0xced24c37` is passed. + +```solidity +/** + * @notice the ERC1155 holder standard provides a common interface to query + * token balance information + */ +interface IERC1155Holder is IERC165 { + /** + * @notice emitted when the token is transferred to the contract + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + * @param tokenAmount held token amount + */ + event Hold( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId, + uint256 tokenAmount + ); + + /** + * @notice emitted when the token is released back to the user + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + * @param tokenAmount held token amount + */ + event Release( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId, + uint256 tokenAmount + ); + + /** + * @notice get the held balance of the token owner + * @dev should throw for invalid queries and return zero for no balance + * @param tokenAddress held token address + * @param owner functional token owner + * @param tokenId held token ID + * @return held token balance + */ + function heldBalanceOf( + address tokenAddress, + address owner, + uint256 tokenId + ) external view returns (uint256); +} +``` + +## Rationale + +This interface is designed to be extremely lightweight and compatible with any existing token contract. Any token holder contract likely already stores all relevant information, so this standard is purely adding a common interface to expose that data. + +The token address parameter is included to support contracts that can hold multiple token contracts simultaneously. While some contracts may only hold a single token address, this is more general to either scenario. + +Separate interfaces are proposed for each token type (EIP-20, EIP-721, EIP-1155) because any contract logic to support holding these different tokens is likely independent. In the scenario where a single contract does hold multiple token types, it can simply implement each appropriate held token interface. + + +## Backwards Compatibility + +Importantly, the proposed specification is fully compatible with all existing EIP-20, EIP-721, and EIP-1155 token contracts. + +Token holder contracts will need to be updated to implement this lightweight interface. + +Consumer of this standard will need to be updated to respect this interface in any relevant ownership logic. + + +## Reference Implementation + +A full example implementation including [interfaces](../assets/eip-4987/IERC721Holder.sol), a vault [token holder](../assets/eip-4987/Vault.sol), and a [consumer](../assets/eip-4987/Consumer.sol), can be found at `assets/eip-4987/`. + +Notably, consumers of the `IERC721Holder` interface can do a chained lookup for the owner of any specific token ID using the following logic. + +```solidity + /** + * @notice get the functional owner of a token + * @param tokenId token id of interest + */ + function getOwner(uint256 tokenId) external view returns (address) { + // get raw owner + address owner = token.ownerOf(tokenId); + + // if owner is not contract, return + if (!owner.isContract()) { + return owner; + } + + // check for token holder interface support + try IERC165(owner).supportsInterface(0x16b900ff) returns (bool ret) { + if (!ret) return owner; + } catch { + return owner; + } + + // check for held owner + try IERC721Holder(owner).heldOwnerOf(address(token), tokenId) returns (address user) { + if (user != address(0)) return user; + } catch {} + + return owner; + } +``` + + +## Security Considerations + +Consumers of this standard should be cautious when using ownership information from unknown contracts. A bad actor could implement the interface, but report invalid or malicious information with the goal of manipulating a governance system, game, membership program, etc. + +Consumers should also verify the overall token balance and ownership of the holder contract as a sanity check. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5.md b/EIPS/eip-5.md index f7c3c47dfbdc99..5c875111d0e9b0 100644 --- a/EIPS/eip-5.md +++ b/EIPS/eip-5.md @@ -6,7 +6,6 @@ status: Final type: Standards Track category: Core created: 2015-11-22 -superseded-by: 211 --- ### Abstract diff --git a/EIPS/eip-5000.md b/EIPS/eip-5000.md new file mode 100644 index 00000000000000..22e278c1ae2bec --- /dev/null +++ b/EIPS/eip-5000.md @@ -0,0 +1,117 @@ +--- +eip: 5000 +title: MULDIV instruction +description: Introduce a new instruction to perform x * y / z in 512-bit precision +author: Harikrishnan Mulackal (@hrkrshnn), Alex Beregszaszi (@axic), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/muldiv-instruction/9930 +status: Draft +type: Standards Track +category: Core +created: 2022-03-14 +--- + +## Abstract + +Introduce a new instruction, `MULDIV(x, y, z)`, to perform `((x * y) / z) % 2**256` in 512-bit precision. `z = 0` is a special case for `(x * y) % 2**256`. + +## Motivation + +Fixed point operations in high level languages are very commonly used on Ethereum, especially in the domain of financial applications. + +While fixed point addition and subtraction can be done with merely `add` and `sub` respectively, being able to efficiently do fixedpoint multiplication and division is a very sought after feature. A commonly used workaround relies on a `mulmod`-based, rather complex implementation (taking around 50 instructions, excluding stack manipulation). This instruction reduces that to a single opcode. + +A secondary use case is likely in cryptographic applications, where the `muldiv` instruction allows full precision 256x256->512 multiplication. `mul(x y)` (or `muldiv(x, y, 1)`) computes the lower order 256 bits and `muldiv(x, y, 0)` computes the higher order 256 bits. + +Finally we aimed to provide an instruction which can be efficiently used both in *checked* and *unchecked arithmetic* use cases. By *checked* we mean to abort on conditions including division-by-zero and wrapping behaviour. + +## Specification + +A new instruction is introduced: `MULDIV` (`0x1e`). + +- Pops 3 values from the stack, first `x`, then `y` and `z`. +- If `z == 0`, `r = (uint512(x) * y) / 2**256`. +- Otherwise `r = (uint512(x) * y / z) % 2**256`, where the intermediate calculation is performed with 512-bit precision. +- Pushes `r` on the stack. + +```python +# operations `*` and `//` are done in 512 bit precision +def muldiv(x, y, z): + if z == 0: + return (x * y) // (2**256) + 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 + +### The special 0 case + +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) { + uint uint_max = type(uint).max; + assembly { + let lo := mul(x, y) + let mm := mulmod(x, y, uint_max) + hi := sub(sub(mm, lo), lt(mm, lo)) + } +} +``` + +While this feature is clever and useful, callers must be aware that unlike other EVM instructions, passing 0 will have a vastly different behaviour. + +### Argument ordering + +The order of arguments matches `addmod` and `mulmod`. + +## Backwards Compatibility + +This is a new instruction not present prior. + +## Test Cases + +``` +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +MULDIV +--- +0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +``` + + +``` +PUSH 0x0000000000000000000000000000000000000000000000000000000000000000 +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +MULDIV +--- +0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +``` + +``` +PUSH 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000 +PUSH 0x000000000000000000000000000000000000000000000000016345785d8a0000 +PUSH 0x00000000000000000000000000000000000000000000d3c21bcecceda1000000 +MULDIV +--- +0x00000000000000000000000000000000000000000000152d02c7e14af6800000 +``` + +## Security Considerations + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5003.md b/EIPS/eip-5003.md new file mode 100644 index 00000000000000..d42d650dd1276d --- /dev/null +++ b/EIPS/eip-5003.md @@ -0,0 +1,88 @@ +--- +eip: 5003 +title: Insert Code into EOAs with AUTHUSURP +description: Allow migrating away from ECDSA by deploying code in place of an externally owned account. +author: Dan Finlay (@danfinlay), Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-5003-auth-usurp-publishing-code-at-an-eoa-address/8979 +status: Stagnant +type: Standards Track +category: Core +created: 2022-03-26 +requires: 3074, 3607 +--- + +## Abstract + +This EIP introduces a new opcode, `AUTHUSURP`, which deploys code at an [EIP-3074](./eip-3074.md) authorized address. For externally owned accounts (EOAs), together with [EIP-3607](./eip-3607.md), this effectively revokes the original signing key's authority. + +## Motivation + +EOAs currently hold a significant amount of user-controlled value on Ethereum blockchains, but are limited by the protocol in a variety of critical ways. These accounts do not support rotating keys for security, batching to save gas, or sponsored transactions to reduce the need to hold ether yourself. There are countless other benefits that come from having a contract account or account abstraction, like choosing one's own authentication algorithm, setting spending limits, enabling social recovery, allowing key rotation, arbitrarily and transitively delegating capabilities, and just about anything else we can imagine. + +New users have access to these benefits using smart contract wallets, and new contracts can adopt recent standards to enable app-layer account abstraction (like [EIP-4337](./eip-4337.md)), but these would neglect the vast majority of existing Ethereum users' accounts. These users exist today, and they also need a path to achieving their security goals. + +Those added benefits would mostly come along with EIP-3074 itself, but with one significant shortcoming: the original signing key has ultimate authority for the account. While an EOA could delegate its authority to some _additional_ contract, the key itself would linger, continuing to provide an attack vector, and a constantly horrifying question lingering: have I been leaked? In other words, EIP-3074 can only grant authority to additional actors, but never revoke it. + +Today's EOAs have no option to rotate their keys. A leaked private key (either through phishing, or accidental access) cannot be revoked. A prudent user concerned about their key security might migrate to a new secret recovery phrase but at best this requires a transaction per asset (making it extremely expensive), and at worst, some powers (like hard-coded owners in a smart contract) might not be transferable at all. + +We know that EOAs cannot provide ideal user experience or safety, and there is a desire in the community to change the norm to contract-based accounts, but if that transition is designed without regard for the vast majority of users today—for whom Ethereum has always meant EOAs—we will be continually struggling against the need to support both of these userbases. This EIP provides a path not to enshrine EOAs, but to provide a migration path off of them, once and for all. + +This proposal combines well with, but is distinct from, [EIP-3074](./eip-3074.md), which provides opcodes that could enable any externally owned account (EOA) to delegate its signing authority to an arbitrary smart contract. It allows an EOA to authorize a contract account to act on its behalf _without forgoing its own powers_, while this EIP provides a final migration path off the EOA's original signing key. + +## 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. + +### Conventions + + - **`top - N`** - the `N`th most recently pushed value on the EVM stack, where `top - 0` is the most recent. + - **invalid execution** - execution that is invalid and must exit the current execution frame immediately, consuming all remaining gas (in the same way as a stack underflow or invalid jump). + - **empty account** - account where its balance is 0, its nonce is 0, and it has no code. + +### `AUTHUSURP` (`0xf8`) + +A new opcode `AUTHUSURP` shall be created at `0xf8`. It shall take two stack elements and return one stack element. + +#### Input + +| Stack | Value | +| --------- | ------------ | +| `top - 0` | `offset` | +| `top - 1` | `length` | + +#### Output + +| Stack | Value | +| ---------- | --------- | +| `top - 0` | `address` | + +#### Behavior + +`AUTHUSURP` behaves identically to `CREATE` (`0xf0`), except as described below: + + - If `authorized` (as defined in EIP-3074) is unset, execution is invalid. + - If `authorized` points to an empty account, then `static_gas` remains 32,000. Otherwise, `static_gas` shall be 7,000. + - `AUTHUSURP` does not check the nonce of the `authorized` account. + - The initcode runs at the address `authorized`. + - If the initcode returns no bytes, its execution frame must be reverted, and `AUTHUSURP` returns zero. + - After executing the initcode, but before the returned code is deployed, if the account's code is non-empty, the initcode's execution frame must be reverted, and `AUTHUSURP` returns zero. + - The code is deployed into the account with the address `authorized`. + +## Rationale + +`AUTHUSURP` does not check the nonce of the `authorized` account because it must work with accounts that have previously sent transactions. + +When using `AUTHUSURP`, if the initcode were to deploy a zero-length contract, there would be no way to prevent using `AUTHUSURP` again later. + +The account's code must be checked immediately before deploying to catch the situation where the initcode attempts to `AUTHUSURP` at the same address. This is unnecessary with other deployment instructions because they increment and check the account's nonce. + +## Backwards Compatibility + +`AUTHUSURP` with EIP-3607 revokes the authority of the original ECDSA signature to send transactions from the account. This is completely new behavior, although it is somewhat similar to the `CREATE2` opcode. + +## Security Considerations + +Contracts using ECDSA signatures outside of transactions will not be aware that the usurped account is no longer controlled by a private key. This means that, for example, the private key will _always_ have access to the `permit` function on token contracts. This can—and should—be mitigated by modifying the `ecrecover` pre-compiled contract. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5005.md b/EIPS/eip-5005.md new file mode 100644 index 00000000000000..b37f5ff1e219e0 --- /dev/null +++ b/EIPS/eip-5005.md @@ -0,0 +1,233 @@ +--- +eip: 5005 +title: Zodiac Modular Accounts +description: Composable interoperable programmable accounts +author: Auryn Macmillan (@auryn-macmillan), Kei Kreutler (@keikreutler) +discussions-to: https://ethereum-magicians.org/t/eip-zodiac-a-composable-design-philosophy-for-daos/8963 +status: Review +type: Standards Track +category: ERC +created: 2022-04-14 +requires: 165 +--- + +## Abstract +This EIP standardizes interfaces for composable and interoperable tooling for programmable Ethereum accounts. These interfaces separate contract accounts ("avatars") from their authentication and execution logic ("guards" and "modules"). Avatars implement the `IAvatar` interface, and guards implement the `IGuard` interface. Modules may take any form. + +## Motivation +Currently, most programmable accounts (like DAO tools and frameworks) are built as monolithic systems where the authorization and execution logic are coupled, either within the same contract or in a tightly integrated system of contracts. This needlessly inhibits the flexibility of these tools and encourages platform lock-in via high switching costs. + +By using the this EIP standard to separate concerns (decoupling authentication and execution logic), users are able to: + +1. Enable flexible, module-based control of programmable accounts +2. Easily switch between tools and frameworks without unnecessary overhead. +3. Enable multiple control mechanism in parallel. +4. Enable cross-chain / cross-layer governance. +5. Progressively decentralize their governance as their project and community matures. + +## 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. + +This EIP consists of four key concepts: + +- **Avatars** are programmable Ethereum accounts. Avatars are the address that holds balances, owns systems, executes transaction, is referenced externally, and ultimately represents your DAO. Avatars MUST implement the `IAvatar` interface. +- **Modules** are contracts enabled by an avatar that implement some execution logic. +- **Modifiers** are contracts that sit between modules and avatars to modify the module's behavior. For example, they might enforce a delay on all functions a module attempts to execute or limit the scope of transactions that can be initiated by the module. Modifiers MUST implement the `IAvatar` interface. +- **Guards** are contracts that MAY be enabled on modules or modifiers and implement pre- or post-checks on each transaction executed by those modules or modifiers. This allows avatars to do things like limit the scope of addresses and functions that a module or modifier can call or ensure a certain state is never changed by a module or modifier. Guards MUST expose the `IGuard` interface. Modules, modifiers, and avatars that wish to be guardable MUST inherit `Guardable`, MUST call `checkTransaction()` before triggering execution on their target, and MUST call `checkAfterExecution()` after execution is complete. + +```solidity +/// @title Avatar - A contract that manages modules that can execute transactions via this contract. + +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; + + +interface IAvatar { + event EnabledModule(address module); + event DisabledModule(address module); + event ExecutionFromModuleSuccess(address indexed module); + event ExecutionFromModuleFailure(address indexed module); + + /// @dev Enables a module on the avatar. + /// @notice Can only be called by the avatar. + /// @notice Modules should be stored as a linked list. + /// @notice Must emit EnabledModule(address module) if successful. + /// @param module Module to be enabled. + function enableModule(address module) external; + + /// @dev Disables a module on the avatar. + /// @notice Can only be called by the avatar. + /// @notice Must emit DisabledModule(address module) if successful. + /// @param prevModule Address that pointed to the module to be removed in the linked list + /// @param module Module to be removed. + function disableModule(address prevModule, address module) external; + + /// @dev Allows a Module to execute a transaction. + /// @notice Can only be called by an enabled module. + /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. + /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execTransactionFromModule( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success); + + /// @dev Allows a Module to execute a transaction and return data + /// @notice Can only be called by an enabled module. + /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. + /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execTransactionFromModuleReturnData( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success, bytes memory returnData); + + /// @dev Returns if an module is enabled + /// @return True if the module is enabled + function isModuleEnabled(address module) external view returns (bool); + + /// @dev Returns array of modules. + /// @param start Start of the page. + /// @param pageSize Maximum number of modules that should be returned. + /// @return array Array of modules. + /// @return next Start of the next page. + function getModulesPaginated(address start, uint256 pageSize) + external + view + returns (address[] memory array, address next); +} +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; + +interface IGuard { + function checkTransaction( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures, + address msgSender + ) external; + + function checkAfterExecution(bytes32 txHash, bool success) external; +} + +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; +import "./BaseGuard.sol"; + +/// @title Guardable - A contract that manages fallback calls made to this contract +contract Guardable { + address public guard; + + event ChangedGuard(address guard); + + /// `guard_` does not implement IERC165. + error NotIERC165Compliant(address guard_); + + /// @dev Set a guard that checks transactions before execution. + /// @param _guard The address of the guard to be used or the 0 address to disable the guard. + function setGuard(address _guard) external { + if (_guard != address(0)) { + if (!BaseGuard(_guard).supportsInterface(type(IGuard).interfaceId)) + revert NotIERC165Compliant(_guard); + } + guard = _guard; + emit ChangedGuard(guard); + } + + function getGuard() external view returns (address _guard) { + return guard; + } +} +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; +import "./IERC165.sol"; +import "./IGuard.sol"; + +abstract contract BaseGuard is IERC165 { + function supportsInterface(bytes4 interfaceId) + external + pure + override + returns (bool) + { + return + interfaceId == type(IGuard).interfaceId || // 0xe6d7a83a + interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7 + } + + /// @dev Module transactions only use the first four parameters: to, value, data, and operation. + /// Module.sol hardcodes the remaining parameters as 0 since they are not used for module transactions. + function checkTransaction( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures, + address msgSender + ) external virtual; + + function checkAfterExecution(bytes32 txHash, bool success) external virtual; +} +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +/// @title Enum - Collection of enums + +contract Enum { + + enum Operation {Call, DelegateCall} + +} +``` + +## Rationale +The interface defined in this standard is designed to be mostly compatible with most popular programmable accounts in use right now, to minimize the need for changes to existing tooling. + +## Backwards Compatibility +No backward compatibility issues are introduced by this standard. + +## Security Considerations +There are some considerations that module developers and users should take into account: +1. **Modules have absolute control:** Modules have absolute control over any avatar on which they are enabled, so any module implementation should be treated as security critical and users should be vary cautious about enabling new modules. ONLY ENABLE MODULES THAT YOU TRUST WITH THE FULL VALUE OF THE AVATAR. +2. **Race conditions:** A given avatar may have any number of modules enabled, each with unilateral control over the safe. In such cases, there may be race conditions between different modules and/or other control mechanisms. +3. **Don't brick your avatar:** There are no safeguards to stop you adding or removing modules. If you remove all of the modules that let you control an avatar, the avatar will cease to function and all funds will be stuck. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5006.md b/EIPS/eip-5006.md new file mode 100644 index 00000000000000..f735e39f9c3acb --- /dev/null +++ b/EIPS/eip-5006.md @@ -0,0 +1,158 @@ +--- +eip: 5006 +title: Rental NFT, NFT User Extension +description: Add a user role with restricted permissions to ERC-1155 tokens +author: Lance (@LanceSnow), Anders (@0xanders), Shrug +discussions-to: https://ethereum-magicians.org/t/eip5006-erc-1155-usage-rights-extension/8941 +status: Final +type: Standards Track +category: ERC +created: 2022-04-12 +requires: 165, 1155 +--- + +## Abstract + +This standard is an extension of [ERC-1155](./eip-1155.md). It proposes an additional role (`user`) which can be granted to addresses that represent a `user` of the assets rather than an `owner`. + +## Motivation + +Like [ERC-721](./eip-721.md), [ERC-1155](./eip-1155.md) tokens may have utility of some kind. The people who “use” the token may be different than the people who own it (such as in a rental). Thus, it would be useful to have separate roles for the “owner” and the “user” so that the “user” would not be able to take actions that the owner could (for example, transferring ownership). + +## Specification + +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. + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +interface IERC5006 { + struct UserRecord { + uint256 tokenId; + address owner; + uint64 amount; + address user; + uint64 expiry; + } + + /** + * @dev Emitted when permission for `user` to use `amount` of `tokenId` token owned by `owner` + * until `expiry` are given. + */ + event CreateUserRecord( + uint256 recordId, + uint256 tokenId, + uint64 amount, + address owner, + address user, + uint64 expiry + ); + + /** + * @dev Emitted when record of `recordId` are deleted. + */ + event DeleteUserRecord(uint256 recordId); + + /** + * @dev Returns the usable amount of `tokenId` tokens by `account`. + */ + function usableBalanceOf(address account, uint256 tokenId) + external + view + returns (uint256); + + /** + * @dev Returns the amount of frozen tokens of token type `id` by `account`. + */ + function frozenBalanceOf(address account, uint256 tokenId) + external + view + returns (uint256); + + /** + * @dev Returns the `UserRecord` of `recordId`. + */ + function userRecordOf(uint256 recordId) + external + view + returns (UserRecord memory); + + /** + * @dev Gives permission to `user` to use `amount` of `tokenId` token owned by `owner` until `expiry`. + * + * Emits a {CreateUserRecord} event. + * + * Requirements: + * + * - If the caller is not `owner`, it must be have been approved to spend ``owner``'s tokens + * via {setApprovalForAll}. + * - `owner` must have a balance of tokens of type `id` of at least `amount`. + * - `user` cannot be the zero address. + * - `amount` must be greater than 0. + * - `expiry` must after the block timestamp. + */ + function createUserRecord( + address owner, + address user, + uint256 tokenId, + uint64 amount, + uint64 expiry + ) external returns (uint256); + + /** + * @dev Atomically delete `record` of `recordId` by the caller. + * + * Emits a {DeleteUserRecord} event. + * + * Requirements: + * + * - the caller must have allowance. + */ + function deleteUserRecord(uint256 recordId) external; +} + +``` + +The `supportsInterface` method MUST return `true` when called with `0xc26d96cc`. + +## Rationale + +This model is intended to facilitate easy implementation. The following are some problems that are solved by this standard: + +### Clear Rights Assignment + +With Dual “owner” and “user” roles, it becomes significantly easier to manage what lenders and borrowers can and cannot do with the NFT (in other words, their rights).  For example, for the right to transfer ownership, the project simply needs to check whether the address taking the action represents the owner or the user and prevent the transaction if it is the user.  Additionally, owners can control who the user is and it is easy for other projects to assign their own rights to either the owners or the users. + +### Easy Third-Party Integration + +In the spirit of permissionless interoperability, this standard makes it easier for third-party protocols to manage NFT usage rights without permission from the NFT issuer or the NFT application. Once a project has adopted the additional `user` role, any other project can directly interact with these features and implement their own type of transaction. For example, a PFP NFT using this standard can be integrated into both a rental platform where users can rent the NFT for 30 days AND, at the same time, a mortgage platform where users can use the NFT while eventually buying ownership of the NFT with installment payments. This would all be done without needing the permission of the original PFP project. + +## Backwards Compatibility + +As mentioned in the specifications section, this standard can be fully ERC compatible by adding an extension function set, and there are no conflicts between [ERC-5006](./eip-5006.md) and ERC-1155. + +In addition, new functions introduced in this standard have many similarities with the existing functions in ERC-1155. This allows developers to easily adopt the standard quickly. + +## Test Cases + +Test cases are included in [test.js](../assets/eip-5006/test/test.ts). + +Run in terminal: + +1. ```cd ../assets/eip-5006``` +1. ```npm install``` +1. ```npx hardhat test``` + +## Reference Implementation + +See [`ERC5006.sol`](../assets/eip-5006/contracts/ERC5006.sol). + +## Security Considerations + +This EIP standard can completely protect the rights of the owner, the owner can change the NFT user, the user can not transfer the NFT. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5007.md b/EIPS/eip-5007.md new file mode 100644 index 00000000000000..a378410137f03a --- /dev/null +++ b/EIPS/eip-5007.md @@ -0,0 +1,149 @@ +--- +eip: 5007 +title: Time NFT, ERC-721 Time Extension +description: Add start time and end time to ERC-721 tokens. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug +discussions-to: https://ethereum-magicians.org/t/eip-5007-eip-721-time-extension/8924 +status: Final +type: Standards Track +category: ERC +created: 2022-04-13 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [ERC-721](./eip-721.md). It proposes some additional functions (`startTime`, `endTime`) to help with on-chain time management. + +## Motivation + +Some NFTs have a defined usage period and cannot be used outside of that period. With traditional NFTs that do not include time information, if you want to mark a token as invalid or enable it at a specific time, you need to actively submit a transaction—a process both cumbersome and expensive. + +Some existing NFTs contain time functions, but their interfaces are not consistent, so it is difficult to develop third-party platforms for them. + +By introducing these functions (`startTime`, `endTime`), it is possible to enable and disable NFTs automatically on chain. + +## 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. + +```solidity +/** + * @dev the ERC-165 identifier for this interface is 0xf140be0d. + */ +interface IERC5007 /* is IERC721 */ { + /** + * @dev Returns the start time of the NFT as a UNIX timestamp. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function startTime(uint256 tokenId) external view returns (uint64); + + /** + * @dev Returns the end time of the NFT as a UNIX timestamp. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function endTime(uint256 tokenId) external view returns (uint64); + +} +``` + +The **composable extension** is OPTIONAL for this standard. This allows your NFT to be minted from an existing NFT or to merge two NFTs into one NFT. + +```solidity +/** + * @dev the ERC-165 identifier for this interface is 0x75cf3842. + */ +interface IERC5007Composable /* is IERC5007 */ { + /** + * @dev Returns the asset id of the time NFT. + * Only NFTs with same asset id can be merged. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function assetId(uint256 tokenId) external view returns (uint256); + + /** + * @dev Split an old token to two new tokens. + * The assetId of the new token is the same as the assetId of the old token + * + * Requirements: + * + * - `oldTokenId` must exist. + * - `newToken1Id` must not exist. + * - `newToken1Owner` cannot be the zero address. + * - `newToken2Id` must not exist. + * - `newToken2Owner` cannot be the zero address. + * - `splitTime` require(oldToken.startTime <= splitTime && splitTime < oldToken.EndTime) + */ + function split( + uint256 oldTokenId, + uint256 newToken1Id, + address newToken1Owner, + uint256 newToken2Id, + address newToken2Owner, + uint64 splitTime + ) external; + + /** + * @dev Merge the first token and second token into the new token. + * + * Requirements: + * + * - `firstTokenId` must exist. + * - `secondTokenId` must exist. + * - require((firstToken.endTime + 1) == secondToken.startTime) + * - require((firstToken.assetId()) == secondToken.assetId()) + * - `newTokenOwner` cannot be the zero address. + * - `newTokenId` must not exist. + */ + function merge( + uint256 firstTokenId, + uint256 secondTokenId, + address newTokenOwner, + uint256 newTokenId + ) external; +} +``` + +## Rationale + +### Time Data Type + +The max value of `uint64` is 18,446,744,073,709,551,615. As a timestamp, 18,446,744,073,709,551,615 is about year 584,942,419,325. `uint256` is too big for C, C++, Java, Go, etc, and `uint64` is natively supported by mainstream programming languages. + +## Backwards Compatibility + +This standard is fully ERC-721 compatible. + +## Test Cases + +Test cases are included in [test.js](../assets/eip-5007/test/test.js). + +Run in terminal: + +```shell +cd ../assets/eip-5007 +npm install truffle -g +npm install +truffle test +``` + +## Reference Implementation + +See [`ERC5007.sol`](../assets/eip-5007/contracts/ERC5007.sol). + +## Security Considerations + +No security issues found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5008.md b/EIPS/eip-5008.md new file mode 100644 index 00000000000000..5dde543fc75c27 --- /dev/null +++ b/EIPS/eip-5008.md @@ -0,0 +1,78 @@ +--- +eip: 5008 +title: ERC-721 Nonce Extension +description: Add a `nonce` function to ERC-721. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug +discussions-to: https://ethereum-magicians.org/t/eip5008-eip-721-nonce-and-metadata-update-extension/8925 +status: Last Call +last-call-deadline: 2023-08-15 +type: Standards Track +category: ERC +created: 2022-04-10 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [ERC-721](./eip-721.md). It proposes adding a `nonce` function to ERC-721 tokens. + +## Motivation + +Some orders of NFT marketplaces have been attacked and the NFTs sold at a lower price than the current market floor price. This can happen when users transfer an NFT to another wallet and, later, back to the original wallet. This reactivates the order, which may list the token at a much lower price than the owner would have intended. + +This EIP proposes adding a `nonce` property to ERC-721 tokens, and the `nonce` will be changed when a token is transferred. If a `nonce` is added to an order, the order can be checked to avoid attacks. + +## Specification + +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. + +```solidity + +/// @dev the ERC-165 identifier for this interface is 0xce03fdab. +interface IERC5008 /* is IERC165 */ { + /// @notice Emitted when the `nonce` of an NFT is changed + event NonceChanged(uint256 tokenId, uint256 nonce); + + /// @notice Get the nonce of an NFT + /// Throws if `tokenId` is not a valid NFT + /// @param tokenId The id of the NFT + /// @return The nonce of the NFT + function nonce(uint256 tokenId) external view returns(uint256); +} +``` + +The `nonce(uint256 tokenId)` function MUST be implemented as `view`. + +The `supportsInterface` method MUST return `true` when called with `0xce03fdab`. + +## Rationale + +At first `transferCount` was considered as function name, but there may some case to change the `nonce` besides transfer, such as important properties changed, then we changed `transferCount` to `nonce`. + +## Backwards Compatibility + +This standard is compatible with ERC-721. + +## Test Cases + +Test cases are included in [test.js](../assets/eip-5008/test/test.ts). + +Run: + +```sh +cd ../assets/eip-5008 +npm install +npm run test +``` + +## Reference Implementation + +See [`ERC5008.sol`](../assets/eip-5008/contracts/ERC5008.sol). + +## Security Considerations + +No security issues found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5018.md b/EIPS/eip-5018.md new file mode 100644 index 00000000000000..8f1bc96acc30e5 --- /dev/null +++ b/EIPS/eip-5018.md @@ -0,0 +1,161 @@ +--- +eip: 5018 +title: Filesystem-like Interface for Contracts +description: An interface to provide access to binary objects similar to filesystems. +author: Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-5018-directory-standard/8958 +status: Review +type: Standards Track +category: ERC +created: 2022-04-18 +--- + + +## Abstract + +The following standardizes an API for directories and files within smart contracts, similar to traditional filesystems. +This standard provides basic functionality to read/write binary objects of any size, as well as allow reading/writing chunks of the object if the object is too large to fit in a single transaction. + +## Motivation + +A standard interface allows any binary objects on EVM-based blockchain to be re-used by other dApps. + +With [EIP-4804](./eip-4804.md), we are able to locate a Web3 resource on blockchain using HTTP-style URIs. One application of Web3 resources are web contents that are referenced within a directory using relative paths such as HTML/SVG. This standard proposes a contract-based directory to simplify the mapping between local web contents and on-chain web contents. Further, with relative paths referenced in the web contents and EIP-4804, the users will have a consistent view of the web contents locally and on-chain. + +## Specification + +### Directory + +#### Methods + +##### write + +Writes binary `data` to the file `name` in the directory by an account with write permission. + +``` +function write(bytes memory name, bytes memory data) external payable +``` + +##### read + +Returns the binary `data` from the file `name` in the directory and existence of the file. + +``` +function read(bytes memory name) external view returns (bytes memory data, bool exist) +``` + +##### fallback read + +Returns the binary `data` from the file `prefixedName` (prefixed with `/`) in the directory. + +``` +fallback(bytes calldata prefixedName) external returns (bytes memory data) +``` + +##### size + +Returns the size of the `data` from the file `name` in the directory and the number of chunks of the data. + +``` +function size(bytes memory name) external view returns (uint256 size, uint256 chunks) +``` + +##### remove + +Removes the file `name` in the directory and returns the number of chunks removed (0 means the file does not exist) by an account with write permission. + +``` +function remove(bytes memory name) external returns (uint256 numOfChunksRemoved) +``` + +##### countChunks + +Returns the number of chunks of the file `name`. + +``` +function countChunks(bytes memory name) external view returns (uint256 numOfChunks); +``` + +##### writeChunk + +Writes a chunk of data to the file by an account with write permission. The write will fail if `chunkId > numOfChunks`, i.e., the write must append the file or replace the existing chunk. + +``` + function writeChunk(bytes memory name, uint256 chunkId, bytes memory chunkData) external payable; +``` + +##### readChunk + +Returns the chunk data of the file `name` and the existence of the chunk. + +``` +function readChunk(bytes memory name, uint256 chunkId) external view returns (bytes memory chunkData, bool exist); +``` + +##### chunkSize + +Returns the size of a chunk of the file `name` and the existence of the chunk. + +``` +function chunkSize(bytes memory name, uint256 chunkId) external view returns (uint256 chunkSize, bool exist); +``` + +##### removeChunk + +Removes a chunk of the file `name` and returns `false` if such chunk does not exist. The method should be called by an account with write permission. + +``` +function removeChunk(bytes memory name, uint256 chunkId) external returns (bool exist); +``` + +##### truncate + +Removes the chunks of the file `name` in the directory from the given `chunkId` and returns the number of chunks removed by an account with write permission. When `chunkId = 0`, the method is essentially the same as `remove()`. + +``` +function truncate(bytes memory name, uint256 chunkId) external returns (uint256 numOfChunksRemoved); +``` + +##### getChunkHash + +Returns the hash value of the chunk data. + +``` +function getChunkHash(bytes memory name, uint256 chunkId) external view returns (bytes32); +``` + +## Rationale + +One issue of uploading the web contents to the blockchain is that the web contents may be too large to fit into a single transaction. As a result, the standard provides chunk-based operations so that uploading a content can be split into several transactions. Meanwhile, the read operation can be done in a single transaction, i.e., with a single Web3 URL defined in EIP-4804. + +### Interactions Between Unchunked/Chunked Functions + +`read` method should return the concatenated chunked data written by `writeChunk` method. The following gives some examples of the interactions: + +- `read("hello.txt")` => "" (file is empty) +- `writeChunk("hello.txt", 0, "abc")` will succeed +- `read("hello.txt")` => "abc" +- `writeChunk("hello.txt", 1, "efg")` will succeed +- `read("hello.txt")` => "abcefg" +- `writeChunk("hello.txt", 0, "aaa")` will succeed (replace chunk 0's data) +- `read("hello.txt")` => "aaaefg" +- `writeChunk("hello.txt", 3, "hij")` will fail because the operation is not replacement or append. + +With `writeChunk` method, we allow writing a file with external data that exceeds the current calldata limit (e.g., 1.8MB now), and it is able to read the whole file in a single `read` method (which is friendly for large web objects such as HTML/SVG/PNG/JPG, etc). + +For `write` method, calling a `write` method will replace all data chunks of the file with `write` method data, and one implementation can be: + +1. `writeChunk(filename, chunkId=0, data_from_write)` to chunk 0 with the same `write` method data; and +2. `truncate(filename, chunkId=1)`, which will remove the rest chunks. + +## Backwards Compatibility + +No backwards compatibility issues were identified. + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5022.md b/EIPS/eip-5022.md new file mode 100644 index 00000000000000..5633cbf4378d77 --- /dev/null +++ b/EIPS/eip-5022.md @@ -0,0 +1,67 @@ +--- +eip: 5022 +title: Increase price of SSTORE from zero to non-zero to 40k gas +author: Green (@greenlucid) +status: Stagnant +type: Standards Track +category: Core +created: 2022-04-20 +discussions-to: https://ethereum-magicians.org/t/eip-proposal-increase-cost-of-sstore-from-20k-to-x-when-creating-new-storage/7614 +--- + +### Abstract + +Increase the price of the SSTORE opcode from `20_000` gas to `40_000` gas when the original slot is zero and the resultant slot is non-zero. + +### Motivation + +The cost of creating a piece of new state increases as state is larger. However, the price for creating every new storage slot has not increased. +All resources are merged into the same pricing mechanism. If the price for creating new storage slots is fixed, then it needs to be manually changed. + +One of the main reasons for not increasing gas limit is the increase of state. In that regard, because the cost of creating storage is higher than its price, all the users of all the other opcodes are subsidizing the creation of state. If state creation was more precisely priced, raising gas limit would be more feasible, and would benefit the users. + +## Rationale + +### Why not also raise the cost of non-zero to non-zero? + +Rewriting storage does not affect state growth, which is the main issue this EIP is addressing. Rewriting storage may also be underpriced. +Increasing the price of state growth will, at least, incentivize developers to reuse storage instead. + +### Why not also increase the gas refund from setting non-zero to zero? + +More discussion is needed on this. + +### Why not a better state solution? + +Whereas solutions like state rent, or state expiry have been researched for a long time, they will not be ready on the short to medium term. So, it is desirable to patch pricing for the short term. Opcode repricing has been done before, so it should not impose a large development time investment for clients. + +### Why was that specific amount chosen? + +The current pricing was made off a naive approach of benchmarking opcodes in a laptop. Not only it did not consider the long term problem of having the same price for a resource that costs more over time, the benchmark itself was wrong. This price is closer to what the naive original benchmark should have been. It could go higher, but that may be too disruptive. + +### Is this too distruptive? + +This change will severely impact the gas cost of many applications. The network does not have to subsidize state growth at the expense of more expensive regular transactions, so even if it is too disruptive, it will increase the health of the network. + +### Specification + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | +| `NEW_STORAGE_PRICE` | `40_000` + +For blocks where `block.number >= FORK_BLOCK`, a new gas schedule applies. Make `SSTORE_SET_GAS`, the price when a slot is set from zero to non-zero, equal `NEW_STORAGE_PRICE`. All other costs remain the same. + +### Backwards compatibility + +Contracts that depend on hardcoded gas costs will break if they create state. + +It is a gas schedule change, so transactions from an epoch before FORK_BLOCK should be treated with previous gas costs. + +## Implementation + +https://github.com/ethereum/go-ethereum/pull/24725 + +## Security considerations + +TODO \ No newline at end of file diff --git a/EIPS/eip-5023.md b/EIPS/eip-5023.md new file mode 100644 index 00000000000000..c3e36b57e2b83c --- /dev/null +++ b/EIPS/eip-5023.md @@ -0,0 +1,169 @@ +--- +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: Final +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 0xded6338b +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) external returns(uint256 newTokenId); + +} +``` + +The 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. + +```text +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. + +```text + B----A----C + \_______/ +``` + +## Backwards Compatibility + +This proposal is backwards compatible with EIP-721 and EIP-1155. + +## 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. + +```solidity +// 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 + +Reference implementation should not be used as is in production. +There are no other security considerations related directly to implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5027.md b/EIPS/eip-5027.md new file mode 100644 index 00000000000000..8d0e9207387e2f --- /dev/null +++ b/EIPS/eip-5027.md @@ -0,0 +1,94 @@ +--- +eip: 5027 +title: Remove the limit on contract code size +description: Change the limit on contract size from 24576 to infinity +author: Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-5027-unlimit-contract-code-size/9010 +status: Draft +type: Standards Track +category: Core +created: 2022-04-21 +requires: 170, 2929, 2930 +--- + + +## Abstract + +Remove the limit on the contract code size, i.e., only limit the contract code size by block gas limit, with minimal changes to existing code and proper gas metering adjustment to avoid possible attacks. + + +## Motivation + +The motivation is to remove the limit on the code size so that users can deploy a large-code contract without worrying about splitting the contract into several sub-contracts. + +With the dramatic growth of dApplications, the functionalities of smart contracts are becoming more and more complicated, and thus, the sizes of newly developed contracts are steadily increasing. As a result, we are facing more and more issues with the 24576-bytes contract size limit. Although several techniques such as splitting a large contract into several sub-contracts can alleviate the issue, these techniques inevitably increase the burden of developing/deploying/maintaining smart contracts. + +The proposal implements a solution to remove the existing 24576-bytes limit of the code size. Further, the proposal aims to minimize the changes in the client implementation (e.g., Geth) with +- proper gas metering to avoid abusing the node resources for contract-related opcodes, i.e, `CODESIZE (0x38)/CODECOPY (0x39)/EXTCODESIZE (0x3B)/EXTCODECOPY (0x3C)/EXTCODEHASH (0x3F)/DELEGATECALL (0xF4)/CALL (0xF1)/CALLCODE (0xF2)/STATICCALL (0xFA)/CREATE (0xF0)/CREATE2 (0xF5)`; and +- no change to the existing structure of the Ethereum state. + + +## Specification + +### Parameters + +| Constant | Value | +| ------------------------- | ---------------- | +| `FORK_BLKNUM` | TBD | +| `CODE_SIZE_UNIT` | 24576 | +| `COLD_ACCOUNT_CODE_ACCESS_COST_PER_UNIT` | 2600 | +| `CREATE_DATA_GAS` | 200 | + +If `block.number >= FORK_BLKNUM`, the contract creation initialization can return data with any length, but the contract-related opcodes will take extra gas as defined below: + +- For `CODESIZE/CODECOPY/EXTCODESIZE/EXTCODEHASH`, the gas is unchanged. + +- For CREATE/CREATE2, if the newly created contract size > `CODE_SIZE_UNIT`, the opcodes will take extra write gas as + +`(CODE_SIZE - CODE_SIZE_UNIT) * CREATE_DATA_GAS`. + +- For `EXTCODECOPY/CALL/CALLCODE/DELEGATECALL/STATICCALL`, if the contract code size > `CODE_SIZE_UNIT`, then the opcodes will take extra gas as + +``` +(CODE_SIZE - 1) // CODE_SIZE_UNIT * COLD_ACCOUNT_CODE_ACCESS_COST_PER_UNIT +``` + +if the contract is not in `accessed_code_in_addresses` or `0` if the contract is in `accessed_code_in_addresses`, where `//` is the integer divide operator, and `accessed_code_in_addresses: Set[Address]` is a tranasction-context-wide set similar to `access_addressses` and `accessed_storage_keys`. + +When a transactoin execution begins, `accessed_code_in_addresses` will include `tx.sender`, `tx.to`, and all precompiles. + +When `CREATE/CREATE2/EXTCODECOPY/CALL/CALLCODE/DELEGATECALL/STATICCALL` is called, immediately add the address to `accessed_code_in_addresses`. + +## Rationale + +### Gas Metering +The goal is to measure the CPU/IO cost of the contract read/write operations reusing existing gas metering so that the resources will not be abused. + +- For code-size-related opcodes (`CODESIZE`/`EXTCODESIZE`), we would expect the client to implement a mapping from the hash of code to the size, so reading the code size of a large contract should still be O(1). + +- For `CODECOPY`, the data is already loaded in memory (as part of `CALL/CALLCODE/DELEGATECALL/STATICCALL`), so we do not charge extra gas. + +- For `EXTCODEHASH`, the value is already in the account, so we do not charge extra gas. + +- For `EXTCODECOPY/CALL/CALLCODE/DELEGATECALL/STATICCALL`, since it will read extra data from the database, we will additionally charge `COLD_ACCOUNT_CODE_ACCESS_COST_PER_UNIT` per extra `CODE_SIZE_UNIT`. + +- For `CREATE/CREATE2`, since it will create extra data to the database, we will additionally charge `CREATE_DATA_GAS` per extra bytes. + + +## Backwards Compatibility + +All existing contracts will not be impacted by the proposal. + +Only contracts deployed before [EIP-170](./eip-170.md) could possibly be longer than the current max code size, and the reference implementation was able to successfully import all blocks before that fork. + +## Reference Implementation + +The reference implementation on Geth is available at [0001-unlimit-code-size.patch](../assets/eip-5027/0001-unlimit-code-size.patch). + +## Security Considerations +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + + diff --git a/EIPS/eip-5050.md b/EIPS/eip-5050.md new file mode 100644 index 00000000000000..aff711cd8babf9 --- /dev/null +++ b/EIPS/eip-5050.md @@ -0,0 +1,351 @@ +--- +eip: 5050 +title: Interactive NFTs with Modular Environments +description: Action messaging and discovery protocol for interactions on and between NFTs +author: Alexi (@alexi) +discussions-to: https://ethereum-magicians.org/t/eip-5050-nft-interaction-standard/9922 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-4-18 +requires: 165, 173, 721, 1155, 1820, 4906 +--- + +## Abstract + +This standard defines a broadly applicable action messaging protocol for the transmission of user-initiated actions between tokens. Modular statefulness is achieved with optional state controller contracts (i.e. environments) that manage shared state, and provide arbitration and settlement of the action process. + +## Motivation + +Tokenized item standards such as [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) serve as the objects of the Ethereum computing environment. A growing number of projects are seeking to build interactivity and *"digital physics"* into NFTs, especially in the contexts of gaming and decentralized identity. A standard action messaging protocol will allow this physics layer to be developed in the same open, Ethereum-native way as the objects they operate on. + +The messaging protocol outlined defines how an action is initiated and transmitted between tokens and (optional) shared state environments. It is paired with a common interface for defining functionality that allows off-chain services to aggregate and query supported contracts for functionality and interoperability; creating a discoverable, human-readable network of interactive token contracts. Not only can contracts that implement this standard be automatically discovered by such services, their *policies for interaction* can be as well. This allows clients to easily discover compatible senders and receivers, and allowed actions. + +Aggregators can also parse action event logs to derive analytics on new action types, trending/popular/new interactive contracts, which token and state contract pairs users are likely to interact with, and other discovery tools to facilitate interaction. + +### Benefits + +1. Make interactive token contracts **discoverable and usable** by applications +2. Create a decentralized "digital physics" layer for gaming and other applications +3. Provide developers a simple solution with viable validity guarantees to make dynamic NFTs and other tokens +4. Allow for generalized action bridges to transmit actions between chains (enabling actions on L1 assets to be saved to L2s, L1 assets to interact with L2 assets, and L2 actions to be "rolled-up"/finalized on L1). + +## 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. + +Smart contracts implementing this EIP standard MUST implement the [EIP-165](./eip-165.md) supportsInterface function and MUST return the constant value `true` if the `IERC5050Sender` interface ID `0xc8c6c9f3` and/or the `IERC5050Receiver` interface ID `0x1a3f02f4` is passed through the `interfaceID` argument (depending on which interface(s) the contract implements). + +```solidity +pragma solidity ^0.8.0; + +/// @param _address The address of the interactive object +/// @param tokenId The token that is interacting (optional) +struct Object { + address _address; + uint256 _tokenId; +} + +/// @param selector The bytes4(keccack256()) encoding of the action string +/// @param user The address of the sender +/// @param from The initiating object +/// @param to The receiving object +/// @param state The state controller contract +/// @param data Additional data with no specified format +struct Action { + bytes4 selector; + address user; + Object from; + Object to; + address state; + bytes data; +} + +/// @title EIP-5050 Interactive NFTs with Modular Environments +interface IERC5050Sender { + /// @notice Send an action to the target address + /// @dev The action's `fromContract` is automatically set to `address(this)`, + /// and the `from` parameter is set to `msg.sender`. + /// @param action The action to send + function sendAction(Action memory action) external payable; + + /// @notice Check if an action is valid based on its hash and nonce + /// @dev When an action passes through all three possible contracts + /// (`fromContract`, `to`, and `state`) the `state` contract validates the + /// action with the initiating `fromContract` using a nonced action hash. + /// This hash is calculated and saved to storage on the `fromContract` before + /// action handling is initiated. The `state` contract calculates the hash + /// and verifies it and nonce with the `fromContract`. + /// @param _hash The hash to validate + /// @param _nonce The nonce to validate + function isValid(bytes32 _hash, uint256 _nonce) external returns (bool); + + /// @notice Retrieve list of actions that can be sent. + /// @dev Intended for use by off-chain applications to query compatible contracts, + /// and to advertise functionality in human-readable form. + function sendableActions() external view returns (string[] memory); + + /// @notice Change or reaffirm the approved address for an action + /// @dev The zero address indicates there is no approved address. + /// Throws unless `msg.sender` is the `_account`, or an authorized + /// operator of the `_account`. + /// @param _account The account of the account-action pair to approve + /// @param _action The action of the account-action pair to approve + /// @param _approved The new approved account-action controller + function approveForAction( + address _account, + bytes4 _action, + address _approved + ) external returns (bool); + + /// @notice Enable or disable approval for a third party ("operator") to conduct + /// all actions on behalf of `msg.sender` + /// @dev Emits the ApprovalForAll event. The contract MUST allow + /// an unbounded number of operators per owner. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if the operator is approved, false to revoke approval + function setApprovalForAllActions(address _operator, bool _approved) + external; + + /// @notice Get the approved address for an account-action pair + /// @dev Throws if `_tokenId` is not a valid NFT. + /// @param _account The account of the account-action to find the approved address for + /// @param _action The action of the account-action to find the approved address for + /// @return The approved address for this account-action, or the zero address if + /// there is none + function getApprovedForAction(address _account, bytes4 _action) + external + view + returns (address); + + /// @notice Query if an address is an authorized operator for another address + /// @param _account The address on whose behalf actions are performed + /// @param _operator The address that acts on behalf of the account + /// @return True if `_operator` is an approved operator for `_account`, false otherwise + function isApprovedForAllActions(address _account, address _operator) + external + view + returns (bool); + + /// @dev This emits when an action is sent (`sendAction()`) + event SendAction( + bytes4 indexed name, + address _from, + address indexed _fromContract, + uint256 _tokenId, + address indexed _to, + uint256 _toTokenId, + address _state, + bytes _data + ); + + /// @dev This emits when the approved address for an account-action pair + /// is changed or reaffirmed. The zero address indicates there is no + /// approved address. + event ApprovalForAction( + address indexed _account, + bytes4 indexed _action, + address indexed _approved + ); + + /// @dev This emits when an operator is enabled or disabled for an account. + /// The operator can conduct all actions on behalf of the account. + event ApprovalForAllActions( + address indexed _account, + address indexed _operator, + bool _approved + ); +} + +interface IERC5050Receiver { + /// @notice Handle an action + /// @dev Both the `to` contract and `state` contract are called via + /// `onActionReceived()`. + /// @param action The action to handle + function onActionReceived(Action calldata action, uint256 _nonce) + external + payable; + + /// @notice Retrieve list of actions that can be received. + /// @dev Intended for use by off-chain applications to query compatible contracts, + /// and to advertise functionality in human-readable form. + function receivableActions() external view returns (string[] memory); + + /// @dev This emits when a valid action is received. + event ActionReceived( + bytes4 indexed name, + address _from, + address indexed _fromContract, + uint256 _tokenId, + address indexed _to, + uint256 _toTokenId, + address _state, + bytes _data + ); +} +``` + +### Action Naming + +Actions SHOULD use dot-separation for namespacing (e.g. `"spells.cast"` specifies the `"cast"` action with namespace `"spells"`), and arrow-separation for sequence specification (e.g. `"settle>build"` indicating `"settle"` must be received before `"build"`). + +### How State Contracts Work + +Actions do not require that a state contract be used. Actions can be transmitted from one token contract (`Object`) to another, or from a user to a single token contract. In these cases, the sending and receiving contracts each control their own state. + +State contracts allow arbitrary senders and receivers to share a user-specified state environment. Each `Object` MAY define its own action handling, which MAY include reading from the state contract during, but the action MUST be finalized by the state contract. This means the state contract serves as ground truth. + +The intended workflow is for state contracts to define stateful game environments, typically with a custom `IState` interface for use by other contracts. `Objects` register with state contracts to initialize their state. Then, users commit actions using a specific state contract to make things happen in the game. + +The modularity of state contracts allows multiple copies of the same or similar "game environment" to be created and swapped in or out by the client. There are many ways this modularity can be used: + +- Aggregator services can analyze action events to determine likely state contracts for a given sender/receiver +- Sender/receiver contracts can require a specific state contract +- Sender/receiver contracts can allow any state contract, but set a default. This is important for NFTs that change their render based on state. This default can also be configurable by the token holder. +- State contracts can be bridges to state contracts on another chain, allowing for L1-verification, L2-storage usage pattern (validate action with layer-1 assets, save on l2 where storage is cheaper). + +#### Example + +State Contract `FightGame` defines a fighting game environment. Token holders call `FightGame.register(contract, tokenId)` to randomly initialize their stats (strength/hp/etc.). An account which holds a registered token A of contract `Fighters`, calls `Fighters.sendAction(AttackAction)`, specifying token A from `Fighters` as the sender, token B from `Pacifists` contract as the receiver, and `FightGame` as the state contract. + +The action is passed to token B, which may handle the action in whatever way it wants before passing the action to the `FightGame` state contract. The state contract can verify the stored action hash with the `Fighters` contract to validate the action is authentic before updating the stats if the tokens, dealing damage to token B. + +Tokens A and B may update their metadata based on stats in the `FightGame` state contract, or based on their own stored data updated in response to sending/receiving actions. + +### Extensions + +#### Interactive + +Some contracts may have custom user interfaces that facilitate interaction. + +```solidity +pragma solidity ^0.8.0; + +/// @title EIP-5050 Interactive NFTs with Modular Environments +interface IERC5050Interactive { + function interfaceURI(bytes4 _action) external view returns (string); +} +``` + +#### Action Proxies + +Action proxies can be used to support backwards compatibility with non-upgradeable contracts, and potentially for cross-chain action bridging. + +They can be implemented using a modified version of [EIP-1820](./eip-1820.md#erc-1820-registry-smart-contract) that allows [EIP-173](./eip-173.md) contract owners to call `setManager()`. + +#### Controllable + +Users of this standard may want to allow trusted contracts to control the action process to provide security guarantees, and support action bridging. Controllers step through the action chain, calling each contract individually in sequence. + +Contracts that support Controllers SHOULD ignore require/revert statements related to action verification, and MUST NOT pass the action to the next contract in the chain. + +```solidity +pragma solidity ^0.8.0; + +/// @title EIP-5050 Action Controller +interface IControllable { + + /// @notice Enable or disable approval for a third party ("controller") to force + /// handling of a given action without performing EIP-5050 validity checks. + /// @dev Emits the ControllerApproval event. The contract MUST allow + /// an unbounded number of controllers per action. + /// @param _controller Address to add to the set of authorized controllers + /// @param _action Selector of the action for which the controller is approved / disapproved + /// @param _approved True if the controller is approved, false to revoke approval + function setControllerApproval(address _controller, bytes4 _action, bool _approved) + external; + + /// @notice Enable or disable approval for a third party ("controller") to force + /// action handling without performing EIP-5050 validity checks. + /// @dev Emits the ControllerApproval event. The contract MUST allow + /// an unbounded number of controllers per action. + /// @param _controller Address to add to the set of authorized controllers + /// @param _approved True if the controller is approved, false to revoke approval + function setControllerApprovalForAll(address _controller, bool _approved) + external; + + /// @notice Query if an address is an authorized controller for a given action. + /// @param _controller The trusted third party address that can force action handling + /// @param _action The action selector to query against + /// @return True if `_controller` is an approved operator for `_account`, false otherwise + function isApprovedController(address _controller, bytes4 _action) + external + view + returns (bool); + + /// @dev This emits when a controller is enabled or disabled for the given + /// action. The controller can force `action` handling on the emitting contract, + /// bypassing the standard EIP-5050 validity checks. + event ControllerApproval( + address indexed _controller, + bytes4 indexed _action, + bool _approved + ); + + /// @dev This emits when a controller is enabled or disabled for all actions. + /// Disabling all action approval for a controller does not override explicit action + /// action approvals. Controller's approved for all actions can force action handling + /// on the emitting contract for any action. + event ControllerApprovalForAll( + address indexed _controller, + bool _approved + ); +} +``` + +#### Metadata Update + +Interactive NFTs are likely to update their metadata in response to certain actions and developers MAY want to implement [EIP-4906](./eip-4906.md) event emitters. + +## Rationale + +The critical features of this interactive token standard are that it 1) creates a common way to define, advertise, and conduct object interaction, 2) enables optional, brokered statefulness with *useful* validity assurances at minimum gas overhead, 3) is easy for developers to implement, and 4) is easy for end-users to use. + +### Action Names & Selectors + +Actions are advertised using human-readable strings, and processed using function selectors (`bytes4(keccack256(action_key))`). Human-readable strings allow end-users to easily interpret functionality, while function selectors allow efficient comparison operations on arbitrarily long action keys. This scheme also allows for simple namespacing and sequence specification. + +Off-chain services can easily convert the strings to `bytes4` selector encoding when interacting with contracts implementing this EIP or parsing `SendAction` and `ActionReceived` event logs. + +### Validation + +Validation of the initiating contract via a hash of the action data was satisfactory to nearly everyone surveyed and was the most gas efficient verification solution explored. We recognize that this solution does not allow the receiving and state contracts to validate the initiating `user` account beyond using `tx.origin`, which is vulnerable to phishing attacks. + +We considered using a signed message to validate user-intiation, but this approach had two major drawbacks: + +1. **UX** users would be required to perform two steps to commit each action (sign the message, and send the transaction) +2. **Gas** performing signature verification is computationally expensive + +Most importantly, the consensus among the developers surveyed is that strict user validation is not necessary because the concern is only that malicious initiating contracts will phish users to commit actions *with* the malicious contract's assets. **This protocol treats the initiating contract's token as the prime mover, not the user.** Anyone can tweet at Bill Gates. Any token can send an action to another token. Which actions are accepted, and how they are handled is left up to the contracts. High-value actions can be reputation-gated via state contracts, or access-gated with allow/disallow-lists. [`Controllable`](#controllable) contracts can also be used via trusted controllers as an alternative to action chaining. + +*Alternatives considered: action transmitted as a signed message, action saved to reusable storage slot on initiating contract* + +### State Contracts + +Moving state logic into dedicated, parameterized contracts makes state an action primitive and prevents state management from being obscured within the contracts. Specifically, it allows users to decide which "environment" to commit the action in, and allows the initiating and receiving contracts to share state data without requiring them to communicate. + +The specifics of state contract interfaces are outside the scope of this standard, and are intended to be purpose-built for unique interactive environments. + +### Gas and Complexity (regarding action chaining) + +Action handling within each contract can be arbitrarily complex, and there is no way to eliminate the possibility that certain contract interactions will run out of gas. However, developers SHOULD make every effort to minimize gas usage in their action handler methods, and avoid the use of for-loops. + +*Alternatives considered: multi-request action chains that push-pull from one contract to the next.* + +## Backwards Compatibility + +Non-upgradeable, already deployed token contracts will not be compatible with this standard unless a proxy registry extension is used. + +## Reference Implementation + +A reference implementation is included in `../assets/eip-5050` with a simple stateless example [`ExampleToken2Token.sol`](../assets/eip-5050/ExampleToken2Token.sol), and a stateful example [`ExampleStateContract.sol`](../assets/eip-5050/ExampleStateContract.sol) + +## Security Considerations + +The core security consideration of this protocol is action validation. Actions are passed from one contract to another, meaning it is not possible for the receiving contract to natively verify that the caller of the initiating contract matches the `action.from` address. One of the most important contributions of this protocol is that it provides an alternative to using signed messages, which require users to perform two operations for every action committed. + +As discussed in [Validation](#validation), this is viable because the initiating contract / token is treated as the prime mover, not the user. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-5058.md b/EIPS/eip-5058.md new file mode 100644 index 00000000000000..e6fe7b6987f46a --- /dev/null +++ b/EIPS/eip-5058.md @@ -0,0 +1,206 @@ +--- +eip: 5058 +title: Lockable Non-Fungible Tokens +description: Lockable EIP-721 tokens +author: Tyler (@radiocaca), Alex (@gojazdev), John (@sfumato00) +discussions-to: https://ethereum-magicians.org/t/eip-5058-erc-721-lockable-standard/9201 +status: Draft +type: Standards Track +category: ERC +created: 2022-04-30 +requires: 20, 165, 721 +--- + +## Abstract + +We propose to extend the [EIP-721](./eip-721.md) standard with a secure locking mechanism. The NFT owners approve the operator to lock the NFT through `setLockApprovalForAll()` or `lockApprove()`. The approved operator locks the NFT through `lock()`. The locked NFTs cannot be transferred until the end of the locking period. An immediate use case is to allow NFTs to participate in smart contracts without leaving the wallets of their owners. + +## Motivation + +NFTs, enabled by [EIP-721](./eip-721.md), have exploded in demand. The total market value and the ecosystem continue to grow with more and more blue chip NFTs, which are approximately equivalent to popular intellectual properties in a conventional sense. Despite the vast success, something is left to be desired. Liquidity has always been one of the biggest challenges for NFTs. Several attempts have been made to tackle the liquidity challenge: NFTFi and BendDAO, to name a few. Utilizing the currently prevalent EIP-721 standard, these projects require participating NFTs to be transferred to the projects' contracts, which poses inconveniences and risks to the owners: + +1. Smart contract risks: NFTs can be lost or stolen due to bugs or vulnerabilities in the contracts. +2. Loss of utility: NFTs have utility values, such as profile pictures and bragging rights, which are lost when the NFTs are no longer seen under the owners' custody. +3. Missing Airdrops: The owners can no longer directly receive airdrops entitled to the NFTs. Considering the values and price fluctuation of some of the airdrops, either missing or not getting the airdrop on time can financially impact the owners. + +All of the above are bad UX, and we believe the EIP-721 standard can be improved by adopting a native locking mechanism: + +1. Instead of being transferred to a smart contract, an NFT remains in self-custody but locked. +2. While an NFT is locked, its transfer is prohibited. Other properties remain unaffected. +3. The owners can receive or claim airdrops themselves. + +The value of an NFT can be reflected in two aspects: collection value and utility value. Collection value needs to ensure that the holder's wallet retains ownership of the NFT forever. Utility value requires ensuring that the holder can verify their NFT ownership in other projects. Both of these aspects require that the NFT remain in its owner's wallet. + +The proposed standard allows the underlying NFT assets to be managed securely and conveniently by extending the EIP-721 standard to natively support common NFTFi use cases including locking, staking, lending, and crowdfunding. We believe the proposed standard will encourage NFT owners to participate more actively in NFTFi projects and, hence, improve the livelihood of the whole NFT ecosystem. + +## 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. + +Lockable EIP-721 **MUST** implement the `IERC5058` interfaces: + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.8; + +/** + * @dev EIP-721 Non-Fungible Token Standard, optional lockable extension + * ERC721 Token that can be locked for a certain period and cannot be transferred. + * This is designed for a non-escrow staking contract that comes later to lock a user's NFT + * while still letting them keep it in their wallet. + * This extension can ensure the security of user tokens during the staking period. + * If the nft lending protocol is compatible with this extension, the trouble caused by the NFT + * airdrop can be avoided, because the airdrop is still in the user's wallet + */ +interface IERC5058 { + /** + * @dev Emitted when `tokenId` token is locked by `operator` from `from`. + */ + event Locked(address indexed operator, address indexed from, uint256 indexed tokenId, uint256 expired); + + /** + * @dev Emitted when `tokenId` token is unlocked by `operator` from `from`. + */ + event Unlocked(address indexed operator, address indexed from, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to lock the `tokenId` token. + */ + event LockApproval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to lock all of its tokens. + */ + event LockApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the locker who is locking the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function lockerOf(uint256 tokenId) external view returns (address locker); + + /** + * @dev Lock `tokenId` token until the block number is greater than `expired` to be unlocked. + * + * Requirements: + * + * - `tokenId` token must be owned by `owner`. + * - `expired` must be greater than block.number + * - If the caller is not `owner`, it must be approved to lock this token + * by either {lockApprove} or {setLockApprovalForAll}. + * + * Emits a {Locked} event. + */ + function lock(uint256 tokenId, uint256 expired) external; + + /** + * @dev Unlock `tokenId` token. + * + * Requirements: + * + * - `tokenId` token must be owned by `owner`. + * - the caller must be the operator who locks the token by {lock} + * + * Emits a {Unlocked} event. + */ + function unlock(uint256 tokenId) external; + + /** + * @dev Gives permission to `to` to lock `tokenId` token. + * + * Requirements: + * + * - The caller must own the token or be an approved lock operator. + * - `tokenId` must exist. + * + * Emits an {LockApproval} event. + */ + function lockApprove(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an lock operator for the caller. + * Operators can call {lock} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {LockApprovalForAll} event. + */ + function setLockApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns the account lock approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getLockApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to lock all of the assets of `owner`. + * + * See {setLockApprovalForAll} + */ + function isLockApprovedForAll(address owner, address operator) external view returns (bool); + + /** + * @dev Returns if the `tokenId` token is locked. + */ + function isLocked(uint256 tokenId) external view returns (bool); + + /** + * @dev Returns the `tokenId` token lock expired time. + */ + function lockExpiredTime(uint256 tokenId) external view returns (uint256); +} +``` + +## Rationale + +### NFT lock approvals + +An NFT owner can give another trusted operator the right to lock his NFT through the approve functions. The `lockApprove()` function only approves for the specified NFT, whereas `setLockApprovalForAll()` approves for all NFTs of the collection under the wallet. When a user participates in an NFTFi project, the project contract calls `lock()` to lock the user's NFT. Locked NFTs cannot be transferred, but the NFTFi project contract can use the unlock function `unlock()` to unlock the NFT. + +### NFT lock/unlock + +Authorized project contracts have permission to lock NFT with the `lock` method. Locked NFTs cannot be transferred until the lock time expires. The project contract also has permission to unlock NFT in advance through the `unlock` function. Note that only the address of the locked NFT has permission to unlock that NFT. + +### NFT lock period + +When locking an NFT, one must specify the lock expiration block number, which must be greater than the current block number. When the current block number exceeds the expiration block number, the NFT is automatically released and can be transferred. + +### Bound NFT + +Bound NFT is an extension of this EIP, which implements the ability to mint a boundNFT during the NFT locking period. The boundNFT is identical to the locked NFT metadata and can be transferred. However, a boundNFT only exists during the NFT locking period and will be destroyed after the NFT is unlocked. +BoundNFT can be used to lend, as a staking credential for the contract. The credential can be locked in the contract, but also to the user. In NFT leasing, boundNFT can be rented to users because boundNFT is essentially equivalent to NFT. This consensus, if accepted by all projects, boundNFT will bring more creativity to NFT. + +### Bound NFT Factory + +Bound NFT Factory is a common boundNFT factory, similar to Uniswap's [EIP-20](./eip-20.md) pairs factory. It uses the create2 method to create a boundNFT contract address for any NFT deterministic. BoundNFT contract that has been created can only be controlled by the original NFT contract. + + +## Backwards Compatibility + +This standard is compatible with EIP-721. + +## Test Cases + +Test cases written using hardhat can be found [here](../assets/eip-5058/test/test.ts) + +## Reference Implementation + +You can find an implementation of this standard in the [assets](../assets/eip-5058/ERC5058.sol) folder. + +## Security Considerations + +After being locked, the NFT can not be transferred, so before authorizing locking rights to other project contracts, you must confirm that the project contract can unlock NFT. Otherwise there is a risk of NFT being permanently locked. It is recommended to give a reasonable locking period in use for projects. NFT can be automatically unlocked, which can reduce the risk to a certain extent. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5065.md b/EIPS/eip-5065.md new file mode 100644 index 00000000000000..9aab276bd2b6ac --- /dev/null +++ b/EIPS/eip-5065.md @@ -0,0 +1,53 @@ +--- +eip: 5065 +title: Instruction for transferring ether +description: Instruction for just transferring ether without transferring the flow of execution +author: Mudit Gupta (@maxsam4) +discussions-to: https://ethereum-magicians.org/t/eip-5065-instruction-for-transferring-ether/9107 +status: Stagnant +type: Standards Track +category: Core +created: 2022-04-30 +requires: 2929 +--- + +## Abstract +Add a new instruction that transfers ether to a destination address without handing over the flow of execution to it. It should work similarly to how `SELFDESTRUCT (0xFF)` transfers ether to the destination without making a call to it. + +## Motivation +From an architectural point of view, execution flow should never be handed over to an untrusted contract. Ethereum currently does not have any ideal way to transfer ether without transferring the flow of execution. People have come up with reentrancy guards and similar solutions to prevent some types of attacks but it's not an ideal solution. The only way to transfer ether from smart contracts without triggering a call is to create a dummy contract, send the precise amount of ether to it and then call `SELFDESTRUCT (0xFF)` from it. + +## Specification +Introduce a new instruction, `AIRDROP` (`0xFG`) that transfers ether to the destination without making a call to it. + +### Stack input +address: the account to send ether to. +value: value in wei to send to the account. + +### Gas + +The total gas cost should be the sum of a static cost + address_access_cost + value_to_empty_account_cost. + - Static cost: 6700 + - Dynamic cost: + 1. address_access_cost: If the target is not in `accessed_addresses`, charge `COLD_ACCOUNT_ACCESS_COST` gas, and add the address to `accessed_addresses`. Otherwise, charge `WARM_STORAGE_READ_COST` gas. Currently, `COLD_ACCOUNT_ACCESS_COST` is 2600 while `WARM_STORAGE_READ_COST` is 100. + 2. value_to_empty_account_cost: If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is the account creation gas cost which currently is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. + + +## Rationale +This behavior is already possible by deploying a new contract that does `SELFDESTRUCT (0xFF)` but it is prohibitively expensive. In most scenarios, the contract author only wants to transfer ether rather than transferring control of the execution. ERC20 can be used as a case study for this where most users transfer funds without a post-transfer hook. + +This instruction allows contracts to safely pass ether to an untrusted address without worrying about reentrancy or other malicious things an untrusted contract can do on. + +The static gas cost is derived by subtracting the gas stipend (2300) from the positive_value_cost of `CALL (0xF1)` opcode which is currently set to 9000. + +## Backwards Compatibility +No known issues as this is a new instruction that does not affect any old instructions and does not break any valid assumptions since it make not anything impossible possible. + +## Test Cases +TODO + +## Security Considerations +No known security risks. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/EIPS/eip-5069.md b/EIPS/eip-5069.md new file mode 100644 index 00000000000000..55e2891585da93 --- /dev/null +++ b/EIPS/eip-5069.md @@ -0,0 +1,119 @@ +--- +eip: 5069 +title: EIP Editor Handbook +description: Organizational structure, decision making process, and other EIP Editor odds and ends. +author: Pooja Ranjan (@poojaranjan), Gavin John (@Pandapip1), Sam Wilson (@SamWilsn), et al. +discussions-to: https://ethereum-magicians.org/t/pr-5069-eip-editor-handbook/9137 +status: Living +type: Meta +created: 2022-05-02 +requires: 1 +--- + +## Introduction + +We, the Ethereum Improvement Proposal (EIP) Editors, maintain a repository of documents related to the Ethereum protocol and its ecosystem. Consider us both _archivists_ making sure the community as a whole does not lose its history, and a _publisher_ making sure interested parties can stay up-to-date with the latest proposals. + +## Mission + +### What we Do + +Our mission is to serve the broad Ethereum community, both present and future, by: + + - **Publishing Proposals**: Making proposals, including their history and associated discussions available over the long term at no cost. + + By doing so, we foster transparency and ensure that valuable insights from past proposals are accessible for future decision-making and learning. + - **Facilitating Discussion**: Providing a forum for discussing proposals open to anyone who wants to participate civilly. + + By encouraging open dialogue and collaboration, we aim to harness the collective knowledge and expertise of the Ethereum community in shaping proposals. + - **Upholding Quality**: Upholding a measure of minimally-subjective quality for each proposal as defined by its target audience. + + By adhering to defined criteria, we promote the development of high-quality and relevant proposals that drive the evolution of Ethereum. + +### What we Don't + +On the other hand, we do _not_: + + - **Decide Winners**: If there are multiple competing proposals, we will publish all of them. We are not in the business of deciding what is the right path for Ethereum, nor do we believe that there is One True Way to satisfy a need. + + - **Assert Correctness**: While we might offer technical feedback from time to time, we are not experts nor do we vet every proposal in depth. Publishing a proposal is not an endorsement or a statement of technical soundness. + + - **Manage**: We do not track implementation status, schedule work, or set fork dates or contents. + + - **Track Registries**: We want all proposals to eventually become immutable, but a registry will never get there if anyone can keep adding items. To be clear, exhaustive and/or static lists are fine. + - **Provide Legal Advice**: Trademarks, copyrights, patents, prior art, and other legal matters are the responsibility of authors and implementers, not EIP Editors. We are not lawyers, and while we may occasionally make comments touching on these areas, we cannot guarantee any measure of correctness. + +Documenting all of the things we would not do is impossible, and the above are just a few examples. We reserve the right to do less work whenever possible! + +## Structure + +### EIP Editors + +We, the Editors, consist of some number of EIP Editors and one Keeper of Consensus (or just Keeper for short) elected by and from the EIP Editors. + +EIP Editors are responsible for governing the EIP process itself, electing a Keeper, and stewarding proposals. + +The Keeper's two responsibilities (on top of their EIP Editor duties) are: to determine when rough consensus has been reached on a matter, and determine when/if it is appropriate to re-open an already settled matter. + +## Membership + +Anyone may apply to join as an EIP Editor. Specific eligibility requirements are left to individual current EIP Editors, but the general requirements are: + + - A strong belief in the above mission; + - Proficiency with English (both written and spoken); + - Reading and critiquing EIPs; + - Participation in governance. + +EIP Editors are expected to meet these requirements throughout their tenure, and not doing so is grounds for removal. Any member may delegate some or all of their responsibilities/powers to tools and/or to other people. + +## Making Decisions + +### Informally + +For decisions that are unlikely to be controversial—especially for decisions affecting a single proposal—an EIP Editor may choose whatever option they deem appropriate in accordance with our mission. + +### Formally + +Electing a Keeper, adding/removing EIP Editors, and any possibly-controversial decisions must all be made using variations of this formal process. + +#### Preparation + +##### Call for Input + +For any matter requiring a decision, a call for input must be published in writing to the usual channels frequented by EIP Editors. + +##### Quorum + +Within thirty days of the call for input, to establish a valid quorum, all EIP Editors must express their opinion, vote (where appropriate), or lack thereof on the matter under consideration. + +After thirty days from the call for input, if not all EIP Editors have responded, the quorum is reduced to the Editors that have responded. This deadline may be extended in exceptional situations. + +#### Deciding + +##### Electing a Keeper of Consensus + +Any EIP Editor can call for an election for Keeper. Business continues as usual while the election is running. The EIP Editor with the most votes once quorum is met is named Keeper until the next election completes. If there is a tie, we'll randomly choose between the EIP Editors with the most votes, using a fair and agreed upon method (for example, a coin toss over a video call or a commit/reveal game of rock paper scissors.) + +##### Adding an EIP Editor + +An EIP Editor is added once quorum is met, provided the candidate consents and no current EIP Editor objects. + +##### Removing an EIP Editor + +An EIP Editor is forcibly removed once quorum is met, provided no current EIP Editor (aside from the one being removed) objects. An EIP Editor may voluntarily leave their position at any time. + +If the departing Editor was also the Keeper, an election for a new Keeper begins immediately. + +##### Other Decisions + +All other decisions are made through a "rough consensus" process. This does not require all EIP Editors to agree, although this is preferred. In general, the dominant view of the Editors shall prevail. Dominance, in this process, is not determined by persistence or volume but rather a more general sense of agreement. Note that 51% does not mean "rough consensus" has been reached, and 99% is better than rough. It is up to the Keeper to determine if rough consensus has been reached. Every EIP Editor is entitled to have their opinion heard and understood before the Keeper makes that determination. + +No one, not the EIP Editors and certainly not the Keeper, holds veto powers (except when adding/removing an Editor as defined above.) It is imperative that the EIP process evolve, albeit cautiously. + +_This section has been adapted from [RFC 2418]._ + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +[RFC 2418]: https://www.rfc-editor.org/rfc/rfc2418 diff --git a/EIPS/eip-5081.md b/EIPS/eip-5081.md new file mode 100644 index 00000000000000..8907fba05d5039 --- /dev/null +++ b/EIPS/eip-5081.md @@ -0,0 +1,91 @@ +--- +eip: 5081 +title: Expirable Trainsaction +description: This EIP adds a new transaction type of that includes expiration with a blocknum +author: Zainan Victor Zhou (@xinbenlv), Nick Johnson (@Arachnid), Konrad Feldmeier +discussions-to: https://ethereum-magicians.org/t/eip-5081-expirable-transaction/9208 +status: Draft +type: Standards Track +category: Core +created: 2022-05-06 +requires: 155, 1559, 2718, 2929, 2930 +--- + +## Abstract +This EIP adds a new transaction type of that includes expiration with a blocknum. + +## Motivation + +When a user sends a transaction `tx0` with a low gas price, sometimes it might not be high enough to be executed. +A common resolution is for the user to submit the transaction again with the same nonce and higher gas price. + +That previous `tx0` can theoretically be included in any time in the future unless a `tx` with the exact same nonce is already executed. + +When network is congested, gas price are high, for critical transactions user might try gas price that is much higher than an average day. +This cause the `tx0` choose might be very easy to executed in the average day. + +If user already uses a `tx1` with different nonce or from another account to execute the intended transaction, +there is currently no clean way to cancel it, +except for signing a new `tx0'` that shares the same nonce but with higher gas fee hoping that it will execute to *preempt*- than `tx0`. + +Given `tx0` was already high gas price, the current way of *preempting* `tx0` could be both unreliable and very costly. + +TODO(@xinbenlv): to include in the motivation: + +- Expiring transactions are transactions that have low time preference, but can easily become invalid in the future. For example, you may want to do a swap on an AMM but you don't want to pay a very high fee for it so you set the max fee to a low number. However, your transaction will almost certainly fail if it takes longer than a couple minutes to be mined. In this scenario, you would rather fail cheaply if your transaction doesn't get included quickly. + +- Similarly, there are situations where there is a limited window of availability of some asset and if your transaction doesn't mine within that period you know with certainty that it will fail. In these cases, it would be nice to be able to express that to the system and not waste unnecessary resources just to have the transaction fail. + +## 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. + +### Parameters +- `FORK_BLKNUM` = `TBD` +- `CHAIN_ID` = `TBD` +- `TX_TYPE` = TBD, > 0x02 ([EIP-1559](./eip-1559.md)) + + +As of `FORK_BLOCK_NUMBER`, a new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` = `TX_TYPE(TBD)`. + +The intrinsic cost of the new transaction is inherited from [EIP-2930](./eip-2930.md), specifically `21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count`. + +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is + +``` +rlp([chain_id, expire_by, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s]) +``` + +The definition of `expire_by` is a block number the latest possible block to +execute this transaction. Any block with a block number `block_num > expire_by` MUST NOT execute this transaction. + +The definitions of all other fields share the same meaning with [EIP-1559](./eip-1559.md) + +The `signature_y_parity, signature_r, signature_s` elements of this transaction represent a secp256k1 signature over `keccak256(0x02 || rlp([chain_id, expire_by, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))`. + +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`. + + +## Rationale +TODO + +## Backwards Compatibility +TODO + +## Security Considerations + +1. If `current_block_num` is available, client MUST drop and stop propagating/broadcasting any transactions that has a +`transacton_type == TX_TYPE` AND `current_block_num > expire_by` + +2. It is suggested but not required that a `currentBlockNum` SHOULD be made available to client. Any client doing PoW calculation on blocks expire tx or propagating such are essentially penalized for wasting of work, mitigating possible denial of service attack. + +3. It is suggested but not required that client SHOULD introduce a +`gossip_ttl` in unit of block_num as a safe net so that it only propagate +a tx if `current_block_num + gossip_ttl <= expire_by`. Backward compatibility: +for nodes that doesn't have `current_block_num` or `gossip_ttl` available, +they should be presume to be `0`. + +4. It is suggested by not required that any propagating client SHOULD properly deduct the `gossip_ttl` +based on the network environment it sees fit. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5094.md b/EIPS/eip-5094.md new file mode 100644 index 00000000000000..ed4fa85d83ad45 --- /dev/null +++ b/EIPS/eip-5094.md @@ -0,0 +1,95 @@ +--- +eip: 5094 +title: URL Format for Ethereum Network Switching +description: A way of representing various network configurations as URLs. +author: Luc van Kampen (@lucemans), Jakob Helgesson (@svemat01), Joshua Hendrix (@thejoshuahendrix) +discussions-to: https://ethereum-magicians.org/t/5094-uri-format-for-ethereum-network-switching/9277 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-05-13 +requires: 681, 831 +--- + +## Abstract + +This standard includes all needed information for adding a network to a wallet via URL, by including parameters such as `chainId`, `rpc_url`, `chain_name` and others, such that the network configuration is provided through the URL itself. + +## Motivation + +As observed with the use of [EIP-681](./eip-681.md) and its implementation in current mobile wallets, transactions can be made, approved, viewed, and used. However, if the wallet is instructed to perform a transaction on a chain they have not yet been configured before, the operation tends to fail. + +This is understandable, as the `chain_id` provided makes up only one part of what is required to connect to a network. This EIP aims to introduce a new type of URL for usage with deep-linking, QR, and more, to allow users to seamlessly add new networks to their (for ex. mobile) wallet to then be able to more easily partake in `pay-`, `tx-`, or other Ethereum URL interactions. + +As an extension to [EIP-831](./eip-831.md) and neighboring [EIP-681](./eip-681.md) and [EIP-2400](./eip-2400.md), this document aims to standardize the addition of new networks and switching thereof through the means of URLs. User convenience in this case is primary. + +Introduction of this EIP is meant to bridge to a safer RPC listing system to be introduced in the near future. + +## Specification + +### Syntax + +Network Switching URLs contain "ethereum" in their schema (protocol) part and are constructed as follows: + + network_add = erc831_part "add" "@" chain_id [ "/" ] "?" parameters + erc831_part = "ethereum:network-" + chain_id = 1*DIGIT + parameters = parameter *( "&" parameter ) + parameter = key "=" value + key = required_keys / optional_keys + required_keys = "rpc_url" / "chain_name" + optional_keys = "name" / "symbol" / "decimals" / "explorer_url" / "icon_url" + value = STRING / number + number = 1*DIGIT + +`STRING` is a URL-encoded Unicode string of arbitrary length, where delimiters and the +percentage symbol (`%`) are mandatorily hex-encoded with a `%` prefix. + +If the *key* in the parameter is `decimals` the *value* MUST be a `number`. + +### Semantics + +`chain_id` is mandatory and denotes the decimal chain ID, such that we have the identifier of the network we would like to add. + +`rpc_url` is represented as an array of RPC URLs. A minimum of 1 `rpc_url` MUST be present, in the format of `rpc_url=https%3A%2F%2Fpolygon-rpc.com`, or when multiple present `rpc_url=https%3A%2F%2Fpolygon-rpc.com&rpc_url=https%3A%2F%2Frpc-mainnet.matic.network`. + +`chain_name` is required to specify the name of the network to be added. + +`name` and `symbol` if provided, SHOULD be a human-readable string representing the native token. + +`decimals` if provided, MUST be a non-negative integer representing the decimal precision of the native token. + +`explorer_url` if provided, MUST specify one or more URLs pointing to block explorer web sites for the chain. + +`icon_url` if provided, MUST specify one or more URLs pointing to reasonably sized images that can be used to visually identify the chain. + +An example of adding a network with RPC endpoints `https://rpc-polygon.com` and `https://rpc-mainnet.matic.network`, the name `Polygon Mainnet`, token `Matic`, symbol `MATIC`, decimals `18`, explorer at `https://polygonscan.com/`, and Chain ID `137` would look as follows: + +```URL +ethereum:network-add@137/?chain_name=Polygon%20Mainnet&rpc_url=https%3A%2F%2Frpc-polygon.com&rpc_url=https%3A%2F%2Frpc-mainnet.matic.network&name=Matic&symbol=MATIC&decimals=18&explorer_url=https%3A%2F%2Fpolygonscan.com +``` + +## Rationale + +In furtherance of the Ethereum URL saga, network configuration is a needed addition to the possibility of Ethereum URLs. This would improve functionality for URLs, and offer non-mainnet users a way to connect without needing to configure their wallet by hand. + +The URL follows [EIP-831](./eip-831.md) with the `PREFIX` being `network` and the `PAYLOAD` being a composite of `add` and [EIP-681](./eip-681.md)-like `chain_id` and parameters. + +The choice for `PREFIX` being `network` is to allow further expansion and allow variants following the pattern `network-x`. + +An example URL for adding the Optimism Network + +```URL +ethereum:network-add@10/?chain_name=Optimistic%20Ethereum +&rpc_url=https%3A%2F%2Fmainnet.optimism.io&name=Ethereum&symbol=ETH&decimals=18&explorer_url=https%3A%2F%2Foptimistic.etherscan.io +``` + +The specification allows for a multitude of `rpc_url` and `explorer_url` to be specified. This is done such to overlap with parsing of the `TYPE` mentioned in [EIP-681](./eip-681.md). + +## Security Considerations + +URLs can be malformed to deceive users. Users SHOULD confirm source of URL before using any links. As well as checking source and transaction details before confirming any transactions. Applications SHOULD display network config, prior to network addition, such that users can confirm the validity of the network configuration being added. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5095.md b/EIPS/eip-5095.md new file mode 100644 index 00000000000000..a2f9899399d970 --- /dev/null +++ b/EIPS/eip-5095.md @@ -0,0 +1,551 @@ +--- +eip: 5095 +title: Principal Token +description: Principal tokens (zero-coupon tokens) are redeemable for a single underlying EIP-20 token at a future timestamp. +author: Julian Traversa (@JTraversa), Robert Robbins (@robrobbins), Alberto Cuesta Cañada (@alcueca) +discussions-to: https://ethereum-magicians.org/t/eip-5095-principal-token-standard/9259 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-05-01 +requires: 20, 2612 +--- + +## Abstract + +Principal tokens represent ownership of an underlying [EIP-20](./eip-20.md) token at a future timestamp. + +This specification is an extension on the [EIP-20](./eip-20.md) token that provides basic functionality for depositing +and withdrawing tokens and reading balances and the [EIP-2612](./eip-2612.md) specification that provides +[EIP-712](./eip-712.md) signature based approvals. + +## Motivation + +Principal tokens lack standardization which has led to a difficult to navigate development space and diverse implementation +schemes. + +The primary examples include yield tokenization platforms which strip future yield leaving a principal +token behind, as well as fixed-rate money-markets which utilize principal tokens as a medium +to lend/borrow. + +This inconsistency in implementation makes integration difficult at the application layer as well as +wallet layer which are key catalysts for the space's growth. +Developers are currently expected to implement individual adapters for each principal token, as well as adapters for +their pool contracts, and many times adapters for their custodial contracts as well, wasting significant developer resources. + +## Specification + +All Principal Tokens (PTs) MUST implement [EIP-20](./eip-20.md) to represent ownership of future underlying redemption. +If a PT is to be non-transferrable, it MAY revert on calls to `transfer` or `transferFrom`. +The [EIP-20](./eip-20.md) operations `balanceOf`, `transfer`, `totalSupply`, etc. operate on the Principal Token balance. + +All Principal Tokens MUST implement [EIP-20](./eip-20.md)'s optional metadata extensions. +The `name` and `symbol` functions SHOULD reflect the underlying token's `name` and `symbol` in some way, as well as the origination protocol, and in the case of yield tokenization protocols, the origination money-market. + +All Principal Tokens MAY implement [EIP-2612](./eip-2612.md) to improve the UX of approving PTs on various integrations. + +### Definitions: + +- underlying: The token that Principal Tokens are redeemable for at maturity. + Has units defined by the corresponding [EIP-20](./eip-20.md) contract. +- maturity: The timestamp (unix) at which a Principal Token matures. Principal Tokens become redeemable for underlying at or after this timestamp. +- fee: An amount of underlying or Principal Token charged to the user by the Principal Token. Fees can exist on redemption or post-maturity yield. +- slippage: Any difference between advertised redemption value and economic realities of PT redemption, which is not accounted by fees. + +### Methods + +#### `underlying` + +The address of the underlying token used by the Principal Token for accounting, and redeeming. + +MUST be an EIP-20 token contract. + +MUST _NOT_ revert. + +```yaml +- name: underlying + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: underlyingAddress + type: address +``` + +#### `maturity` + +The unix timestamp (uint256) at or after which Principal Tokens can be redeemed for their underlying deposit. + +MUST _NOT_ revert. + +```yaml +- name: maturity + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: timestamp + type: uint256 +``` + +#### `convertToUnderlying` + +The amount of underlying that would be exchanged for the amount of PTs provided, in an ideal scenario where all the conditions are met. + +Before maturity, the amount of underlying returned is as if the PTs would be at maturity. + +MUST NOT be inclusive of any fees that are charged against redemptions. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual redemption. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-principal-token, and instead should reflect the "average-user's" price-per-principal-token, meaning what the average user should expect to see when exchanging to and from. + +```yaml +- name: convertToUnderlying + type: function + stateMutability: view + + inputs: + - name: principalAmount + type: uint256 + + outputs: + - name: underlyingAmount + type: uint256 +``` + +#### `convertToPrincipal` + +The amount of principal tokens that the principal token contract would request for redemption in order to provide the amount of underlying specified, in an ideal scenario where all the conditions are met. + +MUST NOT be inclusive of any fees. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-principal-token, and instead should reflect the "average-user's" price-per-principal-token, meaning what the average user should expect to see when redeeming. + +```yaml +- name: convertToPrincipal + type: function + stateMutability: view + + inputs: + - name: underlyingAmount + type: uint256 + + outputs: + - name: principalAmount + type: uint256 +``` + +#### `maxRedeem` + +Maximum amount of principal tokens that can be redeemed from the `holder` balance, through a `redeem` call. + +MUST return the maximum amount of principal tokens that could be transferred from `holder` through `redeem` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxRedeem + type: function + stateMutability: view + + inputs: + - name: holder + type: address + + outputs: + - name: maxPrincipalAmount + type: uint256 +``` + +#### `previewRedeem` + +Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. + +MUST return as close to and no more than the exact amount of underliyng that would be obtained in a `redeem` call in the same transaction. I.e. `redeem` should return the same or more `underlyingAmount` as `previewRedeem` if called in the same transaction. + +MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the redemption would be accepted, regardless if the user has enough principal tokens, etc. + +MUST be inclusive of redemption fees. Integrators should be aware of the existence of redemption fees. + +MUST NOT revert due to principal token contract specific user/global limits. MAY revert due to other conditions that would also cause `redeem` to revert. + +Note that any unfavorable discrepancy between `convertToUnderlying` and `previewRedeem` SHOULD be considered slippage in price-per-principal-token or some other type of condition. + +```yaml +- name: previewRedeem + type: function + stateMutability: view + + inputs: + - name: principalAmount + type: uint256 + + outputs: + - name: underlyingAmount + type: uint256 +``` + +#### `redeem` + +At or after maturity, burns exactly `principalAmount` of Principal Tokens from `from` and sends `underlyingAmount` of underlying tokens to `to`. + +Interfaces and other contracts MUST NOT expect fund custody to be present. While custodial redemption of Principal Tokens through the Principal Token contract is extremely useful for integrators, some protocols may find giving the Principal Token itself custody breaks their backwards compatibility. + +MUST emit the `Redeem` event. + +MUST support a redeem flow where the Principal Tokens are burned from `holder` directly where `holder` is `msg.sender` or `msg.sender` has EIP-20 approval over the principal tokens of `holder`. +MAY support an additional flow in which the principal tokens are transferred to the Principal Token contract before the `redeem` execution, and are accounted for during `redeem`. + +MUST revert if all of `principalAmount` cannot be redeemed (due to withdrawal limit being reached, slippage, the holder not having enough Principal Tokens, etc). + +Note that some implementations will require pre-requesting to the Principal Token before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: redeem + type: function + stateMutability: nonpayable + + inputs: + - name: principalAmount + type: uint256 + - name: to + type: address + - name: from + type: address + + outputs: + - name: underlyingAmount + type: uint256 +``` + +#### `maxWithdraw` + +Maximum amount of the underlying asset that can be redeemed from the `holder` principal token balance, through a `withdraw` call. + +MUST return the maximum amount of underlying tokens that could be redeemed from `holder` through `withdraw` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxWithdraw + type: function + stateMutability: view + + inputs: + - name: holder + type: address + + outputs: + - name: maxUnderlyingAmount + type: uint256 +``` + +#### `previewWithdraw` + +Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. + +MUST return as close to and no fewer than the exact amount of principal tokens that would be burned in a `withdraw` call in the same transaction. I.e. `withdraw` should return the same or fewer `principalAmount` as `previewWithdraw` if called in the same transaction. + +MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though the withdrawal would be accepted, regardless if the user has enough principal tokens, etc. + +MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + +MUST NOT revert due to principal token contract specific user/global limits. MAY revert due to other conditions that would also cause `withdraw` to revert. + +Note that any unfavorable discrepancy between `convertToPrincipal` and `previewWithdraw` SHOULD be considered slippage in price-per-principal-token or some other type of condition. + +```yaml +- name: previewWithdraw + type: function + stateMutability: view + + inputs: + - name: underlyingAmount + type: uint256 + + outputs: + - name: principalAmount + type: uint256 +``` + +#### `withdraw` + +Burns `principalAmount` from `holder` and sends exactly `underlyingAmount` of underlying tokens to `receiver`. + +MUST emit the `Redeem` event. + +MUST support a withdraw flow where the principal tokens are burned from `holder` directly where `holder` is `msg.sender` or `msg.sender` has [EIP-20](./eip-20.md) approval over the principal tokens of `holder`. + MAY support an additional flow in which the principal tokens are transferred to the principal token contract before the `withdraw` execution, and are accounted for during `withdraw`. + +MUST revert if all of `underlyingAmount` cannot be withdrawn (due to withdrawal limit being reached, slippage, the holder not having enough principal tokens, etc). + +Note that some implementations will require pre-requesting to the principal token contract before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: withdraw + type: function + stateMutability: nonpayable + + inputs: + - name: underlyingAmount + type: uint256 + - name: receiver + type: address + - name: holder + type: address + + outputs: + - name: principalAmount + type: uint256 +``` + +### Events + +#### Redeem + +`from` has exchanged `principalAmount` of Principal Tokens for `underlyingAmount` of underlying, and transferred that underlying to `to`. + +MUST be emitted when Principal Tokens are burnt and underlying is withdrawn from the contract in the `EIP5095.redeem` method. + +```yaml +- name: Redeem + type: event + + inputs: + - name: from + indexed: true + type: address + - name: to + indexed: true + type: address + - name: amount + indexed: false + type: uint256 +``` + +## Rationale + +The Principal Token interface is designed to be optimized for integrators with a core minimal interface alongside optional interfaces to enable backwards compatibility. Details such as accounting and management of underlying are intentionally not specified, as Principal Tokens are expected to be treated as black boxes on-chain and inspected off-chain before use. + +[EIP-20](./eip-20.md) is enforced as implementation details such as token approval and balance calculation directly carry over. This standardization makes Principal Tokens immediately compatible with all [EIP-20](./eip-20.md) use cases in addition to EIP-5095. + +All principal tokens are redeemable upon maturity, with the only variance being whether further yield is generated post-maturity. Given the ubiquity of redemption, the presence of `redeem` allows integrators to purchase Principal Tokens on an open market, and them later redeem them for a fixed-yield solely knowing the address of the Principal Token itself. + +This EIP draws heavily on the design of [EIP-4626](./eip-4626.md) because technically Principal Tokens could be described as a subset of Yield Bearing Vaults, extended with a `maturity` variable and restrictions on the implementation. However, extending [EIP-4626](./eip-4626.md) would force PT implementations to include methods (namely, `mint` and `deposit`) that are not necessary to the business case that PTs solve. It can also be argued that partial redemptions (implemented via `withdraw`) are rare for PTs. + +PTs mature at a precise second, but given the reactive nature of smart contracts, there can't be an event marking maturity, because there is no guarantee of any activity at or after maturity. Emitting an event to notify of maturity in the first transaction after maturity would be imprecise and expensive. Instead, integrators are recommended to either use the first `Redeem` event, or to track themselves when each PT is expected to have matured. + +## Backwards Compatibility + +This EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification and has no known compatibility issues with other standards. +For production implementations of Principal Tokens which do not use EIP-5095, wrapper adapters can be developed and used, or wrapped tokens can be implemented. + +## Reference Implementation + +``` +// SPDX-License-Identifier: MIT +pragma solidity 0.8.14; + +import {ERC20} from "yield-utils-v2/contracts/token/ERC20.sol"; +import {MinimalTransferHelper} from "yield-utils-v2/contracts/token/MinimalTransferHelper.sol"; + +contract ERC5095 is ERC20 { + using MinimalTransferHelper for ERC20; + + /* EVENTS + *****************************************************************************************************************/ + + event Redeem(address indexed from, address indexed to, uint256 underlyingAmount); + + /* MODIFIERS + *****************************************************************************************************************/ + + /// @notice A modifier that ensures the current block timestamp is at or after maturity. + modifier afterMaturity() virtual { + require(block.timestamp >= maturity, "BEFORE_MATURITY"); + _; + } + + /* IMMUTABLES + *****************************************************************************************************************/ + + ERC20 public immutable underlying; + uint256 public immutable maturity; + + /* CONSTRUCTOR + *****************************************************************************************************************/ + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_, + ERC20 underlying_, + uint256 maturity_ + ) ERC20(name_, symbol_, decimals_) { + underlying = underlying_; + maturity = maturity_; + } + + /* CORE FUNCTIONS + *****************************************************************************************************************/ + + /// @notice Burns an exact amount of principal tokens in exchange for an amount of underlying. + /// @dev This reverts if before maturity. + /// @param principalAmount The exact amount of principal tokens to be burned. + /// @param from The owner of the principal tokens to be redeemed. If not msg.sender then must have prior approval. + /// @param to The address to send the underlying tokens. + /// @return underlyingAmount The total amount of underlying tokens sent. + function redeem( + uint256 principalAmount, + address from, + address to + ) public virtual afterMaturity returns (uint256 underlyingAmount) { + _decreaseAllowance(from, principalAmount); + + // Check for rounding error since we round down in previewRedeem. + require((underlyingAmount = _previewRedeem(principalAmount)) != 0, "ZERO_ASSETS"); + + _burn(from, principalAmount); + + emit Redeem(from, to, principalAmount); + + _transferOut(to, underlyingAmount); + } + + /// @notice Burns a calculated amount of principal tokens in exchange for an exact amount of underlying. + /// @dev This reverts if before maturity. + /// @param underlyingAmount The exact amount of underlying tokens to be received. + /// @param from The owner of the principal tokens to be redeemed. If not msg.sender then must have prior approval. + /// @param to The address to send the underlying tokens. + /// @return principalAmount The total amount of underlying tokens redeemed. + function withdraw( + uint256 underlyingAmount, + address from, + address to + ) public virtual afterMaturity returns (uint256 principalAmount) { + principalAmount = _previewWithdraw(underlyingAmount); // No need to check for rounding error, previewWithdraw rounds up. + + _decreaseAllowance(from, principalAmount); + + _burn(from, principalAmount); + + emit Redeem(from, to, principalAmount); + + _transferOut(to, underlyingAmount); + } + + /// @notice An internal, overridable transfer function. + /// @dev Reverts on failed transfer. + /// @param to The recipient of the transfer. + /// @param amount The amount of the transfer. + function _transferOut(address to, uint256 amount) internal virtual { + underlying.safeTransfer(to, amount); + } + + /* ACCOUNTING FUNCTIONS + *****************************************************************************************************************/ + + /// @notice Calculates the amount of underlying tokens that would be exchanged for a given amount of principal tokens. + /// @dev Before maturity, it converts to underlying as if at maturity. + /// @param principalAmount The amount principal on which to calculate conversion. + /// @return underlyingAmount The total amount of underlying that would be received for the given principal amount.. + function convertToUnderlying(uint256 principalAmount) external view returns (uint256 underlyingAmount) { + return _convertToUnderlying(principalAmount); + } + + function _convertToUnderlying(uint256 principalAmount) internal view virtual returns (uint256 underlyingAmount) { + return principalAmount; + } + + /// @notice Converts a given amount of underlying tokens to principal exclusive of fees. + /// @dev Before maturity, it converts to principal as if at maturity. + /// @param underlyingAmount The total amount of underlying on which to calculate the conversion. + /// @return principalAmount The amount principal tokens required to provide the given amount of underlying. + function convertToPrincipal(uint256 underlyingAmount) external view returns (uint256 principalAmount) { + return _convertToPrincipal(underlyingAmount); + } + + function _convertToPrincipal(uint256 underlyingAmount) internal view virtual returns (uint256 principalAmount) { + return underlyingAmount; + } + + /// @notice Allows user to simulate redemption of a given amount of principal tokens, inclusive of fees and other + /// current block conditions. + /// @dev This reverts if before maturity. + /// @param principalAmount The amount of principal that would be redeemed. + /// @return underlyingAmount The amount of underlying that would be received. + function previewRedeem(uint256 principalAmount) external view afterMaturity returns (uint256 underlyingAmount) { + return _previewRedeem(principalAmount); + } + + function _previewRedeem(uint256 principalAmount) internal view virtual returns (uint256 underlyingAmount) { + return _convertToUnderlying(principalAmount); // should include fees/slippage + } + + /// @notice Calculates the maximum amount of principal tokens that an owner could redeem. + /// @dev This returns 0 if before maturity. + /// @param owner The address for which the redemption is being calculated. + /// @return maxPrincipalAmount The maximum amount of principal tokens that can be redeemed by the given owner. + function maxRedeem(address owner) public view returns (uint256 maxPrincipalAmount) { + return block.timestamp >= maturity ? _balanceOf[owner] : 0; + } + + /// @notice Allows user to simulate withdraw of a given amount of underlying tokens. + /// @dev This reverts if before maturity. + /// @param underlyingAmount The amount of underlying tokens that would be withdrawn. + /// @return principalAmount The amount of principal tokens that would be redeemed. + function previewWithdraw(uint256 underlyingAmount) external view afterMaturity returns (uint256 principalAmount) { + return _previewWithdraw(underlyingAmount); + } + + function _previewWithdraw(uint256 underlyingAmount) internal view virtual returns (uint256 principalAmount) { + return _convertToPrincipal(underlyingAmount); // should include fees/slippage + } + + /// @notice Calculates the maximum amount of underlying tokens that can be withdrawn by a given owner. + /// @dev This returns 0 if before maturity. + /// @param owner The address for which the withdraw is being calculated. + /// @return maxUnderlyingAmount The maximum amount of underlying tokens that can be withdrawn by a given owner. + function maxWithdraw(address owner) public view returns (uint256 maxUnderlyingAmount) { + return _previewWithdraw(maxRedeem(owner)); + } +} + +``` + +## Security Considerations + +Fully permissionless use cases could fall prey to malicious implementations which only conform to the interface in this EIP but not the specification, failing to implement proper custodial functionality but offering the ability to purchase Principal Tokens through secondary markets. + +It is recommended that all integrators review each implementation for potential ways of losing user deposits before integrating. + +The `convertToUnderlying` method is an estimate useful for display purposes, +and do _not_ have to confer the _exact_ amount of underlying assets their context suggests. + +As is common across many standards, it is strongly recommended to mirror the underlying token's `decimals` if at all possible, to eliminate possible sources of confusion and simplify integration across front-ends and for other off-chain users. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5114.md b/EIPS/eip-5114.md new file mode 100644 index 00000000000000..35cbbd5e41520c --- /dev/null +++ b/EIPS/eip-5114.md @@ -0,0 +1,98 @@ +--- +eip: 5114 +title: Soulbound Badge +description: A token that is attached to a "soul" at mint time and cannot be transferred after that. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/eip-5114-soulbound-token/9417 +status: Last Call +last-call-deadline: 2023-09-19 +type: Standards Track +category: ERC +created: 2022-05-30 +--- + + +## Abstract + +A soulbound badge is a token that, when minted, is bound to another Non-Fungible Token (NFT), and cannot be transferred/moved after that. + + +## Specification + +```solidity +interface IERC5114 { + // fired anytime a new instance of this badge is minted + // this event **MUST NOT** be fired twice for the same `badgeId` + event Mint(uint256 indexed badgeId, address indexed nftAddress, uint256 indexed nftTokenId); + + // returns the NFT that this badge is bound to. + // this function **MUST** throw if the badge hasn't been minted yet + // this function **MUST** always return the same result every time it is called after it has been minted + // this function **MUST** return the same value as found in the original `Mint` event for the badge + function ownerOf(uint256 badgeId) external view returns (address nftAddress, uint256 nftTokenId); + + // returns a URI with details about this badge collection + // the metadata returned by this is merged with the metadata return by `badgeUri(uint256)` + // the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://) + // the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://) + // data from `badgeUri` takes precedence over data returned by this method + // any external links referenced by the content at `collectionUri` also **MUST** follow all of the above rules + function collectionUri() external pure returns (string collectionUri); + + // returns a censorship resistant URI with details about this badge instance + // the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://) + // the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://) + // data from this takes precedence over data returned by `collectionUri` + // any external links referenced by the content at `badgeUri` also **MUST** follow all of the above rules + function badgeUri(uint256 badgeId) external view returns (string badgeUri); + + // returns a string that indicates the format of the `badgeUri` and `collectionUri` results (e.g., 'EIP-ABCD' or 'soulbound-schema-version-4') + function metadataFormat() external pure returns (string format); +} +``` + +Implementers of this standard **SHOULD** also depend on a standard for interface detection so callers can easily find out if a given contract implements this interface. + + +## Rationale + +### Immutability + +By requiring that badges can never move, we both guarantee non-separability and non-mergeability among collections of soulbound badges that are bound to a single NFT while simultaneously allowing users to aggressively cache results. + +### Content Addressable URIs Required + +Soulbound badges are meant to be permanent badges/indicators attached to a persona. +This means that not only can the user not transfer ownership, but the minter also cannot withdraw/transfer/change ownership as well. +This includes mutating or removing any remote content as a means of censoring or manipulating specific users. + +### No Specification for `badgeUri` Data Format + +The format of the data pointed to by `collectionUri()` and `badgeUri(uint256)`, and how to merge them, is intentionally left out of this standard in favor of separate standards that can be iterated on in the future. +The immutability constraints are the only thing defined by this to ensure that the spirit of this badge is maintained, regardless of the specifics of the data format. +The `metadataFormat` function can be used to inform a caller what type/format/version of data they should expect at the URIs, so the caller can parse the data directly without first having to deduce its format via inspection. + + +## Backwards Compatibility + +This is a new token type and is not meant to be backward compatible with any existing tokens other than existing viable souls (any asset that can be identified by `[address,id]`). + + +## Security Considerations + +Users of badges that claim to implement this EIP must be diligent in verifying they actually do. +A badge author can create a badge that, upon initial probing of the API surface, may appear to follow the rules when in reality it doesn't. +For example, the contract could allow transfers via some mechanism and simply not utilize them initially. + +It should also be made clear that soulbound badges are not bound to a human, they are bound to a persona. +A persona is any actor (which could be a group of humans) that collects multiple soulbound badges over time to build up a collection of badges. +This persona may transfer to another human, or to another group of humans, and anyone interacting with a persona should not assume that there is a single permanent human behind that persona. + +It is possible for a soulbound badge to be bound to another soulbound badge. +In theory, if all badges in the chain are created at the same time they could form a loop. +Software that tries to walk such a chain should take care to have an exit strategy if a loop is detected. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5115.md b/EIPS/eip-5115.md new file mode 100644 index 00000000000000..1930983248f55e --- /dev/null +++ b/EIPS/eip-5115.md @@ -0,0 +1,349 @@ +--- +eip: 5115 +title: SY Token +description: Interface for wrapped yield-bearing tokens. +author: Vu Nguyen (@mrenoon), Long Vuong (@UncleGrandpa925), Anton Buenavista (@ayobuenavista) +discussions-to: https://ethereum-magicians.org/t/eip-5115-super-composable-yield-token-standard/9423 +status: Draft +type: Standards Track +category: ERC +created: 2022-05-30 +requires: 20 +--- + +## Abstract + +This standard proposes an API for wrapped yield-bearing tokens within smart contracts. It is an extension on the [ERC-20](./eip-20.md) token that provides basic functionality for transferring, depositing, withdrawing tokens, as well as reading balances. + +## Motivation + +Yield generating mechanisms are built in all shapes and sizes, necessitating a manual integration every time a protocol builds on top of another protocol’s yield generating mechanism. + +[ERC-4626](./eip-4626.md) tackled a significant part of this fragmentation by standardizing the interfaces for vaults, a major category among various yield generating mechanisms. + +In this ERC, we’re extending the coverage to include assets beyond ERC-4626’s reach, namely: + +- yield-bearing assets that have different input tokens used for minting vs accounting for the pool value. + - This category includes AMM liquidity tokens (which are yield-bearing assets that yield swap fees) since the value of the pool is measured in “liquidity units” (for example, $\sqrt k$ in UniswapV2, as defined in UniswapV2 whitepaper) which can’t be deposited in (as they are not tokens). + - This extends the flexibility in minting the yield-bearing assets. For example, there could be an ETH vault that wants to allow users to deposit cETH directly instead of ETH, for gas efficiency or UX reasons. +- Assets with reward tokens by default (e.g. COMP rewards for supplying in Compound). The reward tokens are expected to be sold to compound into the same asset. +- This ERC can be extended further to include the handling of rewards, such as the claiming of accrued multiple rewards tokens. + +While ERC-4626 is a well-designed and suitable standard for most vaults, there will inevitably be some yield generating mechanisms that do not fit into their category (LP tokens for instance). A more flexible standard is required to standardize the interaction with all types of yield generating mechanisms. + +Therefore, we are proposing Standardized Yield (SY), a flexible standard for wrapped yield-bearing tokens that could cover most mechanisms in DeFi. We foresee that: + +- ERC-4626 will still be a popular vault standard, that most vaults should adopt. +- SY tokens can wrap over most yield generating mechanisms in DeFi, including ERC-4626 vaults for projects built on top of yield-bearing tokens. +- Whoever needs the functionalities of SY could integrate with the existing SY tokens or write a new SY (to wrap over the target yield-bearing token). +- Reward handling can be extended from the SY token. + +### Use Cases + +This ERC is designed for flexibility, aiming to accommodate as many yield generating mechanisms as possible. Particularly, this standard aims to be generalized enough that it supports the following use cases and more: + +- Money market supply positions + - Lending DAI in Compound, getting DAI interests and COMP rewards + - Lending ETH in BenQi, getting ETH interests and QI + AVAX rewards + - Lending USDC in Aave, getting USDC interests and stkAAVE rewards +- AMM liquidity provision + - Provide ETH + USDC to ETHUSDC pool in SushiSwap, getting swap fees in more ETH+USDC + - Provide ETH + USDC to ETHUSDC pool in SushiSwap and stake it in Sushi Onsen, getting swap fees and SUSHI rewards + - Provide USDC+DAI+USDT to 3crv pool and stake it in Convex, getting 3crv swap fees and CRV + CVX rewards +- Vault positions + - Provide ETH into Yearn ERC-4626 vault, where the vault accrues yield from Yearn’s ETH strategy + - Provide DAI into Harvest and staking it, getting DAI interests and FARM rewards +- Liquid staking positions + - Holding stETH (in Lido), getting yields in more stETH +- Liquidity mining programs + - Provide USDC in Stargate, getting STG rewards + - Provide LOOKS in LooksRare, getting LOOKS yield and WETH rewards +- Rebasing tokens + - Stake OHM into sOHM/gOHM, getting OHM rebase yield + +The ERC hopes to minimize, if not possibly eliminate, the use of customized adapters in order to interact with many different forms of yield-bearing token mechanisms. + +## Specification + +### Generic Yield Generating Pool + +We will first introduce Generic Yield Generating Pool (GYGP), a model to describe most yield generating mechanisms in DeFi. In every yield generating mechanism, there is a pool of funds, whose value is measured in **assets**. There are a number of users who contribute liquidity to the pool, in exchange for **shares** of the pool, which represents units of ownership of the pool. Over time, the value (measured in **assets**) of the pool grows, such that each **share** is worth more **assets** over time. The pool could earn a number of **reward tokens** over time, which are distributed to the users according to some logic (for example, proportionally the number of **shares**). + +Here are the more concrete definitions of the terms: + +#### GYGP Definitions: + +- **asset**: Is a unit to measure the value of the pool. At time *t*, the pool has a total value of *TotalAsset(t)* **assets**. +- **shares**: Is a unit that represents ownership of the pool. At time *t*, there are *TotalShares(t)* **shares** in total. +- **reward tokens**: Over time, the pool earns $n_{rewards}$ types of reward tokens $(n_{rewards} \ge 0)$. At time *t*, $TotalRewards_i(t)$ is the amount of **reward token *i*** that has accumulated for the pool up until time *t*. +- **exchange rate**: At time *t*, the **exchange rate** *ExchangeRate(t)* is simply how many **assets** each **shares** is worth $ExchangeRate(t) = \frac{TotalAsset(t)}{TotalShares(t)}$ +- **users**: At time *t*, each user *u* has $shares_u(t)$ **shares** in the pool, which is worth $asset_u(t) = shares_u(t) \cdot ExchangeRate(t)$ **assets**. Until time *t*, user *u* is entitled to receive a total of $rewards_{u_i}(t)$ **reward token *i***. The sum of all users’ shares, assets and rewards should be the same as the total shares, assets and rewards of the whole pool. + +#### State changes: + +1. A user deposits $d_a$ **assets** into the pool at time $t$ ($d_a$ could be negative, which means a withdraw from the pool). $d_s = d_a / ExchangeRate(t)$ new **shares** will be created and given +to user (or removed and burned from the user when $d_a$ is negative). +2. The pool earns $d_a$ (or loses $−d_a$ if $d_a$ is negative) **assets** at time $t$. The **exchange rate** simply increases (or decreases if $d_a$ is negative) due to the additional assets. +3. The pool earns $d_r$ **reward token** $i$. Every user will receive a certain amount of **reward token** $i$. + +#### Examples of GYGPs in DeFi: + +| Yield generating mechanism | Asset | Shares | Reward tokens | Exchange rate | +| --- | --- | --- | --- | --- | +| Supply USDC in Compound | USDC | cUSDC | COMP | USDC value per cUSDC, increases with USDC supply interests | +| ETH liquid staking in Lido | stETH | wstETH | None | stETH value per wstETH, increases with ETH staking rewards | +| Stake LOOKS in LooksRare Compounder | LOOKS | shares (in contract) | WETH | LOOKS value per shares, increases with LOOKS rewards | +| Stake APE in $APE Compounder | sAPE | shares (in contract) | APE | sAPE value per shares, increases with APE rewards | +| Provide ETH+USDC liquidity on Sushiswap | ETHUSDC liquidity (a pool of x ETH + y USDC has sqrt(xy) ETHUSDC liquidity) | ETHUSDC Sushiswap LP (SLP) token | None | ETHUSDC liquidity value per ETHUSDC SLP, increases due to swap fees | +| Provide ETH+USDC liquidity on Sushiswap and stake into Onsen | ETHUSDC liquidity (a pool of x ETH + y USDC has sqrt(xy) ETHUSDC liquidity) | ETHUSDC Sushiswap LP (SLP) token | SUSHI | ETHUSDC liquidity value per ETHUSDC SLP, increases due to swap fees | +| Provide BAL+WETH liquidity in Balancer (80% BAL, 20% WETH) | BALWETH liquidity (a pool of x BAL + y WETH has x^0.8*y^0.2 BALWETH liquidity) | BALWETH Balancer LP token | None | BALWETH liquidity per BALWETH Balancer LP token, increases due to swap fees | +| Provide USDC+USDT+DAI liquidity in Curve | 3crv pool’s liquidity (amount of D per 3crv token) | 3crv token | CRV | 3crv pool’s liquidity per 3crv token, increases due to swap fees | +| Provide FRAX+USDC liquidity in Curve then stake LP in Convex | BALWETH liquidity (a pool of x BAL + y WETH has x^0.8*y^0.2 BALWETH liquidity) | BALWETH Balancer LP token | None | BALWETH liquidity per BALWETH Balancer LP token, increases due to swap fees | + + +### Standardized Yield Token Standard + +#### Overview: + +Standardized Yield (SY) is a token standard for any yield generating mechanism that conforms to the GYGP model. Each SY token represents **shares** in a GYGP and allows for interacting with the GYGP via a standard interface. + +All SY tokens: + +- **MUST** implement **`ERC-20`** to represent shares in the underlying GYGP. +- **MUST** implement ERC-20’s optional metadata extensions `name`, `symbol`, and `decimals`, which **SHOULD** reflect the underlying GYGP’s accounting asset’s `name`, `symbol`, and `decimals`. +- **MAY** implement [ERC-2612](./eip-2612.md) to improve the UX of approving SY tokens on various integrations. +- **MAY** revert on calls to `transfer` and `transferFrom` if a SY token is to be non-transferable. +- The ERC-20 operations `balanceOf`, `transfer`, `totalSupply`, etc. **SHOULD** operate on the GYGP “shares”, which represent a claim to ownership on a fraction of the GYGP’s underlying holdings. + +#### SY Definitions: + +On top of the definitions above for GYGPs, we need to define 2 more concepts: + +- **input tokens**: Are tokens that can be converted into assets to enter the pool. Each SY can accept several possible input tokens $tokens_{in_{i}}$ + +- **output tokens**: Are tokens that can be redeemed from assets when exiting the pool. Each SY can have several possible output tokens $tokens_{out_{i}}$ + +#### Interface + +```solidity +interface IStandardizedYield { + event Deposit( + address indexed caller, + address indexed receiver, + address indexed tokenIn, + uint256 amountDeposited, + uint256 amountSyOut + ); + + event Redeem( + address indexed caller, + address indexed receiver, + address indexed tokenOut, + uint256 amountSyToRedeem, + uint256 amountTokenOut + ); + + function deposit( + address receiver, + address tokenIn, + uint256 amountTokenToDeposit, + uint256 minSharesOut, + bool depositFromInternalBalance + ) external returns (uint256 amountSharesOut); + + function redeem( + address receiver, + uint256 amountSharesToRedeem, + address tokenOut, + uint256 minTokenOut, + bool burnFromInternalBalance + ) external returns (uint256 amountTokenOut); + + function exchangeRate() external view returns (uint256 res); + + function getTokensIn() external view returns (address[] memory res); + + function getTokensOut() external view returns (address[] memory res); + + function yieldToken() external view returns (address); + + function previewDeposit(address tokenIn, uint256 amountTokenToDeposit) + external + view + returns (uint256 amountSharesOut); + + function previewRedeem(address tokenOut, uint256 amountSharesToRedeem) + external + view + returns (uint256 amountTokenOut); + + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); +} +``` + +#### Methods + +```solidity +function deposit( + address receiver, + address tokenIn, + uint256 amountTokenToDeposit, + uint256 minSharesOut, + bool depositFromInternalBalance +) external returns (uint256 amountSharesOut); +``` + +This function will deposit *amountTokenToDeposit* of input token $i$ (*tokenIn*) to mint new SY shares. + +If *depositFromInternalBalance* is set to *false*, msg.sender will need to initially deposit *amountTokenToDeposit* of input token $i$ (*tokenIn*) into the SY contract, then this function will convert the *amountTokenToDeposit* of input token $i$ into $d_a$ worth of **asset** and deposit this amount into the pool for the *receiver*, who will receive *amountSharesOut* of SY tokens (**shares**). If *depositFromInternalBalance* is set to *true*, then *amountTokenToDeposit* of input token $i$ (*tokenIn*) will be taken from receiver directly (as msg.sender), and will be converted and shares returned to the receiver similarly to the first case. + +This function should revert if $amountSharesOut \lt minSharesOut$. + +- **MUST** emit the `Deposit` event. +- **MUST** support ERC-20’s `approve` / `transferFrom` flow where `tokenIn` are taken from receiver directly (as msg.sender) or if the msg.sender has ERC-20 approved allowance over the input token of the receiver. +- **MUST** revert if $amountSharesOut \lt minSharesOut$ (due to deposit limit being reached, slippage, or the user not approving enough `tokenIn` **to the SY contract, etc). +- **MAY** be payable if the `tokenIn` depositing asset is the chain's native currency (e.g. ETH). + +```solidity +function redeem( + address receiver, + uint256 amountSharesToRedeem, + address tokenOut, + uint256 minTokenOut, + bool burnFromInternalBalance +) external returns (uint256 amountTokenOut); +``` + +This function will redeem the $d_s$ shares, which is equivalent to $d_a = d_s \times ExchangeRate(t)$ assets, from the pool. The $d_a$ assets is converted into exactly *amountTokenOut* of output token $i$ (*tokenOut*). + +If *burnFromInternalBalance* is set to *false*, the user will need to initially deposit *amountSharesToRedeem* into the SY contract, then this function will burn the floating amount $d_s$ of SY tokens (**shares**) in the SY contract to redeem to output token $i$ (*tokenOut*). This pattern is similar to UniswapV2 which allows for more gas efficient ways to interact with the contract. If *burnFromInternalBalance* is set to *true*, then this function will burn *amountSharesToRedeem* $d_s$ of SY tokens directly from the user to redeem to output token $i$ (*tokenOut*). + +This function should revert if $amountTokenOut \lt minTokenOut$. + +- **MUST** emit the `Redeem` event. +- **MUST** support ERC-20’s `approve` / `transferFrom` flow where the shares are burned from receiver directly (as msg.sender) or if the msg.sender has ERC-20 approved allowance over the shares of the receiver. +- **MUST** revert if $amountTokenOut \lt minTokenOut$ (due to redeem limit being reached, slippage, or the user not approving enough `amountSharesToRedeem` to the SY contract, etc). + +```solidity +function exchangeRate() external view returns (uint256 res); +``` + +This method updates and returns the latest **exchange rate**, which is the **exchange rate** from SY token amount into asset amount, scaled by a fixed scaling factor of 1e18. + +- **MUST** return $ExchangeRate(t_{now})$ such that $ExchangeRate(t_{now}) \times syBalance / 1e18 = assetBalance$. +- **MUST NOT** include fees that are charged against the underlying yield token in the SY contract. + +```solidity +function getTokensIn() external view returns (address[] memory res); +``` + +This read-only method returns the list of all input tokens that can be used to deposit into the SY contract. + +- **MUST** return ERC-20 token addresses. +- **MUST** return at least one address. +- **MUST NOT** revert. + +```solidity +function getTokensOut() external view returns (address[] memory res); +``` + +This read-only method returns the list of all output tokens that can be converted into when exiting the SY contract. + +- **MUST** return ERC-20 token addresses. +- **MUST** return at least one address. +- **MUST NOT** revert. + +```solidity +function yieldToken() external view returns (address); +``` + +This read-only method returns the underlying yield-bearing token (representing a GYGP) address. + +- **MUST** return a token address that conforms to the ERC-20 interface, or zero address +- **MUST NOT** revert. +- **MUST** reflect the exact underlying yield-bearing token address if the SY token is a wrapped token. +- **MAY** return 0x or zero address if the SY token is natively implemented, and not from wrapping. + +```solidity +function previewDeposit(address tokenIn, uint256 amountTokenToDeposit) + external + view + returns (uint256 amountSharesOut); +``` + +This read-only method returns the amount of shares that a user would have received if they deposit *amountTokenToDeposit* of *tokenIn*. + +- **MUST** return less than or equal of *amountSharesOut* to the actual return value of the `deposit` method, and **SHOULD NOT** return greater than the actual return value of the `deposit` method. +- **MUST NOT** revert. + +```solidity +function previewRedeem(address tokenOut, uint256 amountSharesToRedeem) + external + view + returns (uint256 amountTokenOut); +``` + +This read-only method returns the amount of *tokenOut* that a user would have received if they redeem *amountSharesToRedeem* of *tokenOut*. + +- **MUST** return less than or equal of *amountTokenOut* to the actual return value of the `redeem` method, and **SHOULD NOT** return greater than the actual return value of the `redeem` method. +- **MUST NOT** revert. + +#### Events + +```solidity +event Deposit( + address indexed caller, + address indexed receiver, + address indexed tokenIn, + uint256 amountDeposited, + uint256 amountSyOut +); +``` + +`caller` has converted exact *tokenIn* tokens into SY (shares) and transferred those SY to `receiver`. + +- **MUST** be emitted when input tokens are deposited into the SY contract via `deposit` method. + +```solidity +event Redeem( + address indexed caller, + address indexed receiver, + address indexed tokenOut, + uint256 amountSyToRedeem, + uint256 amountTokenOut +); +``` + +`caller` has converted exact SY (shares) into input tokens and transferred those input tokens to `receiver`. + +- **MUST** be emitted when input tokens are redeemed from the SY contract via `redeem` method. + +**"SY" Word Choice:** + +"SY" (pronunciation: */sʌɪ/*), an abbreviation of Standardized Yield, was found to be appropriate to describe a broad universe of standardized composable yield-bearing digital assets. + +## Rationale + +[ERC-20](./eip-20.md) is enforced because implementation details such as transfer, token approvals, and balance calculation directly carry over to the SY tokens. This standardization makes the SY tokens immediately compatible with all ERC-20 use cases. + +[ERC-165](./eip-165.md) can optionally be implemented should you want integrations to detect the IStandardizedYield interface implementation. + +[ERC-2612](./eip-2612.md) can optionally be implemented in order to improve the UX of approving SY tokens on various integrations. + +## Backwards Compatibility + +This ERC is fully backwards compatible as its implementation extends the functionality of [ERC-20](./eip-20.md), however the optional metadata extensions, namely `name`, `decimals`, and `symbol` semantics MUST be implemented for all SY token implementations. + +## Security Considerations + +Malicious implementations which conform to the interface can put users at risk. It is recommended that all integrators (such as wallets, aggregators, or other smart contract protocols) review the implementation to avoid possible exploits and users losing funds. + +`yieldToken` must strongly reflect the address of the underlying wrapped yield-bearing token. For a native implementation wherein the SY token does not wrap a yield-bearing token, but natively represents a GYGP share, then the address returned MAY be a zero address. Otherwise, for wrapped tokens, you may introduce confusion on what the SY token represents, or may be deemed malicious. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5131.md b/EIPS/eip-5131.md new file mode 100644 index 00000000000000..bb2ea1ae235339 --- /dev/null +++ b/EIPS/eip-5131.md @@ -0,0 +1,343 @@ +--- +eip: 5131 +title: SAFE Authentication For ENS +description: Using ENS Text Records to facilitate safer and more convenient signing operations. +author: Wilkins Chung (@wwhchung) , Jalil Wahdatehagh (@jwahdatehagh), Cry (@crydoteth), Sillytuna (@sillytuna), Cyberpnk (@CyberpnkWin) +discussions-to: https://ethereum-magicians.org/t/eip-5131-ens-subdomain-authentication/9458 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-03 +requires: 137, 181, 634 +--- + +## Abstract +This EIP links one or more signing wallets via Ethereum Name Service Specification ([EIP-137](./eip-137.md)) to prove control and asset ownership of a main wallet. + +## Motivation +Proving ownership of an asset to a third party application in the Ethereum ecosystem is common. Users frequently sign payloads of data to authenticate themselves before gaining access to perform some operation. However, this method--akin to giving the third party root access to one's main wallet--is both insecure and inconvenient. + +***Examples:*** + 1. In order for you to edit your profile on OpenSea, you must sign a message with your wallet. + 2. In order to access NFT gated content, you must sign a message with the wallet containing the NFT in order to prove ownership. + 3. In order to gain access to an event, you must sign a message with the wallet containing a required NFT in order to prove ownership. + 4. In order to claim an airdrop, you must interact with the smart contract with the qualifying wallet. + 5. In order to prove ownership of an NFT, you must sign a payload with the wallet that owns that NFT. + +In all the above examples, one interacts with the dApp or smart contract using the wallet itself, which may be + - inconvenient (if it is controlled via a hardware wallet or a multi-sig) + - insecure (since the above operations are read-only, but you are signing/interacting via a wallet that has write access) + +Instead, one should be able to approve multiple wallets to authenticate on behalf of a given wallet. + +### Problems with existing methods and solutions +Unfortunately, we've seen many cases where users have accidentally signed a malicious payload. The result is almost always a significant loss of assets associated with the signing address. + +In addition to this, many users keep significant portions of their assets in 'cold storage'. With the increased security from 'cold storage' solutions, we usually see decreased accessibility because users naturally increase the barriers required to access these wallets. + +Some solutions propose dedicated registry smart contracts to create this link, or new protocols to be supported. This is problematic from an adoption standpoint, and there have not been any standards created for them. + +### Proposal: Use the Ethereum Name Service (EIP-137) +Rather than 're-invent the wheel', this proposal aims to use the widely adopted Ethereum Name Service in conjunction with the ENS Text Records feature ([EIP-634](./eip-634.md)) in order to achieve a safer and more convenient way to sign and authenticate, and provide 'read only' access to a main wallet via one or more secondary wallets. + +From there, the benefits are twofold. This EIP gives users increased security via outsourcing potentially malicious signing operations to wallets that are more accessible (hot wallets), while being able to maintain the intended security assumptions of wallets that are not frequently used for signing operations. + +#### Improving dApp Interaction Security +Many dApps requires one to prove control of a wallet to gain access. At the moment, this means that you must interact with the dApp using the wallet itself. This is a security issue, as malicious dApps or phishing sites can lead to the assets of the wallet being compromised by having them sign malicious payloads. + +However, this risk would be mitigated if one were to use a secondary wallet for these interactions. Malicious interactions would be isolated to the assets held in the secondary wallet, which can be set up to contain little to nothing of value. + +#### Improving Multiple Device Access Security +In order for a non-hardware wallet to be used on multiple devices, you must import the seed phrase to each device. Each time a seed phrase is entered on a new device, the risk of the wallet being compromised increases as you are increasing the surface area of devices that have knowledge of the seed phrase. + +Instead, each device can have its own unique wallet that is an authorized secondary wallet of the main wallet. If a device specific wallet was ever compromised or lost, you could simply remove the authorization to authenticate. + +Further, wallet authentication can be chained so that a secondary wallet could itself authorize one or many tertiary wallets, which then have signing rights for both the secondary address as well as the root main address. This, can allow teams to each have their own signer while the main wallet can easily invalidate an entire tree, just by revoking rights from the root stem. + +#### Improving Convenience +Many invididuals use hardware wallets for maximum security. However, this is often inconvenient, since many do not want to carry their hardware wallet with them at all times. + +Instead, if you approve a non-hardware wallet for authentication activities (such as a mobile device), you would be able to use most dApps without the need to have your hardware wallet on hand. + +## Specification +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +Let: + - `mainAddress` represent the wallet address we are trying to authenticate or prove asset ownership for. + - `mainENS` represent the reverse lookup ENS string for `mainAddress`. + - `authAddress` represent the address we want to use for signing in lieu of `mainAddress`. + - `authENS` represent the reverse lookup ENS string for `authAddress`. + - `authKey` represents a string in the format `[0-9A-Za-z]+`. + +Control of `mainAddress` and ownership of `mainAddress` assets by `authAddress` is proven if all the following conditions are met: + - `mainAddress` has an ENS resolver record and a reverse record set to `mainENS`. + - `authAddress` has an ENS resolver record and a reverse record set to `authENS`. + - `authENS` has an ENS TEXT record `eip5131:vault` in the format `:`. + - `mainENS` has an ENS TEXT record `eip5131:`. + +### Setting up one or many `authAddress` records on a single ENS domain +The `mainAddress` MUST have an ENS resolver record and reverse record configured. +In order to automatically discover the linked account, the `authAddress` SHOULD have an ENS resolver record and reverse record configured. + +1. Choose an unused ``. This can be any string in the format `[0-0A-Za-z]+`. +2. Set a TEXT record `eip5131:` on `mainENS`, with the value set to the desired `authAddress`. +3. Set a TEXT record `eip5131:vault` on `authENS`, with the value set to the `:mainAddress`. + +Currently this EIP does not enforce an upper-bound on the number of `authAddress` entries you can include. Users can repeat this process with as many address as they like. + +### Authenticating `mainAddress` via `authAddress` +Control of `mainAddress` and ownership of `mainAddress` assets is proven if any associated `authAddress` is the `msg.sender` or has signed the message. + +Practically, this would work by performing the following operations: +1. Get the resolver for `authENS` +2. Get the `eip5131:vault` TEXT record of `authENS` +3. Parse `:` to determine the `authKey` and `mainAddress`. +4. MUST get the reverse ENS record for `mainAddress` and verify that it matches ``. + - Otherwise one could set up other ENS nodes (with auths) that point to `mainAddress` and authenticate via those. +5. Get the `eip5131:` TEXT record of `mainENS` and ensure it matches `authAddress`. + +Note that this specification allows for both contract level and client/server side validation of signatures. It is not limited to smart contracts, which is why there is no proposed external interface definition. + +### Revocation of `authAddress` +To revoke permission of `authAddress`, delete the `eip5131:` TEXT record of `mainENS` or update it to point to a new `authAddress`. + +## Rationale + +### Usage of EIP-137 +The proposed specification makes use of EIP-137 rather than introduce another registry paradigm. The reason for this is due to the existing wide adoption of EIP-137 and ENS. + +However, the drawback to EIP-137 is that any linked `authAddress` must contain some ETH in order to set the `authENS` reverse record as well as the `eip5131:vault` TEXT record. This can be solved by a separate reverse lookup registry that enables `mainAddress` to set the reverse record and TEXT record with a message signed by `authAddress`. + +With the advent of L2s and ENS Layer 2 functionalities, off chain verification of linked addresses is possible even with domains managed across different chains. + +### One-to-Many Authentication Relationship +This proposed specification allows for a one (`mainAddress`) to many (`authAddress`) authentication relationship. i.e. one `mainAddress` can authorize many `authAddress` to authenticate, but an `authAddress` can only authenticate itself or a single `mainAddress`. + +The reason for this design choice is to allow for simplicity of authentication via client and smart contract code. You can determine which `mainAddress` the `authAddress` is signing for without any additional user input. + +Further, you can design UX without any user interaction necessary to 'pick' the interacting address by display assets owned by `authAddress` and `mainAddress` and use the appropriate address dependent on the asset the user is attempting to authenticate with. + +## Reference Implementation + +### Client/Server Side +In typescript, the validation function, using ethers.js would be as follows: +``` +export interface LinkedAddress { + ens: string, + address: string, +} + +export async function getLinkedAddress( + provider: ethers.providers.EnsProvider, address: string +): Promise { + const addressENS = await provider.lookupAddress(address); + if (!addressENS) return null; + + const vaultInfo = await (await provider.getResolver(addressENS))?.getText('eip5131:vault'); + if (!vaultInfo) return null; + + const vaultInfoArray = vaultInfo.split(':'); + if (vaultInfoArray.length !== 2) { + throw new Error('EIP5131: Authkey and vault address not configured correctly.'); + } + + const [ authKey, vaultAddress ] = vaultInfoArray; + + const vaultENS = await provider.lookupAddress(vaultAddress); + if (!vaultENS) { + throw new Error(`EIP5131: No ENS domain with reverse record set for vault.`); + }; + + const expectedSigningAddress = await ( + await provider.getResolver(vaultENS) + )?.getText(`eip5131:${authKey}`); + + if (expectedSigningAddress?.toLowerCase() !== address.toLowerCase()) { + throw new Error(`EIP5131: Authentication mismatch.`); + }; + + return { + ens: vaultENS, + address: vaultAddress + }; +} +``` + +### Contract side + +#### With a backend +If your application operates a secure backend server, you could run the client/server code above, then use the result in conjunction with specs like [EIP-1271](./eip-1271.md) : `Standard Signature Validation Method for Contracts` for a cheap and secure way to validate that the the message signer is indeed authenticated for the main address. + +#### Without a backend (JavaScript only) +Provided is a reference implementation for an internal function to verify that the message sender has an authentication link to the main address. + +``` +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/// @author: manifold.xyz + +/** + * ENS Registry Interface + */ +interface ENS { + function resolver(bytes32 node) external view returns (address); +} + +/** + * ENS Resolver Interface + */ +interface Resolver { + function addr(bytes32 node) external view returns (address); + function name(bytes32 node) external view returns (string memory); + function text(bytes32 node, string calldata key) external view returns (string memory); +} + +/** + * Validate a signing address is associtaed with a linked address + */ +library LinkedAddress { + /** + * Validate that the message sender is an authentication address for mainAddress + * + * @param ensRegistry Address of ENS registry + * @param mainAddress The main address we want to authenticate for. + * @param mainENSNodeHash The main ENS Node Hash + * @param authKey The TEXT record of the authKey we are using for validation + * @param authENSNodeHash The auth ENS Node Hash + */ + function validateSender( + address ensRegistry, + address mainAddress, + bytes32 mainENSNodeHash, + string calldata authKey, + bytes32 authENSNodeHash + ) internal view returns (bool) { + return validate(ensRegistry, mainAddress, mainENSNodeHash, authKey, msg.sender, authENSNodeHash); + } + + /** + * Validate that the authAddress is an authentication address for mainAddress + * + * @param ensRegistry Address of ENS registry + * @param mainAddress The main address we want to authenticate for. + * @param mainENSNodeHash The main ENS Node Hash + * @param authAddress The address of the authentication wallet + * @param authENSNodeHash The auth ENS Node Hash + */ + function validate( + address ensRegistry, + address mainAddress, + bytes32 mainENSNodeHash, + string calldata authKey, + address authAddress, + bytes32 authENSNodeHash + ) internal view returns (bool) { + _verifyMainENS(ensRegistry, mainAddress, mainENSNodeHash, authKey, authAddress); + _verifyAuthENS(ensRegistry, mainAddress, authKey, authAddress, authENSNodeHash); + + return true; + } + + // ********************* + // Helper Functions + // ********************* + function _verifyMainENS( + address ensRegistry, + address mainAddress, + bytes32 mainENSNodeHash, + string calldata authKey, + address authAddress + ) private view { + // Check if the ENS nodes resolve correctly to the provided addresses + address mainResolver = ENS(ensRegistry).resolver(mainENSNodeHash); + require(mainResolver != address(0), "Main ENS not registered"); + require(mainAddress == Resolver(mainResolver).addr(mainENSNodeHash), "Main address is wrong"); + + // Verify the authKey TEXT record is set to authAddress by mainENS + string memory authText = Resolver(mainResolver).text(mainENSNodeHash, string(abi.encodePacked("eip5131:", authKey))); + require( + keccak256(bytes(authText)) == keccak256(bytes(_addressToString(authAddress))), + "Invalid auth address" + ); + } + + function _verifyAuthENS( + address ensRegistry, + address mainAddress, + string memory authKey, + address authAddress, + bytes32 authENSNodeHash + ) private view { + // Check if the ENS nodes resolve correctly to the provided addresses + address authResolver = ENS(ensRegistry).resolver(authENSNodeHash); + require(authResolver != address(0), "Auth ENS not registered"); + require(authAddress == Resolver(authResolver).addr(authENSNodeHash), "Auth address is wrong"); + + // Verify the TEXT record is appropriately set by authENS + string memory vaultText = Resolver(authResolver).text(authENSNodeHash, "eip5131:vault"); + require( + keccak256(abi.encodePacked(authKey, ":", _addressToString(mainAddress))) == + keccak256(bytes(vaultText)), + "Invalid auth text record" + ); + } + + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + function sha3HexAddress(address addr) private pure returns (bytes32 ret) { + uint256 value = uint256(uint160(addr)); + bytes memory buffer = new bytes(40); + for (uint256 i = 39; i > 1; --i) { + buffer[i] = _HEX_SYMBOLS[value & 0xf]; + value >>= 4; + } + return keccak256(buffer); + } + + function _addressToString(address addr) private pure returns (string memory ptr) { + // solhint-disable-next-line no-inline-assembly + assembly { + ptr := mload(0x40) + + // Adjust mem ptr and keep 32 byte aligned + // 32 bytes to store string length; address is 42 bytes long + mstore(0x40, add(ptr, 96)) + + // Store (string length, '0', 'x') (42, 48, 120) + // Single write by offsetting across 32 byte boundary + ptr := add(ptr, 2) + mstore(ptr, 0x2a3078) + + // Write string backwards + for { + // end is at 'x', ptr is at lsb char + let end := add(ptr, 31) + ptr := add(ptr, 71) + } gt(ptr, end) { + ptr := sub(ptr, 1) + addr := shr(4, addr) + } { + let v := and(addr, 0xf) + // if > 9, use ascii 'a-f' (no conditional required) + v := add(v, mul(gt(v, 9), 39)) + // Add ascii for '0' + v := add(v, 48) + mstore8(ptr, v) + } + + // return ptr to point to length (32 + 2 for '0x' - 1) + ptr := sub(ptr, 33) + } + + return string(ptr); + } +} +``` + +## Security Considerations +The core purpose of this EIP is to enhance security and promote a safer way to authenticate wallet control and asset ownership when the main wallet is not needed and assets held by the main wallet do not need to be moved. Consider it a way to do 'read only' authentication. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5133.md b/EIPS/eip-5133.md new file mode 100644 index 00000000000000..ffc3e7f4cc48b8 --- /dev/null +++ b/EIPS/eip-5133.md @@ -0,0 +1,59 @@ +--- +eip: 5133 +title: Delaying Difficulty Bomb to mid-September 2022 +description: Delays the difficulty bomb by a further 700000 blocks, to the middle of September 2022. +author: Tomasz Kajetan Stanczak (@tkstanczak), Eric Marti Haynes (@ericmartihaynes), Josh Klopfenstein (@joshklop), Abhimanyu Nag (@AbhiMan1601) +discussions-to: https://ethereum-magicians.org/t/eip-5133-delaying-difficulty-bomb-to-mid-september-2022/9622 +status: Final +type: Standards Track +category: Core +created: 2022-06-01 +--- + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 11,400,000 blocks later than the actual block number. + +## Motivation +To avoid network degradation due to a premature activation of the difficulty bomb. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: +```py +fake_block_number = max(0, block.number - 11_400_000) if block.number >= FORK_BLOCK_NUMBER else block.number +``` +## Rationale + +The following script predicts the bomb will go off at block 15530314, which is expected to be mined around mid-September. + +```python +import math +def predict_bomb_block(current_difficulty, diff_adjust_coeff, block_adjustment): + ''' + Predicts the block number at which the difficulty bomb will become noticeable. + + current_difficulty: the current difficulty + diff_adjust_coeff: intuitively, the percent increase in work that miners have to exert to find a PoW + block_adjustment: the number of blocks to delay the bomb by + ''' + return round(block_adjustment + 100000 * (2 + math.log2(diff_adjust_coeff * current_difficulty // 2048))) + +# current_difficulty = 13891609586928851 (Jun 01, 2022) +# diff_adjust_coeff = 0.1 (historically, the bomb is noticeable when the coefficient is >= 0.1) +# block_adjustment = 11400000 +print(predict_bomb_block(13891609586928851, 0.1, 11400000)) +``` + +Precise increases in block times are very difficult to predict (especially after the bomb is noticeable). +However, based on past manifestations of the bomb, we can anticipate 0.1s delays by mid-September and 0.6-1.2s delays by early October. + +## Backwards Compatibility +No known backward compatibility issues. + +## Security Considerations +Misjudging the effects of the difficulty can mean longer blocktimes than anticipated until a hardfork is released. Wild shifts in difficulty can affect this number severely. Also, gradual changes in blocktimes due to longer-term adjustments in difficulty can affect the timing of difficulty bomb epochs. This affects the usability of the network but unlikely to have security ramifications. + +In this specific instance, it is possible that the network hashrate drops considerably before The Merge, which could accelerate the timeline by which the bomb is felt in block times. The offset value chosen aims to take this into account. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5139.md b/EIPS/eip-5139.md new file mode 100644 index 00000000000000..c5c4a0d68a1b6d --- /dev/null +++ b/EIPS/eip-5139.md @@ -0,0 +1,602 @@ +--- +eip: 5139 +title: Remote Procedure Call Provider Lists +description: Format for lists of RPC providers for Ethereum-like chains. +author: Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-5139-remote-procedure-call-provider-lists/9517 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-06 +requires: 155, 1577 +--- + +## Abstract +This proposal specifies a JSON schema for describing lists of remote procedure call (RPC) providers for Ethereum-like chains, including their supported [EIP-155](./eip-155.md) `CHAIN_ID`. + +## Motivation +The recent explosion of alternate chains, scaling solutions, and other mostly Ethereum-compatible ledgers has brought with it many risks for users. It has become commonplace to blindly add new RPC providers using [EIP-3085](./eip-3085.md) without evaluating their trustworthiness. At best, these RPC providers may be accurate, but track requests; and at worst, they may provide misleading information and frontrun transactions. + +If users instead are provided with a comprehensive provider list built directly by their wallet, with the option of switching to whatever list they so choose, the risk of these malicious providers is mitigated significantly, without sacrificing functionality for advanced users. + +## 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. + +### List Validation & Schema + +List consumers (like wallets) MUST validate lists against the provided schema. List consumers MUST NOT connect to RPC providers present only in an invalid list. + +Lists MUST conform to the following JSON Schema: + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + + "title": "Ethereum RPC Provider List", + "description": "Schema for lists of RPC providers compatible with Ethereum wallets.", + + "$defs": { + "VersionBase": { + "type": "object", + "description": "Version of a list, used to communicate changes.", + + "required": [ + "major", + "minor", + "patch" + ], + + "properties": { + "major": { + "type": "integer", + "description": "Major version of a list. Incremented when providers are removed from the list or when their chain ids change.", + "minimum": 0 + }, + + "minor": { + "type": "integer", + "description": "Minor version of a list. Incremented when providers are added to the list.", + "minimum": 0 + }, + + "patch": { + "type": "integer", + "description": "Patch version of a list. Incremented for any change not covered by major or minor versions, like bug fixes.", + "minimum": 0 + }, + + "preRelease": { + "type": "string", + "description": "Pre-release version of a list. Indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its major, minor, and patch versions.", + "pattern": "^[1-9A-Za-z][0-9A-Za-z]*(\\.[1-9A-Za-z][0-9A-Za-z]*)*$" + } + } + }, + + "Version": { + "type": "object", + "additionalProperties": false, + + "allOf": [ + { + "$ref": "#/$defs/VersionBase" + } + ], + + "properties": { + "major": true, + "minor": true, + "patch": true, + "preRelease": true, + "build": { + "type": "string", + "description": "Build metadata associated with a list.", + "pattern": "^[0-9A-Za-z-]+(\\.[0-9A-Za-z-])*$" + } + } + }, + + "VersionRange": { + "type": "object", + "additionalProperties": false, + + "properties": { + "major": true, + "minor": true, + "patch": true, + "preRelease": true, + "mode": true + }, + + "allOf": [ + { + "$ref": "#/$defs/VersionBase" + } + ], + + "oneOf": [ + { + "properties": { + "mode": { + "type": "string", + "enum": ["^", "="] + }, + "preRelease": false + } + }, + { + "required": [ + "preRelease", + "mode" + ], + + "properties": { + "mode": { + "type": "string", + "enum": ["="] + } + } + } + ] + }, + + "Logo": { + "type": "string", + "description": "A URI to a logo; suggest SVG or PNG of size 64x64", + "format": "uri" + }, + + "ProviderChain": { + "type": "object", + "description": "A single chain supported by a provider", + "additionalProperties": false, + "required": [ + "chainId", + "endpoints" + ], + "properties": { + "chainId": { + "type": "integer", + "description": "Chain ID of an Ethereum-compatible network", + "minimum": 1 + }, + "endpoints": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "format": "uri" + } + } + } + }, + + "Provider": { + "type": "object", + "description": "Description of an RPC provider.", + "additionalProperties": false, + + "required": [ + "chains", + "name" + ], + + "properties": { + "name": { + "type": "string", + "description": "Name of the provider.", + "minLength": 1, + "maxLength": 40, + "pattern": "^[ \\w.'+\\-%/À-ÖØ-öø-ÿ:&\\[\\]\\(\\)]+$" + }, + "logo": { + "$ref": "#/$defs/Logo" + }, + "priority": { + "type": "integer", + "description": "Priority of this provider (where zero is the highest priority.)", + "minimum": 0 + }, + "chains": { + "type": "array", + "items": { + "$ref": "#/$defs/ProviderChain" + } + } + } + }, + + "Path": { + "description": "A JSON Pointer path.", + "type": "string" + }, + + "Patch": { + "items": { + "oneOf": [ + { + "additionalProperties": false, + "required": ["value", "op", "path"], + "properties": { + "path": { + "$ref": "#/$defs/Path" + }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": ["add", "replace", "test"] + }, + "value": { + "description": "The value to add, replace or test." + } + } + }, + { + "additionalProperties": false, + "required": ["op", "path"], + "properties": { + "path": { + "$ref": "#/$defs/Path" + }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": ["remove"] + } + } + }, + { + "additionalProperties": false, + "required": ["from", "op", "path"], + "properties": { + "path": { + "$ref": "#/$defs/Path" + }, + + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": ["move", "copy"] + }, + "from": { + "$ref": "#/$defs/Path", + "description": "A JSON Pointer path pointing to the location to move/copy from." + } + } + } + ] + }, + "type": "array" + } + }, + + "type": "object", + "additionalProperties": false, + + "required": [ + "name", + "version", + "timestamp" + ], + + "properties": { + "name": { + "type": "string", + "description": "Name of the provider list", + "minLength": 1, + "maxLength": 40, + "pattern": "^[\\w ]+$" + }, + "logo": { + "$ref": "#/$defs/Logo" + }, + "version": { + "$ref": "#/$defs/Version" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "The timestamp of this list version; i.e. when this immutable version of the list was created" + }, + "extends": true, + "changes": true, + "providers": true + }, + + "oneOf": [ + { + "type": "object", + + "required": [ + "extends", + "changes" + ], + + "properties": { + "providers": false, + + "extends": { + "type": "object", + "additionalProperties": false, + + "required": [ + "version" + ], + + "properties": { + "uri": { + "type": "string", + "format": "uri", + "description": "Location of the list to extend, as a URI." + }, + "ens": { + "type": "string", + "description": "Location of the list to extend using EIP-1577." + }, + "version": { + "$ref": "#/$defs/VersionRange" + } + }, + + "oneOf": [ + { + "properties": { + "uri": false, + "ens": true + } + }, + { + "properties": { + "ens": false, + "uri": true + } + } + ] + }, + "changes": { + "$ref": "#/$defs/Patch" + } + } + }, + { + "type": "object", + + "required": [ + "providers" + ], + + "properties": { + "changes": false, + "extends": false, + "providers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/Provider" + } + } + } + } + ] +} +``` + +For illustrative purposes, the following is an example list following the schema: + +```json +{ + "name": "Example Provider List", + "version": { + "major": 0, + "minor": 1, + "patch": 0, + "build": "XPSr.p.I.g.l" + }, + "timestamp": "2004-08-08T00:00:00.0Z", + "logo": "https://mylist.invalid/logo.png", + "providers": { + "some-key": { + "name": "Frustrata", + "chains": [ + { + "chainId": 1, + "endpoints": [ + "https://mainnet1.frustrata.invalid/", + "https://mainnet2.frustrana.invalid/" + ] + }, + { + "chainId": 3, + "endpoints": [ + "https://ropsten.frustrana.invalid/" + ] + } + ] + }, + "other-key": { + "name": "Sourceri", + "priority": 3, + "chains": [ + { + "chainId": 1, + "endpoints": [ + "https://mainnet.sourceri.invalid/" + ] + }, + { + "chainId": 42, + "endpoints": [ + "https://kovan.sourceri.invalid" + ] + } + ] + } + } +} +``` + +### Versioning + +List versioning MUST follow the [Semantic Versioning 2.0.0](../assets/eip-5139/semver.md) (SemVer) specification. + +The major version MUST be incremented for the following modifications: + + - Removing a provider. + - Changing a provider's key in the `providers` object. + - Removing the last `ProviderChain` for a chain id. + +The major version MAY be incremented for other modifications, as permitted by SemVer. + +If the major version is not incremented, the minor version MUST be incremented if any of the following modifications are made: + + - Adding a provider. + - Adding the first `ProviderChain` of a chain id. + +The minor version MAY be incremented for other modifications, as permitted by SemVer. + +If the major and minor versions are unchanged, the patch version MUST be incremented for any change. + +### Publishing + +Provider lists SHOULD be published to an Ethereum Name Service (ENS) name using [EIP-1577](./eip-1577.md)'s `contenthash` mechanism on mainnet. + +Provider lists MAY instead be published using HTTPS. Provider lists published in this way MUST allow reasonable access from other origins (generally by setting the header `Access-Control-Allow-Origin: *`.) + +### Priority + +Provider entries MAY contain a `priority` field. A `priority` value of zero SHALL indicate the highest priority, with increasing `priority` values indicating decreasing priority. Multiple providers MAY be assigned the same priority. All providers without a `priority` field SHALL have equal priority. Providers without a `priority` field SHALL always have a lower priority than any provider with a `priority` field. + +List consumers MAY use `priority` fields to choose when to connect to a provider, but MAY ignore it entirely. List consumers SHOULD explain to users how their implementation interprets `priority`. + +### List Subtypes + +Provider lists are subdivided into two categories: root lists, and extension lists. A root list contains a list of providers, while an extension list contains a set of modifications to apply to another list. + +#### Root Lists + +A root list has a top-level `providers` key. + +#### Extension Lists + +An extension list has top-level `extends` and `changes` keys. + +##### Specifying a Parent (`extends`) + +The `uri` and `ens` fields SHALL point to a source for the parent list. + +If present, the `uri` field MUST use a scheme specified in [Publishing](#publishing). + +If present, the `ens` field MUST specify an ENS name to be resolved using EIP-1577. + +The `version` field SHALL specify a range of compatible versions. List consumers MUST reject extension lists specifying an incompatible parent version. + +In the event of an incompatible version, list consumers MAY continue to use a previously saved parent list, but list consumers choosing to do so MUST display a prominent warning that the provider list is out of date. + +###### Default Mode + +If the `mode` field is omitted, a parent version SHALL be compatible if and only if the parent's version number matches the left-most non-zero portion in the major, minor, patch grouping. + +For example: + +```javascript +{ + "major": "1", + "minor": "2", + "patch": "3" +} +``` + +Is equivalent to: + +``` +>=1.2.3, <2.0.0 +``` + +And: + +```javascript +{ + "major": "0", + "minor": "2", + "patch": "3" +} +``` + +Is equivalent to: + +``` +>=0.2.3, <0.3.0 +``` + +###### Caret Mode (`^`) + +The `^` mode SHALL behave exactly as the default mode above. + +###### Exact Mode (`=`) + +In `=` mode, a parent version SHALL be compatible if and only if the parent's version number exactly matches the specified version. + +##### Specifying Changes (`changes`) + +The `changes` field SHALL be a JavaScript Object Notation (JSON) Patch document as specified in RFC 6902. + +JSON pointers within the `changes` field MUST be resolved relative to the `providers` field of the parent list. For example, see the following lists for a correctly formatted extension. + +###### Root List + +```json +TODO +``` + +###### Extension List + +```json +TODO +``` + +##### Applying Extension Lists + +List consumers MUST follow this algorithm to apply extension lists: + + 1. Is the current list an extension list? + * Yes: + 1. Ensure that this `from` has not been seen before. + 1. Retrieve the parent list. + 1. Verify that the parent list is valid according to the JSON schema. + 1. Ensure that the parent list is version compatible. + 1. Set the current list to the parent list and go to step 1. + * No: + 1. Go to step 2. + 1. Copy the current list into a variable `$output`. + 1. Does the current list have a child: + * Yes: + 1. Apply the child's `changes` to `providers` in `$output`. + 1. Verify that `$output` is valid according to the JSON schema. + 1. Set the current list to the child. + 1. Go to step 3. + * No: + 1. Replace the current list's `providers` with `providers` from `$output`. + 1. The current list is now the resolved list; return it. + + +List consumers SHOULD limit the number of extension lists to a reasonable number. + +## Rationale + +This specification has two layers (provider, then chain id) instead of a flatter structure so that wallets can choose to query multiple independent providers for the same query and compare the results. + +Each provider may specify multiple endpoints to implement load balancing or redundancy. + +List version identifiers conform to SemVer to roughly communicate the kinds of changes that each new version brings. If a new version adds functionality (eg. a new chain id), then users can expect the minor version to be incremented. Similarly, if the major version is not incremented, list subscribers can assume dapps that work in the current version will continue to work in the next one. + +## Security Considerations + +Ultimately it is up to the end user to decide on what list to subscribe to. Most users will not change from the default list maintained by their wallet. Since wallets already have access to private keys, giving them additional control over RPC providers seems like a small increase in risk. + +While list maintainers may be incentivized (possibly financially) to include or exclude particular providers, actually doing so may jeopardize the legitimacy of their lists. This standard facilitates swapping lists, so if such manipulation is revealed, users are free to swap to a new list with little effort. + +If the list chosen by the user is published using EIP-1577, the list consumer has to have access to ENS in some way. This creates a paradox: how do you query Ethereum without an RPC provider? This paradox creates an attack vector: whatever method the list consumer uses to fetch the list can track the user, and even more seriously, **can lie about the contents of the list**. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5143.md b/EIPS/eip-5143.md new file mode 100644 index 00000000000000..72afb4bc0d02d3 --- /dev/null +++ b/EIPS/eip-5143.md @@ -0,0 +1,224 @@ +--- +eip: 5143 +title: Slippage Protection for Tokenized Vault +description: An extension of EIP-4626 supporting improved EOA interactions. +author: Hadrien Croubois (@amxx) +discussions-to: https://ethereum-magicians.org/t/eip-5143-slippage-protection-for-tokenized-vaults/9554 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-09 +requires: 20, 4626 +--- + +## Abstract + +The following standard extends the [EIP-4626](./eip-4626.md) Tokenized Vault standard with functions dedicated to the safe interaction between EOAs and the vault when price is subject to slippage. + +## Motivation + +[EIP-4626](./eip-4626.md) security considerations section states that: +> "If implementors intend to support EOA account access directly, they should consider adding an additional function call for deposit/mint/withdraw/redeem with the means to accommodate slippage loss or unexpected deposit/withdrawal limits, since they have no other means to revert the transaction if the exact output amount is not achieved." + +Yet, EIP-4626 does not standardize the corresponding function signatures and behaviors. For improved interroperability, and better support by wallets, it is essential that this optional functions are also standardized. + +## Specification + +This ERC is an extension of EIP-4626. Any contract implementing it MUST also implement EIP-4626. + +### Methods + +#### deposit + +Overloaded version of ERC-4626's `deposit`. + +Mints `shares` Vault shares to `receiver` by depositing exactly `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support [EIP-20](./eip-20.md) `approve` / `transferFrom` on `asset` as a deposit flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `deposit` execution, and are accounted for during `deposit`. + +MUST revert if all of `assets` cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). +MUST revert if depositing `assets` underlying asset mints less then `minShares` shares. + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: deposit + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + - name: minShares + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### mint + +Overloaded version of ERC-4626's `mint`. + +Mints exactly `shares` Vault shares to `receiver` by depositing `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support ERC-20 `approve` / `transferFrom` on `asset` as a mint flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `mint` execution, and are accounted for during `mint`. + +MUST revert if all of `shares` cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). +MUST revert if minting `shares` shares cost more then `maxAssets` underlying tokens. + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: mint + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + - name: maxAssets + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### withdraw + +Overloaded version of ERC-4626's `withdraw`. + +Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a withdraw flow where the shares are burned from `owner` directly where `owner` is `msg.sender` or `msg.sender` has ERC-20 approval over the shares of `owner`. +MAY support an additional flow in which the shares are transferred to the Vault contract before the `withdraw` execution, and are accounted for during `withdraw`. + +MUST revert if all of `assets` cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). +MUST revert if withdrawing `assets` underlying tokens requires burning more then `maxShares` shares. + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: withdraw + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + - name: owner + type: address + - name: maxShares + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### redeem + +Overloaded version of ERC-4626's `redeem`. + +Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a redeem flow where the shares are burned from `owner` directly where `owner` is `msg.sender` or `msg.sender` has ERC-20 approval over the shares of `owner`. +MAY support an additional flow in which the shares are transferred to the Vault contract before the `redeem` execution, and are accounted for during `redeem`. + +MUST revert if all of `shares` cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). +MUST revert if redeeming `shares` shares sends less than `minAssets` underlying tokens to `receiver`. + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: redeem + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + - name: owner + type: address + - name: minAssets + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +## Rationale + +This ERC's functions do not replace ERC-4626 equivalent mechanisms. They are additional (overloaded) methods designed to protect EOAs interacting with the vault. + +When smart contracts interact with an ERC-4626 vault, they can preview any operation using the dedicated functions before executing the operation. This can be done +atomically, with no risk of price change. This is not true of EOA, which will preview their operations on a UI, sign a transaction, and have it mined later. +Between the preview and the transaction being executed, the blockchain state might change, resulting in unexpected outcomes. In particular, frontrunning +make EOA's interractons with an ERC-4626 vault possibly risky. + +Other projects in the DeFi spaces, such as decentralized exchanges, already include similar mechanisms so a user can request its transaction reverts if the +resulting exchange rate is not considered good enough. + +Implementing This ERC on top of an ERC-4626 contract can be done very easily. It just requires calling the corresponding ERC-4626 function and adding a revert +check on the returned value. + +### Alternative approaches + +This ERC aims at solving the security concerns (describes in the motivation section) at the vault level. For completeness, we have to mention that these issues can also be addressed using a generic ERC-4626 router, similar to how Uniswap V2 & V3 use a router to provide good user workflows on top of the Uniswap pairs. The router approach is possibly more versatile and leaves more room for evolutions (the router can be replaced at any point) but it also leads to more expensive operations because the router needs to take temporary custody of the tokens going into the vault. + +## Reference Implementation + +Given an existing ERC-4626 implementation + +``` solidity +contract ERC5143 is ERC4626 { + function deposit(uint256 assets, address receiver, uint256 minShares) public virtual returns (uint256) { + uint256 shares = deposit(assets, receiver); + require(shares >= minShares, "ERC5143: deposit slippage protection"); + return shares; + } + function mint(uint256 shares, address receiver, uint256 maxAssets) public virtual returns (uint256) { + uint256 assets = mint(shares, receiver); + require(assets <= maxAssets, "ERC5143: mint slippage protection"); + return assets; + } + function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual returns (uint256) { + uint256 shares = withdraw(assets, receiver, owner); + require(shares <= maxShares, "ERC5143: withdraw slippage protection"); + return shares; + } + function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual returns (uint256) { + uint256 assets = redeem(shares, receiver, owner); + require(assets >= minAssets, "ERC5143: redeem slippage protection"); + return assets; + } +} +``` +## Security Considerations + +This ERC addresses one of the security consideration raised by ERC-4626. Other considerations still apply. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5164.md b/EIPS/eip-5164.md new file mode 100644 index 00000000000000..2180c02f4bf564 --- /dev/null +++ b/EIPS/eip-5164.md @@ -0,0 +1,223 @@ +--- +eip: 5164 +title: Cross-Chain Execution +description: Defines an interface that supports execution across EVM networks. +author: Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT), Chris Whinfrey (@cwhinfrey) +discussions-to: https://ethereum-magicians.org/t/eip-5164-cross-chain-execution/9658 +status: Review +type: Standards Track +category: ERC +created: 2022-06-14 +--- + +## Abstract + +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 by sending a cross-chain message. + +The specification defines two components: the "Message Dispatcher" and the "Message Executor". The Message Dispatcher lives on the calling side, and the executor lives on the receiving side. When a message is sent, a Message Dispatcher will move the message through a transport layer to a Message Executor, 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. Defining a simple, common specification will increase code re-use and allow us to use common bridge implementations. + +## 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. + +This specification allows contracts on one chain to send messages to contracts on another chain. There are two key interfaces that needs to be implemented: + +- `MessageDispatcher` +- `MessageExecutor` + +The `MessageDispatcher` lives on the origin chain and dispatches messages to the `MessageExecutor` for execution. The `MessageExecutor` lives on the destination chain and executes dispatched messages. + +### MessageDispatcher + +The `MessageDispatcher` lives on the chain from which messages are sent. The Dispatcher's job is to broadcast messages through a transport layer to one or more `MessageExecutor` contracts. + +A unique `messageId` MUST be generated for each message or message batch. The message identifier MUST be unique across chains and dispatchers. This can be achieved by hashing a tuple of `chainId, dispatcherAddress, messageNonce` where messageNonce is a monotonically increasing integer per message. + +#### MessageDispatcher Methods + +**dispatchMessage** + +Will dispatch a message to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`. + +`MessageDispatcher`s MUST emit the `MessageDispatched` event when a message is dispatched. + +`MessageDispatcher`s MUST revert if `toChainId` is not supported. + +`MessageDispatcher`s MUST forward the message to a `MessageExecutor` on the `toChainId`. + +`MessageDispatcher`s MUST use a unique `messageId` for each message. + +`MessageDispatcher`s MUST return the `messageId` to allow the message sender to track the message. + +`MessageDispatcher`s MAY require payment. + +```solidity +interface MessageDispatcher { + function dispatchMessage(uint256 toChainId, address to, bytes calldata data) external payable returns (bytes32 messageId); +} +``` + +```yaml +- name: dispatchMessage + type: function + stateMutability: payable + inputs: + - name: toChainId + type: uint256 + - name: to + type: address + - name: data + type: bytes + outputs: + - name: messageId + type: bytes32 +``` + +#### MessageDispatcher Events + +**MessageDispatched** + +The `MessageDispatched` event MUST be emitted by the `MessageDispatcher` when an individual message is dispatched. + +```solidity +interface MessageDispatcher { + event MessageDispatched( + bytes32 indexed messageId, + address indexed from, + uint256 indexed toChainId, + address to, + bytes data, + ); +} +``` + +```yaml +- name: MessageDispatched + type: event + inputs: + - name: messageId + indexed: true + type: bytes32 + - name: from + indexed: true + type: address + - name: toChainId + indexed: true + type: uint256 + - name: to + type: address + - name: data + type: bytes +``` + +### MessageExecutor + +The `MessageExecutor` executes dispatched messages and message batches. Developers must implement a `MessageExecutor` in order to execute messages on the receiving chain. + +The `MessageExecutor` will execute a messageId only once, but may execute messageIds in any order. This specification makes no ordering guarantees, because messages and message batches may travel non-sequentially through the transport layer. + +#### Execution + +`MessageExecutor`s SHOULD verify all message data with the bridge transport layer. + +`MessageExecutor`s MUST NOT successfully execute a message more than once. + +`MessageExecutor`s MUST revert the transaction when a message fails to be executed allowing the message to be retried at a later time. + +**Calldata** + +`MessageExecutor`s MUST append the ABI-packed (`messageId`, `fromChainId`, `from`) to the calldata for each message being executed. This allows the receiver of the message to verify the cross-chain sender and the chain that the message is coming from. + +```solidity +to.call(abi.encodePacked(data, messageId, fromChainId, from)); +``` + +```yaml +- name: calldata + type: bytes + inputs: + - name: data + type: bytes + - name: messageId + type: bytes32 + - name: fromChainId + type: uint256 + - name: from + type: address +``` + +#### MessageExecutor Events + +**MessageIdExecuted** + +`MessageIdExecuted` MUST be emitted once a message or message batch has been executed. + +```solidity +interface MessageExecutor { + event MessageIdExecuted( + uint256 indexed fromChainId, + bytes32 indexed messageId + ); +} +``` + +```yaml +- name: MessageIdExecuted + type: event + inputs: + - name: fromChainId + indexed: true + type: uint256 + - name: messageId + indexed: true + type: bytes32 +``` + +#### MessageExecutor Errors + +**MessageAlreadyExecuted** + +`MessageExecutor`s MUST revert if a messageId has already been executed and SHOULD emit a `MessageIdAlreadyExecuted` custom error. + +```solidity +interface MessageExecutor { + error MessageIdAlreadyExecuted( + bytes32 messageId + ); +} +``` + +**MessageFailure** + +`MessageExecutor`s MUST revert if an individual message fails and SHOULD emit a `MessageFailure` custom error. + +```solidity +interface MessageExecutor { + error MessageFailure( + bytes32 messageId, + bytes errorData + ); +} +``` + +## Rationale + +The `MessageDispatcher` can be coupled to one or more `MessageExecutor`. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling `dispatchMessage` without being aware of the `MessageExecutor` address. Messages can also be traced by a client using the data logged by the `MessageIdExecuted` event. + +Some bridges may require payment in the native currency, so the `dispatchMessage` function is payable. + +## Backwards Compatibility + +This specification is compatible with existing governance systems as it offers simple cross-chain execution. + +## Security Considerations + +Bridge trust profiles are variable, so users must understand that bridge security depends on the implementation. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5169.md b/EIPS/eip-5169.md new file mode 100644 index 00000000000000..f9023b301807ba --- /dev/null +++ b/EIPS/eip-5169.md @@ -0,0 +1,211 @@ +--- +eip: 5169 +title: Client Script URI for Token Contracts +description: Add a scriptURI to point to an executable script associated with the functionality of the token. +author: James (@JamesSmartCell), Weiwu (@weiwu-zhang) +discussions-to: https://ethereum-magicians.org/t/eip-5169-client-script-uri-for-token-contracts/9674 +status: Final +type: Standards Track +category: ERC +created: 2022-05-03 +requires: 20, 165, 721, 777, 1155 +--- + +## Abstract + +This EIP provides a contract interface adding a `scriptURI()` function for locating executable scripts associated with the token. + +## Motivation + +Often, smart contract authors want to provide some user functionality to their tokens through client scripts. The idea is made popular with function-rich NFTs. It's important that a token's contract is linked to its client script, since the client script may carry out trusted tasks such as creating transactions for the user. + +This EIP allows users to be sure they are using the correct script through the contract by providing a URI to an official script, made available with a call to the token contract itself (`scriptURI`). This URI can be any RFC 3986-compliant URI, such as a link to an IPFS multihash, GitHub gist, or a cloud storage provider. Each contract implementing this EIP implements a `scriptURI` function which returns the download URI to a client script. The script provides a client-side executable to the hosting token. Examples of such a script could be: + +- A 'miniDapp', which is a cut-down DApp tailored for a single token. +- A 'TokenScript' which provides TIPS from a browser wallet. +- A 'TokenScript' that allows users to interact with contract functions not normally provided by a wallet, eg 'mint' function. +- An extension that is downloadable to the hardware wallet with an extension framework, such as Ledger. +- JavaScript instructions to operate a smartlock, after owner receives authorization token in their wallet. + +### Overview + +With the discussion above in mind, we outline the solution proposed by this EIP. For this purpose, we consider the following variables: + +- `SCPrivKey`: The private signing key to administrate a smart contract implementing this EIP. Note that this doesn't have to be a new key especially added for this EIP. Most smart contracts made today already have an administration key to manage the tokens issued. It can be used to update the `scriptURI`. + +- `newScriptURI`: an array of URIs for different ways to find the client script. + +We can describe the life cycle of the `scriptURI` functionality: + +- Issuance + +1. The token issuer issues the tokens and a smart contract implementing this EIP, with the admin key for the smart contract being `SCPrivKey`. +2. The token issuer calls `setScriptURI` with the `scriptURI`. + +- Update `scriptURI` + +1. The token issuer stores the desired `script` at all the new URI locations and constructs a new `scriptURI` structure based on this. +2. The token issuer calls `setScriptURI` with the new `scriptURI` structure. + +## 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. + +We define a scriptURI element using the `string[]`. +Based on this, we define the smart contract interface below: + +```solidity +interface IERC5169 { + /// @dev This event emits when the scriptURI is updated, + /// so wallets implementing this interface can update a cached script + event ScriptUpdate(string[] newScriptURI); + + /// @notice Get the scriptURI for the contract + /// @return The scriptURI + function scriptURI() external view returns(string[] memory); + + /// @notice Update the scriptURI + /// emits event ScriptUpdate(scriptURI memory newScriptURI); + function setScriptURI(string[] memory newScriptURI) external; +} +``` + +The interface MUST be implemented under the following constraints: + +- The smart contract implementing `IERC5169` MUST store variables `address owner` in its state. + +- The smart contract implementing `IERC5169` MUST set `owner=msg.sender` in its constructor. + +- The `ScriptUpdate(...)` event MUST be emitted when the ```setScriptURI``` function updates the `scriptURI`. + +- The `setScriptURI(...)` function MUST validate that `owner == msg.sender` *before* executing its logic and updating any state. + +- The `setScriptURI(...)` function MUST update its internal state such that `currentScriptURI = newScriptURI`. + +- The `scriptURI()` function MUST return the `currentScriptURI` state. + +- The `scriptURI()` function MAY be implemented as pure or view. + +- Any user of the script learned from `scriptURI` MUST validate the script is either at an immutable location, its URI contains its hash digest, or it implements the separate `Authenticity for Client Script` EIP, which asserts authenticity using signatures instead of a digest. + +## Rationale + +This method avoids the need for building secure and certified centralized hosting and allows scripts to be hosted anywhere: IPFS, GitHub or cloud storage. + +## Backwards Compatibility + +This standard is backwards-compatible with most existing token standards, including the following commonly-used ones: + +- [ERC-20](./eip-20.md) +- [ERC-721](./eip-721.md) +- [ERC-777](./eip-777.md) +- [ERC-1155](./eip-1155.md) + +## Test Cases + +### Test Contract + +```solidity + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./IERC5169.sol"; +contract ERC5169 is IERC5169, Ownable { + string[] private _scriptURI; + function scriptURI() external view override returns(string[] memory) { + return _scriptURI; + } + + function setScriptURI(string[] memory newScriptURI) external onlyOwner override { + _scriptURI = newScriptURI; + + emit ScriptUpdate(newScriptURI); + } +} + +``` + +### Test Cases + +```ts + +const { expect } = require('chai'); +const { BigNumber, Wallet } = require('ethers'); +const { ethers, network, getChainId } = require('hardhat'); + +describe('ERC5169', function () { + before(async function () { + this.ERC5169 = await ethers.getContractFactory('ERC5169'); + }); + + beforeEach(async function () { + // targetNFT + this.erc5169 = await this.ERC5169.deploy(); + }); + + it('Should set script URI', async function () { + const scriptURI = [ + 'uri1', 'uri2', 'uri3' + ]; + + await expect(this.erc5169.setScriptURI(scriptURI)) + .emit(this.erc5169, 'ScriptUpdate') + .withArgs(scriptURI); + + const currentScriptURI = await this.erc5169.scriptURI(); + + expect(currentScriptURI.toString()).to.be.equal(scriptURI.toString()); + }); + +``` + +## Reference Implementation + +An intuitive implementation is the STL office door token. This NFT is minted and transferred to STL employees. The TokenScript attached to the token contract via the `scriptURI()` function contains instructions on how to operate the door interface. This takes the form of: + +1. Query for challenge string (random message from IoT interface eg 'Apples-5E3FA1'). + +2. Receive and display challenge string on Token View, and request 'Sign Personal'. + +3. On obtaining the signature of the challenge string, send back to IoT device. + +4. IoT device will unlock door if ec-recovered address holds the NFT. + +With `scriptURI()` the experience is greatly enhanced as the flow for the user is: + +1. Receive NFT. + +2. Use authenticated NFT functionality in the wallet immediately. + +The project with contract, TokenScript and IoT firmware is in use by Smart Token Labs office door and numerous other installations. An example implementation contract: [ERC-5169 Contract Example](../assets/eip-5169/contract/ExampleContract.sol) and TokenScript: [ERC-5169 TokenScript Example](../assets/eip-5169/tokenscript/ExampleScript.xml). Links to the firmware and full sample can be found in the associated discussion linked in the header. +The associated TokenScript can be read from the contract using `scriptURI()`. + +### Script location + +While the most straightforward solution to facilitate specific script usage associated with NFTs, is clearly to store such a script on the smart contract. However, this has several disadvantages: + +1. The smart contract signing key is needed to make updates, causing the key to become more exposed, as it is used more often. + +2. Updates require smart contract interaction. If frequent updates are needed, smart contract calls can become an expensive hurdle. + +3. Storage fee. If the script is large, updates to the script will be costly. A client script is typically much larger than a smart contract. + +For these reasons, storing volatile data, such as token enhancing functionality, on an external resource makes sense. Such an external resource can be either be hosted centrally, such as through a cloud provider, or privately hosted through a private server, or decentralized hosted, such as the interplanetary filesystem. + +While centralized storage for a decentralized functionality goes against the ethos of web3, fully decentralized solutions may come with speed, price or space penalties. This EIP handles this by allowing the function `ScriptURI` to return multiple URIs, which could be a mix of centralized, individually hosted and decentralized locations. + +While this EIP does not dictate the format of the stored script, the script itself could contain pointers to multiple other scripts and data sources, allowing for advanced ways to expand token scripts, such as lazy loading. +The handling of integrity of such secondary data sources is left dependent on the format of the script. + +## Security Considerations + +**When a server is involved** + +When the client script does not purely rely on connection to a blockchain node, but also calls server APIs, the trustworthiness of the server API is called into question. This EIP does not provide any mechanism to assert the authenticity of the API access point. Instead, as long as the client script is trusted, it's assumed that it can call any server API in order to carry out token functions. This means the client script can mistrust a server API access point. + +**When the scriptURI doesn't contain integrity (hash) information** + +We separately authored `Authenticity for Client Script` EIP to guide on how to use digital signatures efficiently and concisely to ensure authenticity and integrity of scripts not stored at a URI which is a digest of the script itself. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5173.md b/EIPS/eip-5173.md new file mode 100644 index 00000000000000..cbe7a4bce776b8 --- /dev/null +++ b/EIPS/eip-5173.md @@ -0,0 +1,398 @@ +--- +eip: 5173 +title: NFT Future Rewards (nFR) +description: A multigenerational reward mechanism that rewards‌ all ‌owners of non-fungible tokens (NFT). +author: Yale ReiSoleil (@longnshort), dRadiant (@dRadiant), D Wang, PhD +discussions-to: https://ethereum-magicians.org/t/non-fungible-future-rewards-token-standard/9203 +status: Draft +type: Standards Track +category: ERC +created: 2022-05-08 +requires: 165, 721 +--- + +## Abstract + +In the proposed EIP, we introduce the Future Rewards (FR) extension for [ERC-721](./eip-721.md) tokens (NFTs), allowing owners to benefit from future price increases even after selling their tokens. With [ERC-5173](./eip-5173.md), we establish a Participatory Value Amplification framework where creators, buyers, and sellers collaborate to amplify value and build wealth together. This innovative approach tackles the challenges faced by traders, creating a fairer and more profitable system that goes beyond zero-sum thinking. Aligned interests between the service provider and users create a sustainable and beneficial trading environment. ERC-5173 compliant token owners enjoy price increases during holding and continue to receive Future Rewards (FRs) even after selling. By eliminating division and promoting collective prosperity, ERC-5173 fosters strong bonds among participants. All sellers, including the original Minter, receive equal FR distributions through the NFT Future Rewards (nFR) framework, ensuring fair sharing of profits across historical ownership. + +## Motivation + +The current trading landscape is rife with unfair practices such as spoofing, insider trading, and wash trading. These techniques often lead to losses for average traders who are caught in the cycle of greed and fear. However, with the emergence of non-fungible tokens (NFTs) and the potential to track every transaction, we have the opportunity to disrupt this unequal value distribution. + +The implementation of ERC-5173 sets a standard for profit sharing throughout the token's ownership history, benefiting all market participants. It introduces a value-amplification system where buyers and owners are rewarded for their contributions to the price discovery process. This model aligns interests and creates a mutually beneficial economic rule for both buyers and sellers. + +NFTs, unlike physical art and collectibles, can accurately reflect the contributions of their owners to their value. By recording every price change of each [ERC-721](./eip-721.md) token, we can establish a Future Rewards program that fairly compensates owners. This program aims to level the playing field and provide average traders with a better chance at success. + +In addition to promoting a new gift economic model, our framework discourages any illicit deals that bypass the rules set by artists and marketplaces. It promotes transparency and integrity within the trading ecosystem. + +When applied to wrapped [ERC-20](./eip-20.md) token trading, this value-amplification framework revolutionizes the asset transaction industry by incorporating identities into the time and sales (T&S) data. This inclusive feature provides a comprehensive view of each transaction, adding a new dimension to trading. + +## 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 following is an extension of the [ERC-721](./eip-721.md) standard. + +[ERC-721](./eip-721.md)-compliant contracts MAY implement this EIP for rewards to provide a standard method of rewarding future buyers and previous owners with realized profits in the future. + +Implementers of this standard MUST have all of the following functions: + +```solidity + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/* + * + * @dev Interface for the Future Rewards Token Standard. + * + * A standardized way to receive future rewards for non-fungible tokens (NFTs.) + * + */ +interface IERC5173 is IERC165 { + + event FRClaimed(address indexed account, uint256 indexed amount); + + event FRDistributed(uint256 indexed tokenId, uint256 indexed soldPrice, uint256 indexed allocatedFR); + + event Listed(uint256 indexed tokenId, uint256 indexed salePrice); + + event Unlisted(uint256 indexed tokenId); + + event Bought(uint256 indexed tokenId, uint256 indexed salePrice); + + function list(uint256 tokenId, uint256 salePrice) external; + + function unlist(uint256 tokenId) external; + + function buy(uint256 tokenId) payable external; + + function releaseFR(address payable account) external; + + function retrieveFRInfo(uint256 tokenId) external returns(uint8, uint256, uint256, uint256, uint256, address[] memory); + + function retrieveAllottedFR(address account) external returns(uint256); + + function retrieveListInfo(uint256 tokenId) external returns(uint256, address, bool); + +} + +``` + +An nFR contract MUST implement and update for each Token ID. The data in the `FRInfo` struct MAY either be stored wholly in a single mapping, or MAY be broken down into several mappings. The struct MUST either be exposed in a public mapping or mappings, or MUST have public functions that access the private data. This is for client-side data fetching and verification. + +```solidity + +struct FRInfo { + uint8 numGenerations; // Number of generations corresponding to that Token ID + uint256 percentOfProfit; // Percent of profit allocated for FR, scaled by 1e18 + uint256 successiveRatio; // The common ratio of successive in the geometric sequence, used for distribution calculation + uint256 lastSoldPrice; // Last sale price in ETH mantissa + uint256 ownerAmount; // Amount of owners the Token ID has seen + address[] addressesInFR; // The addresses currently in the FR cycle +} + +struct ListInfo { + uint256 salePrice; // ETH mantissa of the listed selling price + address lister; // Owner/Lister of the Token + bool isListed; // Boolean indicating whether the Token is listed or not +} + +``` + +Additionally, an nFR smart contract MUST store the corresponding `ListInfo` for each Token ID in a mapping. A method to retrieve a Token ID’s corresponding `ListInfo` MUST also be accessible publicly. + +An nFR smart contract MUST also store and update the amount of Ether allocated to a specific address using the `_allotedFR` mapping. The `_allottedFR` mapping MUST either be public or have a function to fetch the FR payment allotted to a specific address. + +### Percent Fixed Point + +The `allocatedFR` MUST be calculated using a percentage fixed point with a scaling factor of 1e18 (X/1e18) - such as "5e16" - for 5%. This is REQUIRED to maintain uniformity across the standard. The max and min values would be - 1e18 - 1. + +### Default FR Info + +A default `FRInfo` MUST be stored in order to be backward compatible with [ERC-721](./eip-721.md) mint functions. It MAY also have a function to update the `FRInfo`, assuming it has not been hard-coded. + +### ERC-721 Overrides + +An nFR-compliant smart contract MUST override the [ERC-721](./eip-721.md) `_mint`, `_transfer`, and `_burn` functions. When overriding the `_mint` function, a default FR model is REQUIRED to be established if the mint is to succeed when calling the [ERC-721](./eip-721.md) `_mint` function and not the nFR `_mint` function. It is also to update the owner amount and directly add the recipient address to the FR cycle. When overriding the `_transfer` function, the smart contract SHALL consider the NFT as sold for 0 ETH, and update the state accordingly after a successful transfer. This is to prevent FR circumvention. Additionally, the `_transfer` function SHALL prevent the caller from transferring the token to themselves or an address that is already in the FR sliding window, this can be done through a require statement that ensures that the sender or an address in the FR sliding window is not the recipient, otherwise, it’d be possible to fill up the FR sequence with one’s own address or duplicate addresses. Finally, when overriding the `_burn` function, the smart contract SHALL delete the `FRInfo` and `ListInfo` corresponding to that Token ID after a successful burn. + +Additionally, the [ERC-721](./eip-721.md) `_checkOnERC721Received` function MAY be explicitly called after mints and transfers if the smart contract aims to have safe transfers and mints. + +### Safe Transfers + +If the wallet/broker/auction application will accept safe transfers, then it MUST implement the [ERC-721](./eip-721.md) wallet interface. + +### Listing, Unlisting, and Buying + +The `list`, `unlist`, and `buy` functions MUST be implemented, as they provide the capability to sell a token. + +```solidity +function list(uint256 tokenId, uint256 salePrice) public virtual override { + //... +} + + +function unlist(uint256 tokenId) public virtual override { + //... +} + +function buy(uint256 tokenId) public virtual override payable { + //... +} + +``` + +The `list` function accepts a `tokenId` and a `salePrice` and updates the corresponding `ListInfo` for that given `tokenId` after ensuring that the `msg.sender` is either approved or the owner of the token. The `list` function SHOULD emit the `Listed` event. The function signifies that the token is listed and at what price it is listed for. + +The `unlist` function accepts a `tokenId` and it deletes the corresponding `ListInfo` after the owner verifications have been met. The `unlist` function SHOULD emit the `Unlisted` event. + +The `buy` function accepts a `tokenId` and MUST be payable. It MUST verify that the `msg.value` matches the token’s `salePrice` and that the token is listed, before proceeding and calling the FR `_transferFrom` function. The function MUST also verify that the buyer is not already in the FR sliding window. This is to ensure the values are valid and will also allow for the necessary FR to be held in the contract. The `buy` function SHOULD emit the `Bought` event. + + +### Future Rewards `_transferFrom` Function + +The FR `_transferFrom` function MUST be called by all nFR-supporting smart contracts, though the accommodations for non-nFR-supporting contracts MAY also be implemented to ensure backwards compatibility. + +```solidity + +function transferFrom(address from, address to, uint256 tokenId, uint256 soldPrice) public virtual override payable { + //... +} + +``` + +Based on the stored `lastSoldPrice`, the smart contract will determine whether the sale was profitable after calling the [ERC-721](./eip-721.md) transfer function and transferring the NFT. If it was not profitable, the smart contract SHALL update the last sold price for the corresponding Token ID, increment the owner amount, shift the generations, and transfer all of the `msg.value` to the `lister` depending on the implementation. Otherwise, if the transaction was profitable, the smart contract SHALL call the `_distributeFR` function, then update the `lastSoldPrice`, increment the owner amount, and finally shift generations. The `_distributeFR` function or the FR `_transferFrom` MUST return the difference between the allocated FR that is to be distributed amongst the `_addressesInFR` and the `msg.value` to the `lister`. Once the operations have completed, the function MUST clear the corresponding `ListInfo`. Similarly to the `_transfer` override, the FR `_transferFrom` SHALL ensure that the recipient is not the sender of the token or an address in the FR sliding window. + +### Future Rewards Calculation + +Marketplaces that support this standard MAY implement various methods of calculating or transferring Future Rewards to the previous owners. + +```solidity + +function _calculateFR(uint256 totalProfit, uint256 buyerReward, uint256 successiveRatio, uint256 ownerAmount, uint256 windowSize) pure internal virtual returns(uint256[] memory) { + //... +} + +``` + +In this example (*Figure 1*), a seller is REQUIRED to share a portion of their net profit with 10 previous holders of the token. Future Rewards will also be paid to the same seller as the value of the token increases from up to 10 subsequent owners. + +When an owner loses money during their holding period, they MUST NOT be obligated to share Future Rewards distributions, since there is no profit to share. However, he SHALL still receive a share of Future Rewards distributions from future generations of owners, if they are profitable. + +![Figure 1: Geometric sequence distribution](../assets/eip-5173/Total_FR_Payout_Distribution-geo.png) + +*Figure 1: Geometric sequence distribution* + +The buyers/owners receive a portion ( r ) of the realized profit (P ) from an NFT transaction. The remaining proceeds go to the seller. + +As a result of defining a sliding window mechanism ( n ), we can determine which previous owners will receive distributions. The owners are arranged in a queue, starting with the earliest owner and ending with the owner immediately before the current owner (the Last Generation). The First Generation is the last of the next n generations. There is a fixed-size profit distribution window from the First Generation to the Last Generation. + +The profit distribution SHALL be only available to previous owners who fall within the window. + +In this example, there SHALL be a portion of the proceeds awarded to the Last Generation owner (the owner immediately prior to the current seller) based on the geometric sequence in which profits are distributed. The larger portion of the proceeds SHALL go to the Mid-Gen owners, the earlier the greater, until the last eligible owner is determined by the sliding window, the First Generation. Owners who purchase earlier SHALL receive a greater reward, with first-generation owners receiving the greatest reward. + +### Future Rewards Distribution + +![Figure 2: NFT Owners' Future Rewards (nFR)](../assets/eip-5173/nFR_Standard_Outline.jpeg) + +*Figure 2: NFT Owners' Future Rewards (nFR)* + +*Figure 2* illustrates an example of a five-generation Future Rewards Distribution program based on an owner's realized profit. + +```solidity + +function _distributeFR(uint256 tokenId, uint256 soldPrice) internal virtual { + //... + + emit FRDistributed(tokenId, soldPrice, allocatedFR); + } + +``` + +The `_distributeFR` function MUST be called in the FR `_transferFrom` function if there is a profitable sale. The function SHALL determine the addresses eligible for FR, which would essentially be, excluding the last address in `addressesInFR` in order to prevent any address from paying itself. If the function determines there are no addresses eligible, i.e., it is the first sale, then it SHALL either `return 0` if `_transferFrom` is handling FR payment or send `msg.value` to the `lister`. The function SHALL calculate the difference between the current sale price and the `lastSoldPrice`, then it SHALL call the `_calculateFR` function to receive the proper distribution of FR. Then it SHALL distribute the FR accordingly, making order adjustments as necessary. Then, the contract SHALL calculate the total amount of FR that was distributed (`allocatedFR`), in order to return the difference of the `soldPrice` and `allocatedFR` to the `lister`. Finally, it SHALL emit the `FRDistributed` event. Additionally, the function MAY return the allocated FR, which would be received by the FR `_transferFrom` function, if the `_transferFrom` function is sending the `allocatedFR` to the `lister`. + +### Future Rewards Claiming + +The future Rewards payments SHOULD utilize a pull-payment model, similar to that demonstrated by OpenZeppelin with their PaymentSplitter contract. The event FRClaimed would be triggered after a successful claim has been made. + +```solidity + +function releaseFR(address payable account) public virtual override { + //... +} + +``` + +### Owner Generation Shifting + +The `_shiftGenerations` function MUST be called regardless of whether the sale was profitable or not. As a result, it will be called in the `_transfer` [ERC-721](./eip-721.md) override function and the FR `transferFrom` function. The function SHALL remove the oldest account from the corresponding `_addressesInFR` array. This calculation will take into account the current length of the array versus the total number of generations for a given token ID. + +## Rationale + +### Fixed Percentage to 10^18 + +Considering Fixed-Point Arithmetic is to be enforced, it is logical to have 1e18 represent 100% and 1e16 represent 1% for Fixed-Point operations. This method of handling percents is also commonly seen in many Solidity libraries for Fixed-Point operations. + +### Emitting Event for Payment + +Since each NFT contract is independent, and while a marketplace contract can emit events when an item is sold, choosing to emit an event for payment is important. As the royalty and FR recipient may not be aware of/watching for a secondary sale of their NFT, they would never know that they received a payment except that their ETH wallet has been increased randomly. + +The recipient of the secondary sale will therefore be able to verify that the payment has been received by calling the parent contract of the NFT being sold, as implemented in [ERC-2981](./eip-2981.md). + +### Number of Generations of All Owners ( n ) vs Number of Generations of Only Profitable Owners + +It is the number of generations of all owners, not just those who are profitable, that determines the number of owners from which the subsequent owners' profits will be shared, see *Figure 3*. As part of the effort to discourage "ownership hoarding," Future Rewards distributions will not be made to the current owner/purchaser if all the owners lose money holding the NFT. Further information can be found under Security Considerations. + +![Figure 3: Losing owners](../assets/eip-5173/Losing_owners.jpeg) + +*Figure 3: Losing owners* + +### Single vs Multigenerations + +In a single generation reward, the new buyer/owner receives a share of the next single generation's realized profit only. In a multigenerational reward system, buyers will have future rewards years after their purchase. The NFT should have a long-term growth potential and a substantial dividend payout would be possible in this case. + +We propose that the marketplace operator can choose between a single generational distribution system and a multigenerational distribution system. + +### Direct FR Payout by the Seller vs Smart Contract-managed Payout + +FR payouts directly derived from the sale proceeds are immediate and final. As part of the fraud detection detailed later in the Security Considerations section, we selected a method in which the smart contract calculates all the FR amounts for each generation of previous owners, and handles payout according to other criteria set by the marketplace, such as reduced or delayed payments for wallet addresses with low scores, or a series of consecutive orders detected using a time-heuristic analysis. + +### Equal vs Linear Reward Distributions + +#### Equal FR Payout + +![Figure 4: Equal, linear reward distribution](../assets/eip-5173/Total_FR_Payout_Distribution-flat.png?raw=true) + +*Figure 4: Equal, linear reward distribution* + +FR distributions from the realization of profits by later owners are distributed equally to all eligible owners (*Figure 4*). The exponential reward curve, however, may be more desirable, as it gives a slightly larger share to the newest buyer. Additionally, this distribution gives the earliest generations the largest portions as their FR distributions near the end, so they receive higher rewards for their early involvement, but the distribution is not nearly as extreme as one based on arithmetic sequences (*Figure 5*). + +This system does not discriminate against any buyer because each buyer will go through the same distribution curve. + +#### Straight line arithmetic sequence FR payout + +![Figure 5: Arithmetic sequence distribution](../assets/eip-5173/Arithmetic_Sequence_FR_Payout_Distribution.png?raw=true) + +*Figure 5: Arithmetic sequence distribution* + +The profit is distributed according to the arithmetic sequence, which is 1, 2, 3, ... and so on. The first owner will receive 1 portion, the second owner will receive 2 portions, the third owner will receive 3 portions, etc. + +## Backwards Compatibility + +This proposal is fully compatible with current [ERC-721](./eip-721.md) standards and [ERC-2981](./eip-2981.md). It can also be easily adapted to work with [ERC-1155](./eip-1155.md). + +## Test Cases + +[This contract](../assets/eip-5173/Implementation/nFRImplementation.sol) contains the reference implementation for this proposal. + +[Here is a visualization of the test case](../assets/eip-5173/animate-1920x1080-1750-frames.gif?raw=true). + +As a result of implementing ERC-5173, a new project has been launched called untrading.org. + +## Reference Implementation + +This implementation uses OpenZeppelin contracts and the PRB Math library created by Paul R Berg for fixed-point arithmetic. It demonstrates the interface for the nFR standard, an nFR standard-compliant extension, and an [ERC-721](./eip-721.md) implementation using the extension. + +The code for the reference implementation is [here](../assets/eip-5173/Implementation/nFRImplementation.sol). + +### Distribution of NFT Royalties to Artists and Creators + +We agree that artists’ royalties should be uniform and on-chain. We support [ERC-2981](./eip-2981.md) NFT royalty Standard proposal. + +All platforms can support royalty rewards for the same NFT based on on-chain parameters and functions: + +- No profit, no profit sharing, no cost; +- The question of "who owned it" is often crucial to the provenance and value of a collectible; +- The previous owner should be re-compensated for their ownership; +- And the buyer/owner incentive in FR eliminates any motive to circumvent the royalty payout schemes; + +### Distribution of NFT Owners’ Future Rewards (FRs) + +#### Future Rewards calculation + +Any realized profits (P) when an NFT is sold are distributed among the buyers/owners. The previous owners will take a fixed portion of the profit (P), and this portion is called Future Rewards (FRs). The seller takes the rest of the profits. + +We define a sliding window mechanism to decide which previous owners will be involved in the profit distribution. Let's imagine the owners as a queue starting from the first hand owner to the current owner. The profit distribution window starts from the previous owner immediately to the current owner and extends towards the first owner, and the size of the windows is fixed. Only previous owners located inside the window will join the profit distribution. + +![Future Rewards calculation formula](../assets/eip-5173/nFR_distribution_formula.png?raw=true) + +In this equation: + +- P is the total profit, the difference between the selling price minus the buying price; +- r is buyer reward ratio of the total P; +- g is the common ratio of successive in the geometric sequence; +- n is the actual number of owners eligible and participating in the future rewards sharing. To calculate n, we have n = min(m, w), where m is the current number of owners for a token, and w is the window size of the profit distribution sliding window algorithm + +#### Converting into Code + +```solidity + +pragma solidity ^0.8.0; +//... + +/* Assumes usage of a Fixed Point Arithmetic library (prb-math) for both int256 and uint256, and OpenZeppelin Math utils for Math.min. */ +function _calculateFR(uint256 P, uint256 r, uint256 g, uint256 m, uint256 w) pure internal virtual returns(uint256[] memory) { + uint256 n = Math.min(m, w); + uint256[] memory FR = new uint256[](n); + + for (uint256 i = 1; i < n + 1; i++) { + uint256 pi = 0; + + if (successiveRatio != 1e18) { + int256 v1 = 1e18 - int256(g).powu(n); + int256 v2 = int256(g).powu(i - 1); + int256 v3 = int256(P).mul(int256(r)); + int256 v4 = v3.mul(1e18 - int256(g)); + pi = uint256(v4 * v2 / v1); + } else { + pi = P.mul(r).div(n); + } + + FR[i - 1] = pi; + } + + return FR; +} + +``` + +The complete implementation code can be found [here](../assets/eip-5173/Implementation/nFRImplementation.sol). + +## Security Considerations + +### Payment Attacks + +As this ERC introduces royalty and realized profit rewards collection, distribution, and payouts to the ERC-721 standard, the attack vectors increase. As discussed by Andreas Freund regarding mitigations to phishing attacks, we recommend reentrancy protection for all payment functions to reduce the most significant attack vectors for payments and payouts. + +### Royalty Circumventing + +Many methods are being used to avoid paying royalties to creators under the current [ERC-721](./eip-721.md) standard. Through an under-the-table transaction, the new buyer's cost basis will be reduced to zero, increasing their FR liability to the full selling price. Everyone, either the buyer or seller, would pay a portion of the previous owner's net realized profits ( P x r ). Acting in his or her own interests, the buyer rejects any loyalty circumventing proposal. + +### FR Hoarding through Wash Sales + +Quantexa blog and beincrypto articles have reported widespread wash trading on all unregulated cryptocurrency trading platforms and NFT marketplaces. The use of wash trading by dishonest actors can lead to an unfair advantage, as well as inflated prices and money laundering. When a single entity becomes multiple generations of owners to accumulate more rewards in the future, the validity of the system is undermined. + +#### Wash trading by users + +Using a different wallet address, an attacker can "sell" the NFT to themselves at a loss. It is possible to repeat this process n times in order to maximize their share of the subsequent FR distributions (*Figure 6*). A wallet ranking score can partially alleviate this problem. It is evident that a brand new wallet is a red flag, and the marketplace may withhold FR distribution from it if it has a short transaction history (i.e. fewer than a certain number of transactions). + +We do not want a large portion of future rewards to go to a small number of wash traders. Making such practices less profitable is one way to discourage wash trading and award hoarding. It can be partially mitigated, for example, by implementing a wallet-score and holding period-based incentive system. The rewards for both parties are reduced if a new wallet is used or if a holding period is less than a certain period. + +![Figure 6: Same owner using different wallets](../assets/eip-5173/Same_owner_using_different_wallets.jpeg) + +*Figure 6: Same owner using different wallets* + +#### Wash trading by the marketplace operator + +However, the biggest offender appears to be the marketplace, which engages heavily in wash trading, or simply does not care about it, according to Decrypt. The authors have personally experienced this phenomenon. A senior executive of a top-5 cryptocurrency exchange boasted during a mid-night drinking session in 2018, that they had "brushed" (wash-traded) certain newly listed tokens, which they called "marketmaking." The exchange is still ranked among the top five crypto exchanges today. + +Many of these companies engage in wash trading on their own or collude with certain users, and royalties and FR payments are reimbursed under the table. It is crucial that all exchanges have robust features to prevent self-trading. Users should be able to observe watchers transparently. Marketplaces should provide their customers with free access to an on-chain transaction monitoring service like Chainalysis Reactor. + +### Long/Cyclical FR-Entitled Owner Generations + +In most cases, malicious actors will create excessively long or cyclical Future Rewards Owner Generations that will result in applications that attempt to distribute FR or shift generations running out of gas and not functioning. Therefore, clients are responsible for verifying that the contract with which they interact has an appropriate number of generations, so that looping over will not deplete the gas. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5185.md b/EIPS/eip-5185.md new file mode 100644 index 00000000000000..000784dbf2696a --- /dev/null +++ b/EIPS/eip-5185.md @@ -0,0 +1,232 @@ +--- +eip: 5185 +title: NFT Updatable Metadata Extension +description: An interface extension for ERC-721/ERC-1155 controlled metadata updates +author: Christophe Le Bars (@clbrge) +discussions-to: https://ethereum-magicians.org/t/erc-721-erc-1155-updatable-metadata-extension/9077 +status: Stagnant +type: Standards Track +category: ERC +requires: 721, 1155 +created: 2022-06-27 +--- + +## Abstract + +This specification defines a standard way to allow controlled NFTs' metadata updates along predefined formulas. Updates of the original metadata are restricted and defined by a set of recipes and the sequence and results of these recipes are deterministic and fully verifiable with on-chain metadata updates event. The proposal depends on and extends the [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md). + +## Motivation + +Storing voluminous NFT metadata on-chain is often neither practical nor cost-efficient. + +Storing NFT metadata off-chain on distributed file systems like IPFS can answer some needs of verifiable correlation and permanence between an NFT tokenId and its metadata but updates come at the cost of being all or nothing (aka changing the `tokenURI`). Bespoke solutions can be easily developed for a specific NFT smart contract but a common specification is necessary for NFT marketplaces and third parties tools to understand and verify these metadata updates. + +This ERC allows the original JSON metadata to be modified step by step along a set of predefined JSON transformation formulas. Depending on NFT use-cases, the transformation formulas can be more or less restrictive. + +As examples, an NFT representing a house could only allow append-only updates to the list of successive owners, and a game using NFT characters could let some attributes change from time to time (e.g. health, experience, level, etc) while some other would be guaranteed to never change (e.g. physicals traits etc). + +This standard extension is compatible with NFTs bridged between Ethereum and L2 networks and allows efficient caching solutions. + +## 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 **metadata updates extension** is OPTIONAL for [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) contracts. + +```solidity +/// @title ERC-721/ERC-1155 Updatable Metadata Extension +interface IERC5185UpdatableMetadata { + /// @notice A distinct Uniform Resource Identifier (URI) for a set of updates + /// @dev This event emits an URI (defined in RFC 3986) of a set of metadata updates. + /// The URI should point to a JSON file that conforms to the "NFT Metadata Updates JSON Schema" + /// Third-party platforms such as NFT marketplace can deterministically calculate the latest + /// metadata for all tokens using these events by applying them in sequence for each token. + event MetadataUpdates(string URI); +} +``` + +The original metadata SHOULD conform to the "ERC-5185 Updatable Metadata JSON Schema" which is a compatible extension of the "ERC-721 Metadata JSON Schema" defined in ERC-721. + +"ERC-5185 Updatable Metadata JSON Schema" : + +```json +{ + "title": "Asset Updatable Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "updatable": { + "type": "object", + "required": ["engine", "recipes"], + "properties": { + "engine": { + "type": "string", + "description": "Non ambiguous transformation method/language (with version) to process updates along recipes defined below" + }, + "schema": { + "type": "object", + "description": "if present, a JSON Schema that all sequential post transformation updated metadata need to conform. If a transformed JSON does not conform, the update should be considered voided." + }, + "recipes": { + "type": "object", + "description": "A catalog of all possibles recipes identified by their keys", + "patternProperties": { + ".*": { + "type": "object", + "description": "The key of this object is used to select which recipe to apply for each update", + "required": ["eval"], + "properties": { + "eval": { + "type": "string", + "description": "The evaluation formula to transform the last JSON metadata using the engine above (can take arguments)" + } + } + } + } + } + } + } + } +} +``` + +"NFT Metadata Updates JSON Schema" : + +```json +{ + "title": "Metadata Updates JSON Schema", + "type": "object", + "properties": { + "updates": { + "type": "array", + "description": "A list of updates to apply sequentially to calculate updated metadata", + "items": { "$ref": "#/$defs/update" }, + "$defs": { + "update": { + "type": "object", + "required": ["tokenId", "recipeKey"], + "properties": { + "tokenId": { + "type": "string", + "description": "The tokenId for which the update recipe should apply" + }, + "recipeKey": { + "type": "string", + "description": "recipeKey to use to get the JSON transformation expression in current metadata" + }, + "args": { + "type": "string", + "description": "arguments to pass to the JSON transformation" + } + } + } + } + } + } +} +``` + +### Engines + +Only one engine is currently defined in this extension proposal. + +If the engine in the original metadata is "jsonata@1.8.*", updated metadata is calculated starting from original metadata and applying each update sequentially (all updates which are present in all the URIs emitted by the event `MetadataUpdates` for which tokenId matches). + +For each step, the next metadata is obtained by the javascript calculation (or compatible jsonata implementation in other language) : + +```js +const nextMetadata = jsonata(evalString).evaluate(previousMetadata, args) +``` + +With `evalString` is found with `recipeKey` in the original metadata recipes list. + +If the key is not present in the original metadata list, `previousMetadata` is kept as the valid updated metadata. + +If the evaluation throws any errors, `previousMetadata` is kept as the valid updated metadata. + +If a validation Schema JSON has been defined and the result JSON `nextMetadata` does not conform, that update is not valid and `previousMetadata` is kept as the valid updated metadata. + +## Rationale + +There have been numerous interesting uses of [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) smart contracts that associate for each token essential and significant metadata. While some projects (e.g. EtherOrcs) have experimented successfully to manage these metadata on-chain, that experimental solution will always be limited by the cost and speed of generating and storing JSON on-chain. Symmetrically, while storing the JSON metadata at URI endpoint controlled by traditional servers permit limitless updates the the metadata for each NFT, it is somehow defeating in many uses cases, the whole purpose of using a trustless blockchain to manage NFT: indeed users may want or demand more permanence and immutability from the metadata associated with their NFT. + +Most use cases have chosen intermediate solutions like IPFS or arweave to provide some permanence or partial/full immutability of metadata. This is a good solution when an NFT represents a static asset whose characteristics are by nature permanent and immutable (like in the art world) but less so with other use cases like gaming or NFT representing a deed or title. Distinguishable assets in a game often should be allowed to evolve and change over time in a controlled way and titles need to record real life changes. + +The advantages of this standard is precisely to allow these types of controlled transformations over time of each NFT metadata by applying sequential transformations starting with the original metadata and using formulas themselves defined in the original metadata. + +The original metadata for a given NFT is always defined as the JSON pointed by the result of `tokenURI` for [EIP-721](./eip-721.md) and function `uri` for [EIP-1155](./eip-1155.md). + +The on-chain log trace of updates guarantee that anyone can recalculate and verify independently the current updated metadata starting from the original metadata. The fact that the calculation is deterministic allows easy caching of intermediate transformations and the efficient processing of new updates using these caches. + +The number of updates defined by each event is to be determined by the smart contract logic and use case, but it can easily scale to thousands or millions of updates per event. The function(s) that should emit `MetadataUpdates` and the frequency of these on-chain updates is left at the discretion of this standard implementation. + +The proposal is extremely gas efficient, since gas costs are only proportional to the frequency of committing changes. Many changes for many tokens can be batched in one transaction for the cost of only one `emit`. + +## Reference Implementation + +### Transformation engines + +We have been experimenting with this generic Metadata update proposal using the JSONata transformation language. + +Here is a very simple example of a NFT metadata for an imaginary 'little monster' game : + +```json +{ + "name": "Monster 1", + "description": "Little monsters you can play with.", + "attributes": [ + { "trait_type": "Level", "value": 0 }, + { "trait_type": "Stamina", "value": 100 } + ], + "updatable": { + "engine": "jsonata@1.8.*", + "recipes": { + "levelUp": { + "eval": "$ ~> | attributes[trait_type='Level'] | {'value': value + 1} |" + }, + "updateDescription": { + "eval": "$ ~> | $ | {'description': $newDescription} |" + } + } + } +} + ``` + +This updatable metadata can only be updated to increment by one the trait attribute "Level". + +An example JSON updates metadata would be : +```json +{ + "updates": [ + {"tokenId":"1","action":"levelUp"}, + {"tokenId":"2","action":"levelUp"}, + {"tokenId":"1","action":"updateDescription","args":{"newDescription":"Now I'm a big monster"}}, + {"tokenId":"1","action":"levelUp"}, + {"tokenId":"3","action":"levelUp"} + ] +} + ``` + +## Security Considerations + +A malicious recipe in the original metadata might be constructed as a DDoS vector for third parties marketplaces and tools that calculate NFT updated JSON metadata. They are encouraged to properly encapsulate software in charge of these calculations and put limits for the engine updates processing. + +Smart contracts should be careful and conscious of using this extension and still allow the metadata URI to be updated in some contexts (by not having the same URI returned by `tokenURI` or `uri` for a given tokenId over time). They need to take into account if previous changes could have been already broadcasted for that NFT by the contract, if these changes are compatible with the new "original metadata" and what semantic they decide to associate by combining these two kinds of "updates". + +## Backwards Compatibility + +The proposal is fully compatible with both [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md). Third-party applications that don't support this EIP will still be able to use the original metadata for each NFT. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5187.md b/EIPS/eip-5187.md new file mode 100644 index 00000000000000..bb1ff12a15deeb --- /dev/null +++ b/EIPS/eip-5187.md @@ -0,0 +1,148 @@ +--- +eip: 5187 +title: Extend EIP-1155 with rentable usage rights +description: Separate ownership and usage rights of EIP-1155 to allow users to use NFTs for an allotted time and return them to owners after expiration. +author: DerivStudio (@DerivStudio) +discussions-to: https://ethereum-magicians.org/t/eip-draft-extending-erc1155-with-rentable-usage-rights/9553/4 +status: Draft +type: Standards Track +category: ERC +created: 2022-04-17 +requires: 165, 1155 +--- + +## Abstract + +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 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 0x6938e358. + interface IRental /* is IERC165,IERC1155 */ { + /** + * @notice This emits when user rent NFT + * - `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 expire); + + /** + * 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 + * - 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 expire) external; + + /** + * @notice Function to take back usage rights after the end of the tenancy + * - user The address to rent the NFT's usage rights + * - tokenId The id of the current token + **/ + function takeBack(address user,uint256 tokenId) external; + + /** + * @notice Return the NFT to the address of the NFT property right owner. + **/ + function propertyRightOf(uint256 id) external view returns (address); + + /** + * @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 + +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-5189.md b/EIPS/eip-5189.md new file mode 100644 index 00000000000000..786275fc930984 --- /dev/null +++ b/EIPS/eip-5189.md @@ -0,0 +1,225 @@ +--- +eip: 5189 +title: Account Abstraction via Endorsed Operations +description: An account abstraction proposal that avoids protocol changes while maintaining compatibility with existing smart contract wallets. +author: Agustín Aguilar (@agusx1211), Philippe Castonguay (@phabc) +discussions-to: https://ethereum-magicians.org/t/erc-account-abstraction-via-endorsed-operations/9799 +type: Standards Track +category: ERC +status: Stagnant +created: 2022-06-29 +--- + +## Abstract +This EIP proposes a form of account abstraction that ensures compatibility with existing smart contract wallets and provides flexibility for alternative designs while avoiding introducing changes to the consensus layer. Instead of defining a strict structure for meta-transactions, this proposal introduces the figure of `endorser` contracts. These smart contract instances are tasked with determining the quality of the submitted meta-transactions, thus safely helping bundlers determine if a meta-transaction should be kept in the mempool or not. Developers that intend to make their smart contract wallet compatible with this EIP must create and deploy an instance of an `endorser`; this instance must be seeded with a small amount of ETH to be burnt that incentivizes its good behavior. + +## Motivation +This account abstraction proposal aims to implement a generalized system for executing meta-transactions while maintaining the following goals: + +* **Achieve the primary goal of account abstraction:** allow users to use smart contract wallets containing arbitrary verification and execution logic instead of EOAs as their primary account. +* **Decentralization:** +* * Allow any bundler to participate in the process of including meta-transactions. +* * Work with all activity happening over a public mempool without having to concentrate transactions on centralized relayers. +* * Define structures that help maintain a healthy mempool without risking its participants from getting flooded with invalid or malicious payloads. +* * Avoid trust assumptions between bundlers, developers, and wallets. +* **Support existing smart contract wallet implementations:** Work with all the smart contract wallets already deployed and active while avoiding forcing each wallet instance to be manually upgraded. +* **Provide an unrestrictive framework:** Smart contract wallets are very different in design, limitations, and capabilities from one another; the proposal is designed to accommodate almost all possible variations. +* **No overhead:** Smart contract wallets already have a cost overhead compared to EOA alternatives, the proposal does not worsen the current situation. +* **Support other use cases:** +* * Privacy-preserving applications. +* * Atomic multi-operations (similar to EIP-3074). +* * Payment of transaction fees using ERC-20 tokens. +* * Scheduled execution of smart contracts without any user input. +* * Applications that require a generalistic relayer. + +## Specification +To avoid Ethereum consensus changes, we do not attempt to create new transaction types for account-abstracted transactions. Instead, meta-transactions are packed up in a struct called `Operation`, operations are structs composed by the following fields: + +| Field | Type | Description | +|-------------------|---------|--------------------------------------------------------------------------------------------------------| +| entrypoint | address | contract address that must be called with `callData` to execute the `operation`. | +| callData | bytes | data that must be passed to the `entrypoint` call to execute the `operation`. | +| gasLimit | uint64 | minimum gasLimit that must be passed when executing the `operation`. | +| endorser | address | address of the endorser contract that should be used to validate the `operation`. | +| endorserGasLimit | uint64 | amount of gas that should be passed to the endorser when validating the `operation`. | +| maxFeePerGas | uint256 | max amount of basefee that the `operation` execution is expected to pay, (similar to EIP-1559 `max_fee_per_gas`) | +| priorityFeePerGas | uint256 | fixed amount of fees that the `operation` execution is expected to pay to the bundler (similar to EIP-1559 `max_priority_fee_per_gas`). | + +These `Operation` objects can be sent to a dedicated operations mempool. A specialized class of actors called bundlers (either miners running special-purpose code, or just users that can relay transactions to miners) listen for operations on the mempool and execute these transactions. + +Transactions are executed by calling the `entrypoint` with the provided `callData`. The `entrypoint` can be any contract, but most commonly it will be the wallet contract itself, alternatively it can be an intermediary utility that deploys the wallet and then performs the transaction. + +#### Endorser functionality +Mempool participants need to be able to able to filter "good operations" (operations that pay the bundler the defined fee) from "bad operations" (operations that either miss payment or revert altogether). + +This categorization is facilitated by the `endorser`; the endorser must be a deployed smart contract that implements the following interface: + +```solidity +interface Endorser { + struct Dependency { + address addr; + bool balance; + bool code; + bool nonce; + bytes32[] slots; + } + + function isOperationReady( + address _entrypoint, + bytes calldata _data, + uint256 _gasLimit, + uint256 _maxFeePerGas, + uint256 _maxPriorityFeePerGas + ) external view returns ( + bool readiness, + Dependency[] memory dependencies + ); +} +``` + +It should also be registered in the `EndorserRegistry` with a minimum amount of burned ETH (Mempool operators are free to accept operations from endorsers without any burn, but they would increase their risk exposing themselves to denial of service attacks). + +When the `isOperationReady` method is called, the endorser must return this information: + +* **readiness:** when returning`true`, it means the transaction WILL be executed correctly and the bundler WILL be paid the offered gas fees (even if the underlying intent of the operation fails). +* **dependencies:** a comprehensive list of addresses and storage slots that must be monitored; any state change in these dependencies MUST trigger a re-evaluation of the operation's readiness. + +The information provided by the endorser helps the mempool operator maintain a pool of "good" meta-transactions that behave correctly; it DOES NOT guarantee that such transactions will be able to be executed correctly. Bundlers must always simulate the result of the execution before including a transaction in a block. + +#### Dependencies +| Field | Type | Description | +| -------- | -------- | -------- | +| addr | address | Contract address of the dependencies entry *(only one entry per address should be allowed)*. | +| balance | bool | `true` if the balance of `addr` should be considered a dependency of the `operation`. | +| code | bool | `true` if the code of `addr` should be considered a dependency of the `operation`. | +| nonce | bool | `true` if the nonce of `addr` should be considered a dependency of the `operation`. | +| slots | bytes32[] | List of all storage slots of `addr` that should be considered dependencies of `operation`. | + +The `endorser` does not need to include all accessed storage slots on the dependencies list, it only needs to include storage slots that after a change may also result in a change of readiness. + +> E.g. A wallet may pay fees using funds stored as WETH. During `isValidOperation()`, the endorser contract may call the `balanceOf` method of the `WETH` contract to determine if the wallet has enough `WETH` balance. Even though the ETH balance of the WETH contract and the code of the WETH contract are being accessed, the endorser only cares about the user's WETH balance for this operation and hence does not include these as dependencies. + +### Misbehavior detection +The `endorser` contracts may behave maliciously or erratically in the following ways: + +* (1) It may consider an operation `ready`, but when the operation is executed it transfers less than the agreed-upon fees to the bundler. +* (2) It may consider an operation `ready`, but when the operation is executed the top-level call fails. +* (3) It may change the status from `ready` to `not-ready` while none of the dependencies register any change. + +The bundler must always discard and re-evaluate the readiness status after a change on any of the dependencies of the `operation`, meaning that only operations considered `ready` are candidates for constructing the next block. + +If, when simulating the final inclusion of the operation, the bundler discovers that it does not result in correct payment (either because the transaction fails, or transferred amount is below the defined fee), then it should proceed to ban the `endorser` for one of the following reasons: + +1) The `endorser` returns `isOperationReady == true` even though the `operation` is not healthy to be included in a block. +2) The `operation` changed readiness status from `true` to `false` while all dependencies remained unchanged. + +After an `endorser` is banned, the mempool operator should drop all `operations` related to such endorser. + +> Notice: The mempool operator could call one last time `isOperationReady` to determine if the `endorser` should be banned because `(1)` or `(2)`, but this step is not strictly necessary since both scenarios lead to the `endoser` being banned. + +### Client behavior upon receiving an operation +When a client receives an `operation`, it must first run some basic sanity checks, namely that: + +* The `endorserGasLimit` is sufficiently low (<= `MAX_ENDORSER_GAS`). +* The endorser (i) is registered and has enough burn (>= `MIN_ENDORSER_BURN`), and (ii) it has not been internally flagged as banned. +* The `gasLimit` is at least the cost of a `CALL` with a non-zero value. +* The `maxFeePerGas` and `priorityPerGas` are above a configurable minimum value the client is willing to accept. +* If another operation exists in the mempool with the exact same dependency set AND the same endorser address, the `maxFeePerGas` and `priorityFeePerGas` of the newly received operation MUST be 12% higher than the one on the mempool to replace it. (Similar with how EOA with same nonce work) + +If the `operation` passes these checks, then the client MUST call `isOperationReady()` on the `endorser`. If the endorser considers the operation ready, then the client MUST add the operation to the mempool. Otherwise, the operation MUST discarded. + +The `endorser` result MUST be invalidated and its readiness be re-evaluated if any of the values of the provided dependencies change. If the operation readiness changes to `false`, the operation MUST be discarded. + +Before including the operation in a block, a last simulation MUST be performed, this time without calling the `endorser`, but by constructing the block and probing the result. All transactions in the block listed **before** the operation must be simulated and the endorser must be queried again there for readiness in-case some dependencies changed. + +If the operation fails during simulation, the endorser must be banned because (i) it returned a bad readiness state or (ii) it changed the operation readiness independently from the dependencies. + +Additional events that must invalidate the readiness are: + +* A transaction or operation modifies the same storage slots (as the dependencies) is queued before the given operation. + +#### Optional rules +Mempool clients could implement additional rules to further protect against maliciously constructed transactions. +* Limit the size of accepted dependencies to `MAX_OPERATION_DEPENDENCIES`, dropping operations that cross the boundary. +* Limit the number of times an operation may trigger a re-evaluation to `MAX_OPERATION_REEVALS`, dropping operations that cross the boundary. +* Limit the number of operations in the mempool that depend on the same dependency slots. + +If these rules are widely adopted, wallet developers should keep usage of dependencies to the lowest possible levels. + +#### Evaluation +To evaluate an `operation`, the client must call the `isOperationReady` method, with a `gasLimit` above or equal to `endorserGasLimit`. + +If the call fails, or the `endorser` returns `ready == false`, then the operation must be dropped from the mempool. + +If the call succeeds and returns `ready == true`, then the operation can be kept in the mempool and used when constructing the next block. The client must keep track of all fields returned as `dependencies`. If any of these register a change, then readiness should be reevaluated. + + +#### After operation inclusion +There is no limit in-place that defines that an operation can only be executed once. + +The bundler MUST NOT drop an `operation` after successfully including such operation in a block, the `operation` must remain in the mempool and a last `isOperationReady` call must be performed. + +If the `endorser` still returns `readiness == true` (after inclusion) then the operation SHOULD be treated as any other healthy operation, and thus it COULD be kept in the mempool. + +### Endorser registry +The endorser registry serves as a place to register the burn of each endorser, anyone can increase the burn of any endorser by calling the `addBurn` function. + +All burn is effectively locked forever; slashing can't be reliably proved on-chain without protocol alterations, so it remains a virtual event on which mempool operators will ignore the deposited ETH. + +#### Implementation +(EXAMPLE) + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.15; + + +contract EndorserRegistry { + event Burned( + address indexed _endorser, + address indexed _sender, + uint256 _new, + uint256 _total + ); + + mapping(address => uint256) public burn; + + function addBurn(address _endorser) external payable returns (uint256) { + uint256 total = burn[_endorser] + msg.value; + burn[_endorser] = total; + + emit Burned(_endorser, msg.sender, msg.value, total); + + return total; + } +} +``` + +## Rationale +The main challenge with a purely smart contract wallet-based account abstraction system is DoS safety: how can a bundler that includes an operation make sure that it will pay fees without executing the entire operation? + +Bundlers could execute the entire operation to determine if it is healthy or not, but this operation may be expensive and complex for the following reasons: + +* The bundler does not have a way to simulate the transaction with a reduced amount of gas; it has to use the whole `gasLimit`, exposing itself to a higher level of griefing. +* The bundler does not have a way to know if a change to the state will affect the operation or not, and thus it has to re-evaluate the operation after every single change. +* The bundler does not have a way to know if a change to the state will invalidate a large portion of the mempool. + +In this proposal, we add the `endorser` as a tool for the bundlers to validate arbitrary operations in a controlled manner, without the bundler having to know any of the inner workings of such operation. + +In effect, we move the responsibility from the wallet to the wallet developer; the developer must code, deploy and burn ETH for the `endorser`; this is a nearly ideal scenario because developers know how their wallet operations work, and thus they can build tools to evaluate these operations efficiently. + +Additionally, the specification is kept as simple as possible as enforcing a highly structured behavior and schema for smart contract wallet transactions may stagnate the adoption of more innovative types of wallets and the adoption of a shared standard among them. + +#### Differences with alternative proposals +1) This proposal does not require monitoring for forbidden opcodes or storage access boundaries. Wallets have complete freedom to use any EVM capabilities during validation and execution. +2) This proposal does not specify any replay protection logic since all existing smart contract wallets already have their own, and designs can vary among them. Nonces can be communicated to the bundler using a `dependency`. +3) This proposal does not specify a pre-deployment logic because it can be handled directly by the entrypoint. +4) This proposal does not require wallets to accept `execution` transactions from a trusted entrypoint contract, reducing overhead and allowing existing wallets to be compatible with the proposal. +5) This proposal does not distinguish between `execution` and `signature` payloads, this distinction remains implementation-specific. + + +## Backwards Compatibility +This EIP does not change he consensus layer, nor does impose changes on existing smart contract wallets, so there are no backwards compatibility issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5192.md b/EIPS/eip-5192.md new file mode 100644 index 00000000000000..30e5d147e25d5d --- /dev/null +++ b/EIPS/eip-5192.md @@ -0,0 +1,71 @@ +--- +eip: 5192 +title: Minimal Soulbound NFTs +description: Minimal interface for soulbinding EIP-721 NFTs +author: Tim Daubenschütz (@TimDaub), Anders (@0xanders) +discussions-to: https://ethereum-magicians.org/t/eip-5192-minimal-soulbound-nfts/9814 +status: Final +type: Standards Track +category: ERC +created: 2022-07-01 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It proposes a minimal interface to make tokens soulbound using the feature detection functionality of [EIP-165](./eip-165.md). A soulbound token is a non-fungible token bound to a single account. + +## Motivation + +The Ethereum community has expressed a need for non-transferrable, non-fungible, and socially-priced tokens similar to World of Warcraft’s soulbound items. But the lack of a token standard leads many developers to simply throw errors upon a user's invocation of transfer functionalities. Over the long term, this will lead to fragmentation and less composability. + +In this document, we outline a minimal addition to [EIP-721](./eip-721.md) that allows wallet implementers to check for a token contract's permanent (non-)transferability using [EIP-165](./eip-165.md). + +## 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 + +A token with a `uint256 tokenId` may be bound to a receiving account with `function locked(...)` returning `true`. In this case, all [EIP-721](./eip-721.md) functions of the contract that transfer the token from one account to another must throw. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC5192 { + /// @notice Emitted when the locking status is changed to locked. + /// @dev If a token is minted and the status is locked, this event should be emitted. + /// @param tokenId The identifier for a token. + event Locked(uint256 tokenId); + + /// @notice Emitted when the locking status is changed to unlocked. + /// @dev If a token is minted and the status is unlocked, this event should be emitted. + /// @param tokenId The identifier for a token. + event Unlocked(uint256 tokenId); + + /// @notice Returns the locking status of an Soulbound Token + /// @dev SBTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param tokenId The identifier for an SBT. + function locked(uint256 tokenId) external view returns (bool); +} +``` + +To aid recognition that an [EIP-721](./eip-721.md) token implements "soulbinding" via this EIP upon calling [EIP-721](./eip-721.md)'s `function supportsInterface(bytes4 interfaceID) external view returns (bool)` with `interfaceID=0xb45a3c0e`, a contract implementing this EIP must return `true`. + +## Rationale + +The above model is the simplest possible path towards a canonical interface for Soulbound tokens. It reflects upon the numerous Soulbound token implementations that simply revert upon transfers. + +## Backwards Compatibility + +This proposal is fully backward compatible with [EIP-721](./eip-721.md). + +## Security Considerations + +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-5202.md b/EIPS/eip-5202.md new file mode 100644 index 00000000000000..b504c4e281a885 --- /dev/null +++ b/EIPS/eip-5202.md @@ -0,0 +1,152 @@ +--- +eip: 5202 +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: Final +type: Standards Track +category: ERC +created: 2022-06-23 +requires: 170 +--- + +## Abstract + +Define a standard for "blueprint" contracts, or contracts which represent initcode that is stored on-chain. + +## Motivation + +To decrease deployer contract size, a useful pattern is to store initcode on chain as a "blueprint" contract, and then use `EXTCODECOPY` to copy the initcode into memory, followed by a call to `CREATE` or `CREATE2`. However, this comes with the following problems: + +- It is hard for external tools and indexers to detect if a contract is a "regular" runtime contract or a "blueprint" contract. Heuristically searching for patterns in bytecode to determine if it is initcode poses maintenance and correctness problems. +- Storing initcode byte-for-byte on-chain is a correctness and security problem. Since the EVM does not have a native way to distinguish between executable code and other types of code, unless the initcode explicitly implements ACL rules, *anybody* can call such a "blueprint" contract and execute the initcode directly as ordinary runtime code. This is particularly problematic if the initcode stored by the blueprint contract has side effects such as writing to storage or calling external contracts. If the initcode stored by the blueprint contract executes a `SELFDESTRUCT` opcode, the blueprint contract could even be removed, preventing the correct operation of downstream deployer contracts that rely on the blueprint existing. For this reason, it would be good to prefix blueprint contracts with a special preamble to prevent execution. + +## 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. + +A blueprint contract MUST use the preamble `0xFE71`. 6 bits are allocated to the version, and 2 bits to the length encoding. The first version begins at 0 (`0b000000`), and versions increment by 1. The value `0b11` for `` is reserved. In the case that the length bits are `0b11`, the third byte is considered a continuation byte (that is, the version requires multiple bytes to encode). The exact encoding of a multi-byte version is left to a future ERC. + +A blueprint contract MUST contain at least one byte of initcode. + +A blueprint contract MAY insert any bytes (data or code) between the version byte(s) and the initcode. If such variable length data is used, the preamble must be `0xFE71`. The `` represent a number between 0 and 2 (inclusive) describing how many bytes `` takes, and `` is the big-endian encoding of the number of bytes that `` takes. + +## Rationale + +- To save gas and storage space, the preamble should be as minimal as possible. + +- It is considered "bad" behavior to try to CALL a blueprint contract directly, therefore the preamble starts with `INVALID (0xfe)` to end execution with an exceptional halting condition (rather than a "gentler" opcode like `STOP (0x00)`). + +- To help distinguish a blueprint contract from other contracts that may start with `0xFE`, a "magic" byte is used. The value `0x71` was arbitrarily chosen by taking the last byte of the keccak256 hash of the bytestring "blueprint" (i.e.: `keccak256(b"blueprint")[-1]`). + +- An empty initcode is disallowed by the spec to prevent what might be a common mistake. + +- Users may want to include arbitrary data or code in their preamble. To allow indexers to ignore these bytes, a variable length encoding is proposed. To allow the length to be only zero or one bytes (in the presumably common case that `len(data bytes)` is smaller than 256), two bits of the third byte are reserved to specify how many bytes the encoded length takes. + +- In case we need an upgrade path, version bits are included. While we do not expect to exhaust the version bits, in case we do, a continuation sequence is reserved. Since only two bytes are required for `` (as [EIP-170](./eip-170.md) restricts contract length to 24KB), a `` value of 3 would never be required to describe ``. For that reason, the special `` value of `0b11` is reserved as a continuation sequence marker. + +- The length of the initcode itself is not included by default in the preamble because it takes space, and it can be trivially determined using `EXTCODESIZE`. + +- The Ethereum Object Format (EOF) could provide another way of specifying blueprint contracts, by adding another section kind (3 - initcode). However, it is not yet in the EVM, and we would like to be able to standardize blueprint contracts today, without relying on EVM changes. If, at some future point, section kind 3 becomes part of the EOF spec, and the EOF becomes part of the EVM, this ERC will be considered to be obsolesced since the EOF validation spec provides much stronger guarantees than this ERC. + + +## Backwards Compatibility + +No known issues + +## Test Cases + +- An example (and trivial!) blueprint contract with no data section, whose initcode is just the `STOP` instruction: + +``` +0xFE710000 +``` + +- An example blueprint contract whose initcode is the trivial `STOP` instruction and whose data section contains the byte `0xFF` repeated seven times: + +``` +0xFE710107FFFFFFFFFFFFFF00 +``` + +Here, 0xFE71 is the magic header, `0x01` means version 0 + 1 length bit, `0x07` encodes the length in bytes of the data section. These are followed by the data section, and then the initcode. For illustration, the above code with delimiters would be `0xFE71|01|07|FFFFFFFFFFFFFF|00`. + +- An example blueprint whose initcode is the trivial `STOP` instruction and whose data section contains the byte `0xFF` repeated 256 times: + +``` +0xFE71020100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 +``` + +Delimited, that would be `0xFE71|02|0100|FF...FF|00`. + +## Reference Implementation + +```python +from typing import Optional, Tuple + +def parse_blueprint_preamble(bytecode: bytes) -> Tuple[int, Optional[bytes], bytes]: + """ + Given bytecode as a sequence of bytes, parse the blueprint preamble and + deconstruct the bytecode into: + the ERC version, preamble data and initcode. + Raises an exception if the bytecode is not a valid blueprint contract + according to this ERC. + arguments: + bytecode: a `bytes` object representing the bytecode + returns: + (version, + None if is 0, otherwise the bytes of the data section, + the bytes of the initcode, + ) + """ + if bytecode[:2] != b"\xFE\x71": + raise Exception("Not a blueprint!") + + erc_version = (bytecode[2] & 0b11111100) >> 2 + + n_length_bytes = bytecode[2] & 0b11 + if n_length_bytes == 0b11: + raise Exception("Reserved bits are set") + + data_length = int.from_bytes(bytecode[3:3 + n_length_bytes], byteorder="big") + + if n_length_bytes == 0: + preamble_data = None + else: + data_start = 3 + n_length_bytes + preamble_data = bytecode[data_start:data_start + data_length] + + initcode = bytecode[3 + n_length_bytes + data_length:] + + if len(initcode) == 0: + raise Exception("Empty initcode!") + + return erc_version, preamble_data, initcode +``` + +The following reference function takes the desired initcode for a blueprint as a parameter, and returns EVM code which will deploy a corresponding blueprint contract (with no data section): + +```python +def blueprint_deployer_bytecode(initcode: bytes) -> bytes: + blueprint_preamble = b"\xFE\x71\x00" # ERC5202 preamble + blueprint_bytecode = blueprint_preamble + initcode + + # the length of the deployed code in bytes + len_bytes = len(blueprint_bytecode).to_bytes(2, "big") + + # copy to memory and `RETURN` it per EVM creation semantics + # PUSH2 RETURNDATASIZE DUP2 PUSH1 10 RETURNDATASIZE CODECOPY RETURN + deploy_bytecode = b"\x61" + len_bytes + b"\x3d\x81\x60\x0a\x3d\x39\xf3" + + return deploy_bytecode + blueprint_bytecode +``` + +## Security Considerations + +There could be contracts on-chain already which happen to start with the same prefix as proposed in this ERC. However, this is not considered a serious risk, because the way it is envisioned that indexers will use this is to verify source code by compiling it and prepending the preamble. + +As of 2022-07-08, no contracts deployed on the Ethereum mainnet have a bytecode starting with `0xFE71`. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5216.md b/EIPS/eip-5216.md new file mode 100644 index 00000000000000..1e16fd4ce598cb --- /dev/null +++ b/EIPS/eip-5216.md @@ -0,0 +1,104 @@ +--- +eip: 5216 +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: Last Call +last-call-deadline: 2022-11-12 +type: Standards Track +category: ERC +created: 2022-07-11 +requires: 20, 165, 1155 +--- + +## Abstract + +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), 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`. + +## 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. + +Contracts using this EIP MUST implement the `IERC1155ApprovalByAmount` interface. + +### Interface implementation + +```solidity +/** + * @title ERC-1155 Approval By Amount Extension + * Note: the ERC-165 identifier for this interface is 0x1be07d74 + */ +interface IERC1155ApprovalByAmount is IERC1155 { + + /** + * @notice Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to + * `id` and with an amount: `amount`. + */ + event ApprovalByAmount(address indexed account, address indexed operator, uint256 id, uint256 amount); + + /** + * @notice Grants permission to `operator` to transfer the caller's tokens, according to `id`, and an amount: `amount`. + * Emits an {ApprovalByAmount} event. + * + * Requirements: + * - `operator` cannot be the caller. + */ + function approve(address operator, uint256 id, uint256 amount) external; + + /** + * @notice Returns the amount allocated to `operator` approved to transfer `account`'s tokens, according to `id`. + */ + function allowance(address account, address operator, uint256 id) external view returns (uint256); +} +``` + +The `approve(address operator, uint256 id, uint256 amount)` function MUST be either `public` or `external`. + +The `allowance(address account, address operator, uint256 id)` function MUST be either `public` or `external` and MUST be `view`. + +The `safeTrasferFrom` function (as defined by EIP-1155) MUST: + +- Not revert if the user has approved `msg.sender` with a sufficient `amount` +- Subtract the transferred amount of tokens from the approved amount if `msg.sender` is not approved with `setApprovalForAll` + +In addition, the `safeBatchTransferFrom` MUST: + +- Add an extra condition that checks if the `allowance` of all `ids` have the approved `amounts` (See `_checkApprovalForBatch` function reference implementation) + +The `ApprovalByAmount` event MUST be emitted when a certain number of tokens are approved. + +The `supportsInterface` method MUST return `true` when called with `0x1be07d74`. + +## Rationale + +The name "EIP-1155 Approval By Amount Extension" was chosen because it is a succinct description of this EIP. Users can approve their tokens by `id` and `amount` to `operator`s. + +By having a way to approve and revoke in a manner similar to [EIP-20](./eip-20.md), the trust level can be more directly managed by users: + +- Using the `approve` function, users can approve an operator to spend an `amount` of tokens for each `id`. +- Using the `allowance` function, users can see the approval that an operator has for each `id`. + +The [EIP-20](./eip-20.md) name patterns were used due to similarities with [EIP-20](./eip-20.md) approvals. + +## Backwards Compatibility + +This standard is compatible with [EIP-1155](./eip-1155.md). + +## Reference Implementation + +The reference implementation can be found [here](../assets/eip-5216/ERC1155ApprovalByAmount.sol). + +## Security Considerations + +Users of this EIP must thoroughly consider the amount of tokens they give permission to `operators`, and should revoke unused authorizations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5218.md b/EIPS/eip-5218.md new file mode 100644 index 00000000000000..4b7323bdcbf9a7 --- /dev/null +++ b/EIPS/eip-5218.md @@ -0,0 +1,239 @@ +--- +eip: 5218 +title: NFT Rights Management +description: An interface for creating copyright licenses that transfer with an NFT. +author: James Grimmelmann (@grimmelm), Yan Ji (@iseriohn), Tyler Kell (@relyt29) +discussions-to: https://ethereum-magicians.org/t/eip-5218-nft-rights-management/9911 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-11 +requires: 721 +--- + + + +## Abstract + +The following standard defines an API for managing NFT licenses. This standard provides basic functionality to create, transfer, and revoke licenses, and to determine the current licensing state of an NFT. The standard does not define the legal details of the license. Instead, it provides a structured framework for recording licensing details. + +We consider use cases of NFT creators who wish to give the NFT holder a copyright license to use a work associated with the NFT. The holder of an active license can issue sublicenses to others to carry out the rights granted under the license. The license can be transferred with the NFT, so do all the sublicenses. The license can optionally be revoked under conditions specified by the creator. + + +## Motivation + +The [ERC-721](./eip-721.md) standard defines an API to track and transfer ownership of an NFT. When an NFT is to represent some off-chain asset, however, we would need some legally effective mechanism to *tether* the on-chain asset (NFT) to the off-chain property. One important case of off-chain property is creative work such as an image or music file. Recently, most NFT projects involving creative works have used licenses to clarify what legal rights are granted to the NFT owner. But these licenses are almost always off-chain and the NFTs themselves do not indicate what licenses apply to them, leading to uncertainty about rights to use the work associated with the NFT. It is not a trivial task to avoid all the copyright vulnerabilities in NFTs, nor have existing EIPs addressed rights management of NFTs beyond the simple cases of direct ownership (see [ERC-721](./eip-721.md)) or rental (see [ERC-4907](./eip-4907.md)). + +This EIP attempts to provide a standard to facilitate rights management of NFTs in the world of Web3. In particular, [ERC-5218](./eip-5218.md) smart contracts allow all licenses to an NFT, including the *root license* issued to the NFT owner and *sublicenses* granted by a license holder, to be recorded and easily tracked with on-chain data. These licenses can consist of human-readable legal code, machine-readable summaries such as those written in CC REL, or both. An ERC-5218 smart contract points to a license by recording a URI, providing a reliable reference for users to learn what legal rights they are granted and for NFT creators and auditors to detect unauthorized infringing uses. + + + +## Specification + +The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +**Every ERC-5218 compliant contract *must* implement the `IERC5218` interface**: + +```solidity +pragma solidity ^0.8.0; + +/// @title ERC-5218: NFT Rights Management +interface IERC5218 is IERC721 { + + /// @dev This emits when a new license is created by any mechanism. + event CreateLicense(uint256 _licenseId, uint256 _tokenId, uint256 _parentLicenseId, address _licenseHolder, string _uri, address _revoker); + + /// @dev This emits when a license is revoked. Note that under some + /// license terms, the sublicenses may be `implicitly` revoked following the + /// revocation of some ancestral license. In that case, your smart contract + /// may only emit this event once for the ancestral license, and the revocation + /// of all its sublicenses can be implied without consuming additional gas. + event RevokeLicense(uint256 _licenseId); + + /// @dev This emits when the a license is transferred to a new holder. The + /// root license of an NFT should be transferred with the NFT in an ERC721 + /// `transfer` function call. + event TransferLicense(uint256 _licenseId, address _licenseHolder); + + /// @notice Check if a license is active. + /// @dev A non-existing or revoked license is inactive and this function must + /// return `false` upon it. Under some license terms, a license may become + /// inactive because some ancestral license has been revoked. In that case, + /// this function should return `false`. + /// @param _licenseId The identifier for the queried license + /// @return Whether the queried license is active + function isLicenseActive(uint256 _licenseId) external view returns (bool); + + /// @notice Retrieve the token identifier a license was issued upon. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The token identifier the queried license was issued upon + function getLicenseTokenId(uint256 _licenseId) external view returns (uint256); + + /// @notice Retrieve the parent license identifier of a license. + /// @dev Throws unless the license is active. If a license doesn't have a + /// parent license, return a special identifier not referring to any license + /// (such as 0). + /// @param _licenseId The identifier for the queried license + /// @return The parent license identifier of the queried license + function getParentLicenseId(uint256 _licenseId) external view returns (uint256); + + /// @notice Retrieve the holder of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The holder address of the queried license + function getLicenseHolder(uint256 _licenseId) external view returns (address); + + /// @notice Retrieve the URI of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The URI of the queried license + function getLicenseURI(uint256 _licenseId) external view returns (string memory); + + /// @notice Retrieve the revoker address of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The revoker address of the queried license + function getLicenseRevoker(uint256 _licenseId) external view returns (address); + + /// @notice Retrieve the root license identifier of an NFT. + /// @dev Throws unless the queried NFT exists. If the NFT doesn't have a root + /// license tethered to it, return a special identifier not referring to any + /// license (such as 0). + /// @param _tokenId The identifier for the queried NFT + /// @return The root license identifier of the queried NFT + function getLicenseIdByTokenId(uint256 _tokenId) external view returns (uint256); + + /// @notice Create a new license. + /// @dev Throws unless the NFT `_tokenId` exists. Throws unless the parent + /// license `_parentLicenseId` is active, or `_parentLicenseId` is a special + /// identifier not referring to any license (such as 0) and the NFT + /// `_tokenId` doesn't have a root license tethered to it. Throws unless the + /// message sender is eligible to create the license, i.e., either the + /// license to be created is a root license and `msg.sender` is the NFT owner, + /// or the license to be created is a sublicense and `msg.sender` is the holder + /// of the parent license. + /// @param _tokenId The identifier for the NFT the license is issued upon + /// @param _parentLicenseId The identifier for the parent license + /// @param _licenseHolder The address of the license holder + /// @param _uri The URI of the license terms + /// @param _revoker The revoker address + /// @return The identifier of the created license + function createLicense(uint256 _tokenId, uint256 _parentLicenseId, address _licenseHolder, string memory _uri, address _revoker) external returns (uint256); + + /// @notice Revoke a license. + /// @dev Throws unless the license is active and the message sender is the + /// eligible revoker. This function should be used for revoking both root + /// licenses and sublicenses. Note that if a root license is revoked, the + /// NFT should be transferred back to its creator. + /// @param _licenseId The identifier for the queried license + function revokeLicense(uint256 _licenseId) external; + + /// @notice Transfer a sublicense. + /// @dev Throws unless the sublicense is active and `msg.sender` is the license + /// holder. Note that the root license of an NFT should be tethered to and + /// transferred with the NFT. Whenever an NFT is transferred by calling the + /// ERC721 `transfer` function, the holder of the root license should be + /// changed to the new NFT owner. + /// @param _licenseId The identifier for the queried license + /// @param _licenseHolder The new license holder + function transferSublicense(uint256 _licenseId, address _licenseHolder) external; +} +``` + +Licenses to an NFT in general have a tree structure as below: + +![The license tree](../assets/eip-5218/license-tree.png) + +There is one root license to the NFT itself, granting the NFT owner some rights to the linked work. The NFT owner (i.e., the root license holder) may create sublicenses, holders of which may also create sublicenses recursively. + +The full log of license creation, transfer, and revocation *must* be traceable via event logs. Therefore, all license creations and transfers *must* emit a corresponding log event. Revocation may differ a bit. An implementation of this EIP may emit a `Revoke` event only when a license is revoked in a function call, or for every revoked license, both are sufficient to trace the status of all licenses. The former costs less gas if revoking a license automatically revokes all sublicenses under it, while the latter is efficient in terms of interrogation of a license status. Implementers should make the tradeoffs depending on their license terms. + +The `revoker` of a license may be the licensor, the license holder, or a smart contract address which calls the `revokeLicense` function when some conditions are met. Implementers should be careful with the authorization, and may make the `revoker` smart contract forward compatible with transfers by not hardcoding the addresses of `licensor` or `licenseHolder`. + +The license `URI` may point to a JSON file that conforms to the "ERC-5218 Metadata JSON Schema" as below, which adopts the "three-layer" design of the Creative Commons Licenses: + +```json +{ + "title": "License Metadata", + "type": "object", + "properties": { + "legal-code": { + "type": "string", + "description": "The legal code of the license." + }, + "human-readable": { + "type": "string", + "description": "The human readable license deed." + }, + "machine-readable": { + "type": "string", + "description": "The machine readable code of the license that can be recognized by software, such as CC REL." + } + } +} +``` + +Note that this EIP doesn't include a function to update license URI so the license terms should be persistent by default. It is recommended to store the license metadata on a decentralized storage service such as IPFS or adopt the IPFS-style URI which encodes the hash of the metadata for integrity verification. On the other hand, license updatability, if necessary in certain scenarios, can be realized by revoking the original license and creating a new license, or adding a updating function, the eligibile caller of which must be carefully specified in the license and securely implemented in the smart contract. + +The `supportsInterface` method MUST return `true` when called with `0xac7b5ca9`. + +## Rationale + +This EIP aims to allow tracing all licenses to an NFT to facilitate right management. The ERC-721 standard only logs the property but not the legal rights tethered to NFTs. Even when logging the license via the optional ERC-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 ERC-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 ERC-5218 and [ERC-5289](./eip-5289.md) after careful scrutinizing and implement a smart contract that atomically (1) signs the legal contract via ERC-5289, and (2) transfers the NFT together with the copyright ownership via ERC-5218. Either both take place or both revert. + +## Backwards Compatibility + +This standard is compatible with the current ERC-721 standards: a contract can inherit from both ERC-721 and ERC-5218 at the same time. + +## Test Cases + +Test cases are available [here](../assets/eip-5218/contracts/test/Contract.t.sol). + +## Reference Implementation + +A reference implementation maintains the following data structures: + +```solidity + struct License { + bool active; // whether the license is active + uint256 tokenId; // the identifier of the NFT the license is tethered to + uint256 parentLicenseId; // the identifier of the parent license + address licenseHolder; // the license holder + string uri; // the license URI + address revoker; // the license revoker + } + mapping(uint256 => License) private _licenses; // maps from a license identifier to a license object + mapping(uint256 => uint256) private _licenseIds; // maps from an NFT to its root license identifier +``` + +Each NFT has a license tree and starting from each license, one can trace back to the root license via `parentLicenseId` along the path. + +In the reference implementation, once a license is revoked, all sublicenses under it are revoked. This is realized in a *lazy* manner for lower gas cost, i.e., assign `active=false` only for licenses that are explicitly revoked in a `revokeLicense` function call. Therefore, `isLicenseActive` returns `true` only if all its ancestral licenses haven't been revoked. + +For non-root licenses, the creation, transfer and revocation are straightforward: + +1. Only the holder of an active license can create sublicenses. +2. Only the holder of an active license can transfer it to a different license holder. +3. Only the revoker of an active license can revoke it. + +The root license must be compatible with `ERC-721`: + +1. When an NFT is minted, a license is granted to the NFT owner. +2. When an NFT is transferred, the license holder is changed to the new owner of the NFT. +3. When a root license is revoked, the NFT is returned to the NFT creator, and the NFT creator may later transfer it to a new owner with a new license. + +The complete implementation can be found [here](../assets/eip-5218/contracts/src/RightsManagement.sol). + +In addition, the [Token-Bound NFT License](../assets/eip-5218/ic3license/ic3license.pdf) is specifically designed to work with this interface and provides a reference to the language of NFT licenses. + +## Security Considerations + +Implementors of the `IERC5218` standard must consider thoroughly the permissions they give to `licenseHolder` and `revoker`. If the license is ever to be transferred to a different license holder, the `revoker` smart contract should not hardcode the `licenseHolder` address to avoid undesirable scenarios. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-5219.md b/EIPS/eip-5219.md new file mode 100644 index 00000000000000..57567e59d15876 --- /dev/null +++ b/EIPS/eip-5219.md @@ -0,0 +1,79 @@ +--- +eip: 5219 +title: Contract Resource Requests +description: Allows the requesting of resources from contracts +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/pr-5219-discussion-contract-rest/9907 +status: Final +type: Standards Track +category: ERC +created: 2022-07-10 +--- + +## Abstract + +This EIP standardizes an interface to make resource requests to smart contracts and to receive HTTP-like responses. + +## Motivation + +Ethereum is the most-established blockchain for building decentralized applications (referred to as `DApp`s). Due to this, the Ethereum DApp ecosystem is very diverse. However, one issue that plagues DApps is the fact that they are not fully decentralized. Specifically, to interface a "decentralized" application, one first needs to access a *centralized* website containing the DApp's front-end code, presenting a few issues. The following are some risks associated with using centralized websites to interface with decentralized applications: + +- Trust Minimization: An unnecessarily large number of entities need to be trusted +- Censorship: A centralized website is not resistant to being censored +- Permanence: The interface may not have a mechanism that permits it to be permanently stored +- Interoperability: Smart Contracts cannot directly interact with DApp interfaces + +## 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. + +### Name Resolution + +EIPs that propose a name resolution mechanism MAY reference this EIP and MAY recommend that clients support their mechanism. Clients MAY also support regular DNS, as defined in RFC 1034 and RFC 1035. + +### Separation of Concerns + +It is RECOMMENDED to separate the application logic from the front-end logic (the contract implementing the interface defined in [Contract Interface](#contract-interface)). + +### Contract Interface + +DApp contracts MUST implement the interface defined in the following file: [Contract Interface](../assets/eip-5219/IDecentralizedApp.sol). + +### Note to Implementers + +To save gas costs, it is recommended to use the `message/external-body` MIME-type, which allows you to point to data that the smart contract might not have access to. For example, the following response would tell a client to fetch the data off of IPFS: + +```yaml +statusCode: 200 +body: THIS IS NOT REALLY THE BODY! +headers: + - key: Content-type + value: message/external-body; access-type=URL; URL="ipfs://11148a173fd3e32c0fa78b90fe42d305f202244e2739" +``` + +## Rationale + +The `request` method was chosen to be readonly because all data should be sent to the contract from the parsed DApp. Here are some reasons why: + +- Submitting a transaction to send a request would be costly and would require waiting for the transaction to be mined, resulting in bad user experience. +- Complicated front-end logic should not be stored in the smart contract, as it would be costly to deploy and would be better run on the end-user's machine. +- Separation of Concerns: the front-end contract shouldn't have to worry about interacting with the back-end smart contract. +- Other EIPs can be used to request state changing operations in conjunction with a `307 Temporary Redirect` status code. + +Instead of mimicking a full HTTP request, a highly slimmed version was chosen for the following reasons: + +- The only particularly relevant HTTP method is `GET` +- Query parameters can be encoded in the resource. +- Request headers are, for the most part, unnecessary for `GET` requests. + +## Backwards Compatibility + +This EIP is backwards compatible with all standards listed in the [Name Resolution](#name-resolution) section. + +## Security Considerations + +The normal security considerations of accessing normal URLs apply here, such as potential privacy leakage by following `3XX` redirects. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5247.md b/EIPS/eip-5247.md new file mode 100644 index 00000000000000..bb7779d680fa84 --- /dev/null +++ b/EIPS/eip-5247.md @@ -0,0 +1,147 @@ +--- +eip: 5247 +title: Smart Contract Executable Proposal Interface +description: An interface to create and execute proposals. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-5247-executable-proposal-standard/9938 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-13 +--- + +## Abstract + +This EIP presents an interface for "smart contract executable proposals": proposals that are submitted to, recorded on, and possibly executed on-chain. Such proposals include a series of information about +function calls including the target contract address, ether value to be transmitted, gas limits and calldatas. + +## Motivation + +It is oftentimes necessary to separate the code that is to be executed from the actual execution of the code. + +A typical use case for this EIP is in a Decentralized Autonomous Organization (DAO). A proposer will create a smart proposal and advocate for it. Members will then choose whether or not to endorse the proposal and vote accordingly (see [EIP-1202](./eip-1202.md)). Finallym when consensus has been formed, the proposal is executed. + +A second typical use-case is that one could have someone who they trust, such as a delegator, trustee, or an attorney-in-fact, or any bilateral collaboration format, where a smart proposal will be first composed, discussed, approved in some way, and then put into execution. + +A third use-case is that a person could make an "offer" to a second person, potentially with conditions. The smart proposal can be presented as an offer and the second person can execute it if they choose to accept this proposal. + +## 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 +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface IERC5247 { + event ProposalCreated( + address indexed proposer, + uint256 indexed proposalId, + address[] targets, + uint256[] values, + uint256[] gasLimits, + bytes[] calldatas, + bytes extraParams + ); + + event ProposalExecuted( + address indexed executor, + uint256 indexed proposalId, + bytes extraParams + ); + + function createProposal( + uint256 proposalId, + address[] calldata targets, + uint256[] calldata values, + uint256[] calldata gasLimits, + bytes[] calldata calldatas, + bytes calldata extraParams + ) external returns (uint256 registeredProposalId); + + function executeProposal(uint256 proposalId, bytes calldata extraParams) external; +} +``` + +## Rationale + +* Originally, this interface was part of part of [EIP-1202](./eip-1202.md). However, the proposal itself can potentially have many use cases outside of voting. It is possible that voting may not need to be upon a proposal in any particular format. Hence, we decide to *decouple the voting interface and proposal interface*. +* Arrays were used for `target`s, `value`s, `calldata`s instead of single variables, allowing a proposal to carry arbitrarily long multiple functional calls. +* `registeredProposalId` is returned in `createProposal` so the standard can support implementation to decide their own format of proposal id. + +## Test Cases + +A simple test case can be found as + +```ts + it("Should work for a simple case", async function () { + const { contract, erc721, owner } = await loadFixture(deployFixture); + const callData1 = erc721.interface.encodeFunctionData("mint", [owner.address, 1]); + const callData2 = erc721.interface.encodeFunctionData("mint", [owner.address, 2]); + await contract.connect(owner) + .createProposal( + 0, + [erc721.address, erc721.address], + [0,0], + [0,0], + [callData1, callData2], + []); + expect(await erc721.balanceOf(owner.address)).to.equal(0); + await contract.connect(owner).executeProposal(0, []); + expect(await erc721.balanceOf(owner.address)).to.equal(2); + }); +``` + +See [testProposalRegistry.ts](../assets/eip-5247/testProposalRegistry.ts) for the whole testset. + +## Reference Implementation + +A simple reference implementation can be found. + +```solidity + function createProposal( + uint256 proposalId, + address[] calldata targets, + uint256[] calldata values, + uint256[] calldata gasLimits, + bytes[] calldata calldatas, + bytes calldata extraParams + ) external returns (uint256 registeredProposalId) { + require(targets.length == values.length, "GeneralForwarder: targets and values length mismatch"); + require(targets.length == gasLimits.length, "GeneralForwarder: targets and gasLimits length mismatch"); + require(targets.length == calldatas.length, "GeneralForwarder: targets and calldatas length mismatch"); + registeredProposalId = proposalCount; + proposalCount++; + + proposals[registeredProposalId] = Proposal({ + by: msg.sender, + proposalId: proposalId, + targets: targets, + values: values, + calldatas: calldatas, + gasLimits: gasLimits + }); + emit ProposalCreated(msg.sender, proposalId, targets, values, gasLimits, calldatas, extraParams); + return registeredProposalId; + } + function executeProposal(uint256 proposalId, bytes calldata extraParams) external { + Proposal storage proposal = proposals[proposalId]; + address[] memory targets = proposal.targets; + string memory errorMessage = "Governor: call reverted without message"; + for (uint256 i = 0; i < targets.length; ++i) { + (bool success, bytes memory returndata) = proposal.targets[i].call{value: proposal.values[i]}(proposal.calldatas[i]); + Address.verifyCallResult(success, returndata, errorMessage); + } + emit ProposalExecuted(msg.sender, proposalId, extraParams); + } +``` + +See [ProposalRegistry.sol](../assets/eip-5247/ProposalRegistry.sol) for more information. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5252.md b/EIPS/eip-5252.md new file mode 100644 index 00000000000000..c4ef04e746b70b --- /dev/null +++ b/EIPS/eip-5252.md @@ -0,0 +1,247 @@ +--- +eip: 5252 +title: Account-bound Finance +description: An ERC-5114 extension that aids in preventing arbitrary loss of funds +author: Hyungsuk Kang (@hskang9), Viktor Pernjek (@smuxx) +discussions-to: https://ethereum-magicians.org/t/pr-5252-discussion-account-bound-finance/10027 +status: Review +type: Standards Track +category: ERC +created: 2022-06-29 +requires: 20, 721, 1155, 5114 +--- + +## Abstract + +This EIP proposes a form of smart contract design pattern and a new type of account abstraction on how one's finance should be managed, ensuring transparency of managing investments and protection with self-sovereignty even from its financial operators. This EIP enables greater self-sovereignty of one's assets using a personal finance contract for each individual. The seperation between an investor's funds and the operation fee is clearly specified in the personal smart contract, so investors can ensure safety from arbitrary loss of funds by the operating team's control. + +This EIP extends [ERC-5114](./eip-5114.md) to further enable transferring fund to other accounts for mobility between managing multiple wallets. + +## Motivation + +Decentralized finance (DeFi) faces a trust issue. Smart contracts are often proxies, with the actual logic of the contract hidden away in a separate logic contract. Many projects include a multi-signature "wallet" with unnecessarily-powerful permissions. And it is not possible to independently verify that stablecoins have enough real-world assets to continue maintaining their peg, creating a large loss of funds (such as happened in the official bankruptcy announcement of Celsius and UST de-pegging and anchor protocol failure). One should not trust exchanges or other third parties with one's own investments with the operators' clout in Web3.0. + +Smart contracts are best implemented as a promise between two parties written in code, but current DeFi contracts are often formed using less than 7 smart contracts to manage their whole investors' funds, and often have a trusted key that has full control. This is evidently an issue, as investors have to trust contract operators with their funds, meaning that users do not actually own their funds. + +The pattern with personal finance contract also offers more transparency than storing mixed fund financial data in the operating team's contract. With a personal finance contract, an account's activity is easier to track than one global smart contract's activity. The pattern introduces a Non-Fungiible Account-Bound Token (ABT) to store credentials from the personal finance contract. + +### Offchain-identity vs Soul-bound token on credentials + +This EIP provides a better alternative to off-chain identity solutions which take over the whole system because their backends eventually rely on the trust of the operator, not cryptographic proof (e.g. Proof-of-work, Proof-of-stake, etc). Off-chain identity as credentials are in direct opposition to the whole premise of crypto. Soulbound tokens are a better, verifiable credential, and data stored off-chain is only to store token metadata. + +## 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 specification consists of two patterns for **Interaction** and **Governance**. + +### Interaction + +#### Interfaces + +The interaction pattern consists of 4 components for interaction; manager, factory, finance, account-bound token, and extension. + +Interaction contract pattern is defined with these contracts: + +- A soul-bound or account bound token contract to give access to interact with a financial contract with credentials +- A manager contract that interacts first contact with an investor +- A factory contract that creates a financial contract for each user +- A finance contract that can interact with the investor + +#### Requirements + +A soul-bound or account bound token contract is defined with these properties: + +1. It SHALL be non-fungible and MUST satisfy [ERC-721](./eip-721.md). +2. Credentials SHOULD be represented with its metadata with `tokenURI()` function. +3. It MUST only reference factory to verify its minting. +4. If it is transferrable, it is account-bound. If not, it is soul-bound. + +A manager contract is defined with these properties: + +1. It MUST be the only kind of contract which calls factory to create. +2. It SHOULD store all related configurations for financial parameters. + +A factory contract is defined with these properties: + +1. It SHALL clone the finance contract with uniform implementation. +2. It MUST be the only contract that can mint account-bound token. +3. It MUST keep an recent id of account bound token. + +A finance contract is defined with these properties: + +1. A finance contract MUST only be initialized once from factory contract in constructor. +2. Funds in the contract SHALL NOT be transferred to other contracts nor accounts unless sender who owns soul-bound or account bound token signs to do so. +3. Every state-changing function of the smart contract MUST only accept sender who owns soul-bound or account bound-token except global function(e.g. liquidation). +4. Global function SHOULD be commented as `/* global */` to clarify the function is can be accessed with anyone. +5. Each finance contract SHOULD be able to represent transaction that has happened only with those who had account-bound token. +6. If soul-bound token is used for access, the finance contract MUST be able to represent transaction that has happened only between whom had the private key and the finance contract. + +#### Contracts + +![Diagram](../assets/eip-5252/media/media.svg) + +
+Contract Diagram of [ERC-5252](eip-5252.md) +
+ +**`Manager`**: **`Manager`** contract acts as an entry point to interact with the investor. The contract also stores parameters for **`Finance`** contract. + +**`Factory`**: **`Factory`** contract manages contract bytecode to create for managing investor's fund and clones **`Finance`** contract on **`Manager`** contract's approval. It also mints account-bound tokens to interact with the `Finance` contract. + +**`Finance`**: **`Finance`** contract specifies all rules on managing an investor's fund. The contract is only accessible with an account that has an Account-bound token. When an investor deposits a fund to **`Manager`** contract, the contract sends the fund to **`Finance`** contract account after separating fees for operation. + +**`Account-bound token`**: **`Account-bound token`** contract in this EIP can bring the **`Finance`** contract's data and add metadata. For example, if there is a money market lending +**`Finance`** contract, its **`Account-bound token`** can show how much balance is in agreement using SVG. + +**`Extension`**: **`Extension`** contract is another contract that can utilize locked funds in **`Finance`** contract. The contract can access with **`Finance`** contract on operator's approval managed in **`Manager`** contract. Example use case of `Extension` can be a membership. + +**`Metadata`**: **`Metadata`** contract is the contract where it stores metadata related to account credentials. Credential related data are stored with specific key. Images are usually displayed as SVG, but offchain image is possible. + +--- + +### Governance + +The governance pattern consists of 2 components; influencer and governor. + +#### Interfaces + +#### Requirements + +An influencer contract is defined with these properties: + +1. The contract SHALL manage multiplier for votes. +2. The contract SHALL set a decimal to calculated normalized scores. +3. The contract SHALL set a function where governance can decide factor parameters. + +A governor contract is defined with these properties: + +1. The contract MUST satisfy Governor contract from OpenZeppelin. +2. The contract SHALL refer influencer contract for multiplier +3. The contract MUST limit transfer of account bound token once claimed for double vote prevention. + +#### From Token Governance To Contribution Based Governance + +| | Token Governance | Credential-based Governance | +| ----------- | ---------------------------- | ---------------------------------- | +| Enforcement | More tokens, more power | More contribution, More power | +| Incentives | More tokens, more incentives | More contribution, more incentives | +| Penalty | No penalty | Loss of power | +| Assignment | One who holds the token | One who has the most influence | + +
+Token Governance vs Credential Based Governance +
+ +Token governance is not sustainable in that it gives **more** power to "those who most want to rule". Any individual who gets more than 51% of the token supply can forcefully take control. + +New governance that considers contributions to the protocol is needed because: + +- **Rulers can be penalized on breaking the protocol** +- **Rulers can be more effectively incentivized on maintaining the protocol** + +The power should be given to "those who are most responsible". Instead of locked or owned tokens, voting power is determined with contributions marked in Account Bound Tokens (ABT). This EIP defines this form of voting power as **`Influence`**. + +#### Calculating Influence + +**`Influence`** is a multiplier on staked tokens that brings more voting power of a DAO to its contributors. To get **`Influence`**, a score is calculated on weighted contribution matrix. Then, the score is normalized to give a member's position in whole distribution. Finally, the multiplier is determined on the position in every community members. + +#### Calculating score + +The weights represent relative importance on each factor. The total importance is the total sum of the factors. More factors that can be normalized at the time of submitting proposal can be added by community. + +| | Description | +| --- | ----------------------------------------------------------------------------------------- | +| α | Contribution value per each **`Finance`** contract from current proposal | +| β | Time they maintained **`Finance`** per each contract from current timestamp of a proposal | + +```math +(score per each ABT) = α * (contribution value) + β * (time that abt was maintained from now) +``` + +#### Normalization + +Normalization is applied for data integrity on user's contribution in a DAO. +Normalized score can be calculated from the state of submitting a proposal + +```math +(Normalized score per each ABT) = α * (contribution value)/(total contribution value at submitting tx) + β * (time that abt was maintained)/(time passed from genesis to proposal creation) +``` + +and have a value between 0 and 1 (since α + β = 1). + +#### Multiplier + +The multiplier is determined linearly from base factor (b) and multiplier(m). + +The equation for influence is : + +```math +(influence) = m * (sum(normalized_score)) +``` + +#### Example + +For example, if a user has 3 **`Account-bound tokens`** with normalized score of each 1.0, 0.5, 0.3 and the locked token is 100, and multiplier is 0.5 and base factor is 1.5. Then the total influence is + +````math +0.5 * {(1.0 + 0.5 + 0.3) / 3} + 1.5 = 1.8 + + The total voting power would be + +```math +(voting power) = 1.8 * sqrt(100) = 18 +```` + +#### Stakers vs Enforcers + +| | Stakers | Enforcers | +| ------------ | --------------------------------- | --------------------------------------------------------------------------------------- | +| Role | stake governance token for voting | Contributed on the system, can make proposal to change rule, more voting power like 1.5 | +| Populations | many | small | +| Contribution | Less effect | More effect | +| Influence | sqrt(locked token) | Influence \* sqrt(locked token) | + +
+Stakers vs Enforcers +
+ +**Stakers**: Stakers are people who vote to enforcers' proposals and get dividend for staked tokens + +**Enforcers**: Enforcers are people who takes risk on managing protocol and contributes to the protocol by making a proposal and change to it. + +#### Contracts + +**`Influencer`**: An **`Influencer`** contract stores influence configurations and measures the contribution of a user from his activities done in a registered Account Bound Token contract. The contract puts a lock on that Account Bound Token until the proposal is finalized. + +**`Governor`**: **`Governor`** contract is compatible with the current governor contract in OpenZeppelin. For its special use case, it configures factors where the influencer manages and has access to changing parameters of **`Manager`** configs. Only the `Enforcer` can propose new parameters. + +## Rationale + +### Gas saving for end user + +The gas cost of using multiple contracts (as opposed to a single one) actually saves gas long-run if the clone factory pattern is applied. One contract storing users' states globally means each user is actually paying for the storage cost of other users after interacting with the contract. This, for example, means that MakerDAO's contract operating cost is sometimes over 0.1 ETH, limitimg users' minimum deposit for CDP in order to save gas costs. To solve inefficient n-times charging gas cost interaction for future users, one contract per user is used. + +#### Separation between investor's and operation fund + +The separation between an investor's funds and operation fee is clearly specified in the smart contract, so investors can ensure safety from arbitrary loss of funds by the operating team's control. + +## Backwards Compatibility + +This EIP has no known backward compatibility issues. + +## Reference Implementation + +[Reference implementation](../assets/eip-5252/README.md) is a simple deposit account contract as `Finance` contract and its contribution value α is measured with deposit amount with ETH. + +## Security Considerations + +- **`Factory`** contracts must ensure that each **`Finance`** contract is registered in the factory and check that **`Finance`** contracts are sending transactions related to their bounded owner. + +- Reentrancy attack guard should be applied or change state before delegatecall in each user function in **`Manager`** contract or **`Finance`** contract. Otherwise, **`Finance`** can be generated as double and ruin whole indices. + +- Once a user locks influence on a proposal's vote, an **`Account Bound Token`** cannot be transferred to another wallet. Otherwise, double influence can happen. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5267.md b/EIPS/eip-5267.md new file mode 100644 index 00000000000000..7201e790a638d1 --- /dev/null +++ b/EIPS/eip-5267.md @@ -0,0 +1,175 @@ +--- +eip: 5267 +title: Retrieval of EIP-712 domain +description: A way to describe and retrieve an EIP-712 domain to securely integrate EIP-712 signatures. +author: Francisco Giordano (@frangio) +discussions-to: https://ethereum-magicians.org/t/eip-5267-retrieval-of-eip-712-domain/9951 +status: Final +type: Standards Track +category: ERC +created: 2022-07-14 +requires: 155, 712, 2612 +--- + +## Abstract + +This EIP complements [EIP-712](./eip-712.md) by standardizing how contracts should publish the fields and values that describe their domain. This enables applications to retrieve this description and generate appropriate domain separators in a general way, and thus integrate EIP-712 signatures securely and scalably. + +## Motivation + +EIP-712 is a signature scheme for complex structured messages. In order to avoid replay attacks and mitigate phishing, the scheme includes a "domain separator" that makes the resulting signature unique to a specific domain (e.g., a specific contract) and allows user-agents to inform end users the details of what is being signed and how it may be used. A domain is defined by a data structure with fields from a predefined set, all of which are optional, or from extensions. Notably, EIP-712 does not specify any way for contracts to publish which of these fields they use or with what values. This has likely limited adoption of EIP-712, as it is not possible to develop general integrations, and instead applications find that they need to build custom support for each EIP-712 domain. A prime example of this is [EIP-2612](./eip-2612.md) (permit), which has not been widely adopted by applications even though it is understood to be a valuable improvement to the user experience. The present EIP defines an interface that can be used by applications to retrieve a definition of the domain that a contract uses to verify EIP-712 signatures. + +## 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. + +Compliant contracts MUST define `eip712Domain` exactly as declared below. All specified values MUST be returned even if they are not used, to ensure proper decoding on the client side. + +```solidity +function eip712Domain() external view returns ( + bytes1 fields, + string name, + string version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] extensions +); +``` + +The return values of this function MUST describe the domain separator that is used for verification of EIP-712 signatures in the contract. They describe both the form of the `EIP712Domain` struct (i.e., which of the optional fields and extensions are present) and the value of each field, as follows. + +- `fields`: A bit map where bit `i` is set to 1 if and only if domain field `i` is present (`0 ≤ i ≤ 4`). Bits are read from least significant to most significant, and fields are indexed in the order that is specified by EIP-712, identical to the order in which they are listed in the function type. +- `name`, `version`, `chainId`, `verifyingContract`, `salt`: The value of the corresponding field in `EIP712Domain`, if present according to `fields`. If the field is not present, the value is unspecified. The semantics of each field is defined in EIP-712. +- `extensions`: A list of EIP numbers, each of which MUST refer to an EIP that extends EIP-712 with new domain fields, along with a method to obtain the value for those fields, and potentially conditions for inclusion. The value of `fields` does not affect their inclusion. + +The return values of this function (equivalently, its EIP-712 domain) MAY change throughout the lifetime of a contract, but changes SHOULD NOT be frequent. The `chainId` field, if used, SHOULD change to mirror the [EIP-155](./eip-155.md) id of the underlying chain. Contracts MAY emit the event `EIP712DomainChanged` defined below to signal that the domain could have changed. + +```solidity +event EIP712DomainChanged(); +``` + +## Rationale + +A notable application of EIP-712 signatures is found in EIP-2612 (permit), which specifies a `DOMAIN_SEPARATOR` function that returns a `bytes32` value (the actual domain separator, i.e., the result of `hashStruct(eip712Domain)`). This value does not suffice for the purposes of integrating with EIP-712, as the RPC methods defined there receive an object describing the domain and not just the separator in hash form. Note that this is not a flaw of the RPC methods, it is indeed part of the security proposition that the domain should be validated and informed to the user as part of the signing process. On its own, a hash does not allow this to be implemented, given it is opaque. The present EIP fills this gap in both EIP-712 and EIP-2612. + +Extensions are described by their EIP numbers because EIP-712 states: "Future extensions to this standard can add new fields [...] new fields should be proposed through the EIP process." + +## Backwards Compatibility + +This is an optional extension to EIP-712 that does not introduce backwards compatibility issues. + +Upgradeable contracts that make use of EIP-712 signatures MAY be upgraded to implement this EIP. + +User-agents or applications that use this EIP SHOULD additionally support those contracts that due to their immutability cannot be upgraded to implement it. The simplest way to achieve this is to hardcode common domains based on contract address and chain id. However, it is also possible to implement a more general solution by guessing possible domains based on a few common patterns using the available information, and selecting the one whose hash matches a `DOMAIN_SEPARATOR` or `domainSeparator` function in the contract. + +## Reference Implementation + +### Solidity Example + +```solidity +pragma solidity 0.8.0; + +contract EIP712VerifyingContract { + function eip712Domain() external view returns ( + bytes1 fields, + string memory name, + string memory version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] memory extensions + ) { + return ( + hex"0d", // 01101 + "Example", + "", + block.chainid, + address(this), + bytes32(0), + new uint256[](0) + ); + } +} +``` + +This contract's domain only uses the fields `name`, `chainId`, and `verifyingContract`, therefore the `fields` value is `01101`, or `0d` in hexadecimal. + +Assuming this contract is on Ethereum mainnet and its address is 0x0000000000000000000000000000000000000001, the domain it describes is: + +```json5 +{ + name: "Example", + chainId: 1, + verifyingContract: "0x0000000000000000000000000000000000000001" +} +``` + +### JavaScript + +A domain object can be constructed based on the return values of an `eip712Domain()` invocation. + +```javascript +/** Retrieves the EIP-712 domain of a contract using EIP-5267 without extensions. */ +async function getDomain(contract) { + const { fields, name, version, chainId, verifyingContract, salt, extensions } = + await contract.eip712Domain(); + + if (extensions.length > 0) { + throw Error("Extensions not implemented"); + } + + return buildBasicDomain(fields, name, version, chainId, verifyingContract, salt); +} + +const fieldNames = ['name', 'version', 'chainId', 'verifyingContract', 'salt']; + +/** Builds a domain object without extensions based on the return values of `eip712Domain()`. */ +function buildBasicDomain(fields, name, version, chainId, verifyingContract, salt) { + const domain = { name, version, chainId, verifyingContract, salt }; + + for (const [i, field] of fieldNames.entries()) { + if (!(fields & (1 << i))) { + delete domain[field]; + } + } + + return domain; +} +``` + +#### Extensions + +Suppose EIP-XYZ defines a new field `subdomain` of type `bytes32` and a function `getSubdomain()` to retrieve its value. + +The function `getDomain` from above would be extended as follows. + +```javascript +/** Retrieves the EIP-712 domain of a contract using EIP-5267 with support for EIP-XYZ. */ +async function getDomain(contract) { + const { fields, name, version, chainId, verifyingContract, salt, extensions } = + await contract.eip712Domain(); + + const domain = buildBasicDomain(fields, name, version, chainId, verifyingContract, salt); + + for (const n of extensions) { + if (n === XYZ) { + domain.subdomain = await contract.getSubdomain(); + } else { + throw Error(`EIP-${n} extension not implemented`); + } + } + + return domain; +} +``` + +Additionally, the type of the `EIP712Domain` struct needs to be extended with the `subdomain` field. This is left out of scope of this reference implementation. + +## Security Considerations + +While this EIP allows a contract to specify a `verifyingContract` other than itself, as well as a `chainId` other than that of the current chain, user-agents and applications should in general validate that these do match the contract and chain before requesting any user signatures for the domain. This may not always be a valid assumption. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5269.md b/EIPS/eip-5269.md new file mode 100644 index 00000000000000..eb1b25ea608d9e --- /dev/null +++ b/EIPS/eip-5269.md @@ -0,0 +1,276 @@ +--- +eip: 5269 +title: EIP/ERC Detection and Discovery +description: An interface to identify if major behavior or optional behavior specified in an ERC is supported for a given caller. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc5269-human-readable-interface-detection/9957 +status: Review +type: Standards Track +category: ERC +created: 2022-07-15 +requires: 5750 +--- + +## Abstract + +An interface for better identification and detection of EIP/ERC by numbers. +It designates a field in which it's called `majorEIPIdentifier` which is normally known or referred to as "EIP number". For example, `ERC-721` aka [EIP-721](./eip-721.md) has a `majorEIPIdentifier = 721`. This EIP has a `majorEIPIdentifier = 5269`. + +Calling it a `majorEIPIdentifier` instead of `EIPNumber` makes it future-proof: anticipating there is a possibility where future EIP is not numbered or if we want to incorporate other types of standards. + +It also proposes a new concept of `minorEIPIdentifier` which is left for authors of +individual EIP to define. For example, EIP-721's author may define `ERC721Metadata` +interface as `minorEIPIdentifier= keccak256("ERC721Metadata")`. + +It also proposes an event to allow smart contracts to optionally declare the EIPs they support. + +## Motivation + +This EIP is created as a competing standard for [EIP-165](./eip-165.md). + +Here are the major differences between this EIP and [EIP-165](./eip-165.md). + +1. [EIP-165](./eip-165.md) uses the hash of a method's signature which declares the existence of one method or multiple methods, +therefore it requires at least one method to *exist* in the first place. In some cases, some EIP/ERCs interface does not have a method, such as some EIPs related to data format and signature schemes or the "Soul-Bound-ness" aka SBT which could just revert a transfer call without needing any specific method. +1. [EIP-165](./eip-165.md) doesn't provide query ability based on the caller. +The compliant contract of this EIP will respond to whether it supports certain EIP *based on* a given caller. + +Here is the motivation for this EIP given EIP-165 already exists: + +1. Using EIP/ERC numbers improves human readability as well as make it easier to work with named contract such as ENS. + +2. Instead of using an EIP-165 identifier, we have seen an increasing interest to use EIP/ERC numbers as the way to identify or specify an EIP/ERC. For example + +- [EIP-5267](./eip-5267.md) specifies `extensions` to be a list of EIP numbers. +- [EIP-600](./eip-600.md), and [EIP-601](./eip-601.md) specify an `EIP` number in the `m / purpose' / subpurpose' / EIP' / wallet'` path. +- [EIP-5568](./eip-5568.md) specifies `The instruction_id of an instruction defined by an EIP MUST be its EIP number unless there are exceptional circumstances (be reasonable)` +- [EIP-6120](./eip-6120.md) specifies `struct Token { uint eip; ..., }` where `uint eip` is an EIP number to identify EIPs. +- `EIP-867`(Stagnant) proposes to create `erpId: A string identifier for this ERP (likely the associated EIP number, e.g. “EIP-1234”).` + +3. Having an ERC/EIP number detection interface reduces the need for a lookup table in smart contract to +convert a function method or whole interface in any EIP/ERC in the bytes4 EIP-165 identifier into its respective EIP number and massively simplifies the way to specify EIP for behavior expansion. + +4. We also recognize a smart contract might have different behavior given different caller accounts. One of the most notable use cases is that when using Transparent Upgradable Pattern, a proxy contract gives an Admin account and Non-Admin account different treatment when they call. + +## Specification + +In the following description, we use EIP and ERC inter-exchangeably. This was because while most of the time the description applies to an ERC category of the Standards Track of EIP, the ERC number space is a subspace of EIP number space and we might sometimes encounter EIPs that aren't recognized as ERCs but has behavior that's worthy of a query. + +1. Any compliant smart contract MUST implement the following interface + +```solidity +// DRAFTv1 +pragma solidity ^0.8.9; + +interface IERC5269 { + event OnSupportEIP( + address indexed caller, // when emitted with `address(0x0)` means all callers. + uint256 indexed majorEIPIdentifier, + bytes32 indexed minorEIPIdentifier, // 0 means the entire EIP + bytes32 eipStatus, + bytes extraData + ); + + /// @dev The core method of EIP/ERC Interface Detection + /// @param caller, a `address` value of the address of a caller being queried whether the given EIP is supported. + /// @param majorEIPIdentifier, a `uint256` value and SHOULD BE the EIP number being queried. Unless superseded by future EIP, such EIP number SHOULD BE less or equal to (0, 2^32-1]. For a function call to `supportEIP`, any value outside of this range is deemed unspecified and open to implementation's choice or for future EIPs to specify. + /// @param minorEIPIdentifier, a `bytes32` value reserved for authors of individual EIP to specify. For example the author of [EIP-721](/EIPS/eip-721) MAY specify `keccak256("ERC721Metadata")` or `keccak256("ERC721Metadata.tokenURI")` as `minorEIPIdentifier` to be quired for support. Author could also use this minorEIPIdentifier to specify different versions, such as EIP-712 has its V1-V4 with different behavior. + /// @param extraData, a `bytes` for [EIP-5750](/EIPS/eip-5750) for future extensions. + /// @return eipStatus, a `bytes32` indicating the status of EIP the contract supports. + /// - For FINAL EIPs, it MUST return `keccak256("FINAL")`. + /// - For non-FINAL EIPs, it SHOULD return `keccak256("DRAFT")`. + /// During EIP procedure, EIP authors are allowed to specify their own + /// eipStatus other than `FINAL` or `DRAFT` at their discretion such as `keccak256("DRAFTv1")` + /// or `keccak256("DRAFT-option1")`and such value of eipStatus MUST be documented in the EIP body + function supportEIP( + address caller, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata extraData) + external view returns (bytes32 eipStatus); +} +``` + +In the following description, `EIP_5269_STATUS` is set to be `keccak256("DRAFTv1")`. + +In addition to the behavior specified in the comments of `IERC5269`: + +1. Any `minorEIPIdentifier=0` is reserved to be referring to the main behavior of the EIP being queried. +2. The Author of compliant EIP is RECOMMENDED to declare a list of `minorEIPIdentifier` for their optional interfaces, behaviors and value range for future extension. +3. When this EIP is FINAL, any compliant contract MUST return an `EIP_5269_STATUS` for the call of `supportEIP((any caller), 5269, 0, [])` + +*Note*: at the current snapshot, the `supportEIP((any caller), 5269, 0, [])` MUST return `EIP_5269_STATUS`. + +4. Any complying contract SHOULD emit an `OnSupportEIP(address(0), 5269, 0, EIP_5269_STATUS, [])` event upon construction or upgrade. +5. Any complying contract MAY declare for easy discovery any EIP main behavior or sub-behaviors by emitting an event of `OnSupportEIP` with relevant values and when the compliant contract changes whether the support an EIP or certain behavior for a certain caller or all callers. +6. For any `EIP-XXX` that is NOT in `Final` status, when querying the `supportEIP((any caller), xxx, (any minor identifier), [])`, it MUST NOT return `keccak256("FINAL")`. It is RECOMMENDED to return `0` in this case but other values of `eipStatus` is allowed. Caller MUST treat any returned value other than `keccak256("FINAL")` as non-final, and MUST treat 0 as strictly "not supported". +7. The function `supportEIP` MUST be mutability `view`, i.e. it MUST NOT mutate any global state of EVM. + +## Rationale + +1. When data type `uint256 majorEIPIdentifier`, there are other alternative options such as: + +- (1) using a hashed version of the EIP number, +- (2) use a raw number, or +- (3) use an EIP-165 identifier. + +The pros for (1) are that it automatically supports any evolvement of future EIP numbering/naming conventions. +But the cons are it's not backward readable: seeing a `hash(EIP-number)` one usually can't easily guess what their EIP number is. + +We choose the (2) in the rationale laid out in motivation. + +2. We have a `bytes32 minorEIPIdentifier` in our design decision. Alternatively, it could be (1) a number, forcing all EIP authors to define its numbering for sub-behaviors so we go with a `bytes32` and ask the EIP authors to use a hash for a string name for their sub-behaviors which they are already doing by coming up with interface name or method name in their specification. + +3. Alternatively, it's possible we add extra data as a return value or an array of all EIP being supported but we are unsure how much value this complexity brings and whether the extra overhead is justified. + +4. Compared to [EIP-165](./eip-165.md), we also add an additional input of `address caller`, given the increasing popularity of proxy patterns such as those enabled by [EIP-1967](./eip-1967.md). One may ask: why not simply use `msg.sender`? This is because we want to allow query them without transaction or a proxy contract to query whether interface ERC-`number` will be available to that particular sender. + +1. We reserve the input `majorEIPIdentifier` greater than or equals `2^32` in case we need to support other collections of standards which is not an ERC/EIP. + +## Test Cases + +```typescript + +describe("ERC5269", function () { + async function deployFixture() { + // ... + } + + describe("Deployment", function () { + // ... + it("Should emit proper OnSupportEIP events", async function () { + let { txDeployErc721 } = await loadFixture(deployFixture); + let events = txDeployErc721.events?.filter(event => event.event === 'OnSupportEIP'); + expect(events).to.have.lengthOf(4); + + let ev5269 = events!.filter( + (event) => event.args!.majorEIPIdentifier.eq(5269)); + expect(ev5269).to.have.lengthOf(1); + expect(ev5269[0].args!.caller).to.equal(BigNumber.from(0)); + expect(ev5269[0].args!.minorEIPIdentifier).to.equal(BigNumber.from(0)); + expect(ev5269[0].args!.eipStatus).to.equal(ethers.utils.id("DRAFTv1")); + + let ev721 = events!.filter( + (event) => event.args!.majorEIPIdentifier.eq(721)); + expect(ev721).to.have.lengthOf(3); + expect(ev721[0].args!.caller).to.equal(BigNumber.from(0)); + expect(ev721[0].args!.minorEIPIdentifier).to.equal(BigNumber.from(0)); + expect(ev721[0].args!.eipStatus).to.equal(ethers.utils.id("FINAL")); + + expect(ev721[1].args!.caller).to.equal(BigNumber.from(0)); + expect(ev721[1].args!.minorEIPIdentifier).to.equal(ethers.utils.id("ERC721Metadata")); + expect(ev721[1].args!.eipStatus).to.equal(ethers.utils.id("FINAL")); + + // ... + }); + + it("Should return proper eipStatus value when called supportEIP() for declared supported EIP/features", async function () { + let { erc721ForTesting, owner } = await loadFixture(deployFixture); + expect(await erc721ForTesting.supportEIP(owner.address, 5269, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(ethers.utils.id("DRAFTv1")); + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(ethers.utils.id("FINAL")); + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.id("ERC721Metadata"), [])).to.equal(ethers.utils.id("FINAL")); + // ... + + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.id("WRONG FEATURE"), [])).to.equal(BigNumber.from(0)); + expect(await erc721ForTesting.supportEIP(owner.address, 9999, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(BigNumber.from(0)); + }); + + it("Should return zero as eipStatus value when called supportEIP() for non declared EIP/features", async function () { + let { erc721ForTesting, owner } = await loadFixture(deployFixture); + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.id("WRONG FEATURE"), [])).to.equal(BigNumber.from(0)); + expect(await erc721ForTesting.supportEIP(owner.address, 9999, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(BigNumber.from(0)); + }); + }); +}); +``` + +See [`TestERC5269.ts`](../assets/eip-5269/test/TestERC5269.ts). + +## Reference Implementation + +Here is a reference implementation for this EIP: + +```solidity +contract ERC5269 is IERC5269 { + bytes32 constant public EIP_STATUS = keccak256("DRAFTv1"); + constructor () { + emit OnSupportEIP(address(0x0), 5269, bytes32(0), EIP_STATUS, ""); + } + + function _supportEIP( + address /*caller*/, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata /*extraData*/) + internal virtual view returns (bytes32 eipStatus) { + if (majorEIPIdentifier == 5269) { + if (minorEIPIdentifier == bytes32(0)) { + return EIP_STATUS; + } + } + return bytes32(0); + } + + function supportEIP( + address caller, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata extraData) + external virtual view returns (bytes32 eipStatus) { + return _supportEIP(caller, majorEIPIdentifier, minorEIPIdentifier, extraData); + } +} +``` + +See [`ERC5269.sol`](../assets/eip-5269/contracts/ERC5269.sol). + +Here is an example where a contract of [EIP-721](./eip-721.md) also implement this EIP to make it easier +to detect and discover: + +```solidity +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "../ERC5269.sol"; +contract ERC721ForTesting is ERC721, ERC5269 { + + bytes32 constant public EIP_FINAL = keccak256("FINAL"); + constructor() ERC721("ERC721ForTesting", "E721FT") ERC5269() { + _mint(msg.sender, 0); + emit OnSupportEIP(address(0x0), 721, bytes32(0), EIP_FINAL, ""); + emit OnSupportEIP(address(0x0), 721, keccak256("ERC721Metadata"), EIP_FINAL, ""); + emit OnSupportEIP(address(0x0), 721, keccak256("ERC721Enumerable"), EIP_FINAL, ""); + } + + function supportEIP( + address caller, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata extraData) + external + override + view + returns (bytes32 eipStatus) { + if (majorEIPIdentifier == 721) { + if (minorEIPIdentifier == 0) { + return keccak256("FINAL"); + } else if (minorEIPIdentifier == keccak256("ERC721Metadata")) { + return keccak256("FINAL"); + } else if (minorEIPIdentifier == keccak256("ERC721Enumerable")) { + return keccak256("FINAL"); + } + } + return super._supportEIP(caller, majorEIPIdentifier, minorEIPIdentifier, extraData); + } +} + +``` + +See [`ERC721ForTesting.sol`](../assets/eip-5269/contracts/testing/ERC721ForTesting.sol). + +## Security Considerations + +Similar to [EIP-165](./eip-165.md) callers of the interface MUST assume the smart contract +declaring they support such EIP interfaces doesn't necessarily correctly support them. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5283.md b/EIPS/eip-5283.md new file mode 100644 index 00000000000000..19e3972228f5c0 --- /dev/null +++ b/EIPS/eip-5283.md @@ -0,0 +1,108 @@ +--- +eip: 5283 +title: Semaphore for Reentrancy Protection +description: A Precompile-based parallelizable reentrancy protection using the call stack +author: Sergio D. Lerner (@SergioDemianLerner) +discussions-to: https://ethereum-magicians.org/t/eip-5283-a-semaphore-for-parallelizable-reentrancy-protection/10236 +status: Stagnant +type: Standards Track +category: Core +created: 2022-07-17 +requires: 20, 1283, 1352 +--- + +## Abstract + +This EIP proposes adding a precompiled contract that provides a semaphore function for creating a new type of reentrancy protection guard (RPG). This function aims to replace the typical RPG based on modifying a contract storage cell. The benefit is that the precompile-based RPG does not write to storage, and therefore it enables contracts to be forward-compatible with all designs that provide fine-grained (i.e. cell level) parallelization for the multi-threaded execution of EVM transactions. + +## Motivation + +The typical smart contract RPG uses a contract storage cell. The algorithm is simple: the code checks that a storage cell is 0 (or any other predefined constant) on entry, aborting if not, and then sets it to 1. After executing the required code, it resets the cell back to 0 before exiting. This is the algorithm implemented in OpenZeppelin's ReentrancyGuard. The algorithm results in a read-write pattern on the RPG's storage cell. This pattern prevents the parallelization of the execution of the smart contract for all known designs that try to provide fine-grained parallelization (detecting conflicts at the storage cell level). + +Several EVM-based blockchains have successfully tested designs for the parallelization of the EVM. The best results have been obtained with fine-grained parallelization where conflicts are detected by tracking writes and reads of individual storage cells. The designs based on tracking the use of accounts or contracts provide only minor benefits because most transactions use the same [EIP-20](./eip-20.md) contracts. + +To summarize, the only available RPG construction today is based on using a contract storage cell. This construction is clean but it is not forward-compatible with transaction execution parallelization. + +## Specification + +Starting from an activation block (TBD) a new precompiled contract `Semaphore` is created at address `0x0A`. When `Semaphore` is called, if the caller address is present more than once in the call stack, the contract behaves as if the first instruction had been a `REVERT`, therefore the CALL returns 0. Otherwise, it executes no code and returns 1. The gas cost of the contract execution is set to 100, which is consumed independently of the call result. + +## Rationale + +The address `0x0A` is the next one available within the range defined by [EIP-1352](./eip-1352). + +### Sample usage + +```solidity +pragma solidity ^0.8.0; + +abstract contract ReentrancyGuard2 { + + uint8 constant SemaphoreAddress = 0x0A; + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is supported. + */ + modifier nonReentrant() { + _nonReentrantBefore(); + _; + } + + function _nonReentrantBefore() private { + assembly { + if iszero(staticcall(1000,SemaphoreAddress, 0, 0, 0, 0)) { + revert(0, 0) + } + } + } +} +``` + +### Parallellizable storage-based RPGs + +The only way to paralellize prexistent contracts that are using the storage RPG construction is that the VM automatically detects that a storage variable is used for the RPG, and proves that is works as required. This requires static code analysys. This is difficult to implement in consensus for two reasons. First, the CPU cost of detection and/or proving may be high. Second, some contract functions may not be protected by the RPG, meaning that some execution paths do not alter the RPG, which may complicate proving. Therefore this proposal aims to protect future contracts and let them be parallelizable, rather than to paralellize already deployed ones. + +### Alternatives + +There are alternative designs to implement RPGs on the EVM: + +1. Transient storage opcodes (`TLOAD`/`TSTORE`) provide contract state that is kept between calls in the same transaction but it is not committed to the world state afterward. These opcodes also enable fine-grained parallelization. +2. An opcode `SSTORE_COUNT` that retrieves the number of `SSTORE` instructions executed. It enables also fine-grained execution parallelization, but `SSTORE_COUNT` is much more complex to use correctly as it returns the number `SSTORE` opcodes executed, not the number of reentrant calls. Reentrancy must be deducted from this value. +3. A new `LOCKCALL` opcode that works similar to `STATICALL` but only blocks storage writes in the caller contract. This results in cheaper RPG, but it doesn't allow some contract functions to be free of the RPG. + +All these alternative proposals have the downside that they create new opcodes, and this is discouraged if the same functionality can be implemented with the same gas cost using precompiles. A new opcode requires modifying compilers, debuggers and static analysis tools. + +### Gas cost + +A gas cost of 100 represents a worst-case resource consumption, which occurs when the stack is almost full (approximately 400 addresses) and it is fully scanned. As the stack is always present in RAM, the scanning is fast. + +Note: Once code is implemented in geth, it can be benchmarked and the cost can be re-evaluated, as it may result to be lower in practice. As a precompile call currently costs 700 gas, the cost of stack scanning has a low impact on the total cost of the precompile call (800 gas in total). + +The storage-based RPG currently costs 200 gas (because of the savings introduced in [EIP-1283](./eip-1283.md). Using the `Semaphore` precompile as a reentrancy check would currently cost 800 gas (a single call from one of the function modifiers). While this cost is higher than the traditional RPG cost and therefore discourages its use, it is still much lower than the pre-EIP-1283 cost. If a reduction in precompile call cost is implemented, then the cost of using the `Semaphore` precompile will be reduced to approximately 140 gas, below the current 200 gas consumed by a storage-based RPG. To encourage to use of the precompile-based RPG, it is suggested that this EIP is implemented together with a reduction in precompile calls cost. + +## Backwards Compatibility + +This change requires a hard fork and therefore all full nodes must be updated. + +## Test Cases + +```solidity +contract Test is ReentrancyGuard2 { + function second() external nonReentrant { + } + function first() external nonReentrant { + this.second(); + } +} +``` + +A call to `second()` directly from a transaction does not revert, but a call to `first()` does revert. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5289.md b/EIPS/eip-5289.md new file mode 100644 index 00000000000000..2675ba044ce129 --- /dev/null +++ b/EIPS/eip-5289.md @@ -0,0 +1,95 @@ +--- +eip: 5289 +title: Ethereum Notary Interface +description: Allows Smart Contracts to be Legally Binding Off-Chain +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/pr-5289-discussion-notary-interface/9980 +status: Review +type: Standards Track +category: ERC +created: 2022-07-16 +requires: 165, 5568 +--- + +## Abstract + +Currently, the real-world applications of smart contracts are limited by the fact that they aren't legally binding. This EIP proposes a standard that allows smart contracts to be legally binding by providing IPFS links to legal documents and ensuring that the users of the smart contract have privity with the relevant legal documents. + +Please note that the authors are not lawyers, and that this EIP is not legal advice. + +## Motivation + +NFTs have oftentimes been branded as a way to hold and prove copyright of a specific work. However, this, in practice, has almost never been the case. Most of the time, NFTs have no legally-binding meaning, and in the rare cases that do, the NFT simply provides a limited license for the initial holder to use the work (but cannot provide any license for any future holders). + +## 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. + +### Legal Contract Library Interface + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC5289Library is IERC165 { + /// @notice Emitted when signDocument is called + event DocumentSigned(address indexed signer, uint16 indexed documentId); + + /// @notice An immutable link to the legal document (RECOMMENDED to be hosted on IPFS). This MUST use a common file format, such as PDF, HTML, TeX, or Markdown. + function legalDocument(uint16 documentId) external view returns (string memory); + + /// @notice Returns whether or not the given user signed the document. + function documentSigned(address user, uint16 documentId) external view returns (bool signed); + + /// @notice Returns when the the given user signed the document. + /// @dev If the user has not signed the document, the timestamp may be anything. + function documentSignedAt(address user, uint16 documentId) external view returns (uint64 timestamp); + + /// @notice Sign a document + /// @dev This MUST be validated by the smart contract. This MUST emit DocumentSigned or throw. + function signDocument(address signer, uint16 documentId) external; +} +``` + +### Requesting a Signature + +To request that certain documents be signed, revert with an [ERC-5568](./eip-5568.md) signal. The format of the `instruction_data` is an ABI-encoded `(address, uint16)` pair, where the address is the address of the library, and the `uint16` is the `documentId` of the document: + +```solidity +throw WalletSignal24(0, 5289, abi.encode(0xcbd99eb81b2d8ca256bb6a5b0ef7db86489778a7, 12345)); +``` + +### Signing a Document + +When a signature is requested, wallets MUST call `legalDocument`, display the resulting document to the user, and prompt them to either sign the document or cancel: + +![image](../assets/eip-5289/example-popup.png) + +If the user agrees, the wallet MUST call `signDocument`. + +## Rationale + +- `uint64` was chosen for the timestamp return type as 64-bit time registers are standard. +- `uint16` was chosen for the document ID as 65536 documents are likely sufficient for any use case, and the contract can always be re-deployed. +- `signDocument` doesn't take an ECDSA signature for future compatibility with account abstraction. In addition, future extensions can supply this functionality. +- IPFS is mandatory because the authenticity of the signed document can be proven. + +## Backwards Compatibility + +No backwards compatibility issues found. + +## Reference Implementation + +### Legal Contract Library + +See [`IERC5289Library`](../assets/eip-5289/interfaces/IERC5289Library.sol), [`ERC5289Library`](../assets/eip-5289/ERC5289Library.sol). + +## Security Considerations + +Users can claim that their private key was stolen and used to fraudulently "sign" contracts. As such, **documents must only be permissive in nature, not restrictive.** For example, a document granting a license to use the image attached to an NFT would be acceptable, as there is no reason for the signer to plausibly deny signing the document. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5298.md b/EIPS/eip-5298.md new file mode 100644 index 00000000000000..5f885097c80ab4 --- /dev/null +++ b/EIPS/eip-5298.md @@ -0,0 +1,153 @@ +--- +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: Review +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 identified by `bytes32 ensNode` (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). + +4. Any `ensNode` is allowed. + +## Rationale + +1. ENS was chosen because it is a well-established scoped ownership namespace. +This is nonetheless compatible with other scoped ownership namespaces. + +2. We didn't expose getters or setters for ensRoot because it is outside of the scope of this EIP. + +## 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-5313.md b/EIPS/eip-5313.md new file mode 100644 index 00000000000000..7c7e53f0cfe77f --- /dev/null +++ b/EIPS/eip-5313.md @@ -0,0 +1,61 @@ +--- +eip: 5313 +title: Light Contract Ownership +description: An interface for identifying ownership of contracts +author: William Entriken (@fulldecent) +discussions-to: https://ethereum-magicians.org/t/eip-5313-light-contract-ownership/10052 +status: Final +type: Standards Track +category: ERC +created: 2022-07-22 +requires: 165, 173 +--- + +## Abstract + +This specification defines the minimum interface required to identify an account that controls a contract. + +## Motivation + +This is a slimmed-down alternative to [EIP-173](./eip-173.md). + +## Specification + +The key word “MUST” in this document is to be interpreted as described in RFC 2119. + +Every contract compliant with this EIP MUST implement the `EIP5313` interface. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.15; + +/// @title EIP-5313 Light Contract Ownership Standard +interface EIP5313 { + /// @notice Get the address of the owner + /// @return The address of the owner + function owner() view external returns(address); +} +``` + +## Rationale + +Key factors influencing the standard: + +- Minimize the number of functions in the interface +- Backwards compatibility with existing contracts + +This standard can be (and has been) extended by other standards to add additional ownership functionality. The smaller scope of this specification allows more and more straightforward ownership implementations, see limitations explained in EIP-173 under "other schemes that were considered". + +Implementing [EIP-165](./eip-165.md) could be a valuable addition to this interface specification. However, this EIP is being written to codify existing protocols that connect contracts (often NFTs), with third-party websites (often a well-known NFT marketplace). + +## Backwards Compatibility + +Every contract that implements EIP-173 already implements this specification. + +## Security Considerations + +Because this specification does not extend EIP-165, calling this EIP's `owner` function cannot result in complete certainty that the result is indeed the owner. For example, another function with the same function signature may return some value that is then interpreted to be the true owner. If this EIP is used solely to identify if an account is the owner of a contract, then the impact of this risk is minimized. But if the interrogator is, for example, sending a valuable NFT to the identified owner of any contract on the network, then the risk is heightened. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5334.md b/EIPS/eip-5334.md new file mode 100644 index 00000000000000..e0c213975a2408 --- /dev/null +++ b/EIPS/eip-5334.md @@ -0,0 +1,115 @@ +--- +eip: 5334 +title: EIP-721 User And Expires And Level Extension +description: Add a time-limited role with restricted permissions to EIP-721 tokens. +author: Yan (@yan253319066) +discussions-to: https://ethereum-magicians.org/t/erc-721-user-and-expires-and-level-extension/10097 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-25 +requires: 165, 721 +--- + +## Abstract + +An [EIP-721](./eip-721.md) extension that adds an additional role (`user`) which can be granted to addresses, and a time where the role is automatically revoked (`expires`) and (`level`) . The `user` role represents permission to "use" the NFT, but not the ability to transfer it or set users. + +## Motivation + +Some NFTs have certain utilities. For example, virtual land can be "used" to build scenes, and NFTs representing game assets can be "used" in-game. In some cases, the owner and user may not always be the same. There may be an owner of the NFT that rents it out to a “user”. The actions that a “user” should be able to take with an NFT would be different from the “owner” (for instance, “users” usually shouldn’t be able to sell ownership of the NFT).  In these situations, it makes sense to have separate roles that identify whether an address represents an “owner” or a “user” and manage permissions to perform actions accordingly. + +Some projects already use this design scheme under different names such as “operator” or “controller” but as it becomes more and more prevalent, we need a unified standard to facilitate collaboration amongst all applications. + +Furthermore, applications of this model (such as renting) often demand that user addresses have only temporary access to using the NFT. Normally, this means the owner needs to submit two on-chain transactions, one to list a new address as the new user role at the start of the duration and one to reclaim the user role at the end. This is inefficient in both labor and gas and so an “expires” and “level” function is introduced that would facilitate the automatic end of a usage term without the need of a second transaction. + +Here are some of the problems that are solved by this standard: + +### Clear Rights Assignment + +With Dual “owner” and “user” roles, it becomes significantly easier to manage what lenders and borrowers can and cannot do with the NFT (in other words, their rights). Additionally, owners can control who the user is and it’s easy for other projects to assign their own rights to either the owners or the users. + +### Simple On-chain Time Management + +Once a rental period is over, the user role needs to be reset and the “user” has to lose access to the right to use the NFT. This is usually accomplished with a second on-chain transaction but that is gas inefficient and can lead to complications because it’s imprecise. With the `expires` function, there is no need for another transaction because the “user” is invalidated automatically after the duration is over. + +### Easy Third-Party Integration + +In the spirit of permission less interoperability, this standard makes it easier for third-party protocols to manage NFT usage rights without permission from the NFT issuer or the NFT application. Once a project has adopted the additional `user` role and `expires` and `level`, any other project can directly interact with these features and implement their own type of transaction. For example, a PFP NFT using this standard can be integrated into both a rental platform where users can rent the NFT for 30 days AND, at the same time, a mortgage platform where users can use the NFT while eventually buying ownership of the NFT with installment payments. This would all be done without needing the permission of the original PFP project. + +## 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 with NatSpec & OpenZeppelin v4 Interfaces (also available at [`IERC5334.sol`](../assets/eip-5334/IERC5334.sol)): + +```solidity +interface IERC5334 { + + // Logged when the user of a NFT, expires, or level is changed + /// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed or the user `level` is changed + /// The zero address for user indicates that there is no user address + event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires, uint8 level); + + /// @notice set the user and expires and level of a NFT + /// @dev The zero address indicates there is no user + /// Throws if `tokenId` is not valid NFT + /// @param user The new user of the NFT + /// @param expires UNIX timestamp, The new user could use the NFT before expires + /// @param level user level + function setUser(uint256 tokenId, address user, uint64 expires, uint8 level) external; + + /// @notice Get the user address of an NFT + /// @dev The zero address indicates that there is no user or the user is expired + /// @param tokenId The NFT to get the user address for + /// @return The user address for this NFT + function userOf(uint256 tokenId) external view returns(address); + + /// @notice Get the user expires of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user expires for + /// @return The user expires for this NFT + function userExpires(uint256 tokenId) external view returns(uint256); + + /// @notice Get the user level of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user level for + /// @return The user level for this NFT + function userLevel(uint256 tokenId) external view returns(uint256); +} +``` + +The `userOf(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `userExpires(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `userLevel(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `setUser(uint256 tokenId, address user, uint64 expires)` function MAY be implemented as `public` or `external`. + +The `UpdateUser` event MUST be emitted when a user address is changed or the user expires is changed or the user level is changed. + + + +## Rationale + +TBD + +## 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 +A reference implementation of this standard can be found in the assets folder. + + +## Security Considerations + +This EIP standard can completely protect the rights of the owner, the owner can change the NFT user and expires and level at any time. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-5345.md b/EIPS/eip-5345.md new file mode 100644 index 00000000000000..68bc9fffeb1f63 --- /dev/null +++ b/EIPS/eip-5345.md @@ -0,0 +1,126 @@ +--- +eip: 5345 +title: Silent Signing Extension for JSON-RPC +description: Temporary transaction signing without user interaction +author: Stanley Wu (@fruit37), Mücahit Büyükyılmaz (@anndro), Muhammed Emin Aydın (@muhammedea) +discussions-to: https://ethereum-magicians.org/t/walletconnect-silent-signing-extension/10137 +status: Stagnant +type: Standards Track +category: Interface +created: 2022-07-26 +--- + +## Abstract + +Mobile applications supporting lots of transactions might become a source of bad user experience due to uncontrolled switching between the wallet's and application's UI. By this proposal, we would like to introduce the means to sign and send wallet transactions without the need for user participation. This feature can be implemented by providing user consent for a specific time duration. We call the feature Silent Signing. + +## Motivation + +Some blockchain applications interact with a blockchain much more frequently than others. It is especially true for gaming applications having their own sidechains. Interrupting the gaming process and switching to the wallet to perform a transaction drastically affect the user experience. + +## Specification + +To remedy the situation, we'd like to introduce new RPC methods for the ethereum JSON-RPC. Those methods help enable wallets to implement the Silent Signing feature. + +### Silent Signing User Flow + +The Silent Signing process has the following structure: + +1. First, the application requests the wallet to use Silent Signing via the RPC's `wallet_requestSilentSign` method. +2. Second, the wallet prompts the user to confirm enabling the Silent Singing functionality for a specific time duration. +3. If the user does not confirm Silent Signing or the RPC method is not allowed, the application will continue using the regular methods. +4. If the user confirms Silent Signing, then each subsequent transaction will be sent using the `wallet_silentSendTransaction` method for the time duration specified. + +### Implementation + +The implementation introduces new RPC methods and flow for application and wallet side. + +### New RPC Methods + +#### `wallet_requestSilentSign` + +This RPC method opens the wallet and prompts the user to enable automatic signing for a specific time duration. This function grants the application to call the following methods until the timestamp expires. Standard methods like `eth_signTrancaction` remain untouched. + +```shell +Parameters + Object: request object + until: NUMBER - unix timesptamp, the end time the permission will be valid + chainId: NUMBER - the chain id that the contract located in + contractAddress: ADDRESS - address of the contract to be allowed + allowedFunctions: STRING ARRAY - allowed function signatures + Ex: ["equip(address,uint256)", "unequip(address,uint256)"] + description: STRING - extra description that can be shown to user by the wallet + +Returns + DATA, 20 Bytes: permissionSecret - a secret key for silent-signing requests (randomly generated) +``` + +#### `wallet_silentSignTransaction` + +This RPC method creates a transaction and sends its data to the wallet for signing. The wallet signs the data in the background, interfering with no processes the user is involved in. Afterward, the application sends the signed transaction to the blockchain using Nethereum's or other libraries' `sendRawTransaction` method. + +```shell +Parameters + DATA, 20 Bytes: permissionSecret - secret key obtained from `wallet_requestSilentSign` method + Object - The transaction object + from: DATA, 20 Bytes - The address the transaction is sent from. + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + gas: QUANTITY - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. + gasPrice: QUANTITY - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas, in Wei. + value: QUANTITY - (optional) Integer of the value sent with this transaction, in Wei. + data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. + nonce: QUANTITY - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. + +Returns + DATA, The signed transaction object. +``` + +#### `wallet_silentSendTransaction` + +This RPC method creates a transaction and sends it to the blockchain without interfering with the process the user is involved in. + +```shell +Parameters + DATA, 20 Bytes: permissionSecret - secret key obtained from `wallet_requestSilentSign` method + Object - The transaction object + from: DATA, 20 Bytes - The address the transaction is sent from. + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + gas: QUANTITY - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. + gasPrice: QUANTITY - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas. + value: QUANTITY - (optional) Integer of the value sent with this transaction. + data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. + nonce: QUANTITY - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. + +Returns + DATA, 32 Bytes - the transaction hash, or the zero hash if the transaction is not yet available. +``` + +### Application and Wallet Communication + +Sending RPC requests between application and wallet can be as usual. For example browser extension wallets can use these new methods easily. Even hardware wallets can implement this too. But for mobile wallets extra communication techniques should be considered. Because mobile wallets can be inactive when it is not in use. + +Mobile wallets mostly use Walletconnect protocol. The application closed or active in the background can't connect to the Bridge server via WebSocket. Therefore, we have to trigger the wallet to connect to the Bridge and to start waiting for requests. For this purpose, push notifications are to be used. That means that only the wallets supporting push notifications can implement the feature. + +![](../assets/eip-5345/walletconnect-flow.png) + +Whenever the wallet receives a push notification, it connects to the Bridge server and gets access to the pending requests. If there are `wallet_silenSignTransaction` or `wallet_silentSendTransaction` silent signing requests pending and the interaction with the requesting client has been confirmed for this particular time duration, then the wallet executes the request without interfering with the ongoing user activity. + +## Rationale + +Games and Metaverse applications imply lots of cases when the user interacts with the wallet, switching to it and approving transactions. This switching aspect might interfere with gaming per se and create a bad user experience. That is why such applications can benefit if the wallets can support the Silent Signing functionality allowing transactions to be signed with no user interaction. + +## Backwards Compatibility + +These new RPC methods don't interfere with the current ones, and for mobile wallets the push notifications API is currently a part of the `WalletConnect` specification. Implementing the proposal's functionality changes nothing for other applications and wallets. + +## Security Considerations + +The proposed feature aims to improve the user experience and can only be enabled with user consent. Users might freely choose to use the application as usual. + +Silent Signing permission has restrictions that makes it more secure. +* Permission granted only for a specified time duration +* Permission granted only for specific contract in a specific chain and restricted to specified functions. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5375.md b/EIPS/eip-5375.md new file mode 100644 index 00000000000000..3eaa5516c7ee1f --- /dev/null +++ b/EIPS/eip-5375.md @@ -0,0 +1,304 @@ +--- +eip: 5375 +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: Final +type: Standards Track +category: ERC +created: 2022-07-30 +requires: 55, 155, 712, 721, 1155 +--- + +## Abstract + +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 standard to identify authors of an NFT, and existing techniques have issues: + +- 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 + - Does not support multiple authors +- Using a custom method/custom JSON field + - 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. 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 +- NFTs minted by an intermediary (which can be particularly useful when the author is not tech-savvy and/or the minting process is convoluted) + +This document thus defines a standard which allows the minter to provide authorship information, while also preventing authorship claims without the author's consent. + +## 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. + +All addresses used in this standard MUST follow the casing rules described in [EIP-55](./eip-55.md). + +### Definitions + +- **Authors**: creators of an NFT +- **Minter**: entity responsible for the actual minting transaction; the minter and the authors MAY be the same +- **Verifier**: entity that wants to verify the authorship of an NFT (e.g. a user or an NFT marketplace) +- **Author Consent Proof (ACP)**: a signed message that proves that the signer agrees to be considered the author of the NFT + +### Authorship Support + +The standard introduces a new JSON field, named `authorInfo`. It provides a REQUIRED interface for authorship claiming, as well as an OPTIONAL interface for author consent proofs. + +`authorInfo` is a top-level field of the NFT metadata. Specifically: + +- If a contract supports the metadata extension for [EIP-721](./eip-721.md), the JSON document pointed by `tokenURI(uint256 _tokenId)` MUST include the top-level field `authorInfo` +- If a contract supports the metadata extension for [EIP-1155](./eip-1155.md), the JSON document pointed by `uri(uint256 _id)` MUST include a top-level field `authorInfo` + +The JSON schema of `authorInfo` (named `ERC5375AuthorInfoSchema`) is defined as follows: + +```json +{ + "type": "object", + "properties": { + "consentInfo": { + "type": "object", + "description": "Helper fields for consent verification", + "properties": { + "chainId": { + "type": "integer", + "description": "EIP-155 chain id" + }, + "id": { + "type": "string", + "description": "NFT id" + }, + "contractAddress": { + "type": "string", + "description": "0x-prefixed address of the smart contract" + } + } + }, + "authors": { + "type": "array", + "items": "ERC5375AuthorSchema" + } + }, + "required": [ "authors" ] +} +``` + +Note that `authors` MAY be an empty array. + +`ERC5375AuthorSchema` is defined as follows: + +```json +{ + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "0x-prefixed address of the author" + }, + "consent": { + "type": "ERC5375AuthorConsentSchema", + "description": "Author consent information" + } + }, + "required": [ "address" ] +} +``` + +Moreover, if the `consent` field is present, the `consentInfo` field of `authorInfo` MUST be present. + +`ERC5375AuthorConsentSchema` is defined as follows: + +```json +{ + "type": "object", + "properties": { + "consentData": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "NFT authorship consent schema version" + }, + "issuer": { + "type": "string", + "description": "0x-prefixed address of the author" + }, + "metadataFields": { + "type": "object" + } + }, + "required": ["version", "issuer", "metadataFields"] + }, + "publicKey": { + "type": "string", + "description": "EVM public key of the author" + }, + "signature": { + "type": "string", + "description": "EIP-712 signature of the consent message" + } + }, + "required": ["consentData", "publicKey", "signature"] +} +``` + +where `metadataFields` is an object containing the JSON top-level fields (excluding `authorInfo`) that the author will certify. Note that the keys of `metadataFields` MAY be a (potentially empty) subset of the set of fields. + +`consentData` MAY support additional fields as defined by other EIPs. `consentData` MUST contain all the information (which is not already present in other fields) required to verify the validity of an authorship consent proof. + +### Author Consent + +Consent is obtained by signing an [EIP-712](./eip-712.md) compatible message. Specifically, the structure is defined as follows: + +```solidity +struct Author { + address subject; + uint256 tokenId; + string metadata; +} +``` + +where `subject` is the address of the NFT contract, `tokenId` is the id of the NFT and `metadata` is the JSON encoding of the fields listed in `metadataFields`. `metadata`: + +- MUST contain exactly the same fields as the ones listed in `metadataFields`, in the same order +- MUST escape all non-ASCII characters. If the escaped character contains hexadecimal letters, they MUST be uppercase +- MUST not contain any whitespace that is not part of a field name or value + +For example, if the top-level JSON fields are: + +```json +{ + "name": "The Holy Hand Grenade of Antioch", + "description": "Throw in the general direction of your favorite rabbit, et voilà", + "damage": 500, + "authors": [...], + ... +} +``` + +and the content of `metadataFields` is `["name", "description"]`, the content of `metadata` is: + +```json +{ + "name": "The Holy Hand Grenade of Antioch", + "description": "Throw in the general direction of your favorite rabbit, et voil\u00E0" +} +``` + +Similarly to `consentData`, this structure MAY support additional fields as defined by other EIPs. + +The domain separator structure is + +```solidity +struct EIP712Domain { + string name; + string version; + uint256 chainId; +} +``` + +where `name` and `version` are the same fields described in `consentData` + +This structure MAY support additional fields as defined by other EIPs. + +### Author Consent Verification + +Verification is performed using EIP-712 on an author-by-author basis. Specifically, given a JSON document D1, a consent proof is valid if all of the following statements are true: + +- D1 has a top-level `authorInfo` field that matches `ERC5375AuthorInfoSchema` +- `consent` exists and matches `ERC5375AuthorConsentSchema`; +- If calling `tokenURI` (for EIP-721) or `uri` (for EIP-1155) returns the URI of a JSON document D2, all the top-level fields listed in `metadataFields` MUST exist and have the same value; +- The EIP-712 signature in `signature` (computed using the fields specified in the JSON document) is valid; + +Verifiers MUST NOT assume that an NFT with a valid consent proof from address X means that X is the actual author. On the other hand, verifiers MAY assume that if an NFT does not provide a valid consent proof for address X, then X is not the actual author. + +## Rationale + +### Why provide only an author consent proof? + +Adding support for full authorship proofs (i.e. Alice is the author and no one else is the author) requires a protocol to prove that someone is the only author of an NFT. +In other words, we need to answer the question: "Given an NFT Y and a user X claiming to be the author, is X the original author of Y?". + +For the sake of the argument, assume that there exists a protocol that, given an NFT Y, can determine the original author of Y. Even if such method existed, an attacker could slightly modify Y, thus obtaining a new NFT Y', and rightfully claim to be the author of Y', despite the fact that it is not an original work. Real-world examples include changing some pixels of an image or replacing some words of a text with synonyms. +Preventing this behavior would require a general formal definition of when two NFTs are semantically equivalent. Even if defining such a concept were possible, it would still be beyond the scope of this EIP. + +Note that this issue is also present when using the minter's address as a proxy for the author. + +### Why off-chain? + +There are three reasons: + +- Adding off-chain support does not require modifications to existing smart contracts; +- Off-chain storage is usually much cheaper than on-chain storage, thus reducing the implementation barrier; +- While there may be some use cases for full on-chain authorship proofs (e.g. a marketplace providing special features for authors), there are limited applications for on-chain author consent, due to the fact that it is mostly used by users to determine the subjective value of an NFT. + +### Why repeat id, chainId and contractAddress? + +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? + +Authorship is usually final: either someone created an NFT or they didn't. Moreover, a revocation system would impose additional implementation requirements on smart contracts and increase the complexity of verification. Smart contracts MAY implement a revocation system, such as the one defined in other EIPs. + +#### Why escape non-ASCII characters in the signature message? + +EIP-712 is designed with the possibility of on-chain verification in mind; while on-chain verification is not a priority for this EIP, non-ASCII characters are escaped due to the high complexity of dealing with non-ASCII strings in smart contracts. + +### Usability Improvements for Authors + +Since the author only needs to sign an EIP-712 message, this protocol allows minters to handle the technical aspects of minting while still preserving the secrecy of the author's wallet. Specifically, the author only needs to: + +- 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; +- 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. + +### Limitations of Address-Based Consent + +The standard defines a protocol to verify that a certain _address_ provided consent. However, it does not guarantee that the address corresponds to the expected author (such as the one provided in the `name` field). Proving a link between an address and the entity behind it is beyond the scope of this document. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Security Considerations + +### Attacks + +A potential attack that exploits this EIP involves tricking authors into signing authorship consent messages against their wishes. For this reason, authors MUST verify that all signature fields match the required ones. + +A more subtle approach involves not adding important fields to `metadataFields`. By doing so, the author signature might be valid even if the minter changes critical information. + +### Deprecated Features + +`ERC5375AuthorInfoSchema` also originally included a field to specify a human-readable name for the author (without any kind of verification). This was scrapped due to the high risk of author spoofing, i.e.: + +- Alice mints an NFT using Bob's name and Alice's address +- Charlie does not check the address and instead relies on the provided name +- Charlie buys Alice's NFT while believing that it was created by Bob + +For this reason, smart contract developers SHOULD NOT add support for unverifiable information to the JSON document. We believe that the most secure way to provide complex authorship information (e.g. the name of the author) is to prove that the information is associated with the _author's address_, instead of with the NFT itself. + +### Replay Attack Resistance + +The chain id, the contract address and the token id uniquely identify an NFT; for this reason, there is no need to implement additional replay attack countermeasures (e.g. a nonce system). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5380.md b/EIPS/eip-5380.md new file mode 100644 index 00000000000000..320784e5252558 --- /dev/null +++ b/EIPS/eip-5380.md @@ -0,0 +1,176 @@ +--- +eip: 5380 +title: ERC-721 Entitlement Extension +description: Allows token owners to grant the ability for others to use specific properties of those tokens +author: Gavin John (@Pandapip1), Tim Daubenschütz (@TimDaub) +discussions-to: https://ethereum-magicians.org/t/pr-5380-eip-4907-alternative-design/10190 +status: Final +type: Standards Track +category: ERC +created: 2022-03-11 +requires: 165, 721, 1046 +--- + +## Abstract + +This EIP proposes a new interface that allows [ERC-721](./eip-721.md) token owners to grant limited usage of those tokens to other addresses. + +## Motivation + +There are many scenarios in which it makes sense for the owner of a token to grant certain properties to another address. One use case is renting tokens. If the token in question represents a trading card in an on-chain TCG (trading card game), one might want to be able to use that card in the game without having to actually buy it. Therefore, the owner might grant the renter the "property" of it being able to be played in the TCG. However, this property should only be able to be assigned to one person at a time, otherwise a contract could simply "rent" the card to everybody. If the token represents usage rights instead, the property of being allowed to use the associated media does not need such a restriction, and there is no reason that the property should be as scarce as the token. + +## 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. + +### Base + +Compliant entitlement contracts MUST implement the following Solidity interface: + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface ERC5380Entitlement is ERC165 { + /// @notice Emitted when the amount of entitlement a user has changes. If user is the zero address, then the user is the owner + event EntitlementChanged(address indexed user, address indexed contract, uint256 indexed tokenId); + + /// @notice Set the user associated with the given ERC-721 token as long as the owner is msg.sender. + /// @dev SHOULD NOT revert if the owner is not msg.sender. + /// @param user The user to grant the entitlement to + /// @param contract The property to grant + /// @param tokenId The tokenId to grant the properties of + function entitle(address user, address contract, uint256 tokenId) external; + + /// @notice Get the maximum number of users that can receive this entitlement + /// @param contract The contract to query + /// @param tokenId The tokenId to query + function maxEntitlements(address contract, uint256 tokenId) external view (uint256 max); + + /// @notice Get the user associated with the given contract and tokenId. + /// @dev Defaults to maxEntitlements(contract, tokenId) assigned to contract.ownerOf(tokenId) + /// @param user The user to query + /// @param contract The contract to query + /// @param tokenId The tokenId to query + function entitlementOf(address user, address contract, uint256 tokenId) external view returns (uint256 amt); +} +``` + +`supportsInterface` MUST return true when called with `ERC5380Entitlement`'s interface ID. + +### Enumerable Extension + +This OPTIONAL Solidity interface is RECOMMENDED. + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface ERC5380EntitlementEnumerable is ERC5380Entitlement { // Also implicitly supports ERC-165 + /// @notice Enumerate tokens with nonzero entitlement assigned to a user + /// @dev Throws if the index is out of bounds or if user == address(0) + /// @param user The user to query + /// @param index A counter + function entitlementOfUserByIndex(address user, uint256 index) external view returns (address contract, uint256 tokenId); +} +``` + +`supportsInterface` MUST return true when called with `ERC5380EntitlementEnumerable`'s interface ID. + +### Metadata Extension + +This OPTIONAL Solidity interface is RECOMMENDED. + +This extension uses [ERC-1046](./eip-1046.md) for `tokenURI` compatibility. + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface ERC5380EntitlementMetadata is ERC5380Entitlement { // Also implicitly supports ERC-165 + /// @notice ERC-1046 token URI + /// @dev See ERC-1046 and the metadata schema below + function tokenURI() external view returns (string); +} +``` + +`supportsInterface` MUST return true when called with `ERC5380EntitlementMetadata`'s interface ID. + +#### Interoperability Metadata Extension + +ERC-1046's `InteroperabilityMetadata` is extended with the following TypeScript interface: + +```typescript +/** + * ERC-5380's extension to ERC-1046's Interoperability metadata. + */ +interface ERC5380InteroperabilityMetadata is InteroperabilityMetadata { + /** + * This MUST be true if this is ERC-5380 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as an ERC-5380 entitlement. + **/ + erc5380?: boolean | undefined; +} +``` + +#### `tokenURI` Metadata Schema + +The resolved `tokenURI` data MUST conform to the following TypeScript interface: + +```typescript +/** + * ERC-5380 Asset Metadata + * Can be extended + */ +interface ERC5380TokenMetadata { + /** + * Interoperabiliy, to differentiate between different types of tokens and their corresponding URIs. + **/ + interop: ERC5380InteroperabilityMetadata; + + /** + * The name of the ERC-5380 token. + */ + name?: string; + + /** + * The symbol of the ERC-5380 token. + */ + symbol?: string; + + /** + * Provides a short one-paragraph description of the ERC-5380 token, without any markup or newlines. + */ + description?: string; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represents this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels + * Images SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive. + */ + images?: string[]; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represent an icon for this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels, and MUST have a height equal to its width + * Images MUST have an aspect ratio of 1:1, and use a transparent background + */ + icons?: string[]; +} +``` + +## Rationale + +[ERC-20](./eip-20.md) and [ERC-1155](./eip-1155.md) are unsupported as partial ownership is much more complex to track than boolean ownership. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Security Considerations + +The security considerations of [ERC-721](./eip-721.md) and [ERC-1046](./eip-1046.md) apply. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5409.md b/EIPS/eip-5409.md new file mode 100644 index 00000000000000..a276e696cee28c --- /dev/null +++ b/EIPS/eip-5409.md @@ -0,0 +1,61 @@ +--- +eip: 5409 +title: EIP-1155 Non-Fungible Token extension +description: Allow EIP-1155 to represent Non-Fungible Tokens (tokens who have a unique owner) +author: Ronan Sandford (@wighawag) +discussions-to: https://ethereum-magicians.org/t/eip-5409-non-fungible-token-extension-for-eip-1155/10240 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-07-23 +requires: 165, 721, 1155 +--- + +## Abstract + +This standard is an extension of [EIP-1155](./eip-1155.md). It proposes an additional function, `ownerOf`, which allows EIP-1155 tokens to support Non-Fungibility (unique owners). By implementing this extra function, EIP-1155 tokens can benefit from [EIP-721](./eip-721.md)'s core functionality without implementing the (less efficient) EIP-721 specification in the same contract. + +## Motivation + +Currently, EIP-1155 does not allow an external caller to detect whether a token is truly unique (can have only one owner) or fungible. This is because EIP-1155 do not expose a mechanism to detect whether a token will have its supply remain to be "1". Furthermore, it does not let an external caller retrieve the owner directly on-chain. + +The EIP-1155 specification does mention the use of split id to represent non-fungible tokens, but this requires a pre-established convention that is not part of the standard, and is not as simple as EIP-721's `ownerOf`. + +The ability to get the owner of a token enables novel use-cases, including the ability for the owner to associate data with it. + +## 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 IERC1155OwnerOf { + + /// @notice Find the owner of an NFT + /// @dev The zero address indicates that there is no owner: either the token does not exist or it is not an NFT (supply potentially bigger than 1) + /// @param tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 tokenId) external view returns (address); +} +``` + +The `ownerOf(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `supportsInterface` method MUST return `true` when called with `0x6352211e`. + +## Rationale + +`ownerOf` does not throw when a token does not exist (or does not have an owner). This simplifies the handling of such a case. Since it would be a security risk to assume all EIP-721 implementation would throw, it should not break compatibility with contract handling EIP-721 when dealing with this EIP-1155 extension. + +## Backwards Compatibility + +This EIP is fully backward compatible with EIP-1155. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5437.md b/EIPS/eip-5437.md new file mode 100644 index 00000000000000..5d1058c94dfd9c --- /dev/null +++ b/EIPS/eip-5437.md @@ -0,0 +1,154 @@ +--- +eip: 5437 +title: Security Contact Interface +description: An interface for security notice using asymmetric encryption +author: Zainan Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-interface-for-security-contract/10303 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-09 +requires: 165 +--- + +## Abstract +An interface for security notice using asymmetric encryption. The interface exposes a asymmetric encryption key and a destination of delivery. + +## Motivation +Currently there is no consistent way to specify an official channel for security researchers to report security issues to smart contract maintainers. + +## 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 +interface IEIP5437 { + + /// REQUIRED + function getSecurityContact(uint8 type, bytes memory data) public view + returns ( + uint8 type, + bytes memory publicKey, + bytes memory extraData + ); + + /// OPTIONAL + // TODO consider remove if not needed before finalized + function setSecurityContact( + uint8 type, + bytes memory publicKey, + bytes memory extraData) public; + event SecurityContactChanged(uint8 type, bytes memory publicKeyForEncryption, bytes memory extraData); + + /// OPTIONAL + function securityNotify(uint8 type, bytes memory data) public payable; + /// OPTIONAL + event OnSecurityNotification(uint8 type, bytes memory sourceData, uint256 value); + + /// OPTIONAL + // TODO consider to make it a separate EIP + function bountyPolicy(uint256 id) public view returns(string, bytes memory extraData); +} +``` + +1. Compliant interfaces MUST implement the `getSecurityContact` method. + +`type` is a one byte data with valid range of `[0x10, 0x7f]`. The ranges of `[0x00, 0x0f]` and `[0x80, 0xff]` are reserved for future extension. + +The `type` indicates the format of the `publicKey` and `extraData` in the following way + +------------------------------------------------------------------------------------------------ +| Type | Encryption scheme | extraData | +-------|-------------------------------------|-------------------------------------------------- +| 0x10 | GnuPG - RSA/3072 | Email address(es) encoded in format of RFC 2822 | +------------------------------------------------------------------------------------------------ + +A new version of this table can be proposed by future EIPs by specifying a new `type` number. + +2. The `publicKey` returned from `getSecurityContact` MUST follow the encryption scheme specified +in the table above. + +The following is an example of a `publicKey` using `RSA/3072` generated via GnuPG in an RFC 20 ASCII-encoding of the public key string: + +```text +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGLzM2YBDADnCxAW/A0idvKNeQ6s/iYUeIIE+2mWmHcBGqLi0zrfz7pKWI+D +m6Hek51sg2c7ZlswPEp8KqANrj/CV1stXHF+KAZtYeFiAqpIZl1wtB6QgKYWGsJf +sXjBU3duLzLut2yvTfbEZsWAvrEaDjlXywdpboorHvfTE2vOvI6iGcjdh7PW7W7g +IGzlL6ukLGG7y9FUO2dSMjCR/tWMLCupnDDLN2cUHnfEnHZ34FMd61NxcHLC7cIk +P8xkFt8GCxURniTjqI5HAB8bGfR34kflVpr2+iKD5e+vQxcWK7vB443nruVf8osn +udDF8Z6mgl7bKBbGyYH58QsVlmZ8g3E4YaMKjpwOzEK3V2R8Yh4ETdr670ZCRrIz +QWVkibGgmQ3J/9RYps5Hfqpj4wV60Bsh1xUIJEIAs3ubMt7Z5JYFeze7VlXGlwot +P+SnAfKzlZT4CDEl2LEEDrbpnpOEdp0x9hYsEaXTxBGSpTDaxP2MyhW3u6pYeehG +oD0UVTLjWgU+6akAEQEAAbQjc29tZXJlYWxuYW1lIDxncGcubG9jYWwuZ2VuQHp6 +bi5pbT6JAdQEEwEIAD4WIQTDk/9jzRZ+lU2cY8rSVJNbud1lrQUCYvMzZgIbAwUJ +EswDAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDSVJNbud1lraulDACqFbQg +e9hfoK17UcPVz/u4ZnwmFd9zFAWSYkGqrK9XMvz0R8pr7Y3Dp5hfvaptqID/lHhA +2oPEZ1ViIYDBcqG9WoWjCOYNoIosEAczrvf8YtUC2MHI+5DdYHtST74jDLuWMw3U +AbBXHds3KcRY5/j01kqqi4uwsMBCYyH3Jl3IwjKgy0KDBbuQakvaHPmNnt81ayvZ +ucdsNB9n/JMDxUWNCcySR+cllW4mk68pdiuK5qw0JMaoUjHFoWsgMTbFSlAV/lre +qu8MnrLSs5iPvvaJ3uDOuYROB2FsbvWxayfAAVS1iZf2vQFBJPnDwDdYoPNYMjLp +s2SfU02MVRGp3wanbtvM52uP42SLLNjBqUvJV03/QwfxCRejgAJOBn+iaOxP9NOe +qfQdKzYPbA9FohdkL9991n21XBZcZzAgF9RyU9IZAPAnwZyex1zfzJsUp/HrjhP8 +Ljs8MIcjIlmpLk66TmJte4dN5eML1bpohmfMX8k0ILESLSUhxEg1JBNYIDK5AY0E +YvMzZgEMALnIkONpqCkV+yaP8Tb8TBjmM+3TioJQROViINUQZh6lZM3/M+DPxAWZ +r0MIh1a3+o+ThlZ70tlS67w3Sjd62sWAFzALzW4F+gTqjBTh6LURDqDV8OXUrggA +SKK222aDP+Fr21h/TtPLeyDvcgm8Xvi4Cy7Jmf5CfT5jDio7a+FyFBNlTFSVqzLM +TgFOkUFBg8kJKvDjWIrS2fcTkELwZ8+IlQ52YbrXwbDar843x1fRmsY+x9nnuGuP +RYn1U4Jbptu2pEkG5q94jzUzTkGZHCzBJY7a8mtvS0mLqIE0Se1p+HFLY76Rma/F +HB6J4JNOTzBZ0/1FVvUOcMkjuZ2dX81qoCZ8NP6eafzKvNYZrGa5NJnjWO1ag5jQ +D8qHuOwxs8Fy9evmkwAVl51evLFNT532I4LK0zHSbF8MccZjpEFMSKwalKJn02Ml +yTd+ljYLf8SKMOLVps8kc4VyMR1lz0PwSpKDFOmkC1LRURpM7UTtCK+/RFg1OLyQ +SKBmdI37KQARAQABiQG8BBgBCAAmFiEEw5P/Y80WfpVNnGPK0lSTW7ndZa0FAmLz +M2YCGwwFCRLMAwAACgkQ0lSTW7ndZa2oFgv8DAxHtRZchTvjxtdLhQEUSHt80JCQ +zgHd7OUI9EU3K+oDj9AKtKZF1fqMlQoOskgBsLy/xpWwyhatv2ONLtHSjYDkZ7qs +jsXshqpuvJ3X00Yn9PXG1Z1jKl7rzy2/0DnQ8aFP+gktfu2Oat4uIu4YSqRsVW/Z +sbdTsW3T4E6Uf0qUKDf49mK3Y2nhTwY0YZqJnuQkSuUvpuM5a/4zSoaIRz+vSNjX +MoXUIK/f8UnWABPm90OCptTMTzXCC1UXEHTNm6iBJThFiq3GeLZH+GnIola5KLO1 ++YbsFEchLfLZ27pWGfIbyppvsuQmrHef+J3g6sXybOWDHVYr3Za1fzxQVIbwoIEe +ndKG0bu7ZAi2b/c8uH/wHT5IvtfzHLeSTjDqG8UyLTnaDxHQZIE9JIzWSQ1DSoNC +YrU7CQtL+/HRpiGFHfClaXln8VWkjnUvp+Fg1ZPtE1t/SKddZ7m29Hd9nzUc0OQW +MOA+HDqgA3a9kWbQKSloORq4unft1eu/FCra +=O6Bf +-----END PGP PUBLIC KEY BLOCK----- +``` + +3. IF `setSecurityContact` is implemented and a call to it has succeeded in setting a new security contact, an event `SecurityContactChanged` MUST be emitted with the identical passed-in-parameters of `setSecurityContact` + +4. It's also RECOMMENDED that an on-chain security notify method `securityNotify` +to implemented to receive security notice onchain. If it's implemented and a call +has succeeded, it MUST emit an `OnSecurityNotification` with identical pass-in-parameter data. + +5. Compliant interfaces MUST implement [EIP-165](./eip-165.md). + + + +6. It's recommended to set a bounty policy via `bountyPolicy` method. The `id = 0` is preserved for a full overview, while other digits are used for different individual bounty policies. The returned +string will be URI to content of bounty policies. +No particular format of bounty policy is specified. + +## Rationale +1. For simplicity, this EIP specifies a simple GPG scheme with a given encryption scheme and uses email addresses as a contact method. It's possible that future EIPs will specify new encryption schemes or delivery methods. +2. This EIP adds an optional method, `setSecurityContact`, to set the security contact, because it might change due to circumstances such as the expiration of the cryptographic keys. +3. This EIP explicitly marks `securityNotify` as `payable`, in order to allow implementers to set a staking amount to report a security vulnerability. +4. This EIP allows for future expansion by adding the `bountyPolicy` the `extraData` fields. Additional values of these fields may be added in future EIPs. + +## Backwards Compatibility +Currently, existing solutions such as OpenZeppelin use plaintext in source code + +```solidity +/// @custom:security-contact some-user@some-domain.com +``` + +It's recommend that new versions of smart contracts adopt this EIP in addition to the legacy `@custom:security-contact` approach. + +## Security Considerations + +Implementors should properly follow security practices required by the encryption scheme to ensure the security of the chosen communication channel. Some best practices are as follows: + +1. Keep security contact information up-to-date; +2. Rotate encryption keys in the period recommended by best practice; +3. Regularly monitor the channel to receive notices in a timely manner. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5450.md b/EIPS/eip-5450.md new file mode 100644 index 00000000000000..d419ce19f625cb --- /dev/null +++ b/EIPS/eip-5450.md @@ -0,0 +1,129 @@ +--- +eip: 5450 +title: EOF - Stack Validation +description: Deploy-time validation of stack usage for EOF functions. +author: Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Danno Ferrin (@shemnon) +discussions-to: https://ethereum-magicians.org/t/eip-5450-eof-stack-validation/10410 +status: Review +type: Standards Track +category: Core +created: 2022-08-12 +requires: 3540, 3670, 4200, 4750 +--- + +## Abstract + +Introduce extended validation of EOF code sections to guarantee that neither stack underflow nor overflow can happen during execution of validated contracts. + +## Motivation + +The current EVM performs a number of validity checks for each executed instruction, such as checking +for instruction being defined, stack overflow and underflow, and enough amount of gas remaining. + +This EIP minimizes the number of such checks required at run-time +by verifying that no exceptional conditions can happen +and preventing the execution and deployment of any invalid code. + +The operand stack validation provides several benefits: + +- removes run-time stack underflow check for all instructions, +- removes run-time stack overflow check for all instruction except `CALLF`, +- ensures that an execution terminates with one of the terminating instructions, +- prevents the deployment of code with unreachable instructions, which discourages the use of code sections for data storage. + +It also has some disadvantages: + +- adds constraints to the code structure (similar to JVM, CPython bytecode, WebAssembly and others); however, these constraints can be lifted in a backward-compatible manner if they are shown to be user-unfriendly, +- adds second validation pass; however, validation's computational and space complexity remains linear. + +The guarantees created by these validation rules also improve the feasabiliy of Ahead-Of-Time and Just-In-Time compilation of EVM code. Single pass transpilation passes can be safely executed with the code validation and advanced stack/register handling can be applied with the stack height validaitons. While not as impactful to a mainnet validator node that is bound mostly by storage state sizes, these can significantly speed up witness validation and other non-mainnet use cases. + +## Specification + +### Code validation + +*Remark:* We rely on the notions of *operand stack* and *type section* as defined by [EIP-4750](./eip-4750.md). + +Each code section is validated independently. + +#### Instructions validation + +In the first validation phase defined in [EIP-3670](./eip-3670.md) (and extended by [EIP-4200](./eip-4200.md) and [EIP-4750](./eip-4750.md)) instructions are inspected independently to check if their opcodes and immediate values are valid. + +#### Operand stack validation + +In the second validation phase control-flow analysis is performed on the code. + +*Operand stack height* here refers to the number of stack values accessible by this function, i.e. it does not take into account values of caller functions' frames (but does include this function's inputs). Note that validation procedure does not require actual operand stack implementation, but only to keep track of its height. + +*Terminating instructions* refers to the instructions either: + +- ending function execution: `RETF`, or +- ending whole EVM execution: `STOP`, `RETURN`, `REVERT`, `INVALID`. + +For each reachable instruction in the code the operand stack height is recorded. +The first instruction has the recorded stack height equal to the number of inputs to the function type matching the code (`type[code_section_index].inputs`). + +The FIFO queue *worklist* is used for tracking "to be inspected" instructions. Initially, it contains the first instruction. + +While *worklist* is not empty, dequeue an instruction and: + +1. Determine the effect the instruction has on the operand stack: + 1. **Check** if the recorded stack height satisfies the instruction requirements. Specifically: + - for `CALLF` instruction the recorded stack height must be at least the number of inputs of the called function according to its type defined in the type section. + - for `RETF` instruction the recorded stack height must be exactly the number of outputs of the function matching the code. + 2. Compute new stack height after the instruction execution. +2. Determine the list of successor instructions that can follow the current instructions: + 1. The next instruction for all instructions other than terminating instructions and unconditional jump. + 2. All targets of a conditional or unconditional jump. +3. For each successor instruction: + 1. **Check** if the instruction is present in the code (i.e. execution must not "fall off" the code). + 2. If the instruction does not have stack height recorded (visited for the first time): + 1. Record the instruction stack height as the value computed in 1.2. + 2. Add the instruction to the *worklist*. + 3. Otherwise, **check** if the recorded stack height equals the value computed in 1.2. + +When *worklist* is empty: + +1. **Check** if all instruction were reached by the analysis. +2. Determine the function maximum operand stack height: + 1. Compute the maximum stack height as the maximum of all recorded stack heights. + 2. **Check** if the maximum stack height does not exceed the limit of 1023 (`0x3FF`). + 3. **Check** if the maximum stack height matches the corresponding code section's `max_stack_height` within the type section. + +The computational and space complexity of this pass is *O(len(code))*. Each instruction is visited at most once. + +### Execution + +Given the deploy-time validation guarantees, an EVM implementation is not required anymore to have run-time stack underflow nor overflow checks for each executed instruction. The exception is the `CALLF` performing operand stack overflow check for the entire called function. + +## Rationale + +### Stack overflow check only in CALLF + +In this EIP, we provide a more efficient variant of the EVM where stack overflow check is performed only in `CALLF` instruction using the called function's `max_stack_height` information. This decreases flexibility of an EVM program because `max_stack_height` corresponds to the worst-case control-flow path in the function. + +### Unreachable code + +The operand stack validation algorithm rejects any code having any unreachable instructions. This check can be performed very cheaply. It prevents deploying degenerated code. Moreover, it enables combining instruction validation and operand stack validation into single pass. + +### Clean stack upon termination + +It is currently required that the operand stack is empty (in the current function context) after the `RETF` instruction. +Otherwise, the `RETF` semantic would be more complicated. For `n` function outputs and `s` the stack height at `RETF` the EVM must erase `s-n` non-top stack items and move the `n` stack items to the place of erased ones. Cost of such operation may be relatively cheap but is not constant. +However, lifting the requirement and modifying the `RETF` semantic as described above is backward +compatible and can be easily introduced in the future. + +## Backwards Compatibility + +This change requires a "network upgrade", since it modifies consensus rules. + +It poses no risk to backwards compatibility, as it is introduced only for EOF1 contracts, for which deploying undefined instructions is not allowed, therefore there are no existing contracts using these instructions. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted). + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5453.md b/EIPS/eip-5453.md new file mode 100644 index 00000000000000..322cc3b2ceced4 --- /dev/null +++ b/EIPS/eip-5453.md @@ -0,0 +1,345 @@ +--- +eip: 5453 +title: Endorsement - Permit for Any Functions +description: A general protocol for approving function calls in the same transaction rely on EIP-5750. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-5453-endorsement-standard/10355 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-12 +requires: 165, 712, 1271, 5750 +--- + +## Abstract + +This EIP establish a general protocol for permitting approving function calls in the same transaction rely on [EIP-5750](./eip-5750.md). +Unlike a few prior art ([EIP-2612](./eip-2612.md) for [EIP-20](./eip-20.md), [EIP-4494](./eip-4494.md) for [EIP-721](./eip-721.md) that +usually only permit for a single behavior (`transfer` for EIP-20 and `safeTransferFrom` for EIP-721) and a single approver in two transactions (first a `permit(...)` TX, then a `transfer`-like TX), this EIP provides a way to permit arbitrary behaviors and aggregating multiple approvals from arbitrary number of approvers in the same transaction, allowing for Multi-Sig or Threshold Signing behavior. + + +## Motivation + +1. Support permit(approval) alongside a function call. +2. Support a second approval from another user. +3. Support pay-for-by another user +4. Support multi-sig +5. Support persons acting in concert by endorsements +6. Support accumulated voting +7. Support off-line signatures + + + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Interfaces + +The interfaces and structure referenced here are as followed + + + +```solidity +pragma solidity ^0.8.9; + +struct ValidityBound { + bytes32 functionParamStructHash; + uint256 validSince; + uint256 validBy; + uint256 nonce; +} + +struct SingleEndorsementData { + address endorserAddress; // 32 + bytes sig; // dynamic = 65 +} + +struct GeneralExtensionDataStruct { + bytes32 erc5453MagicWord; + uint256 erc5453Type; + uint256 nonce; + uint256 validSince; + uint256 validBy; + bytes endorsementPayload; +} + +interface IERC5453EndorsementCore { + function eip5453Nonce(address endorser) external view returns (uint256); + function isEligibleEndorser(address endorser) external view returns (bool); +} + +interface IERC5453EndorsementDigest { + function computeValidityDigest( + bytes32 _functionParamStructHash, + uint256 _validSince, + uint256 _validBy, + uint256 _nonce + ) external view returns (bytes32); + + function computeFunctionParamHash( + string memory _functionName, + bytes memory _functionParamPacked + ) external view returns (bytes32); +} + +interface IERC5453EndorsementDataTypeA { + function computeExtensionDataTypeA( + uint256 nonce, + uint256 validSince, + uint256 validBy, + address endorserAddress, + bytes calldata sig + ) external view returns (bytes memory); +} + + +interface IERC5453EndorsementDataTypeB { + function computeExtensionDataTypeB( + uint256 nonce, + uint256 validSince, + uint256 validBy, + address[] calldata endorserAddress, + bytes[] calldata sigs + ) external view returns (bytes memory); +} +``` + +See [`IERC5453.sol`](../assets/eip-5453/IERC5453.sol). + +### Behavior specification + +As specified in [EIP-5750 General Extensibility for Method Behaviors](./eip-5750.md), any compliant method that has an `bytes extraData` as its +last method designated for extending behaviors can conform to [EIP-5453](./eip-5453.md) as the way to indicate a permit from certain user. + +1. Any compliant method of this EIP MUST be a [EIP-5750](./eip-5750.md) compliant method. +2. Caller MUST pass in the last parameter `bytes extraData` conforming a solidity memory encoded layout bytes of `GeneralExtensonDataStruct` specified in _Section Interfaces_. The following descriptions are based on when decoding `bytes extraData` into a `GeneralExtensonDataStruct` +3. In the `GeneralExtensonDataStruct`-decoded `extraData`, caller MUST set the value of `GeneralExtensonDataStruct.erc5453MagicWord` to be the `keccak256("ERC5453-ENDORSEMENT")`. +4. Caller MUST set the value of `GeneralExtensonDataStruct.erc5453Type` to be one of the supported values. + +```solidity +uint256 constant ERC5453_TYPE_A = 1; +uint256 constant ERC5453_TYPE_B = 2; +``` + +5. When the value of `GeneralExtensonDataStruct.erc5453Type` is set to be `ERC5453_TYPE_A`, `GeneralExtensonDataStruct.endorsementPayload` MUST be abi encoded bytes of a `SingleEndorsementData`. +6. When the value of `GeneralExtensonDataStruct.erc5453Type` is set to be `ERC5453_TYPE_B`, `GeneralExtensonDataStruct.endorsementPayload` MUST be abi encoded bytes of `SingleEndorsementData[]` (a dynamic array). + +7. Each `SingleEndorsementData` MUST have a `address endorserAddress;` and a 65-bytes `bytes sig` signature. + +8. Each `bytes sig` MUST be an ECDSA (secp256k1) signature using private key of signer whose corresponding address is `endorserAddress` signing `validityDigest` which is the a hashTypeDataV4 of [EIP-712](./eip-712.md) of hashStruct of `ValidityBound` data structure as followed: + +```solidity +bytes32 validityDigest = + eip712HashTypedDataV4( + keccak256( + abi.encode( + keccak256( + "ValidityBound(bytes32 functionParamStructHash,uint256 validSince,uint256 validBy,uint256 nonce)" + ), + functionParamStructHash, + _validSince, + _validBy, + _nonce + ) + ) + ); +``` + + +9. The `functionParamStructHash` MUST be computed as followed + +```solidity + bytes32 functionParamStructHash = keccak256( + abi.encodePacked( + keccak256(bytes(_functionStructure)), + _functionParamPacked + ) + ); + return functionParamStructHash; +``` + +whereas + +- `_functionStructure` MUST be computed as `function methodName(type1 param1, type2 param2, ...)`. +- `_functionParamPacked` MUST be computed as `enc(param1) || enco(param2) ...` + +10. Upon validating that `endorserAddress == ecrecover(validityDigest, signature)` or `EIP1271(endorserAddress).isValidSignature(validityDigest, signature) == ERC1271.MAGICVALUE`, the single endorsement MUST be deemed valid. +11. Compliant method MAY choose to impose a threshold for a number of endorsements needs to be valid in the same `ERC5453_TYPE_B` kind of `endorsementPayload`. + +12. The `validSince` and `validBy` are both inclusive. Implementer MAY choose to use blocknumber or timestamp. Implementor SHOULD find away to indicate whether `validSince` and `validBy` is blocknumber or timestamp. + +## Rationale + +1. We chose to have both `ERC5453_TYPE_A`(single-endorsement) and `ERC5453_TYPE_B`(multiple-endorsements, same nonce for entire contract) so we +could balance a wider range of use cases. E.g. the same use cases of EIP-2612 and EIP-4494 can be supported by `ERC5453_TYPE_A`. And threshold approvals can be done via `ERC5453_TYPE_B`. More complicated approval types can also be extended by defining new `ERC5453_TYPE_?` + +2. We chose to include both `validSince` and `validBy` to allow maximum flexibility in expiration. This can be also be supported by EVM natively at if adopted [EIP-5081](./eip-5081.md) but EIP-5081 will not be adopted anytime soon, we choose to add these two numbers in our protocol to allow +smart contract level support. + +## Backwards Compatibility + +The design assumes a `bytes calldata extraData` to maximize the flexibility of future extensions. This assumption is compatible with [EIP-721](eip-721.md), [EIP-1155](eip-1155.md) and many other ERC-track EIPs. Those that aren't, such as [EIP-20](./eip-20.md), can also be updated to support it, such as using a wrapper contract or proxy upgrade. + +## Reference Implementation + +In addition to the specified algorithm for validating endorser signatures, we also present the following reference implementations. + +```solidity +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; + +import "./IERC5453.sol"; + +abstract contract AERC5453Endorsible is EIP712, + IERC5453EndorsementCore, IERC5453EndorsementDigest, IERC5453EndorsementDataTypeA, IERC5453EndorsementDataTypeB { + // ... + + function _validate( + bytes32 msgDigest, + SingleEndorsementData memory endersement + ) internal virtual { + require( + endersement.sig.length == 65, + "AERC5453Endorsible: wrong signature length" + ); + require( + SignatureChecker.isValidSignatureNow( + endersement.endorserAddress, + msgDigest, + endersement.sig + ), + "AERC5453Endorsible: invalid signature" + ); + } + // ... + + modifier onlyEndorsed( + bytes32 _functionParamStructHash, + bytes calldata _extensionData + ) { + require(_isEndorsed(_functionParamStructHash, _extensionData)); + _; + } + + function computeExtensionDataTypeB( + uint256 nonce, + uint256 validSince, + uint256 validBy, + address[] calldata endorserAddress, + bytes[] calldata sigs + ) external pure override returns (bytes memory) { + require(endorserAddress.length == sigs.length); + SingleEndorsementData[] + memory endorsements = new SingleEndorsementData[]( + endorserAddress.length + ); + for (uint256 i = 0; i < endorserAddress.length; ++i) { + endorsements[i] = SingleEndorsementData( + endorserAddress[i], + sigs[i] + ); + } + return + abi.encode( + GeneralExtensionDataStruct( + MAGIC_WORLD, + ERC5453_TYPE_B, + nonce, + validSince, + validBy, + abi.encode(endorsements) + ) + ); + } +} + +``` + +See [`AERC5453.sol`](../assets/eip-5453/AERC5453.sol) + +### Reference Implementation of `EndorsableERC721` + +Here is a reference implementation of `EndorsableERC721` that achieves similar behavior of [EIP-4494](./eip-4494.md). + +```solidity +pragma solidity ^0.8.9; + +contract EndorsableERC721 is ERC721, AERC5453Endorsible { + //... + + function mint( + address _to, + uint256 _tokenId, + bytes calldata _extraData + ) + external + onlyEndorsed( + _computeFunctionParamHash( + "function mint(address _to,uint256 _tokenId)", + abi.encode(_to, _tokenId) + ), + _extraData + ) + { + _mint(_to, _tokenId); + } +} +``` + +See [`EndorsableERC721.sol`](../assets/eip-5453/EndorsableERC721.sol) + +### Reference Implementation of `ThresholdMultiSigForwarder` + +Here is a reference implementation of ThresholdMultiSigForwarder that achieves similar behavior of multi-sig threshold approval +remote contract call like a Gnosis-Safe wallet. + +```solidity +pragma solidity ^0.8.9; + +contract ThresholdMultiSigForwarder is AERC5453Endorsible { + //... + function forward( + address _dest, + uint256 _value, + uint256 _gasLimit, + bytes calldata _calldata, + bytes calldata _extraData + ) + external + onlyEndorsed( + _computeFunctionParamHash( + "function forward(address _dest,uint256 _value,uint256 _gasLimit,bytes calldata _calldata)", + abi.encode(_dest, _value, _gasLimit, keccak256(_calldata)) + ), + _extraData + ) + { + string memory errorMessage = "Fail to call remote contract"; + (bool success, bytes memory returndata) = _dest.call{value: _value}( + _calldata + ); + Address.verifyCallResult(success, returndata, errorMessage); + } + +} + +``` + +See [`ThresholdMultiSigForwarder.sol`](../assets/eip-5453/ThresholdMultiSigForwarder.sol) + +## Security Considerations + +### Replay Attacks + +A replay attack is a type of attack on cryptography authentication. In a narrow sense, it usually refers to a type of attack that circumvents the cryptographically signature verification by reusing an existing signature for a message being signed again. Any implementations relying on this EIP must realize that all smart endorsements described here are cryptographic signatures that are _public_ and can be obtained by anyone. They must foresee the possibility of a replay of the transactions not only at the exact deployment of the same smart contract, but also other deployments of similar smart contracts, or of a version of the same contract on another `chainId`, or any other similar attack surfaces. The `nonce`, `validSince`, and `validBy` fields are meant to restrict the surface of attack but might not fully eliminate the risk of all such attacks, e.g. see the [Phishing](#phishing) section. + +### Phishing + +It's worth pointing out a special form of replay attack by phishing. An adversary can design another smart contract in a way that the user be tricked into signing a smart endorsement for a seemingly legitimate purpose, but the data-to-designed matches the target application + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5478.md b/EIPS/eip-5478.md new file mode 100644 index 00000000000000..e190681c676a47 --- /dev/null +++ b/EIPS/eip-5478.md @@ -0,0 +1,49 @@ +--- +eip: 5478 +title: CREATE2COPY Opcode +description: Reducing the gas cost of contract creation with existing code +author: Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-5478-reducing-the-gas-cost-of-contract-creation-with-existing-code/10419 +status: Stagnant +type: Standards Track +category: Core +created: 2022-08-17 +requires: 1014, 2929 +--- + +## Abstract + +Adding a new opcode, `CREATE2COPY`, that is identical to `CREATE2` but with potentially much lower gas cost by accepting an additional argument `existing_contract_address` that already stored the code of the new contract. + +## Motivation + +This EIP aims to reduce the smart contract creation cost of account abstraction (AA) contracts that have identical code. + +The major cost of creating an AA contract is the contract creation cost, especially data gas. For example, creating an AA contract with 10,000 bytes will consume 2,000,000 data gas. Considering the code for each user's AA contract is the same, `CREATE2COPY` can reduce the data gas cost to 2600 (cold account) or even 100 (warm account) if the contract code already exists in the local storage. + +## Specification + +### Parameters + +| Constant | Value | +| ---------------------------- | ---------------- | +| `FORK_BLKNUM` | TBD | +| `CREATE_DATA_GAS_PER_BYTE` | 200 | +| `COLD_ACCOUNT_ACCESS_COST` | 2600 | +| `WARM_ACCOUNT_ACCESS_COST` | 100 | + +If `block.number >= FORK_BLKNUM`, a new opcode is added (`CREATE2COPY`) at `0xf6`, which takes 5 stack arguments: `endowment`, `memory_start`, `memory_length`, `salt`, `existing_contract_address`. `CREATE2COPY` behaves identically to `CREATE2` (`0xf5` as defined in [EIP-1014](./eip-1014.md)), except that the code hash of the creating contract MUST be the same as that of `existing_contract_address`. + +`CREATE2COPY` has the same `gas` schema as `CREATE2`, but replacing the data gas from `CREATE_DATA_GAS_PER_BYTE * CONTRACT_BYTES` to the gas cost of `EXTCODEHASH` opcode, which is `COLD_ACCOUNT_ACCESS_COST` if the `existing_contract_address` is first-time accessed in the transaction or `WARM_ACCOUNT_ACCESS_COST` if `existing_contract_address` is already in the access list according to [EIP-2929](./eip-2929.md). + +If the code of the contract returned from the init code differs from that of `existing_contract_address`, the creation fails with the error "mismatched contract creation code with existing code", and will burn all gas for the contract creation. +## Rationale + +TBD +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5484.md b/EIPS/eip-5484.md new file mode 100644 index 00000000000000..a885f529885651 --- /dev/null +++ b/EIPS/eip-5484.md @@ -0,0 +1,116 @@ +--- +eip: 5484 +title: Consensual Soulbound Tokens +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: Final +type: Standards Track +category: ERC +created: 2022-08-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. + +## 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. + +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: + + 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 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 +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC5484 { + /// A guideline to standardlize burn-authorization's number coding + enum BurnAuth { + IssuerOnly, + OwnerOnly, + Both, + Neither + } + + /// @notice Emitted when a soulbound token is issued. + /// @dev This emit is an add-on to nft's transfer emit in order to distinguish sbt + /// from vanilla nft while providing backward compatibility. + /// @param from The issuer + /// @param to The receiver + /// @param tokenId The id of the issued token + event Issued ( + address indexed from, + address indexed to, + uint256 indexed tokenId, + BurnAuth burnAuth + ); + + /// @notice provides burn authorization of the token id. + /// @dev unassigned tokenIds are invalid, and queries do throw + /// @param tokenId The identifier for a token. + function burnAuth(uint256 tokenId) external view returns (BurnAuth); +} +``` + +## 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. + +### 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 +- `ReceiverOnly`: Paid membership +- `Both`: Credentials +- `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. + +## Backwards Compatibility + +This proposal is fully backward compatible with [EIP-721](./eip-721.md) + +## Security Considerations + +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-5485.md b/EIPS/eip-5485.md new file mode 100644 index 00000000000000..c5b1f98594795e --- /dev/null +++ b/EIPS/eip-5485.md @@ -0,0 +1,95 @@ +--- +eip: 5485 +title: Legitimacy, Jurisdiction and Sovereignty +description: An interface for identifying the legitimacy, jurisdiction and sovereignty. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-5485-interface-for-legitimacy-jurisdiction-and-sovereignty/10425 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-17 +requires: 165, 5247 +--- + +## Abstract + +Provide a way for compliant smart contracts to declare their legitimacy lineage, jurisdiction they observe, and sovereignty if they choose to not fall onto any jurisdiction. + +## Motivation + +Today, smart contracts have no standard way to specify their legitimacy lineage, jurisdiction, or sovereignty relationship. The introduction of such a standard, supports better integration with today's legal and regulative scenarios: + +1. it supports a regulative body to allow or deny interoperability with smart contracts. +2. it also allows DAOs to clearly declare "self-sovereignty" by announcing via this interface by saying they do not assert legitimacy from any source other than themselves. + +A real-world example is that ContractA represents an **A company registered in a country**, ContractB represents a **The Secretary of State of the country**, and ContractC represents the **Supreme Court of the Country**. + +Another real example is a contract that declares "self-sovereignty" that doesn't follow any jurisdiction. + +This interface supports both cases, providing a way to allow smart contracts to determine if they want to allow/prohibit interaction based on sovereignty. + +For example, a country might want to require any digital money service's all smart contracts to observe their [ERC-5485](./eip-5485.md) jurisdiction before they are allowed to operate money in their (real world) legal jurisdiction. + +Another real world use-case is that in some jurisdiction e.g. in United States, if an token issuer choose to issue a token, +they can try to petition SEC to recognize their token as registered security, if approved, will gain legitimacy from SEC. +Should they choose to petition Commodity Futures Trading Commission (CFTC) to recognize them as a commodity, if approved, will +gain legitimacy from CFTC. + +On the other hand, a DAO with a strong decentralized ideology might choose to only inter-operate with EOA or "self-sovereign" smart contracts to avoid being affected by any country. + +## 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. + +1. Compliant contract MUSTS implement the following interface. + +```solidity +interface IERC5485 { + /// A function that returns the source of legitimacy in an address + /// + /// @returns address of EOA or contract that grants legitimacy status to this contract. + /// See Specification #2 and #3 for more details. + function sourceOfLegitimacy() public view returns (address); + + /// A function that returns the sovereign jurisdiction in an address + /// + /// @returns address of EOA or contract that *ultimately* grants legitimacy status (root of legitimacy lineage). + /// See Specification #4 and #4 for more details. + function jurisdiction() public view returns (address); +} +``` + +2. Suppose a compliant `ContractA` is deployed at address `0x` and gains its legitimacy from another contract or External Owned Account (EOA) at `0x`. In this case, it MUST return the `0x` when being queried for `sourceOfLegitimacy()`. For example. If `ContractA` gains legitimacy from `ContractB`, `ContractB` gains legitimacy from `ContractC` but `ContractC` doesn't gain legitimacy from anyone, ContractC is considered the "jurisdiction" that both `ContractB` and `ContractA` observe. Both `ContractA` and `ContractB` MUST return the address of `ContractC` as the result of jurisdiction. + +3. A contract that matches ANY of the following cases is considered a "sovereign contract": + +- Case 1: it does not implement this EIP +- Case 2: it implements this EIP but return its *own address*. +- Case 3: it implements this EIP but return a ZERO address. + +4. Any compliant contract MUST return its legitimacy sources chain up to the sovereign contract when queried by `jurisdiction()`. + +5. If ContractA gains legitimacy from ContractB, ContractA MUST observe the ContractB's order to ANY of the following +- Order Type 1: execute a `selfdestruct` of ContractA + +6. If ContractA observes jurisdiction of ContractC (a sovereignty), ContractA MUST observe ContractC's order to ANY the following +- Order Type 1: execute a `selfdestruct` of ContractA +- Order Type 2: execute ANY smart contract executable proposals as specified in [ERC-5247](./eip-5247.md) + +TODO determine whether to address "add/remove legitimacy" in this EIP or leave it as a future EIP + +## Rationale + +Needs discussion. + +## Backwards Compatibility + +Needs discussion. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5489.md b/EIPS/eip-5489.md new file mode 100644 index 00000000000000..aec096b66bc551 --- /dev/null +++ b/EIPS/eip-5489.md @@ -0,0 +1,172 @@ +--- +eip: 5489 +title: NFT Hyperlink Extension +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: Final +type: Standards Track +category: ERC +created: 2022-08-16 +requires: 165, 721 +--- + +## Abstract + +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. + + +## Motivation + +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. + +### Interface + +#### `IERC5489` + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC5489 { + /** + * @dev this event emits when the slot on `tokenId` is authorzized to `slotManagerAddr` + */ + event SlotAuthorizationCreated(uint256 indexed tokenId, address indexed slotManagerAddr); + + /** + * @dev this event emits when the authorization on slot `slotManagerAddr` of token `tokenId` is revoked. + * So, the corresponding DApp can handle this to stop on-going incentives or rights + */ + event SlotAuthorizationRevoked(uint256 indexed tokenId, address indexed slotManagerAddr); + + /** + * @dev this event emits when the uri on slot `slotManagerAddr` of token `tokenId` has been updated to `uri`. + */ + event SlotUriUpdated(uint256 indexed tokenId, address indexed slotManagerAddr, string uri); + + /** + * @dev + * Authorize a hyperlink slot on `tokenId` to address `slotManagerAddr`. + * Indeed slot is an entry in a map whose key is address `slotManagerAddr`. + * Only the address `slotManagerAddr` can manage the specific slot. + * This method will emit SlotAuthorizationCreated event + */ + function authorizeSlotTo(uint256 tokenId, address slotManagerAddr) external; + + /** + * @dev + * Revoke the authorization of the slot indicated by `slotManagerAddr` on token `tokenId` + * This method will emit SlotAuthorizationRevoked event + */ + function revokeAuthorization(uint256 tokenId, address slotManagerAddr) external; + + /** + * @dev + * Revoke all authorizations of slot on token `tokenId` + * This method will emit SlotAuthorizationRevoked event for each slot + */ + function revokeAllAuthorizations(uint256 tokenId) external; + + /** + * @dev + * Set uri for a slot on a token, which is indicated by `tokenId` and `slotManagerAddr` + * Only the address with authorization through {authorizeSlotTo} can manipulate this slot. + * This method will emit SlotUriUpdated event + */ + function setSlotUri( + uint256 tokenId, + string calldata newUri + ) external; + + /** + * @dev Throws if `tokenId` is not a valid NFT. URIs are defined in RFC 3986. + * The URI MUST point to a JSON file that conforms 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) + external + view + returns (string memory); +} +``` + +The `authorizeSlotTo(uint256 tokenId, address slotManagerAddr)` function MAY be implemented as public or external. + +The `revokeAuthorization(uint256 tokenId, address slotManagerAddr)` function MAY be implemented as public or external. + +The `revokeAllAuthorizations(uint256 tokenId)` function MAY be implemented as public or external. + +The `setSlotUri(uint256 tokenId, string calldata newUri)` function MAY be implemented as public or external. + +The `getSlotUri(uint256 tokenId, address slotManagerAddr)` function MAY be implemented as pure or view. + +The `SlotAuthorizationCreated` event MUST be emitted when a slot is authorized to an address. + +The `SlotAuthorizationRevoked` event MUST be emitted when a slot authorization is revoked. + +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. + +### 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" + } + } +} +``` + +## 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. + +## 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-5496.md b/EIPS/eip-5496.md new file mode 100644 index 00000000000000..ea436746269521 --- /dev/null +++ b/EIPS/eip-5496.md @@ -0,0 +1,254 @@ +--- +eip: 5496 +title: Multi-privilege Management NFT Extension +description: Create shareable multi-privilege NFTs for EIP-721 +author: Jeremy Z (@wnft) +discussions-to: https://ethereum-magicians.org/t/eip-5496-multi-privilege-management-extension-for-erc-721/10427 +status: Last Call +last-call-deadline: 2022-11-29 +type: Standards Track +category: ERC +created: 2022-07-30 +requires: 721 +--- + + +## Abstract + +This EIP defines an interface extending [EIP-721](./eip-721.md) to provide shareable multi-privileges for NFTs. Privileges may be on-chain (voting rights, permission to claim an airdrop) or off-chain (a coupon for an online store, a discount at a local restaurant, access to VIP lounges in airports). Each NFT may contain many privileges, and the holder of a privilege can verifiably transfer that privilege to others. Privileges may be non-shareable or shareable. Shareable privileges can be cloned, with the provider able to adjust the details according to the spreading path. Expiration periods can also be set for each privilege. + +## Motivation + +This standard aims to efficiently manage privileges attached to NFTs in real-time. Many NFTs have functions other than just being used as profile pictures or art collections, they may have real utilities in different scenarios. For example, a fashion store may give a discount for its own NFT holders; a DAO member NFT holder can vote for the proposal of how to use their treasury; a dApp may create an airdrop event to attract a certain group of people like some blue chip NFT holders to claim; the grocery store can issue its membership card on chain (as an NFT) and give certain privileges when the members shop at grocery stores, etc. There are cases when people who own NFTs do not necessarily want to use their privileges. By providing additional data recording different privileges a NFT collection has and interfaces to manage them, users can transfer or sell privileges without losing their ownership of the NFT. + +[EIP-721](./eip-721.md) only records the ownership and its transfer, the privileges of an NFT are not recorded on-chain. This extension would allow merchants/projects to give out a certain privilege to a specified group of people, and owners of the privileges can manage each one of the privileges independently. This facilitates a great possibility for NFTs to have real usefulness. + +For example, an airline company issues a series of [EIP-721](./eip-721.md)/[EIP-1155](./eip-1155.md) tokens to Crypto Punk holders to give them privileges, in order to attract them to join their club. However, since these tokens are not bound to the original NFT, if the original NFT is transferred, these privileges remain in the hands of the original holders, and the new holders cannot enjoy the privileges automatically. +So, we propose a set of interfaces that can bind the privileges to the underlying NFT, while allowing users to manage the privileges independently. + +## 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. + +Every contract complying with this standard MUST implement the `IERC5496` interface. The **shareable multi-privilege extension** is OPTIONAL for EIP-721 contracts. + +```solidity +/// @title multi-privilege extension for EIP-721 +/// Note: the EIP-165 identifier for this interface is 0x076e1bbb +interface IERC5496{ + /// @notice Emitted when `owner` changes the `privilege holder` of a NFT. + event PrivilegeAssigned(uint256 tokenId, uint256 privilegeId, address user, uint256 expires); + /// @notice Emitted when `contract owner` changes the `total privilege` of the collection + event PrivilegeTotalChanged(uint256 newTotal, uint256 oldTotal); + + /// @notice set the privilege holder of a NFT. + /// @dev expires should be less than 30 days + /// Throws if `msg.sender` is not approved or owner of the tokenId. + /// @param tokenId The NFT to set privilege for + /// @param privilegeId The privilege to set + /// @param user The privilege holder to set + /// @param expires For how long the privilege holder can have + function setPrivilege(uint256 tokenId, uint256 privilegeId, address user, uint256 expires) external; + + /// @notice Return the expiry timestamp of a privilege + /// @param tokenId The identifier of the queried NFT + /// @param privilegeId The identifier of the queried privilege + /// @return Whether a user has a certain privilege + function privilegeExpires(uint256 tokenId, uint256 privilegeId) external view returns(uint256); + + /// @notice Check if a user has a certain privilege + /// @param tokenId The identifier of the queried NFT + /// @param privilegeId The identifier of the queried privilege + /// @param user The address of the queried user + /// @return Whether a user has a certain privilege + function hasPrivilege(uint256 tokenId, uint256 privilegeId, address user) external view returns(bool); +} +``` + +Every contract implementing this standard SHOULD set a maximum privilege number before setting any privilege, the `privilegeId` MUST NOT be greater than the maximum privilege number. + +The `PrivilegeAssigned` event MUST be emitted when `setPrivilege` is called. + +The `PrivilegeTotalChanged` event MUST be emitted when the `total privilege` of the collection is changed. + +The `supportsInterface` method MUST return `true` when called with `0x076e1bbb`. + +```solidity +/// @title Cloneable extension - Optional for EIP-721 +interface IERC721Cloneable { + /// @notice Emitted when set the `privilege ` of a NFT cloneable. + event PrivilegeCloned(uint tokenId, uint privId, address from, address to); + + /// @notice set a certain privilege cloneable + /// @param tokenId The identifier of the queried NFT + /// @param privilegeId The identifier of the queried privilege + /// @param referrer The address of the referrer + /// @return Whether the operation is successful or not + function clonePrivilege(uint tokenId, uint privId, address referrer) external returns (bool); +} +``` + +The `PrivilegeCloned` event MUST be emitted when `clonePrivilege` is called. + +For Compliant contract, it is RECOMMENDED to use [EIP-1271](./eip-1271.md) to validate the signatures. + +## Rationale + +### Shareable Privileges + +The number of privilege holders is limited by the number of NFTs if privileges are non-shareable. A shareable privilege means the original privilege holder can copy the privilege and give it to others, not transferring his/her own privilege to them. This mechanism greatly enhances the spread of privileges as well as the adoption of NFTs. + +### Expire Date Type + +The expiry timestamp of a privilege is a timestamp and stored in `uint256` typed variables. + +### Beneficiary of Referrer + +For example, a local pizza shop offers a 30% off Coupon and the owner of the shop encourages their consumers to share the coupon with friends, then the friends can get the coupon. Let's say Tom gets 30% off Coupon from the shop and he shares the coupon with Alice. Alice gets the coupon too and Alice's referrer is Tom. For some certain cases, Tom may get more rewards from the shop. This will help the merchants in spreading the promotion among consumers. + +### Proposal: NFT Transfer + +If the owner of the NFT transfers ownership to another user, there is no impact on "privileges". But errors may occur if the owner tries to withdraw the original [EIP-721](./eip-721.md) token from the wrapped NFT through `unwrap()` if any available privileges are still ongoing. We protect the rights of holders of the privileges to check the last expiration date of the privilege. + +```solidity +function unwrap(uint256 tokenId, address to) external { + require(getBlockTimestamp() >= privilegeBook[tokenId].lastExpiresAt, "privilege not yet expired"); + + require(ownerOf(tokenId) == msg.sender, "not owner"); + + _burn(tokenId); + + IERC721(nft).transferFrom(address(this), to, tokenId); + + emit Unwrap(nft, tokenId, msg.sender, to); +} +``` + +## Backwards Compatibility + +This EIP is compatible with any kind of NFTs that follow the EIP-721 standard. It only adds more functions and data structures without interfering with the original [EIP-721](./eip-721.md) standard. + +## Test Cases + +Test cases are implemented with the reference implementation. + +### Test Code + +[test.js](../assets/eip-5496/test/test.js) + +Run in terminal: + +```shell +truffle test ./test/test.js +``` + +[testCloneable.js](../assets/eip-5496/test/testCloneable.js) + +Run in terminal: + +```shell +truffle test ./test/testCloneable.js +``` + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "./IERC5496.sol"; + +contract ERC5496 is ERC721, IERC5496 { + struct PrivilegeRecord { + address user; + uint256 expiresAt; + } + struct PrivilegeStorage { + uint lastExpiresAt; + // privId => PrivilegeRecord + mapping(uint => PrivilegeRecord) privilegeEntry; + } + + uint public privilegeTotal; + // tokenId => PrivilegeStorage + mapping(uint => PrivilegeStorage) public privilegeBook; + mapping(address => mapping(address => bool)) private privilegeDelegator; + + constructor(string memory name_, string memory symbol_) + ERC721(name_,symbol_) + { + + } + + function setPrivilege( + uint tokenId, + uint privId, + address user, + uint64 expires + ) external virtual { + require((hasPrivilege(tokenId, privId, ownerOf(tokenId)) && _isApprovedOrOwner(msg.sender, tokenId)) || _isDelegatorOrHolder(msg.sender, tokenId, privId), "ERC721: transfer caller is not owner nor approved"); + require(expires < block.timestamp + 30 days, "expire time invalid"); + require(privId < privilegeTotal, "invalid privilege id"); + privilegeBook[tokenId].privilegeEntry[privId].user = user; + if (_isApprovedOrOwner(msg.sender, tokenId)) { + privilegeBook[tokenId].privilegeEntry[privId].expiresAt = expires; + if (privilegeBook[tokenId].lastExpiresAt < expires) { + privilegeBook[tokenId].lastExpiresAt = expires; + } + } + emit PrivilegeAssigned(tokenId, privId, user, uint64(privilegeBook[tokenId].privilegeEntry[privId].expiresAt)); + } + + function hasPrivilege( + uint256 tokenId, + uint256 privId, + address user + ) public virtual view returns(bool) { + if (privilegeBook[tokenId].privilegeEntry[privId].expiresAt >= block.timestamp){ + return privilegeBook[tokenId].privilegeEntry[privId].user == user; + } + return ownerOf(tokenId) == user; + } + + function privilegeExpires( + uint256 tokenId, + uint256 privId + ) public virtual view returns(uint256){ + return privilegeBook[tokenId].privilegeEntry[privId].expiresAt; + } + + function _setPrivilegeTotal( + uint total + ) internal { + emit PrivilegeTotalChanged(total, privilegeTotal); + privilegeTotal = total; + } + + function getPrivilegeInfo(uint tokenId, uint privId) external view returns(address user, uint256 expiresAt) { + return (privilegeBook[tokenId].privilegeEntry[privId].user, privilegeBook[tokenId].privilegeEntry[privId].expiresAt); + } + + function setDelegator(address delegator, bool enabled) external { + privilegeDelegator[msg.sender][delegator] = enabled; + } + + function _isDelegatorOrHolder(address delegator, uint256 tokenId, uint privId) internal virtual view returns (bool) { + address holder = privilegeBook[tokenId].privilegeEntry[privId].user; + return (delegator == holder || isApprovedForAll(holder, delegator) || privilegeDelegator[holder][delegator]); + } + + function supportsInterface(bytes4 interfaceId) public override virtual view returns (bool) { + return interfaceId == type(IERC5496).interfaceId || super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +Implementations must thoroughly consider who has the permission to set or clone privileges. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5501.md b/EIPS/eip-5501.md new file mode 100644 index 00000000000000..3d34905e9e40c2 --- /dev/null +++ b/EIPS/eip-5501.md @@ -0,0 +1,251 @@ +--- +eip: 5501 +title: Rental & Delegation NFT - EIP-721 Extension +description: Adds a conditional time-limited user role to EIP-721. This role can be delegated or borrowed. +author: Jan Smrža (@smrza), David Rábel (@rabeles11), Tomáš Janča , Jan Bureš (@JohnyX89), DOBBYLABS (@DOBBYLABS) +discussions-to: https://ethereum-magicians.org/t/eip-tbd-rental-delegation-nft-erc-721-extension/10441 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-18 +requires: 165, 721, 4400, 4907 +--- + +## Abstract +The following standard proposes an additional `user` role for [EIP-721](./eip-721.md). This role grants the permission to use the NFT with no ability to transfer or set users. It has an expiry and a flag if the token is borrowed or not. `Owner` can delegate the NFT for usage to hot wallets or lend the NFT. If the token is borrowed, not even the owner can change the user until the status expires or both parties agree to terminate. This way, it is possible to keep both roles active at the same time. + +## Motivation +Collectibles, gaming assets, metaverse, event tickets, music, video, domains, real item representation are several among many NFT use cases. With [EIP-721](./eip-721.md) only the owner can reap the benefits. However, with most of the utilities it would be beneficial to distinguish between the token owner and its user. For instance music or movies could be rented. Metaverse lands could be delegated for usage. + +The two reasons why to set the user are: + +* **delegation** - Assign user to your hot wallet to interact with applications securely. In this case, the owner can change the user at any time. +* **renting** - This use case comes with additional requirements. It is needed to terminate the loan once the established lending period is over. This is provided by `expires` of the user. It is also necessary to protect the borrower against resetting their status by the owner. Thus, `isBorrowed` check must be implemented to disable the option to set the user before the contract expires. + +The most common use cases for having an additional user role are: + +* **delegation** - For security reasons. +* **gaming** - Would you like to try a game (or particular gaming assets) but are you unsure whether or not you will like it? Rent assets first. +* **guilds** - Keep the owner of the NFTs as the multisig wallet and set the user to a hot wallet with shared private keys among your guild members. +* **events** - Distinguish between `ownerOf` and `userOf`. Each role has a different access. +* **social** - Differentiate between roles for different rooms. For example owner has read + write access while userOf has read access only. + +This proposal is a follow up on [EIP-4400](./eip-4400.md) and [EIP-4907](./eip-4907.md) and introduces additional upgrades for lending and borrowing which include: + +* **NFT stays in owner's wallet during rental period** +* **Listing and sale of NFT without termination of the rent** +* **Claiming owner benefits during rental period** + +Building the standard with additional isBorrowed check now allows to create rental marketplaces which can set the user of NFT without the necessary staking mechanism. With current standards if a token is not staked during the rental period, the owner can simply terminate the loan by setting the user repeatedly. This is taken care of by disabling the function if the token is borrowed which in turn is providing the owner additional benefits. They can keep the token tied to their wallet, meaning they can still receive airdrops, claim free mints based on token ownership or otherwise use the NFT provided by third-party services for owners. They can also keep the NFT listed for sale. Receiving airdrops or free mints was previously possible but the owner was completely reliant on the implementation of rental marketplaces and their discretion. + +Decentralized applications can now differentiate between ownerOf and userOf while both statuses can coexist. + +## 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. + +**Every compliant contract MUST implement the `IERC5501` interface. This extension is OPTIONAL for [EIP-721](./eip-721.md) contracts.** + +```solidity +/** + * @title IERC5501: Rental & Delegation NFT - EIP-721 Extension + * @notice the EIP-165 identifier for this interface is 0xf808ec37. + */ +interface IERC5501 /* is IERC721 */ { + /** + * @dev Emitted when the user of an NFT is modified. + */ + event UpdateUser(uint256 indexed _tokenId, address indexed _user, uint64 _expires, bool _isBorrowed); + + /** + * @notice Set the user info of an NFT. + * @dev User address cannot be zero address. + * Only approved operator or NFT owner can set the user. + * If NFT is borrowed, the user info cannot be changed until user status expires. + * @param _tokenId uint256 ID of the token to set user info for + * @param _user address of the new user + * @param _expires Unix timestamp when user info expires + * @param _isBorrowed flag whether or not the NFT is borrowed + */ + function setUser(uint256 _tokenId, address _user, uint64 _expires, bool _isBorrowed) external; + + /** + * @notice Get the user address of an NFT. + * @dev Reverts if user is not set. + * @param _tokenId uint256 ID of the token to get the user address for + * @return address user address for this NFT + */ + function userOf(uint256 _tokenId) external view returns (address); + + /** + * @notice Get the user expires of an NFT. + * @param _tokenId uint256 ID of the token to get the user expires for + * @return uint64 user expires for this NFT + */ + function userExpires(uint256 _tokenId) external view returns (uint64); + + /** + * @notice Get the user isBorrowed of an NFT. + * @param _tokenId uint256 ID of the token to get the user isBorrowed for + * @return bool user isBorrowed for this NFT + */ + function userIsBorrowed(uint256 _tokenId) external view returns (bool); +} +``` + +Every contract implementing the `IERC5501` interface is free to define the permissions of a `user`. However, user MUST NOT be considered an `owner`. They MUST NOT be able to execute transfers and approvals. Furthermore, `setUser` MUST be blocked from executing if `userIsBorrowed` returns `true` and `userExpires` is larger than or equal to `block.timestamp`. + +The `UpdateUser` event MUST be emitted when a `user` is changed. +The `setUser(uint256 _tokenId, address _user, uint64 _expires, bool _isBorrowed)` function SHOULD `revert` unless the `msg.sender` is the `owner` or an approved operator. It MUST revert if a token is borrowed and status has not expired yet. It MAY be `public` or `external`. +The `userOf(uint256 _tokenId)` function SHOULD revert if `user` is not set or expired. +The `userExpires(uint256 _tokenId)` function returns a timestamp when user status expires. +The `userIsBorrowed(uint256 _tokenId)` function returns whether NFT is borrowed or not. +The `supportsInterface` function MUST return `true` when called with `0xf808ec37`. +On every `transfer`, the `user` MUST be reset if the token is not borrowed. If the token is borrowed the `user` MUST stay the same. + +**The Balance extension is OPTIONAL. This gives the option to query the number of tokens a `user` has.** + +```solidity +/** + * @title IERC5501Balance + * Extension for ERC5501 which adds userBalanceOf to query how many tokens address is userOf. + * @notice the EIP-165 identifier for this interface is 0x0cb22289. + */ +interface IERC5501Balance /* is IERC5501 */{ + /** + * @notice Count of all NFTs assigned to a user. + * @dev Reverts if user is zero address. + * @param _user an address for which to query the balance + * @return uint256 the number of NFTs the user has + */ + function userBalanceOf(address _user) external view returns (uint256); +} +``` + +The `userBalanceOf(address _user)` function SHOULD `revert` for zero address. + +**The Enumerable extension is OPTIONAL. This allows to iterate over user balance.** + +```solidity +/** + * @title IERC5501Enumerable + * This extension for ERC5501 adds the option to iterate over user tokens. + * @notice the EIP-165 identifier for this interface is 0x1d350ef8. + */ +interface IERC5501Enumerable /* is IERC5501Balance, IERC5501 */ { + /** + * @notice Enumerate NFTs assigned to a user. + * @dev Reverts if user is zero address or _index >= userBalanceOf(_owner). + * @param _user an address to iterate over its tokens + * @return uint256 the token ID for given index assigned to _user + */ + function tokenOfUserByIndex(address _user, uint256 _index) external view returns (uint256); +} +``` + +The `tokenOfUserByIndex(address _user, uint256 _index)` function SHOULD `revert` for zero address and `throw` if the index is larger than or equal to `user` balance. + +**The Terminable extension is OPTIONAL. This allows terminating the rent early if both parties agree.** + +```solidity +/** + * @title IERC5501Terminable + * This extension for ERC5501 adds the option to terminate borrowing if both parties agree. + * @notice the EIP-165 identifier for this interface is 0x6a26417e. + */ +interface IERC5501Terminable /* is IERC5501 */ { + /** + * @dev Emitted when one party from borrowing contract approves termination of agreement. + * @param _isLender true for lender, false for borrower + */ + event AgreeToTerminateBorrow(uint256 indexed _tokenId, address indexed _party, bool _isLender); + + /** + * @dev Emitted when agreements to terminate borrow are reset. + */ + event ResetTerminationAgreements(uint256 indexed _tokenId); + + /** + * @dev Emitted when borrow of token ID is terminated. + */ + event TerminateBorrow(uint256 indexed _tokenId, address indexed _lender, address indexed _borrower, address _caller); + + /** + * @notice Agree to terminate a borrowing. + * @dev Lender must be ownerOf token ID. Borrower must be userOf token ID. + * If lender and borrower are the same, set termination agreement for both at once. + * @param _tokenId uint256 ID of the token to set termination info for + */ + function setBorrowTermination(uint256 _tokenId) external; + + /** + * @notice Get if it is possible to terminate a borrow agreement. + * @param _tokenId uint256 ID of the token to get termination info for + * @return bool, bool first indicates lender agrees, second indicates borrower agrees + */ + function getBorrowTermination(uint256 _tokenId) external view returns (bool, bool); + + /** + * @notice Terminate a borrow if both parties agreed. + * @dev Both parties must have agreed, otherwise revert. + * @param _tokenId uint256 ID of the token to terminate borrow of + */ + function terminateBorrow(uint256 _tokenId) external; +} +``` + +The `AgreeToTerminateBorrow` event MUST be emitted when either the lender or borrower agrees to terminate the rent. +The `ResetTerminationAgreements` event MUST be emitted when a token is borrowed and transferred or `setUser` and `terminateBorrow` functions are called. +The `TerminateBorrow` event MUST be emitted when the rent is terminated. +The `setBorrowTermination(uint256 _tokenId)`. It MUST set an agreement from either party whichever calls the function. If the lender and borrower are the same address, it MUST assign an agreement for both parties at once. +The `getBorrowTermination(uint256 _tokenId)` returns if agreements from both parties are `true` or `false`. +The `terminateBorrow(uint256 _tokenId)` function MAY be called by anyone. It MUST `revert` if both agreements to terminate are not `true`. This function SHOULD change the `isBorrowed` flag from `true` to `false`. +On every `transfer`, the termination agreements from either party MUST be reset if the token is borrowed. + +## Rationale +The main factors influencing this standard are: + +* **[EIP-4400](./eip-4400.md) and [EIP-4907](./eip-4907.md)** +* **Allow lending and borrowing without the necessary stake or overcollateralization while owner retains ownership** +* **Leave the delegation option available** +* **Keep the number of functions in the interfaces to a minimum while achieving desired functionality** +* **Modularize additional extensions to let developers choose what they need for their project** + +### Name +The name for the additional role has been chosen to fit the purpose and to keep compatibility with EIP-4907. + +### Ownership retention +Many collections offer their owners airdrops or free minting of various tokens. This is essentially broken if the owner is lending a token by staking it into a contract (unless the contract is implementing a way to claim at least airdropped tokens). Applications can also provide different access and benefits to owner and user roles in their ecosystem. + +### Balance and Enumerable extensions +These have been chosen as OPTIONAL extensions due to the complexity of implementation based on the fact that balance is less once user status expires and there is no immediate on-chain transaction to evaluate that. In both `userBalanceOf` and `tokenOfUserByIndex` functions there must be a way to determine whether or not user status has expired. + +### Terminable extension +If the owner mistakenly sets a user with borrow status and expires to a large value they would essentially be blocked from setting the user ever again. The problem is addressed by this extension if both parties agree to terminate the user status. + +### Security +Once applications adopt the user role, it is possible to delegate ownership to hot wallet and interact with them with no fear of connecting to malicious websites. + +## Backwards Compatibility +This standard is compatible with current [EIP-721](./eip-721.md) by adding an extension function set. The new functions introduced are similar to existing functions in EIP-721 which guarantees easy adoption by developers and applications. This standard also shares similarities to [EIP-4907](./eip-4907.md) considering user role and its expiry which means applications will be able to determine the user if either of the standards is used. + +## Test Cases +Test cases can be found in the reference implementation: +* [Main contract](../assets/eip-5501/test/ERC5501Test.ts) +* [Balance extension](../assets/eip-5501/test/ERC5501BalanceTest.ts) +* [Enumerable extension](../assets/eip-5501/test/ERC5501EnumerableTest.ts) +* [Terminable extension](../assets/eip-5501/test/ERC5501TerminableTest.ts) +* [Scenario combined of all extensions](../assets/eip-5501/test/ERC5501CombinedTest.ts) + +## Reference Implementation +The reference implementation is available here: +* [Main contract](../assets/eip-5501/contracts/ERC5501.sol) +* [Balance extension](../assets/eip-5501/contracts/ERC5501Balance.sol) +* [Enumerable extension](../assets/eip-5501/contracts/ERC5501Enumerable.sol) +* [Terminable extension](../assets/eip-5501/contracts/ERC5501Terminable.sol) +* [Solution combined of all extensions](../assets/eip-5501/contracts/ERC5501Combined.sol) + +## Security Considerations +Developers implementing this standard and applications must consider all the permissions they give to users and owners. Since owner and user are both active roles at the same time, double-spending problem must be avoided. Balance extension must be implemented in such a way which will not cause any gas problems. Marketplaces should let users know if a token listed for sale is borrowed or not. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5505.md b/EIPS/eip-5505.md new file mode 100644 index 00000000000000..a4a5a08e52c0f9 --- /dev/null +++ b/EIPS/eip-5505.md @@ -0,0 +1,77 @@ +--- +eip: 5505 +title: EIP-1155 asset backed NFT extension +description: Extends EIP-1155 to support crucial operations for asset-backed NFTs +author: liszechung (@liszechung) +discussions-to: https://ethereum-magicians.org/t/eip-draft-erc1155-asset-backed-nft-extension/10437 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-08-18 +requires: 1155 +--- + +## Abstract +To propose an extension of smart contract interfaces for asset-backed, fractionalized projects using the [EIP-1155](./eip-1155.md) standard such that total acquisition will become possible. This proposal focuses on physical asset, where total acquisition should be able to happen. + +## Motivation +Fractionalized, asset backed NFTs face difficulty when someone wants to acquire the whole asset. For example, if someone wants to bring home a fractionalized asset, he needs to buy all NFT pieces so he will become the 100% owner. However he could not do so as it is publicly visible that someone is trying to perform a total acquisition in an open environment like Ethereum. Sellers will take advantage to set unreasonable high prices which hinders the acquisition. Or in other cases, NFTs are owned by wallets with lost keys, such that the ownership will never be a complete one. We need a way to enable potential total acquisition. + +## 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-1155](./eip-1155.md) compliant contracts MAY implement this EIP for adding functionalities to support total acquisition. + +```solidity +//set the percentage required for any acquirer to trigger a forced sale +//set also the payment token to settle for the acquisition + +function setForcedSaleRequirement( + uint128 requiredBP, + address erc20Token +) public onlyOwner + +//set the unit price to acquire the remaining NFTs (100% - requiredBP) +//suggest to use a Time Weighted Average Price for a certain period before reaching the requiredBP +//emit ForcedSaleSet + +function setForcedSaleTWAP( + uint256 amount +) public onlyOwner + +//acquirer deposit remainingQTY*TWAP +//emit ForcedSaleFinished +//after this point, the acquirer is the new owner of the whole asset + +function execForcedSale ( + uint256 amount +) public external payable + +//burn ALL NFTs and collect funds +//emit ForcedSaleClaimed + +function claimForcedSale() +public + +event ForcedSaleSet( + bool isSet +) +event ForceSaleClaimed( + uint256 qtyBurned, + uint256 amountClaimed, + address claimer +) +``` + + +## Rationale +Native ETH is supported by via Wrapped Ether [EIP-20](./eip-20.md). +After forcedSale is set, the remaining NFTs metadata should be updated to reflect the NFTs are at most valued at the previously set TWAP price. + +## Security Considerations +The major security risks considered include +- The execution of the forcedSale is only executed by the contract owner, after a governance proposal. If there is any governance attack, the forcedSale TWAP price might be manipulated on a specific timing. The governance structure for using this extension should consider adding a **council** to safeguard the fairness of the forcedSale. +- Payment tokens are deposited into the contract account when forcedSale is executed. These tokens will then await the minority holders to withdraw on burning the NFT. There might be a potential security risk. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5507.md b/EIPS/eip-5507.md new file mode 100644 index 00000000000000..edf17d460286b8 --- /dev/null +++ b/EIPS/eip-5507.md @@ -0,0 +1,332 @@ +--- +eip: 5507 +title: Refundable Tokens +description: Adds refund functionality to ERC-20, ERC-721, and ERC-1155 tokens +author: elie222 (@elie222), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-5507-refundable-nfts/10451 +status: Final +type: Standards Track +category: ERC +created: 2022-08-19 +requires: 20, 165, 721, 1155 +--- + +## Abstract + +This ERC adds refund functionality for initial token offerings to [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), and [ERC-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 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. + +A standard interface for this particular use case allows for certain benefits: + +- Greater Compliance with EU "Distance Selling Regulations," which require a 14-day refund period for goods (such as tokens) purchased online +- Interoperability with various NFT-related applications, such as portfolio browsers, and marketplaces + - NFT marketplaces could place a badge indicating that the NFT is still refundable on listings, and offer to refund NFTs instead of listing them on the marketplace + - DExes could offer to refund tokens if doing so would give a higher yield +- Better wallet confirmation dialogs + - Wallets can better inform the user of the action that is being taken (tokens being refunded), similar to how transfers often have their own unique dialog + - DAOs can better display the functionality of smart proposals that include refunding 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. + +All implementations MUST use and follow the directions of [ERC-165](./eip-165.md). + +### ERC-20 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.17; + +import "ERC20.sol"; +import "ERC165.sol"; + +/// @notice Refundable ERC-20 tokens +/// @dev The ERC-165 identifier of this interface is `0xf0ca2917` +interface ERC20Refund is ERC20, ERC165 { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _from The account whose assets are refunded + /// @param _amount The amount of token (in terms of the indivisible unit) that was refunded + event Refund( + address indexed _from, + uint256 indexed _amount + ); + + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refundFrom` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _amount The amount of token (in terms of the indivisible unit) that was refunded + event RefundFrom( + address indexed _sender, + address indexed _from, + 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 As long as the refund is active and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// @param from The user from which to refund the assets + /// @param amount The `amount` to refund + function refundFrom(address from, 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 indivisible units) + function refundOf() external view returns (uint256 _wei); + + /// @notice Gets the first block for which the refund is not active + /// @return block The first block where the token cannot be refunded + function refundDeadlineOf() external view returns (uint256 block); +} +``` + +### ERC-721 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.17; + +import "ERC721.sol"; +import "ERC165.sol"; + +/// @notice Refundable ERC-721 tokens +/// @dev The ERC-165 identifier of this interface is `0xe97f3c83` +interface ERC721Refund is ERC721 /* , ERC165 */ { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + event Refund( + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refundFrom` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + event RefundFrom( + address indexed _sender, + address indexed _from, + uint256 indexed _tokenId + ); + + /// @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 tokenId) external; + + /// @notice As long as the refund is active and the sender has sufficient approval, refund the token and send the ether to the sender + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// @param from The user from which to refund the token + /// @param tokenId The `tokenId` to refund + function refundFrom(address from, uint256 tokenId) 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 + 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` + /// @param tokenId The `tokenId` to query + /// @return block The first block where token cannot be refunded + function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block); +} +``` + +#### Optional ERC-721 Batch Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0; + +import "ERC721Refund.sol"; + +/// @notice Batch Refundable ERC-721 tokens +/// @dev The ERC-165 identifier of this interface is `` +contract ERC721BatchRefund is ERC721Refund { + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundBatch` + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenIds` that were refunded + event RefundBatch( + address indexed _from, + uint256[] _tokenIds // This may or may not be indexed + ); + + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundFromBatch` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + event RefundFromBatch( + address indexed _sender, + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice As long as the refund is active for the given `tokenIds`, refunds the user + /// @dev Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors + /// These must either succeed or fail together; there are no partial refunds. + /// @param tokenIds The `tokenId`s to refund + function refundBatch(uint256[] tokenIds) external; + + /// @notice As long as the refund is active for the given `tokenIds` and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// These must either succeed or fail together; there are no partial refunds. + /// @param from The user from which to refund the token + /// @param tokenIds The `tokenId`s to refund + function refundFromBatch(address from, uint256[] tokenIds) external; +} +``` + +### ERC-1155 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.17; + +import "ERC1155.sol"; +import "ERC165.sol"; + +/// @notice Refundable ERC-1155 tokens +/// @dev The ERC-165 identifier of this interface is `0x94029f5c` +interface ERC1155Refund is ERC1155 /* , ERC165 */ { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _from The account that requested a refund + /// @param _tokenId The `tokenId` that was refunded + /// @param _amount The amount of `tokenId` that was refunded + event Refund( + address indexed _from, + uint256 indexed _tokenId, + uint256 _amount + ); + + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refundFrom` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + /// @param _amount The amount of `tokenId` that was refunded + event RefundFrom( + address indexed _sender, + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice As long as the refund is active for the given `tokenId`, refunds the user + /// @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 tokenId, uint256 amount) external; + + /// @notice As long as the refund is active and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// @param from The user from which to refund the token + /// @param tokenId The `tokenId` to refund + /// @param amount The amount of `tokenId` to refund + function refundFrom(address from, 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 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` + /// @param tokenId The `tokenId` to query + /// @return block The first block where the token cannot be refunded + function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block); +} +``` + +#### Optional ERC-1155 Batch Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0; + +import "ERC1155Refund.sol"; + +/// @notice Batch Refundable ERC-1155 tokens +/// @dev The ERC-165 identifier of this interface is `` +contract ERC1155BatchRefund is ERC1155Refund { + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundBatch` + /// @param _from The account that requested a refund + /// @param _tokenIds The `tokenIds` that were refunded + /// @param _amounts The amount of each `tokenId` that was refunded + event RefundBatch( + address indexed _from, + uint256[] _tokenIds, // This may or may not be indexed + uint256[] _amounts + ); + + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundFromBatch` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenIds The `tokenIds` that was refunded + /// @param _amounts The amount of each `tokenId` that was refunded + event RefundFromBatch( + address indexed _sender, + address indexed _from, + uint256[] _tokenId, // This may or may not be indexed + uint256[] _amounts + ); + + /// @notice As long as the refund is active for the given `tokenIds`, refunds the user + /// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors + /// These must either succeed or fail together; there are no partial refunds. + /// @param tokenIds The `tokenId`s to refund + /// @param amounts The amount of each `tokenId` to refund + function refundBatch(uint256[] tokenIds, uint256[] amounts) external; + + /// @notice As long as the refund is active for the given `tokenIds` and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// These must either succeed or fail together; there are no partial refunds. + /// @param from The user from which to refund the token + /// @param tokenIds The `tokenId`s to refund + /// @param amounts The amount of each `tokenId` to refund + function refundFromBatch(address from, uint256[] tokenIds, uint256[] amounts external; +} +``` + +## Rationale + +`refundDeadlineOf` uses blocks instead of timestamps, as timestamps are less reliable than block numbers. + +The function names of `refund`, `refundOf`, and `refundDeadlineOf` were chosen to fit the naming style of ERC-20, ERC-721, and ERC-1155. + +[ERC-165](./eip-165.md) is required as introspection by DApps would be made significantly harder if it were not. + +Custom ERC-20 tokens are not supported, as it needlessly increases complexity, and the `refundFrom` function allows for this functionality when combined with a DEx. + +Batch refunds are optional, as account abstraction would make atomic operations like these significantly easier. However, they might still reduce gas costs if properly implemented. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Security Considerations + +There is a potential re-entrancy risk with the `refund` function. Make sure to perform the ether transfer **after** the tokens are destroyed (i.e. obey the checks, effects, interactions pattern). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5516.md b/EIPS/eip-5516.md new file mode 100644 index 00000000000000..bc9c8e7d6955a6 --- /dev/null +++ b/EIPS/eip-5516.md @@ -0,0 +1,186 @@ +--- +eip: 5516 +title: Soulbound Multi-owner Tokens +description: An interface for non-transferable, Multi-owner NFTs binding to Ethereum accounts +author: Lucas Martín Grasso Ramos (@LucasGrasso), Matias Arazi (@MatiArazi) +discussions-to: https://ethereum-magicians.org/t/EIP-5516-soulbound-multi-token-standard/10485 +status: Review +type: Standards Track +category: ERC +created: 2022-08-19 +requires: 165, 1155 +--- + +## Abstract +This EIP proposes a standard interface for non-fungible double signature Soulbound multi-tokens. Previous account-bound token standards face the issue of users losing their account keys or having them rotated, thereby losing their tokens in the process. This EIP provides a solution to this issue that allows for the recycling of SBTs. + +## Motivation +This EIP was inspired by the main characteristics of the [EIP-1155](./eip-1155.md) token and by articles in which benefits and potential use cases of Soulbound/Accountbound Tokens (SBTs) were presented. +This design also allows for batch token transfers, saving on transaction costs. Trading of multiple tokens can be built on top of this standard and it removes the need to approve individual token contracts separately. It is also easy to describe and mix multiple fungible or non-fungible token types in a single contract. + +### Characteristics +- The NFT will be non-transferable after the initial transfer +- Partially compatible with [EIP-1155](./eip-1155.md) +- Double Signature +- Multi-Token +- Multi-Owner +- Semi-Fungible + +### Applications +- Academic Degrees +- Code audits +- POAPs (Proof of Attendance Protocol 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. + +**Smart contracts implementing this EIP MUST implement all of the functions in the `EIP-5516` interface.** + +**Smart contracts implementing this EIP MUST implement the [EIP-165](./eip-165.md) `supportsInterface` function and and MUST return the constant value `true` if `0x8314f22b` is passed through the `interfaceID` argument. They also MUST implement the [EIP-1155](./eip-1155.md) Interface and MUST return the constant value `true` if `0xd9b67a26` is passed through the `interfaceID` argument. Furthermore, they MUST implement the [EIP-1155](./eip-1155.md) Metadata interface, and MUST return the constant value `true` if `0x0e89341c` is passed through the `interfaceID` argument.** + +_See [EIP-1155](./eip-1155.md#specification)_ + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.4; + +/** + @title Soulbound, Multi-Token standard. + @notice Interface of the EIP-5516 + Note: The ERC-165 identifier for this interface is 0x8314f22b. + */ + +interface IERC5516 { + /** + * @dev Emitted when `account` claims or rejects pending tokens under `ids[]`. + */ + event TokenClaimed( + address indexed operator, + address indexed account, + bool[] actions, + uint256[] ids + ); + + /** + * @dev Emitted when `from` transfers token under `id` to every address at `to[]`. + */ + event TransferMulti( + address indexed operator, + address indexed from, + address[] to, + uint256 amount, + uint256 id + ); + + /** + * @dev Get tokens owned by a given address. + */ + function tokensFrom(address from) external view returns (uint256[] memory); + + /** + * @dev Get tokens awaiting to be claimed by a given address. + */ + function pendingFrom(address from) external view returns (uint256[] memory); + + /** + * @dev Claims or Reject pending `id`. + * + * Requirements: + * - `account` must have a pending token under `id` at the moment of call. + * - `account` must not own a token under `id` at the moment of call. + * + * Emits a {TokenClaimed} event. + * + */ + function claimOrReject( + address account, + uint256 id, + bool action + ) external; + + /** + * @dev Claims or Reject pending tokens under `ids[]`. + * + * Requirements for each `id` `action` pair: + * - `account` must have a pending token under `id` at the moment of call. + * - `account` must not own a token under `id` at the moment of call. + * + * Emits a {TokenClaimed} event. + * + */ + function claimOrRejectBatch( + address account, + uint256[] memory ids, + bool[] memory actions + ) external; + + /** + * @dev Transfers `id` token from `from` to every address at `to[]`. + * + * Requirements: + * + * - `from` MUST be the creator(minter) of `id`. + * - All addresses in `to[]` MUST be non-zero. + * - All addresses in `to[]` MUST have the token `id` under `_pendings`. + * - All addresses in `to[]` MUST not own a token type under `id`. + * + * Emits a {TransfersMulti} event. + * + */ + function batchTransfer( + address from, + address[] memory to, + uint256 id, + uint256 amount, + bytes memory data + ) external; + +} + +``` + +## Rationale + +### SBT as an extension of EIP-1155 +We believe that Soulbound Tokens serve as a specialized subset of existing [EIP-1155](./eip-1155.md) tokens. The advantage of such a design is the seamless compatibility of SBTs with existing NFT services. Service providers can treat SBTs like NFTs and do not need to make drastic changes to their existing codebase. + +Making the standard mostly compatible with [EIP-1155](./eip-1155.md) also allows for SBTs to bind to multiple addresses and to Smart Contracts. + +### Double-Signature +The Double-Signature functionality was implemented to prevent the receipt of unwanted tokens. It symbolizes a handshake between the token receiver and sender, implying that **both** parties agree on the token transfer. + +### Metadata. +The [EIP-1155](./eip-1155.md#metadata) Metadata Interface was implemented for further compatibility with [EIP-1155](./eip-1155.md). + +### Guaranteed log trace +> As the Ethereum ecosystem continues to grow, many DApps are relying on traditional databases and explorer API services to retrieve and categorize data. The EIP-1155 standard guarantees that event logs emitted by the smart contract will provide enough data to create an accurate record of all current token balances. A database or explorer may listen to events and be able to provide indexed and categorized searches of every EIP-1155 token in the contract. + +_Quoted from [EIP-1155](./eip-1155.md#guaranteed-log-trace)_ + +This EIP extends this concept to the Double Signature functionality: The `{TokenClaimed}` event logs all the necessary information of a `ClaimOrReject(...)` or `ClaimOrRejectBatch(...)` function call, storing relevant information about the actions performed by the user. This also applies to the `batchTransfer(...)` function: It emits the `{TransferMulti}` event and logs necessary data. + +### Exception handling +Given the non-transferability property of SBTs, if a user's keys to an account get compromised or rotated, such user may lose the ability to associate themselves with the token. + +**Given the multi-owner characteristic of [EIP-1155](./eip-1155.md) compliant interfaces and contracts, SBTs will be able to bind to multiple accounts, providing a potential solution to the issue.** + +Multi-owner SBTs can also be issued to a contract account that implements a multi-signature functionality (As recommended in [EIP-4973](./eip-4973.md#exception-handling)); this can be achieved via the [EIP-1155](./eip-1155.md#erc-1155-token-receiver) Token Receiver interface. + +### Multi-token +The multi-token functionality permits the implementation of multiple token types in the same contract. Furthermore, all emitted tokens are stored in the same contract, preventing redundant bytecode from being deployed to the blockchain. It also facilitates transfer to token issuers, since all issued tokens are stored and can be accessed under the same contract address. + +### The `batchTransfer` function +This EIP supports transfers to multiple recipients. This eases token transfer to a large number of addresses, making it more gas-efficient and user-friendly. + +## Backwards Compatibility +This proposal is only partially compatible with EIP-1155, because it makes tokens non-transferable after the first transfer. + +## Reference Implementation +You can find an implementation of this standard in [../assets/EIP-5516](../assets/eip-5516/ERC5516.sol). + +## Security Considerations +Needs discussion. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5521.md b/EIPS/eip-5521.md new file mode 100644 index 00000000000000..4bf16cae954358 --- /dev/null +++ b/EIPS/eip-5521.md @@ -0,0 +1,250 @@ +--- +eip: 5521 +title: Referable NFT +description: An ERC-721 extension to construct reference relationships among NFTs +author: Saber Yu (@OniReimu), Qin Wang , Shange Fu , Yilin Sai , Shiping Chen , Sherry Xu , Jiangshan Yu +discussions-to: https://ethereum-magicians.org/t/eip-x-erc-721-referable-nft/10310 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-10 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [ERC-721](./eip-721.md). It proposes two referrable indicators, referring and referred, and a time-based indicator `createdTimestamp`. The relationship between each NFT forms a Directed acyclic graph (DAG). The standard allows users to query, track and analyze their relationships. + +## Motivation + +Many scenarios require inheritance, reference, and extension of NFTs. For instance, an artist may develop his NFT work based on a previous NFT, or a DJ may remix his record by referring to two pop songs, etc. Proposing a referable solution for existing NFTs and enabling efficient queries on cross-references make much sense. + +By adding the `referring` indicator, users can mint new NFTs (e.g., C, D, E) by referring to existing NFTs (e.g., A, B), while `referred` enables the referred NFTs (A, B) to be aware that who has quoted it (e.g., A ← D; C ← E; B ← E, and A ← E). The `createdTimestamp` is an indicator used to show the creation time of NFTs (A, B, C, D, E). + +## 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. + +`Relationship`: a structure that contains `referring`, `referred`, `createdTimestamp`, and other customized attributes such as `mapping (uint256 => address) privityOfAgreement` recording the ownerships of referred NFTs at the time the rNFTs were being created. +`referring`: an out-degree indicator, used to show the users this NFT refers to; +`referred`: an in-degree indicator, used to show the users who have refereed this NFT; +`createdTimestamp`: a time-based indicator, used to compare the timestamp of mint. + +`safeMint`: mint a new rNFT; +`setNode`: set the referring list of an rNFT and update the referred list of each one in the referring list; +`setNodeReferring`: set the referring list of an rNFT; +`setNodeReferred`: set the referred list of the given rNFTs; +`setNodeReferredExternal`: set the referred list of the given rNFTs sourced from other contracts; +`referringOf`: Get the referring list of an rNFT; +`referredOf`: Get the referred list of an rNFT. + +## Rationale + +This standard is intended to establish the referable DAG for queries on cross-relationship and accordingly provide the simplest functions. It provides advantages as follows. + +*Clear ownership inheritance*: This standard extends the static NFT into a virtually extensible NFT network. Artists do not have to create work isolated from others. The ownership inheritance avoids reinventing the same wheel. + +*Incentive Compatibility*: This standard clarifies the referable relationship across different NFTs, helping to integrate multiple up-layer incentive models for both original NFT owners and new creators. + +*Easy Integration*: This standard makes it easier for the existing token standards or third-party protocols. For instance, the rNFT can be applied to rentable scenarios (cf. [ERC-5006](./eip-5006.md) to build a hierarchical rental market, where multiple users can rent the same NFT during the same time or one user can rent multiple NFTs during the same duration). + +*Scalable Interoperability* From March 26th 2023, this standard has been stepping forward by enabling cross-contract references, giving a scalable adoption for the broader public with stronger interoperability. + +## Backwards Compatibility + +This standard can be fully [ERC-721](./eip-721.md) compatible by adding an extension function set. + +## Test Cases + +Test cases are included in [ERC_5521.test.js](../assets/eip-5521/ERC_5521.test.js) + +## Reference Implementation + +```solidity + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC_5521.sol"; + +contract ERC_5521 is ERC721, IERC_5521, TargetContract { + + struct Relationship { + mapping (address => uint256[]) referring; + mapping (address => uint256[]) referred; + uint256 createdTimestamp; // unix timestamp when the rNFT is being created + } + + mapping (uint256 => Relationship) internal _relationship; + address contractOwner = address(0); + + mapping (uint256 => address[]) private referringKeys; + mapping (uint256 => address[]) private referredKeys; + + constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) { + contractOwner = msg.sender; + } + + function safeMint(uint256 tokenId, address[] memory addresses, uint256[][] memory _tokenIds) public { + // require(msg.sender == contractOwner, "ERC_rNFT: Only contract owner can mint"); + _safeMint(msg.sender, tokenId); + setNode(tokenId, addresses, _tokenIds); + } + + /// @notice set the referred list of an rNFT associated with different contract addresses and update the referring list of each one in the referred list + /// @param tokenIds array of rNFTs, recommended to check duplication at the caller's end + function setNode(uint256 tokenId, address[] memory addresses, uint256[][] memory tokenIds) public virtual override { + require( + addresses.length == tokenIds.length, + "Addresses and TokenID arrays must have the same length" + ); + for (uint i = 0; i < tokenIds.length; i++) { + if (tokenIds[i].length == 0) { revert("ERC_5521: the referring list cannot be empty"); } + } + setNodeReferring(addresses, tokenId, tokenIds); + setNodeReferred(addresses, tokenId, tokenIds); + } + + /// @notice set the referring list of an rNFT associated with different contract addresses + /// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end + function setNodeReferring(address[] memory addresses, uint256 tokenId, uint256[][] memory _tokenIds) private { + require(_isApprovedOrOwner(msg.sender, tokenId), "ERC_5521: transfer caller is not owner nor approved"); + + Relationship storage relationship = _relationship[tokenId]; + + for (uint i = 0; i < addresses.length; i++) { + if (relationship.referring[addresses[i]].length == 0) { referringKeys[tokenId].push(addresses[i]); } // Add the address if it's a new entry + relationship.referring[addresses[i]] = _tokenIds[i]; + } + + relationship.createdTimestamp = block.timestamp; + emitEvents(tokenId, msg.sender); + } + + /// @notice set the referred list of an rNFT associated with different contract addresses + /// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end + function setNodeReferred(address[] memory addresses, uint256 tokenId, uint256[][] memory _tokenIds) private { + for (uint i = 0; i < addresses.length; i++) { + if (addresses[i] == address(this)) { + for (uint j = 0; j < _tokenIds[i].length; j++) { + if (_relationship[_tokenIds[i][j]].referred[addresses[i]].length == 0) { referredKeys[_tokenIds[i][j]].push(addresses[i]); } // Add the address if it's a new entry + Relationship storage relationship = _relationship[_tokenIds[i][j]]; + + require(tokenId != _tokenIds[i][j], "ERC_5521: self-reference not allowed"); + if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_5521: the referred rNFT needs to be a predecessor"); } // Make sure the reference complies with the timing sequence + + relationship.referred[address(this)].push(tokenId); + emitEvents(_tokenIds[i][j], ownerOf(_tokenIds[i][j])); + } + } else { + TargetContract targetContractInstance = TargetContract(addresses[i]); + targetContractInstance.setNodeReferredExternal(address(this), tokenId, _tokenIds[i]); + } + } + } + + /// @notice set the referred list of an rNFT associated with different contract addresses + /// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end + function setNodeReferredExternal(address _address, uint256 tokenId, uint256[] memory _tokenIds) external { + for (uint i = 0; i < _tokenIds.length; i++) { + if (_relationship[_tokenIds[i]].referred[_address].length == 0) { referredKeys[_tokenIds[i]].push(_address); } // Add the address if it's a new entry + Relationship storage relationship = _relationship[_tokenIds[i]]; + + require(_address != address(this), "ERC_5521: this must be an external contract address"); + if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_5521: the referred rNFT needs to be a predecessor"); } // Make sure the reference complies with the timing sequence + + relationship.referred[_address].push(tokenId); + emitEvents(_tokenIds[i], ownerOf(_tokenIds[i])); + } + } + + /// @notice Get the referring list of an rNFT + /// @param tokenId The considered rNFT, _address The corresponding contract address + /// @return The referring mapping of an rNFT + function referringOf(address _address, uint256 tokenId) external view virtual override(IERC_5521, TargetContract) returns (address[] memory, uint256[][] memory) { + address[] memory _referringKeys; + uint256[][] memory _referringValues; + + if (_address == address(this)) { + require(_exists(tokenId), "ERC_5521: token ID not existed"); + (_referringKeys, _referringValues) = convertMap(tokenId, true); + } else { + TargetContract targetContractInstance = TargetContract(_address); + (_referringKeys, _referringValues) = targetContractInstance.referringOf(_address, tokenId); + } + return (_referringKeys, _referringValues); + } + + /// @notice Get the referred list of an rNFT + /// @param tokenId The considered rNFT, _address The corresponding contract address + /// @return The referred mapping of an rNFT + function referredOf(address _address, uint256 tokenId) external view virtual override(IERC_5521, TargetContract) returns (address[] memory, uint256[][] memory) { + address[] memory _referredKeys; + uint256[][] memory _referredValues; + + if (_address == address(this)) { + require(_exists(tokenId), "ERC_5521: token ID not existed"); + (_referredKeys, _referredValues) = convertMap(tokenId, false); + } else { + TargetContract targetContractInstance = TargetContract(_address); + (_referredKeys, _referredValues) = targetContractInstance.referredOf(_address, tokenId); + } + return (_referredKeys, _referredValues); + } + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC_5521).interfaceId + || interfaceId == type(TargetContract).interfaceId + || super.supportsInterface(interfaceId); + } + + // @notice Emit an event of UpdateNode + function emitEvents(uint256 tokenId, address sender) private { + (address[] memory _referringKeys, uint256[][] memory _referringValues) = convertMap(tokenId, true); + (address[] memory _referredKeys, uint256[][] memory _referredValues) = convertMap(tokenId, false); + + emit UpdateNode(tokenId, sender, _referringKeys, _referringValues, _referredKeys, _referredValues); + } + + // @notice Convert a specific `local` token mapping to a key array and a value array + function convertMap(uint256 tokenId, bool isReferring) private view returns (address[] memory, uint256[][] memory) { + Relationship storage relationship = _relationship[tokenId]; + + address[] memory returnKeys; + uint256[][] memory returnValues; + + if (isReferring) { + returnKeys = referringKeys[tokenId]; + returnValues = new uint256[][](returnKeys.length); + for (uint i = 0; i < returnKeys.length; i++) { + returnValues[i] = relationship.referring[returnKeys[i]]; + } + } else { + returnKeys = referredKeys[tokenId]; + returnValues = new uint256[][](returnKeys.length); + for (uint i = 0; i < returnKeys.length; i++) { + returnValues[i] = relationship.referred[returnKeys[i]]; + } + } + return (returnKeys, returnValues); + } +} + +``` + +## Security Considerations + +The `createdTimestamp` only covers the block-level timestamp (based on block headers), which does not support fine-grained comparisons such as transaction-level. + +The change of ownership has nothing to do with the reference relationship. Normally, the distribution of profits complies to the aggreement when the NFT was being created regardless of the change of ownership unless specified in the agreement. + +Referring a token will not refer its descendants by default. In the case that only a specific child token gets referred, it means the privity of contract will involve nobody other than the owner of this specific child token. Alternatively, a chain-of-reference all the way from the root token to a specific very bottom child token (from root to leaf) can be constructured and recorded in the `referring` to explicitly define the distribution of profits. + +The `safeMint` function has been deliberately designed to allow unrestricted minting and relationship setting, akin to the open referencing system seen in platforms like Google Scholar. This decision facilitates strong flexibility, enabling any user to create and define relationships between tokens without centralized control. While this design aligns with the intended openness of the system, it inherently carries certain risks. Unauthorized or incorrect references can be created, mirroring the challenges faced in traditional scholarly referencing where erroneous citations may occur. Additionally, the open nature may expose the system to potential abuse by malicious actors, who might manipulate relationships or inflate token supply. It is important to recognize that these risks are not considered design flaws but intentional trade-offs, balancing the system's flexibility against potential reliability concerns. Stakeholders should be aware that the on-chain data integrity guarantees extend only to what has been recorded on the blockchain and do not preclude the possibility of off-chain errors or manipulations. Thus, users and integrators should exercise caution and judgment in interpreting and using the relationships and other data provided by this system. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5528.md b/EIPS/eip-5528.md new file mode 100644 index 00000000000000..cb822cc106a584 --- /dev/null +++ b/EIPS/eip-5528.md @@ -0,0 +1,266 @@ +--- +eip: 5528 +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: Final +type: Standards Track +category: ERC +created: 2022-08-16 +requires: 20 +--- + +## Abstract + +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 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 + +Because of the pseudonymous nature of cryptocurrencies, there is no automatic recourse to recover funds that have already been paid. + +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*. 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` + +The *Escrow Contract* demonstrates details of escrow policies as none-mutable matter in constructor implementation. + +The *Escrow Contract* MUST define the following policies: + +- Seller token contract address +- Buyer token contract address + +The *Escrow Contract* MAY define the following policies: + +- Escrow period +- Maximum (or minimum) number of investors +- Maximum (or minimum) number of tokens to fund +- Exchange rates of seller/buyer token +- KYC verification of users + +#### `escrowFund` + +Funds `_value` amount of tokens to address `_to`. + +In the case of *Escrow Contract*: + + - `_to` MUST be the user address. + - `msg.sender` MUST be the *Payable Contract* address. + - MUST check policy validations. + +In the case of *Payable 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) +``` + +#### `escrowRefund` + +Refunds `_value` amount of tokens from address `_from`. + +In the case of *Escrow Contract*: + + - `_from` MUST be the user address. + - `msg.sender` MUST be the *Payable Contract* address. + - MUST check policy validations. + +In the case of *Payable 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) +``` + +#### `escrowWithdraw` + +Withdraws funds from the escrow account. + +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. + +```solidity +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 { + + function escrowFund(address _to, uint256 _value) 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 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. + +`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 completes, the seller can get the buyer's token, and the buyers can get the seller's token. + +## Backwards Compatibility + +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. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5539.md b/EIPS/eip-5539.md new file mode 100644 index 00000000000000..eb3e50cac35977 --- /dev/null +++ b/EIPS/eip-5539.md @@ -0,0 +1,252 @@ +--- +eip: 5539 +title: Revocation List Registry +description: Registry of revocation lists for revoking arbitrary data. +author: Philipp Bolte (@strumswell), Lauritz Leifermann (@lleifermann), Dennis von der Bey (@DennisVonDerBey) +discussions-to: https://ethereum-magicians.org/t/eip-5539-revocation-list-registry/10573 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-26 +requires: 712 +--- + +## Abstract +This EIP proposes a set of methods and standards for a role-based registry of indicators aimed for usage in revocations. + +## Motivation +Revocation is a universally needed construct both in the traditional centralized and decentralized credential attestation. This EIP aims to provide an interface to standardize a decentralized approach to managing and resolving revocation states in a contract registry. + +The largest problem with traditional revocation lists is the centralized aspect of them. Most of the world's CRLs rely on HTTP servers as well as caching and are therefore vulnerable to known attack vectors in the traditional web space. This aspect severely weakens the underlying strong asymmetric key architecture in current PKI systems. + +In addition, issuers in existing CRL approaches are required to host an own instance of their public revocation list, as shared or centralized instances run the risk of misusage by the controlling entity. +This incentivizes issuers to shift this responsibility to a third party, imposing the risk of even more centralization of the ecosystem (see Cloudflare, AWS). +Ideally, issuers should be able to focus on their area of expertise, including ownership of their revocable material, instead of worrying about infrastructure. + +We see value in a future of the Internet where anyone can be an issuer of verifiable information. This proposal lays the groundwork for anyone to also own the lifecycle of this information to build trust in ecosystems. + +## 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. + +This EIP specifies a contract called `EthereumRevocationRegistry` that is deployed once and may then be commonly used by everyone. By default, an Ethereum address **MAY** own and manage a multitude of revocation lists in a namespace that **MUST** contain the revocation states for a set of revocation keys. + +An owner of a namespace **MAY** allow delegates to manage one or more of its revocation lists. Delegates **MUST** be removable by the respective list's owner. In certain situations, an owner **MAY** also want to transfer a revocation list in a namespace and its management rights to a new owner. + +### Definitions +- `namespace`: A namespace is a representation of an Ethereum address inside the registry that corresponds to its owners address. All revocation lists within a namespace are initially owned by the namespace's owner address. +- `revocation list`: A namespace can contain a number of revocation lists. Each revocation list is identified by a unique key of the type bytes32 that can be used to address it in combination with the namespace address. +- `revocation key`: A revocation list can contain a number of revocation keys of the type bytes32. In combination with the namespace address and the revocation list key, it resolves to a boolean value that indicates whether the revocation key is revoked or not. +- `owner`: An Ethereum address that has modifying rights to revocation lists within its own and possibly foreign namespaces. An owner can give up modifying rights of revocation lists within its namespace by transferring ownership to another address. +- `delegate`: An Ethereum address that received temporary access to a revocation list in a namespace. It has to be granted by the current owner of the revocation list in question. + +### Revocation Management + +#### isRevoked +**MUST** implement a function that returns the revocation status of a particular revocation key in a namespace's revocation list. It **MAY** also respect the revocation lists revocation status. +```solidity +function isRevoked(address namespace, bytes32 list, bytes32 key) public view returns (bool); +``` + +#### changeStatus +**MUST** implement a function to change the revocation status of a particular revocation key in a namespace's revocation list +```solidity +function changeStatus(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey) public; +``` + +#### changeStatusSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change the revocation status of a particular revocation key in a namespace's revocation list with a raw signature. +```solidity +function changeStatusSigned(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey, address signer, bytes calldata signature) public; +``` + +#### changeStatusDelegated +**OPTIONAL** implements a function to change the revocation status of a particular revocation key in a namespace's revocation list by a revocation list's delegate. +```solidity +function changeStatusDelegated(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey) public; +``` + +#### changeStatusDelegatedSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change the revocation status of a particular revocation key in a namespace's revocation list with a raw signature. +```solidity +function changeStatusDelegatedSigned(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey, address signer, bytes calldata signature) public; +``` + +#### changeStatusesInList +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once. +```solidity +function changeStatusesInList(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys) public; +``` + +#### changeStatusesInListSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once with a raw signature. +```solidity +function changeStatusesInListSigned(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys, address signer, bytes calldata signature) public; +``` + +#### changeStatusesInListDelegated +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once by a revocation list's delegate. +```solidity +function changeStatusesInListDelegated(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys) public; +``` + +#### changeStatusesInListDelegatedSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once with a raw signature generated by a revocation list's delegate. +```solidity +function changeStatusesInListDelegatedSigned(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys, address signer, bytes calldata signature) public; +``` + +### Revocation List Management + +#### +**OPTIONAL** implements a function that returns the revocation status of a particular revocation list in a namespace. +```solidity +function listIsRevoked(address namespace, bytes32 revocationList) view public returns (bool); +``` + +#### changeListStatus +**OPTIONAL** implements a function to change the revocation of a revocation list itself. If a revocation list is revoked, all its keys are considered revoked as well. +```solidity +function changeListStatus(bool revoked, address namespace, bytes32 revocationList) public; +``` + +#### changeListStatusSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change the revocation of a revocation list itself with a raw signature. If a revocation list is revoked, all its keys are considered revoked as well. +```solidity +function changeListStatusSigned(bool revoked, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +### Owner management + +#### changeListOwner +**OPTIONAL** implement a function to change the revocation status of a revocation list. If a revocation list is revoked, all keys in it are considered revoked. +```solidity +function changeListOwner(address newOwner, address namespace, bytes32 revocationList) public; +``` + +#### changeListOwnerSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implement a function to change the revocation status of a revocation list with a raw signature. If a revocation list is revoked, all keys in it are considered revoked. +```solidity +function changeListOwnerSigned(address newOwner, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +### Delegation management + +#### addListDelegate +**OPTIONAL** implements a function to add a delegate to an owner's revocation list in a namespace. +```solidity +function addListDelegate(address delegate, address namespace, bytes32 revocationList) public; +``` + +#### addListDelegateSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to add a delegate to an owner's revocation list in a namespace with a raw signature. +```solidity +function addListDelegateSigned(address delegate, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +#### removeListDelegate +**OPTIONAL** implements a function to remove a delegate from an owner's revocation list in a namespace. +```solidity +function removeListDelegate(address delegate, address owner, bytes32 revocationList) public; +``` + +#### removeListDelegateSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to remove a delegate from an owner's revocation list in a namespace with a raw signature. +```solidity +function removeListDelegateSigned(address delegate, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +### Events + +#### RevocationStatusChanged +**MUST** be emitted when `changeStatus`, `changeStatusSigned`, `changeStatusDelegated`, `changeStatusDelegatedSigned`, `changeStatusesInList`, `changeStatusesInListSigned`, `changeStatusesInListDelegated`, or `changeStatusesInListDelegatedSigned` was successfully executed. + +```solidity +event RevocationStatusChanged( + address indexed namespace, + bytes32 indexed revocationList, + bytes32 indexed revocationKey, + bool revoked +); +``` + +#### RevocationListOwnerChanged +**MUST** be emitted when `changeListOwner` or `changeListOwnerSigned` was successfully executed. + +```solidity +event RevocationListOwnerChanged( + address indexed namespace, + bytes32 indexed revocationList, + address indexed newOwner +); +``` + +#### RevocationListDelegateAdded +**MUST** be emitted when `addListDelegate` or `addListDelegateSigned` was successfully executed. + +```solidity +event RevocationListDelegateAdded( + address indexed namespace, + bytes32 indexed revocationList, + address indexed delegate +); +``` + +#### RevocationListDelegateRemoved +**MUST** be emitted when `removeListDelegate` or `removeListDelegateSigned` was successfully executed. + +```solidity +event RevocationListDelegateRemoved( + address indexed namespace, + bytes32 indexed revocationList, + address indexed delegate +); +``` + +#### RevocationListStatusChanged +**MUST** be emitted when `changeListStatus` or `changeListStatusSigned` was successfully executed. + +```solidity +event RevocationListStatusChanged( + address indexed namespace, + bytes32 indexed revocationlist, + bool revoked +); +``` + +### Meta Transactions + +This section uses the following terms: +- **`transaction signer`**: An Ethereum address that signs arbitrary data for the contract to execute **BUT** does not commit the transaction. +- **`transaction sender`**: An Ethereum address that takes signed data from a **transaction signer** and commits it wrapped with its own signature to the smart contract. + +An address (**transaction signer**) **MAY** be able to deliver a signed payload off-band to another address (**transaction sender**) that initiates the Ethereum interaction with the smart contract. The signed payload **MUST** be limited to be used only once ([Signed Hash](#SignedHash) + [nonces](#Nonce)). + +#### Signed Hash + +The signature of the **transaction signer** **MUST** conform [EIP-712](./eip-712.md). This helps users understand what the payload they're signing consists of & it improves the protection against replay attacks. + +#### Nonce + +This EIP **RECOMMENDS** the use of a **dedicated nonce mapping** for meta transactions. If the signature of the **transaction sender** and its meta contents are verified, the contract increases a nonce for this **transaction signer**. This effectively removes the possibility for any other sender to execute the same transaction again with another wallet. + +## Rationale + +### Why the concept of namespaces? +This provides every Ethereum address a reserved space, without the need to actively claim it in the contract. Initially addresses only have owner access in their own namespace. + +### Why does a namespace always represent the initial owner address? +The change of an owner of a list shouldn't break the link to a revocation key in it, as already existing off-chain data may depend on it. + +## Backwards Compatibility +No backward compatibility issues were found. + +## Security Considerations + +### Meta Transactions +The signature of signed transactions could potentially be replayed on different chains or deployed versions of the registry implementing this ERC. This security consideration is addressed by the usage of [EIP-712](./eip-712.md) + +### Rights Management +The different roles and their inherent permissions are meant to prevent changes from unauthorized entities. The revocation list owner should always be in complete control over its revocation list and who has writing access to it. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5553.md b/EIPS/eip-5553.md new file mode 100644 index 00000000000000..b131e89e021991 --- /dev/null +++ b/EIPS/eip-5553.md @@ -0,0 +1,256 @@ +--- +eip: 5553 +title: Representing IP and its Royalty Structure +description: A way of representing intellectual property and its respective royalty structure on chain +author: Roy Osherove (@royosherove) +discussions-to: https://ethereum-magicians.org/t/eip-5553-representing-intellectual-property-on-chain-with-royalty-rights/10551 +status: Review +type: Standards Track +category: ERC +created: 2022-08-17 +requires: 20, 721 +--- + +## Abstract +This proposal introduces a generic way to represent intellectual property on chain, along with a refined royalty representation mechanism and associated metadata link. This standard is not associated with a specific type of IP and could represent many types of IP, such as musical IP, videos, books, images, and more. +The standard is kept very generic to allow the industry to evolve new ecosystems that can all rely on the same basic standard at their core. + +This standard allows market participants to: +1) Observe the canonical on-chain representation of an intellectual property +2) Discover its attached metadata +3) Discover its related royalty structure +4) This will enable building registration, licensing, and payout mechanisms for intellectual property assets in the future. + +## Motivation + +There is no accepted standard mechanism to license intellectual property or to represent it, except using traditional NFTs. However, regular NFTs only represent a collectible item use case and cannot easily represent more complicated use cases of licensing IP for different types of uses. +We can enable such licensing mechanisms if we can: + +1) Declare that IP exists, SEPARATELY from its purchase ability +2) Declare possibly multiple interested parties to be paid for such IP + +For 1, no standard exists today. + +For 2, traditional split standards exist based on NFT purchases or through mechanisms like 0xsplits. While these solve the main problem, they do not contain the ability to name multiple types of collaboration participants. + + + +## 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. + +**contracts that want to represent IP on chain MUST implement [EIP-721](./eip-721.md) AND this Proposal** + +This standard extends [EIP-721](./eip-721.md) with the following `IIPRepresentation` (IPR for short) interface. +Implementers of this standard **MUST** have all of the following functions: + +### royaltyPortionTokens() function +This function MUST return an array of addresses related to [EIP-20](./eip-20.md) tokens that MUST represent royalty portions to different types of interested parties. These royalty portion tokens represent a more granular and streamlined way to declare royalty splits for multiple collaboration participants for the creation of the IP. + +For example, for a musical IP, we might have two tokens representing the composition/writing/publishing royalty portion side and the recording/master side. These royalty portion tokens are distributed to the collaboration participants and can later be queried by the various holders to distribute royalties. I.e., if one holds 10% of a royalty portion token, that holder will get 10% of the financial distribution related to that type of royalty. + +### metadataURI() function +This function MUST return the URI to a metadata file containing any required metadata for the IP or an empty string. Each IP type MAY implement its metadata standard, defined separately. The file MUST be hosted in IPFS, Arweave, or other decentralized content-addressable systems in which the file's contents are not changeable without changing the URI. + +### changeMetadataURI() function +This function allows changing the metadata URI to point to a new version of the metadata file. Calling this function MUST trigger the event `MetadataChanged` in case of success. + +### ledger() function +This function MUST return the registry or registrar contract address or an EOA account that initialized the IP and associated royalty tokens. An IP representation MAY be registered in multiple places by different actors for different purposes. This function enables market participants to discover which registry mechanism is the parent of the IP and might have special access rights to manage the IP. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.9; +import '@openzeppelin/contracts/interfaces/IERC165.sol'; + + +/// +/// @dev Interface for Intellectual Property Representation +/// +interface IIPRepresentation is IERC165 { + + /// @notice Called with the new URI to an updated metadata file + /// @param _newUri - the URI pointing to a metadata file (file standard is up to the implementer) + /// @param _newFileHash - The hash of the new metadata file for future reference and verification + function changeMetadataURI(string memory _newUri, string memory _newFileHash) external ; + + /// @return array of addresses of ERC20 tokens representing royalty portion in the IP + /// @dev i.e implementing ERC5501 (IRoyaltyInterestToken interface) + function royaltyPortionTokens() external view returns (address[] memory) ; + + /// @return the address of the contract or EOA that initialized the IP registration + /// @dev i.e., a registry or registrar, to be implemented in the future + function ledger() external view returns (address) ; + + /// @return the URI of the current metadata file for the II P + function metadataURI() external view returns (string memory) ; + + /// @dev event to be triggered whenever metadata URI is changed + /// @param byAddress the addresses that triggered this operation + /// @param oldURI the URI to the old metadata file before the change + /// @param oldFileHash the hash of the old metadata file before the change + /// @param newURI the URI to the new metadata file + /// @param newFileHash the hash of the new metadata file + event MetadaDataChanged(address byAddress, string oldURI, string oldFileHash, string newURI, string newFileHash); +} +``` + + +## Rationale + +### Returning an array of EIP-20 tokens presents a more robust royalty portions structure/ + +Current royalty implementations deal only with a single type of royalty payment: NFT sales. They also only allow a single type of royalty - i.e., Music NFTs cannot pay different people in different scenarios. +In other words, currently, a royalty split works the same way no matter what type of purchase or license deal has happened for all parties involved. + +With this proposal, multiple **types** of royalty scenarios are allowed. A classic case is the music industry, in which we have writing/composition royalties and recording/master royalties. Different licensing types will pay different percentages to different parties based on context. + +In the case of a song cover, a license payment formula can be created so that that +a) Original IP's writers get paid for using the lyrics or composition of the song +b) recording artists of the original song do not get paid since their recording is not used +c) recording artists of the new IP will get paid +d) there are no writing royalties for the creators of the cover. + +Moreover, this EIP has a single structure that connects to all types of royalty types and allows finding them more easily. +Lastly, moving EIP-20 tokens around is much easier than managing an 0xsplits contract. + +### Separating the IP contract from the collectible and licensing NFTs enables scaling licensing types +By separating the canonical version of the IP from its various licensed uses (NFT purchase, streaming, usage of art and more.), this EIP introduces a path for an ecosystem of various license types and payment distributions to evolve. +In other words, when people use this scheme, they will not start by creating a music NFT or art NFT; they start by creating the IP Representation and then create types of licenses or collectibles for it, each as its own sellable NFT. + +### A single pointer to the IP's metadata +The IPR points to metadata housed in IPFS or Arweave and allows changing it and keeping track of the changes in a simple and standard way. Today the only metadata standard is NFT metadata extension, but it is impossible to know to which standard the document adheres. With different IP types, different metadata standards for different IP types can be formulated and have a simple, easy place to discover attached metadata. + +## Reference Implementation + +#### Implementing a Musical IP Representation (MIPR for short) based on IIPRepresentation +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.9; +import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; +import "./interfaces/IIPRepresentation.sol"; +import "./interfaces/Structs.sol"; + + +contract MusicalIP is ERC721, IIPRepresentation { + address public songLedger; + address public compToken; + address public recToken; + string public metadataURI; + string public fileHash; + uint256 public tokenId; + bool public activated =false; + + function supportsInterface(bytes4 interfaceId) public view virtual override( ERC721, IERC165) returns (bool) { + return + interfaceId == type(IIPRepresentation).interfaceId || + super.supportsInterface(interfaceId); + } + + function getInterfaceId() public pure returns (bytes4){ + return type(IIPRepresentation).interfaceId; + } + + constructor ( + uint256 _tokenId, + address _songLedger, + SongMintingParams memory _params, + address _compAddress, + address _recAddress + ) + ERC721(_params.shortName, _params.symbol){ + + songLedger = _songLedger; + compToken = _compAddress; + recToken = _recAddress; + metadataURI = _params.metadataUri; + fileHash = _params.fileHash; + tokenId = _tokenId; + + _safeMint(_songLedger, _tokenId); + emit Minted(_params.shortName,_songLedger,_compAddress,_recAddress,_msgSender(),tokenId,_params.metadataUri); + } + + function changeMetadataURI(string memory _newURI,string memory _newFileHash) public + { + string memory oldURI = metadataURI; + string memory oldHash = fileHash; + metadataURI = _newURI; + fileHash = _newFileHash; + + emit MetadataChanged(oldURI, oldHash,_newURI,_newFileHash); + } + + function royaltyPortionTokens() external view returns (address[] memory) { + address[] memory items = new address[](2); + items[0] = compToken; + items[1] = recToken; + return items; + } + function ledger() external view returns (address) { + return songLedger; + } + + event MetadataChanged( + string oldUri, string oldFileHash, + string newUri, string newFileHash + ); + event Minted( + string abbvName, + address ledger, + address compToken, + address recToken, + address creator, + uint256 tokenId, + string metadataUri + ); +} + + + +``` + +#### Deploying a new Musical IP using a simple song registry contract + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.9; +import "@openzeppelin/contracts/utils/Counters.sol"; +import "./MusicalIP.sol"; +import "./CompositionRoyaltyToken.sol"; +import "./RecordingRoyaltyToken.sol"; + + +contract SimpleSongLedger is IERC721Receiver { + using Counters for Counters.Counter; + Counters.Counter private mipIds; + function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { + return IERC721Receiver.onERC721Received.selector; + } + + function mintSong(SongMintingParams memory _params) public { + CompositionRoyaltyToken comp = new CompositionRoyaltyToken(address(this),"SONGCOMP","COMP"); + RecordingRoyaltyToken rec = new RecordingRoyaltyToken(address(this),"SONGREC","REC"); + mipIds.increment(); + + MusicalIP mip = new MusicalIP( + mipIds.current(), + address(this), + _params, + address(comp), + address(rec) + ); + } +} + + +``` +## Security Considerations + +There might be potential security challenges of attackers persuading holders of royalty portion tokens to send them those tokens and gaining royalty portion in various IPRs. However, these are not specific to royalties and are a common issue with EIP-20 tokens. + +In the case of the IP registration ownership, it will be recommended that registry contracts own the IP registration, which will be non-transferrable (account bound to the registry that created it). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5554.md b/EIPS/eip-5554.md new file mode 100644 index 00000000000000..eb70c862e37b8c --- /dev/null +++ b/EIPS/eip-5554.md @@ -0,0 +1,213 @@ +--- +eip: 5554 +title: NFT Legal Use, Repurposing, and Remixing +description: An interface for describing and enforcing the legal use and remix of an NFT. On-chain registry of rights, attribution and derivative links. +author: Isaac Patka (@ipatka), COALA Licensing Taskforce +discussions-to: https://ethereum-magicians.org/t/eip-5999-legal-use-sharing-repurposing-and-remixing-standard-compatible-with-creative-commons/10553 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-07 +requires: 5218 +--- + +## Abstract + +This EIP extends any other token standard to provide: + +* Explicit rights for the token holder related to commercial exploitation, derivative works, and reproduction; +* [EIP-5218](./eip-5218.md) interface for creating, viewing, and checking the status of licenses +* Standard format for extended license information in the token metadata; +* Standard events to track off chain creation of derivative works, commercial exploitation, and reproduction; +* On chain tracking of derivative works and reproductions +* Additional required fields in the smart contract to reference the copyright owner +* Function calls for commercial exploitation, derivative works and reproduction. + +## Motivation +NFTs still face legal uncertainty, and many now realize that the rights associated with an NFT are just as important as the NFT itself. Our goal is to help the ecosystem reach clear consensus and broad understanding of what purchasers of NFTs are acquiring in terms of copyright or other rights. + +Today, purchasing the NFT of a digital work is not the same as purchasing the copyright in that work. In most cases, the NFT does not even incorporate the digital work; it only references it via a hash. Hence, the NFT holder owns a unique digital copy of the work, but does not necessarily enjoy the right to reproduce, redistribute, or otherwise exploit that work—unless explicitly provided for by the copyright owner. It typically only includes the right to privately enjoy the work and display it publicly on social media or in virtual galleries. + +We aim to create a new set of licenses with modular terms and conditions—à la Creative Commons—in order to enable artists to increase the value of their NFT by associating additional rights to them (e.g. the right to create derivative works, or to allow for the commercial usage of the underlying works). Our solution will allow for any licensed rights to be granted, only and exclusively, to the current holders of an NFT, and to be transferred automatically to the new token holders every time the NFT is being transferred. + +An on chain registry of copyrighted material will help in discovery of the rights associated with the NFTs that have been created with this protocol. + +Our current work is drafting the legalese and technical specifications. + +## 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. + +Every contract compliant with this EIP must implement the `IERC5554` interface: + +```solidity +pragma solidity ^0.8.0; + +interface IERC5554 is IERC5218 { + + event CommercialExploitation(uint256 _tokenId, uint256 _licenseId, string _externalUri); + event ReproductionCreated(uint256 _tokenId, uint256 _licenseId, uint256 _reproductionId, address _reproduction, uint256 _reproductionTokenId); + event DerivativeCreated(uint256 _tokenId, uint256 _licenseId, uint256 _derivativeId, address _derivative, uint256 _derivativeTokenId); + + /// @notice Retrieve the copyright owner address + /// @dev Throws unless the token exists + /// @param tokenId The identifier for the queried token + /// @return address of the copyright owner + function getCopyrightOwner(uint256 tokenId) + external + virtual + returns (address); + + /// @notice Requests to log an execution of a license + /// @dev Throws unless the token issuance conditions are met + /// @param tokenId The identifier for the queried token + /// @return uint256 tracking reproduction ID + function logReproduction(uint256 tokenId, address reproduction, uint256 reproductionTokenId) + external + virtual + returns (uint256); + + /// @notice Requests to log an executions of a license + /// @dev Throws unless the token issuance conditions are met + /// @param tokenId The identifier for the queried token + /// @return uint256 tracking derivative ID + function logDerivative(uint256 tokenId, address derivative, uint256 derivativeTokenId) + external + virtual + returns (uint256); + + /// @notice Requests to log an execution of a license + /// @dev Throws unless the commercial exploitation conditions are met + /// @param tokenId The identifier for the queried token + function logCommercialExploitation(uint256 tokenId, string calldata uri) + external; + + /// @notice Retrieve the token associated with a reproduction + /// @dev Throws unless the reproduction exists + /// @param _reproductionId The identifier for the reproduction + /// @return uint256 The identifier for the token used to generate the reproduction + function getReproductionTokenId(uint256 _reproductionId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a reproduction + /// @dev Throws unless the reproduction exists + /// @param _reproductionId The identifier for the reproduction + /// @return uint256 The identifier for the license used to generate the reproduction + function getReproductionLicenseId(uint256 _reproductionId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a reproduction + /// @dev Throws unless the reproduction exists + /// @param _reproductionId The identifier for the derivative work + /// @return address The address of the reproduction collection + function getReproductionCollection(uint256 _reproductionId) + external + view + returns (address); + + /// @notice Retrieve the token associated with a derivative + /// @dev Throws unless the derivative exists + /// @param _derivativeId The identifier for the derivative work + /// @return uint256 The identifier for the token used to generate the derivative work + function getDerivativeTokenId(uint256 _derivativeId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a derivative + /// @dev Throws unless the derivative exists + /// @param _derivativeId The identifier for the derivative work + /// @return uint256 The identifier for the license used to generate the derivative work + function getDerivativeLicenseId(uint256 _derivativeId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a derivative + /// @dev Throws unless the derivative exists + /// @param _derivativeId The identifier for the derivative work + /// @return address The address of the derivative collection + function getDerivativeCollection(uint256 _derivativeId) + external + view + returns (address); + +} +``` + + + +### Token based Attribution/ Remix +On chain derivative works and reproductions +* Reproductions and derivative works are tracked in the contract. + + +### Event based attribution +For commercial exploitation or other off-chain uses of a creative work, this EIP defines events to be emitted to track the use of the work. + +```solidity +event CommercialExploitation(uint256 tokenID, string uri) + +function logCommercialExploitation(uint256 tokenId, string calldata uri) external returns bool; +``` + +#### Example: +When a token holder uses an NFT for off-chain merchandise, log a reference to the off-chain work in the event uri + +### Required fields + +```solifity +function copyrightOwner(uint256 tokenId) external returns address; +``` + +Copyright owner per tokenID. Could just be the tokenID owner in a simple use case, or something else if desired by the creator. + +## Rationale +We expand here upon the Motivation section to justify every decision made with regard to the specs of the standard: + +The `getLicenseId()` function takes a tokenID as a parameter, making it possible for different tokenID to be associated with different licensing terms. + +LicenseURI links to a content-addressed file that stipulates the terms and conditions of the license in actual legal language, so that the license can be read and understood by those who want to understand which rights are associated with the work of authorship, and which additional rights are granted through the acquisition of the NFT. + +When the license allows for the reproduction and/or for the creation of a derivative work only to the token holders, there needs to be a way to verify that the new NFT or the derivative NFT was created legitimately. The standard ensures this by enabling the current token holder to call a function, e.g. logDerivative which checks that the caller has a valid license to execute + +For commercial exploitation or other off-chain uses of a creative work, the standard implements the `logCommercialExploitation()` that makes it possible to keep track of which commercial exploitations have been made, and when. This makes it possible to verify that all commercial exploitation were legitimately done. + +The standard introduces a new field, `copyrightOwner`, which indicates the address of the current holder of the copyright in the work. If multiple copyright owners exist, a multisig address (or DAO) can be used. + +The artist address is not registered as an on-chain variable, but rather as part of the metadata, because it is an immutable field. + +If any, the parents of the work (i.e. the works that it is derived upon) must be part of the metadata information, so that people can verify that the NFT has obtained a DerivativeWork for each one of its parents. + +This licensing framework is intended to create a system to facilitate the licensing of rights that “follow the token” through a public licensing framework. This is not meant to be used for cases in which an exclusive right is licensed through a personal license to a specific actor (e.g. the copyright owner providing a third-party with the right to commercially exploit the work, regardless of whether they hold the token). This also is not designed to account for the sub-licensing case (e.g. licensing the right to one party to license third parties to engage in commercial exploitation), since this should rather be done via a personal copyright licensing scheme. + + +### Examples + +#### Bored Koalas merchandising + +Vigdís creates a PFP collection of Bored Koalas, which is subject to standard copyright restrictions: no one has the right to reproduce, distribute, communicate, commercialize or remix these works. However, she wants to give specific permissions to those who hold a NFT from the collection. She mints the collection with this EIP, introducing a conditional license that allows for the current token holder to display the Bored Koala associated with each NFT and commercialize it for the purpose of merchandising only. + +Neža has purchased one of these Bored Koalas. She wants to produce merchandising to be distributed at his blockchain conference. She goes to a print shop and asks them to make t-shirts with the Bored Koala image of the NFT she has purchased. The print shop can verify that she has the right to commercially exploit the work by verifying that they are the holder of the Bored Koala NFT, and verifying the terms of the license associated with it. (NB: this does not require a sub-license to be granted to the print shop, because the commercial exploitation implies the right to commission third parties to engage in such commercial exploitation). Neža brings the t-shirts to her conference and puts them for sale. When doing so, she calls the `logCommercialExploitation()` function from the NFT smart contract in order to track that the commercial exploitation was done at a time while she was the token holder. + +#### Musical Remix + +Matti is an up and coming songwriter in the emerging web3 music ecosystem. For the upcoming crypto conference, he creates a hit song called “Degens in the Night”. Instead of listing the song on a web2 platform, Matti mints the song as an NFT using this EIP, with a dual licensing scheme: a general public licenses that allows for the free reproduction and redistribution of the work, given proper attribution (e.g. Creative Commons BY-NC-ND) and a conditional license which allows for the token holder to remix the song, in exchange of a particular lump sum (e.g. 1ETH) and under the condition that the derivative work is released under the same licensing terms as the original work Lyyli wants to create a cover of that song, which she calls “Degens in the Parisian Night”. She purchases the NFT and mints a new derivative NFT under a new smart contract using this EIP standard. She then calls the `requestDerivativeToken()` function and send 1ETH to the original NFT smart contract, in order to request that a DerivativeToken be assigned to the new smart contract she has created. The smart contract automatically approves the request to assign a Derivative Token to the new smart contract of Lyyli. This can be used as a proof that the derivative work is indeed a legitimate work, which has been approved by the copyright owner of the original work. During the conference hundreds of other web3 music creators host a side event with Degens in the Night remixes playing until 4am. + +#### Royalties Remix + +Alice created a 3D model of a motorcycle, which she wants everyone to remix, under the condition that she gets royalty from the commercial exploitation of all derivative works. She release her work as an NFT with this EIP, with a dual licensing scheme: a general public licenses that allows for the free reproduction and redistribution of the work, given proper attribution (e.g. Creative Commons BY-NC-ND) and a conditional license which allows for the token holder to remix the song, under the condition that the derivative work is released under the same licensing terms as the original work, and that there is a split of the royalties between himself and the remixer. + +Jane wants to create a derivative work of the motorcycle. She purchases the NFT and mints a new derivative NFT under a new smart contract that uses this EIP, which includes a royalty split for Alice. She then calls the `requestDerivativeToken()` function from the original NFT smart contract in order to request that a DerivativeToken be assigned to the new smart contract she has created. Alice decided that the smart contract shall not automate the approval or rejection of the request, but rather wait for her to validate or invalidate the request, after she has verified that the design and provisions of the new smart contract, namely that it does indeed replicate the same terms and conditions as the original work and that it incorporates the proper amount of royalties. She approves the request to assign a Derivative Token to the new smart contract of Jane. When people purchase Jane’s NFT, the royalties are split to ensure the proper redistribution of the generated profit to Alice. + +## Backwards Compatibility +The interface defined in this standard is backward compatible with most NFT standards used in the Ethereum ecosystem as of this writing. + +## Security Considerations +Needs discussion. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5559.md b/EIPS/eip-5559.md new file mode 100644 index 00000000000000..471ca9fbc95c71 --- /dev/null +++ b/EIPS/eip-5559.md @@ -0,0 +1,471 @@ +--- +eip: 5559 +title: "Cross Chain Write Deferral Protocol" +description: The cross chain write deferral protocol provides a mechanism to defer the storage & resolution of mutations to off-chain handlers +author: Paul Gauvreau (@0xpaulio), Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/eip-cross-chain-write-deferral-protocol/10576 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-23 +requires: 712 +--- + +## Abstract +The following standard provides a mechanism in which smart contracts can request various tasks to be resolved by an external handler. This provides a mechanism in which protocols can reduce the gas fees associated with storing data on mainnet by deferring the handling of it to another system/network. These external handlers act as an extension to the core L1 contract. + +This standard outlines a set of handler types that can be used for managing the execution and storage of mutations (tasks), as well as their corresponding tradeoffs. Each handler type has associated operational costs, finality guarantees, and levels of decentralization. By further specifying the type of handler that the mutation is deferred to, the protocol can better define how to permission and secure their system. + +This standard can be implemented in conjunction with [EIP-3668](./eip-3668) to provide a mechanism in which protocols can reside on and be interfaced through an L1 contract on mainnet, while being able to resolve and mutate data stored in external systems. + +## Motivation +[EIP-3668](./eip-3668) provides a mechanism by which off-chain lookups can be defined inside smart contracts in a transparent manner. In addition, it provides a scheme in which the resolved data can be verified on-chain. However, there lacks a standard by which mutations can be requested through the native contract, to be performed on the off-chain data. Furthermore, with the increase in L2 solutions, smart contract engineers have additional tools that can be used to reduce the storage and transaction costs of performing mutations on the Ethereum mainnet. + +A specification that allows smart contracts to defer the storage and resolution of data to external handlers facilitates writing clients agnostic to the storage solution being used, enabling new applications that can operate without knowledge of the underlying handlers associated with the contracts they interact with. + +Examples of this include: + - Allowing the management of ENS domains externally resolved on an L2 solution or off-chain database as if they were native L1 tokens. + - Allowing the management of digital identities stored on external handlers as if they were in the stored in the native L1 smart contract. + +## Specification +### Overview +There are two main handler classifications: L2 Contract and Off-Chain Database. These are determined based off of where the handler is deployed. The handler classifications are used to better define the different security guarantees and requirements associated with its deployment. + +From a high level: +- Handlers hosted on an L2 solution are EVM compatible and can use attributes native to the Ethereum ecosystem (such as address) to permission access. +- Handlers hosted on an Off-Chain Database require additional parameters and signatures to correctly enforce the authenticity and check the validity of a request. + +A deferred mutation can be handled in as little as two steps. However, in some cases the mutation might be deferred multiple times. + +1. Querying or sending a transaction to the contract +2. Querying or sending a transaction to the handler using the parameters provided in step 1 + +In step 1, a standard blockchain call operation is made to the contract. The contract either performs the operation as intended or reverts with an error that specifies the type of handler that the mutation is being deferred to and the corresponding parameters required to perform the subsequent mutation. There are two types of errors that the contract can revert with, but more may be defined in other EIPs: + +- `StorageHandledByL2(chainId, contractAddress)` +- `StorageHandledByOffChainDatabase(sender, url, data)` + +In step 2, the client builds and performs a new request based off of the type of error received in (1). These handshakes are outlined in the sections below: + +- [StorageHandledByL2](#data-stored-in-an-l2) +- [StorageHandledByOffChainDatabase](#data-stored-in-an-off-chain-database) + +In some cases, the mutation may be deferred multiple times +- [Storage Deferred Twice L1 > L2 > Off-Chain](#data-stored-in-an-l2--an-off-chain-database) + +### Data Stored in an L1 +``` +┌──────┐ ┌───────────┐ +│Client│ │L1 Contract│ +└──┬───┘ └─────┬─────┘ + │ │ + │ somefunc(...) │ + ├─────────────────────────►│ + │ │ + │ response │ + │◄─────────────────────────┤ + │ │ +``` + +In the case in which no reversion occurs, data is stored in the L1 contract when the transaction is executed. + +### Data Stored in an L2 + +``` +┌──────┐ ┌───────────┐ ┌─────────────┐ +│Client│ │L1 Contract│ │ L2 Contract │ +└──┬───┘ └─────┬─────┘ └──────┬──────┘ + │ │ │ + │ somefunc(...) │ │ + ├────────────────────────────────────────────────────►│ │ + │ │ │ + │ revert StorageHandledByL2(chainId, contractAddress) │ │ + │◄────────────────────────────────────────────────────┤ │ + │ │ │ + │ Execute Tx [chainId] [contractAddress] [callData] │ │ + ├─────────────────────────────────────────────────────┼──────────────►│ + │ │ │ + │ response │ │ + │◄────────────────────────────────────────────────────┼───────────────┤ + │ │ │ +``` + +The call or transaction to the L1 contract reverts with the `StorageHandledByL2(chainId, contractAddress)` error. + +In this case, the client builds a new transaction for `contractAddress` with the original `callData` and sends it to a RPC of their choice for the corresponding `chainId`. The `chainId` parameter corresponds to an L2 Solution that is EVM compatible. + +#### Example + +Suppose a contract has the following method: + +```solidity +function setAddr(bytes32 node, address a) external; +``` + +Data for this mutations is stored and tracked on an EVM compatible L2. The contract author wants to reduce the gas fees associated with the contract, while maintaining the interoperability and decentralization of the protocol. Therefore, the mutation is deferred to a off-chain handler by reverting with the `StorageHandledByL2(chainId, contractAddress)` error. + +One example of a valid implementation of `setAddr` would be: + +```solidity +function setAddr(bytes32 node, address a) external { + revert StorageHandledByL2( + 10, + _l2HandlerContractAddress + ); +} +``` + +For example, if a contract returns the following data in an `StorageHandledByL2`: + +```text +chainId = 10 +contractAddress = 0x0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff +``` + +The user, receiving this error, creates a new transaction for the corresponding `chainId`, and builds a transaction with the original `callData` to send to `contractAddress`. The user will have to choose an RPC of their choice to send the transaction to for the corresponding `chainId`. + +### Data Stored in an Off-Chain Database +``` +┌──────┐ ┌───────────┐ ┌────────────────────┐ +│Client│ │L1 Contract│ │ Off-Chain Database │ +└──┬───┘ └─────┬─────┘ └──────────┬─────────┘ + │ │ │ + │ somefunc(...) │ │ + ├────────────────────────────────────────────────────►│ │ + │ │ │ + │ revert StorageHandledByOffChainDatabase(sender, | │ + │ urls, requestParams) │ │ + │◄────────────────────────────────────────────────────┤ │ + │ │ │ + │ HTTP Request [requestParams, signature] │ │ + ├─────────────────────────────────────────────────────┼──────────────────►│ + │ │ │ + │ response │ │ + │◄────────────────────────────────────────────────────┼───────────────────┤ + │ │ │ +``` + +The call or transaction to the L1 contract reverts with the `StorageHandledByOffChainDatabase(sender, url, data)` error. + +In this case, the client performs a HTTP POST request to the gateway service. The gateway service is defined by `url`. The body attached to the request is a JSON object that includes `sender`, `data`, and a signed copy of `data` denoted `signature`. The signature is generated according to a [EIP-712](./eip-712), in which a typed data signature is generated using domain definition, `sender`, and the message context, `data`. + +`sender` ia an ABI-encoded struct defined as: + +```solidity +/** +* @notice Struct used to define the domain of the typed data signature, defined in EIP-712. +* @param name The user friendly name of the contract that the signature corresponds to. +* @param version The version of domain object being used. +* @param chainId The ID of the chain that the signature corresponds to (ie Ethereum mainnet: 1, Goerli testnet: 5, ...). +* @param verifyingContract The address of the contract that the signature pertains to. +*/ +struct domainData { + string name; + string version; + uint64 chainId; + address verifyingContract; +} +``` + +`data` ia an abi encoded struct defined as: + +```solidity +/** +* @notice Struct used to define the message context used to construct a typed data signature, defined in EIP-712, +* to authorize and define the deferred mutation being performed. +* @param functionSelector The function selector of the corresponding mutation. +* @param sender The address of the user performing the mutation (msg.sender). +* @param parameter[] A list of pairs defining the inputs used to perform the deferred mutation. +*/ +struct messageData { + bytes4 functionSelector; + address sender; + parameter[] parameters; + uint256 expirationTimestamp; +} + +/** +* @notice Struct used to define a parameter for Off-Chain Database Handler deferral. +* @param name The variable name of the parameter. +* @param value The string encoded value representation of the parameter. +*/ +struct parameter { + string name; + string value; +} +``` + +`signature` is generated by using the `sender` & `data` parameters to construct an [EIP-712](./eip-712) typed data signature. + +The body used in the HTTP POST request is defined as: + +```json +{ + "sender": "", + "data": "", + "signature": "" +} +``` + +#### Example + +Suppose a contract has the following method: + +```solidity +function setAddr(bytes32 node, address a) external; +``` + +Data for this mutations is stored and tracked in some kind of off-chain database. The contract author wants the user to be able to authorize and make modifications to their `Addr` without having to pay a gas fee. Therefore, the mutation is deferred to a off-chain handler by reverting with the `StorageHandledByOffChainDatabase(sender, url, data)` error. + +One example of a valid implementation of `setAddr` would be: + +```solidity +function setAddr(bytes32 node, address a) external { + IWriteDeferral.parameter[] memory params = new IWriteDeferral.parameter[](3); + + params[0].name = "node"; + params[0].value = BytesToString.bytes32ToString(node); + + params[1].name = "coin_type"; + params[1].value = Strings.toString(coinType); + + params[2].name = "address"; + params[2].value = BytesToString.bytesToString(a); + + revert StorageHandledByOffChainDatabase( + IWriteDeferral.domainData( + { + name: WRITE_DEFERRAL_DOMAIN_NAME, + version: WRITE_DEFERRAL_DOMAIN_VERSION, + chainId: 1, + verifyingContract: address(this) + } + ), + _offChainDatabaseUrl, + IWriteDeferral.messageData( + { + functionSelector: msg.sig, + sender: msg.sender, + parameters: params, + expirationTimestamp: block.timestamp + _offChainDatabaseTimeoutDuration + } + ) + ); +} +``` + +For example, if a contract reverts with the following: + +```text +StorageHandledByOffChainDatabase( + ( + "CoinbaseResolver", + "1", + 1, + 0x32f94e75cde5fa48b6469323742e6004d701409b + ), + "https://example.com/r/{sender}", + ( + 0xd5fa2b00, + 0x727f366727d3c9cc87f05d549ee2068f254b267c, + [ + ("node", "0x418ae76a9d04818c7a8001095ad01a78b9cd173ee66fe33af2d289b5dc5f4cba"), + ("coin_type", "60"), + ("address", "0x727f366727d3c9cc87f05d549ee2068f254b267c") + ], + 181 + ) +) +``` + +The user, receiving this error, constructs the typed data signature, signs it, and performs that request via a HTTP POST to `url`. + +Example HTTP POST request body including `requestParams` and `signature`: + +```json +{ + "sender": "", + "data": "", + "signature": "" +} +``` + +Note that the message could be altered could be altered in any way, shape, or form prior to signature and request. It is the backend's responsibility to correctly permission and process these mutations. From a security standpoint, this is no different then a user being able to call a smart contract with any params they want, as it is the smart contract's responsibility to permission and handle those requests. + + +### Data Stored in an L2 & an Off-Chain Database + +```text +┌──────┐ ┌───────────┐ ┌─────────────┐ ┌────────────────────┐ +│Client│ │L1 Contract│ │ L2 Contract │ │ Off-Chain Database │ +└──┬───┘ └─────┬─────┘ └──────┬──────┘ └──────────┬─────────┘ + │ │ │ │ + │ somefunc(...) │ │ │ + ├────────────────────────────────────────────────────►│ │ │ + │ │ │ │ + │ revert StorageHandledByL2(chainId, contractAddress) │ │ │ + │◄────────────────────────────────────────────────────┤ │ │ + │ │ │ │ + │ Execute Tx [chainId] [contractAddress] [callData] │ │ │ + ├─────────────────────────────────────────────────────┼──────────────►│ │ + │ │ │ │ + │ revert StorageHandledByOffChainDatabase(sender, url, data) │ │ + │◄────────────────────────────────────────────────────┼───────────────┤ │ + │ │ │ │ + │ HTTP Request {requestParams, signature} │ │ │ + ├─────────────────────────────────────────────────────┼───────────────┼───────────────────►│ + │ │ │ │ + │ response │ │ │ + │◄────────────────────────────────────────────────────┼───────────────┼────────────────────┤ + │ │ │ │ +``` + +The call or transaction to the L1 contract reverts with the `StorageHandledByL2(chainId, contractAddress)` error. + +In this case, the client builds a new transaction for `contractAddress` with the original `callData` and sends it to a RPC of their choice for the corresponding `chainId`. + +That call or transaction to the L2 contract then reverts with the `StorageHandledByOffChainDatabase(sender, url, data)` error. + +In this case, the client then performs a HTTP POST request against the gateway service. The gateway service is defined by `url`. The body attached to the request is a JSON object that includes `sender`, `data`, and `signature` -- a typed data signature corresponding to [EIP-712](./eip-712). + +### Events + +When making changes to core variables of the handler, the corresponding event MUST be emitted. This increases the transparency associated with different managerial actions. Core variables include `chainId` and `contractAddress` for L2 solutions and `url` for Off-Chain Database solutions. The events are outlined below in the WriteDeferral Interface. + +### Write Deferral Interface + +Below is a basic interface that defines and describes all of the reversion types and their corresponding parameters. + +```solidity +pragma solidity ^0.8.13; + +interface IWriteDeferral { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + /// @notice Event raised when the default chainId is changed for the corresponding L2 handler. + event L2HandlerDefaultChainIdChanged(uint256 indexed previousChainId, uint256 indexed newChainId); + /// @notice Event raised when the contractAddress is changed for the L2 handler corresponding to chainId. + event L2HandlerContractAddressChanged(uint256 indexed chainId, address indexed previousContractAddress, address indexed newContractAddress); + + /// @notice Event raised when the url is changed for the corresponding Off-Chain Database handler. + event OffChainDatabaseHandlerURLChanged(string indexed previousUrl, string indexed newUrl); + + /*////////////////////////////////////////////////////////////// + STRUCTS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Struct used to define the domain of the typed data signature, defined in EIP-712. + * @param name The user friendly name of the contract that the signature corresponds to. + * @param version The version of domain object being used. + * @param chainId The ID of the chain that the signature corresponds to (ie Ethereum mainnet: 1, Goerli testnet: 5, ...). + * @param verifyingContract The address of the contract that the signature pertains to. + */ + struct domainData { + string name; + string version; + uint64 chainId; + address verifyingContract; + } + + /** + * @notice Struct used to define the message context used to construct a typed data signature, defined in EIP-712, + * to authorize and define the deferred mutation being performed. + * @param functionSelector The function selector of the corresponding mutation. + * @param sender The address of the user performing the mutation (msg.sender). + * @param parameter[] A list of pairs defining the inputs used to perform the deferred mutation. + */ + struct messageData { + bytes4 functionSelector; + address sender; + parameter[] parameters; + uint256 expirationTimestamp; + } + + /** + * @notice Struct used to define a parameter for off-chain Database Handler deferral. + * @param name The variable name of the parameter. + * @param value The string encoded value representation of the parameter. + */ + struct parameter { + string name; + string value; + } + + + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Error to raise when mutations are being deferred to an L2. + * @param chainId Chain ID to perform the deferred mutation to. + * @param contractAddress Contract Address at which the deferred mutation should transact with. + */ + error StorageHandledByL2( + uint256 chainId, + address contractAddress + ); + + /** + * @dev Error to raise when mutations are being deferred to an Off-Chain Database. + * @param sender the EIP-712 domain definition of the corresponding contract performing the off-chain database, write + * deferral reversion. + * @param url URL to request to perform the off-chain mutation. + * @param data the EIP-712 message signing data context used to authorize and instruct the mutation deferred to the + * off-chain database handler. + * In order to authorize the deferred mutation to be performed, the user must use the domain definition (sender) and message data + * (data) to construct a type data signature request defined in EIP-712. This signature, message data (data), and domainData (sender) + * are then included in the HTTP POST request, denoted sender, data, and signature. + * + * Example HTTP POST request: + * { + * "sender": , + * "data": , + * "signature": + * } + * + */ + error StorageHandledByOffChainDatabase( + domainData sender, + string url, + messageData data + ); +} +``` + +### Use of transactions with storage-deferral reversions +In some cases the contract might conditionally defer and handle mutations, in which case a transaction may be required. It is simple to use this method for sending transactions that may result in deferral reversions, as a client should receive the corresponding reversion while `preflighting` the transaction. + +This functionality is ideal for applications that want to allow their users to define the security guarantees and costs associated with their actions. For example, in the case of a decentralized identity profile, a user might not care if their data is decentralized and chooses to defer the handling of their records to the off-chain handler to reduce gas fees and on-chain transactions. + +## Rationale +### Use of `revert` to convey call information +[EIP-3668](./eip-3668) adopted the idea of using a `revert` to convey call information. It was proposed as a simple mechanism in which any pre-existing interface or function signature could be satisfied while maintain a mechanism to instruct and trigger an off-chain lookup. + +This is very similar for the write deferral protocol, defined in this EIP; without any modifications to the ABI or underlying EVM, `revert` provides a clean mechanism in which we can "return" a typed instruction - and the corresponding elements to complete that action - without modifying the signature of the corresponding function. This makes it easy to comply with pre-existing interfaces and infrastructure. + +### Use of multiple reversion & handler types to better define security guarantees +By further defining the class of the handler, it gives the developer increased granularity to define the characteristics and different guarantees associated storing the data off-chain. In addition, different handlers require different parameters and verification mechanisms. This is very important for the transparency of the protocol, as they store data outside of the native ethereum ecosystem. Common implementations of this protocol could include storing non-operational data in L2 solutions and off-chain databases to reduce gas fees, while maintaining open interoperability. + + +## Backwards Compatibility +Existing contracts that do not wish to use this specification are unaffected. Clients can add support for Cross Chain Write Deferrals to all contract calls without introducing any new overhead or incompatibilities. + +Contracts that require Cross Chain Write Deferrals will not function in conjunction with clients that do not implement this specification. Attempts to call these contracts from non-compliant clients will result in the contract throwing an exception that is propagated to the user. + +## Security Considerations +Deferred mutations should never resolve to mainnet ethereum. Such attempts to defer the mutation back to ETH could include hijacking attempts in which the contract developer is trying to get the user to sign and send a malicious transaction. Furthermore, when a transaction is deferred to an L2 system, it must use the original `calldata`, this prevents against potentially malicious contextual changes in the transaction. + +### Fingerprinting attacks +As all deferred mutations will include the `msg.sender` parameter in `data`, it is possible that `StorageHandledByOffChainDatabase` reversions could fingerprint wallet addresses and the corresponding IP address used to make the HTTP request. The impact of this is application-specific and something the user should understand is a risk associated with off-chain handlers. To minimize the security impact of this, we make the following recommendations: + +1. Smart contract developers should provide users with the option to resolve data directly on the network. Allowing them to enable on-chain storage provides the user with a simple cost-benefit analysis of where they would like their data to resolve and different guarantees / risks associated with the resolution location. +2. Client libraries should provide clients with a hook to override Cross Chain Write Deferral `StorageHandledByOffChainDatabase` calls - either by rewriting them to use a proxy service, or by denying them entirely. This mechanism or another should be written so as to easily facilitate adding domains to allowlists or blocklists. + +We encourage applications to be as transparent as possible with their setup and different precautions put in place. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5560.md b/EIPS/eip-5560.md new file mode 100644 index 00000000000000..a0ba5a481c5c48 --- /dev/null +++ b/EIPS/eip-5560.md @@ -0,0 +1,125 @@ +--- +eip: 5560 +title: Redeemable NFTs +description: Makes an NFT redeemable for a physical object +author: Olivier Fernandez (@fernandezOli), Frédéric Le Coidic (@FredLC29), Julien Béranger (@julienbrg) +discussions-to: https://ethereum-magicians.org/t/eip-redeemable-nft-extension/10589 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-08-30 +requires: 165, 721 +--- + +## Abstract + +The EIP is a Redeemable NFT extension which adds a `redeem` function to [EIP-721](./eip-721.md). It can be implemented when an NFT issuer wants his/her NFT to be redeemed for a physical object. + +## Motivation + +An increasing amount of NFT issuers such as artists, fine art galeries, auction houses, brands and others want to offer a physical object to the holder of a given NFT. This standard allows EIP-721 NFTs to signal reedemability. + +## 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-721` compliant contracts MAY implement this EIP to provide a standard method of receiving information on redeemability. + +The NFT issuer **MUST** decide who is allowed to redeem the NFT, and restrict access to the `redeem()` function accordingly. + +Anyone **MAY** access the `isRedeemable()` function to check the redeemability status: it returns `true` when the NFT redeemable, and `false` when already redeemed. + +Third-party services that support this standard **MAY** use the `Redeem` event to listen to changes on the redeemable status of the NFT. + +Implementers of this standard **MUST** have all of the following functions: + +```solidity +import '@openzeppelin/contracts/utils/introspection/ERC165.sol'; + +/** + * @dev Implementation of Redeemable for ERC-721s + * + */ + +interface IRedeemable is ERC165 { + /* + * ERC165 bytes to add to interface array - set in parent contract implementing this standard + * + * bytes4 private constant _INTERFACE_ID_ERC721REDEEM = 0x2f8ca953; + */ + + /// @dev This event emits when a token is redeemed. + event Redeem(address indexed from, uint256 indexed tokenId); + + /// @notice Returns the redeem status of a token + /// @param tokenId Identifier of the token. + function isRedeemable(uint256 _tokenId) external view returns (bool); + + /// @notice Redeeem a token + /// @param tokenId Identifier of the token to redeeem + function redeem(uint256 _tokenId) external; +} +``` + +The `Redeem` event is emitted when the `redeem()` function is called. + +The `supportsInterface` method **MUST** return `true` when called with `0x2f8ca953`. + +## Rationale + +When the NFT contract is deployed, the `isRedeemable()` function returns `true` by default. + +By default, the `redeem()` function visibility is public, so anyone can trigger it. It is **RECOMMENDED** to add a `require` to restrict the access: + +```solidity +require(ownerOf(tokenId) == msg.sender, "ERC721Redeemable: You are not the owner of this token"); +``` + +After the `redeem()` function is triggered, `isRedeemable()` function returns `false`. + +### `Redeem` event + +When the `redeem()` function is triggered, the following event **MUST** be emitted: + +```solidity +event Redeem(address indexed from, uint256 indexed tokenId); +``` + +## Backwards Compatibility + +This standard is compatible with EIP-721. + +## Reference Implementation + +Here's an example of an EIP-721 that includes the Redeemable extension: + +```solidity +contract ERC721Redeemable is ERC721, Redeemable { + + constructor(string memory name, string memory symbol) ERC721(name, symbol) { + } + + function isRedeemable(uint256 tokenId) public view virtual override returns (bool) { + require(_exists(tokenId), "ERC721Redeemable: Redeem query for nonexistent token"); + return super.isRedeemable(tokenId); + } + + function redeem(uint256 tokenId) public virtual override { + require(_exists(tokenId), "ERC721Redeemable: Redeem query for nonexistent token"); + require(ownerOf(tokenId) == msg.sender, "ERC721Redeemable: You are not the owner of this token"); + super.redeem(tokenId); + } + + function supportsInterface(bytes4 interfaceId) public view override(ERC721, Redeemable) returns (bool) { + return super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5564.md b/EIPS/eip-5564.md new file mode 100644 index 00000000000000..4e7ab8456abda8 --- /dev/null +++ b/EIPS/eip-5564.md @@ -0,0 +1,283 @@ +--- +eip: 5564 +title: Stealth Addresses +description: Private, non-interactive transfers and interactions +author: Toni Wahrstätter (@nerolation), Matt Solomon (@mds1), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/eip-5566-stealth-addresses-for-smart-contract-wallets/10614 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-13 +--- + +## Abstract + +This specification establishes a standardized method for interacting with stealth addresses, which allow senders of transactions or transfers to non-interactively generate private accounts exclusively accessible by their recipients. Moreover, this specification enables developers to create stealth address protocols based on the foundational implementation outlined in this EIP, utilizing a singleton contract to emit the necessary information for recipients. In addition to the base implementation, this ERC also outlines the first implementation of a cryptographic scheme, specifically the SECP256k1 curve. + + +## Motivation + +The standardization of non-interactive stealth address generation presents the potential to significantly improve the privacy capabilities of the Ethereum network and other EVM-compatible chains by allowing recipients to remain private when receiving assets. This is accomplished through the sender generating a stealth address based on a shared secret known exclusively to the sender and recipient. The recipients alone can access the funds stored at their stealth addresses, as they are the sole possessors of the necessary private key. As a result, observers are unable to associate the recipient's stealth address with their identity, thereby preserving the recipient's privacy and leaving the sender as the only party privy to this information. By offering a foundational implementation in the form of a single contract that is compatible with multiple cryptographic schemes, recipients are granted a centralized location to monitor, ensuring they do not overlook any incoming transactions. + +## 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. + +Definitions: + +- A "stealth meta-address" is a set of one or two public keys that can be used to compute a stealth address for a given recipient. +- A "spending key" is a private key that can be used to spend funds sent to a stealth address. A "spending public key" is the corresponding public key. +- A "viewing key" is a private key that can be used to determine if funds sent to a stealth address belong to the recipient who controls the corresponding spending key. A "viewing public key" is the corresponding public key. + +Different stealth address schemes will have different expected stealth meta-address lengths. A scheme that uses public keys of length `n` bytes MUST define stealth meta-addresses as follows: + +- A stealth meta-address of length `n` uses the same stealth meta-address for the spending public key and viewing public key. +- A stealth meta-address of length `2n` uses the first `n` bytes as the spending public key and the last `n` bytes as the viewing public key. + +Given a recipient's stealth meta-address, a sender MUST be able generate a stealth address for the recipient by calling a method with the following signature: + +```solidity +/// @notice Generates a stealth address from a stealth meta address. +/// @param stealthMetaAddress The recipient's stealth meta-address. +/// @return stealthAddress The recipient's stealth address. +/// @return ephemeralPubKey The ephemeral public key used to generate the stealth address. +/// @return viewTag The view tag derived from the shared secret. +function generateStealthAddress(bytes memory stealthMetaAddress) + external + view + returns (address stealthAddress, bytes memory ephemeralPubKey, bytes1 viewTag); +``` + +A recipient MUST be able to check if a stealth address belongs to them by calling a method with the following signature: + +```solidity +/// @notice Returns true if funds sent to a stealth address belong to the recipient who controls +/// the corresponding spending key. +/// @param stealthAddress The recipient's stealth address. +/// @param ephemeralPubKey The ephemeral public key used to generate the stealth address. +/// @param viewingKey The recipient's viewing private key. +/// @param spendingPubKey The recipient's spending public key. +/// @return True if funds sent to the stealth address belong to the recipient. +function checkStealthAddress( + address stealthAddress, + bytes memory ephemeralPubKey, + bytes memory viewingKey, + bytes memory spendingPubKey +) external view returns (bool); +``` + +A recipient MUST be able to compute the private key for a stealth address by calling a method with the following signature: + +```solidity +/// @notice Computes the stealth private key for a stealth address. +/// @param stealthAddress The expected stealth address. +/// @param ephemeralPubKey The ephemeral public key used to generate the stealth address. +/// @param spendingKey The recipient's spending private key. +/// @return stealthKey The stealth private key corresponding to the stealth address. +/// @dev The stealth address input is not strictly necessary, but it is included so the method +/// can validate that the stealth private key was generated correctly. +function computeStealthKey( + address stealthAddress, + bytes memory ephemeralPubKey, + bytes memory spendingKey +) external view returns (bytes memory); +``` + +The implementation of these methods is scheme-specific. The specification of a new stealth address scheme MUST specify the implementation for each of these methods. Additionally, although these function interfaces are specified in Solidity, they do not necessarily ever need to be implemented in Solidity, but any library or SDK conforming to this specification MUST implement these methods with compatible function interfaces. + +A 256 bit integer (`schemeId`) is used to identify stealth address schemes. A mapping from the schemeId to its specification MUST be declared in the EIP that proposes to standardize a new stealth address scheme. It is RECOMMENDED that `schemeId`s are chosen to be monotonically incrementing integers for simplicity, but arbitrary or meaningful `schemeId`s may be chosen. Furthermore, the schemeId MUST be added to [this overview](../assets/eip-5564/scheme_ids.md). These extensions MUST specify: + +- The integer identifier for the scheme. + +- The algorithm for encoding a stealth meta-address (i.e. the spending public key and viewing public key) into a `bytes` array, and decoding it from `bytes` to the native key types of that scheme. + +- The algorithm for the `generateStealthAddress` method. + +- The algorithm for the `checkStealthAddress` method. + +- The algorithm for the `computeStealthKey` method. + +This specification additionally defines a singleton `ERC5564Announcer` contract that emits events to announce when something is sent to a stealth address. This MUST be a singleton contract, with one instance per chain. The contract is specified as follows: + +```solidity +/// @notice Interface for announcing when something is sent to a stealth address. +contract IERC5564Announcer { + /// @dev Emitted when sending something to a stealth address. + /// @dev See the `announce` method for documentation on the parameters. + event Announcement ( + uint256 indexed schemeId, + address indexed stealthAddress, + address indexed caller, + bytes ephemeralPubKey, + bytes metadata + ); + + /// @dev Called by integrators to emit an `Announcement` event. + /// @param schemeId The integer specifying the applied stealth address scheme. + /// @param stealthAddress The computed stealth address for the recipient. + /// @param ephemeralPubKey Ephemeral public key used by the sender. + /// @param metadata An arbitrary field MUST include the view tag in the first byte. + /// Besides the view tag, the metadata can be used by the senders however they like, + /// but the below guidelines are recommended: + /// The first byte of the metadata MUST be the view tag. + /// - When sending/interacting with the native token of the blockchain (cf. ETH), the metadata SHOULD be structured as follows: + /// - Byte 1 MUST be the view tag, as specified above. + /// - Bytes 2-5 are `0xeeeeeeee` + /// - Bytes 6-25 are the address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. + /// - Bytes 26-57 are the amount of ETH being sent. + /// - When interacting with ERC-20/ERC-721/etc. tokens, the metadata SHOULD be structured as follows: + /// - Byte 1 MUST be the view tag, as specified above. + /// - Bytes 2-5 are a function identifier. When a function selector (e.g. + /// the first (left, high-order in big-endian) four bytes of the Keccak-256 + /// hash of the signature of the function, like Solidity and Vyper use) is + /// available, it MUST be used. + /// - Bytes 6-25 are the token contract address. + /// - Bytes 26-57 are the amount of tokens being sent/interacted with for fungible tokens, or + /// the token ID for non-fungible tokens. + function announce ( + uint256 schemeId, + address stealthAddress, + bytes memory ephemeralPubKey, + bytes memory metadata + ) + external + { + emit Announcement(schemeId, stealthAddress, msg.sender, ephemeralPubKey, metadata); + } +} +``` + + +### Stealth meta-address format + +The new address format for the stealth meta-address is based on [ERC-3770](./eip-3770.md) and extends it by adding a `st:` (*stealth*) prefix. +Thus, a stealth meta-address on Ethereum has the following format: + +``` +st:eth:0x +``` + +Stealth meta-addresses may be managed by the user and/or registered within a publicly available `Registry` contract, as delineated in [ERC-6538](./eip-6538). This provides users with a centralized location for identifying stealth meta-addresses associated with other individuals while simultaneously enabling recipients to express their openness to engage via stealth addresses. + +*Notably, the address format is used only to differentiate stealth addresses from standard addresses, as the prefix is removed before performing any computations on the stealth meta-address.* + +--- + +### Initial Implementation of SECP256k1 with View Tags + +This EIP provides a foundation that is not tied to any specific cryptographic system through the `IERC5564Announcer` contract. In addition, it introduces the first implementation of a [stealth address scheme](../assets/eip-5564/scheme_ids.md) that utilizes the SECP256k1 elliptic curve and view tags. The SECP256k1 elliptic curve is defined with the equation $y^2 = x^3 + 7 \pmod{p}$, where $p = 2^{256} - 2^{32} - 977$. + +The following reference is divided into three sections: + +1. Stealth address generation + +2. Parsing announcements + +3. Stealth private key derivation + + Definitions: + +- $G$ represents the generator point of the curve. + +#### Generation - Generate stealth address from stealth meta-address: + +- Recipient has access to the private keys $p_{spend}$, $p_{view}$ from which public keys $P_{spend}$ and $P_{view}$ are derived. + +- Recipient has published a stealth meta-address that consists of the public keys $P_{spend}$ and $P_{view}$. + +- Sender passes the stealth meta-address to the `generateStealthAddress` function. + +- The `generateStealthAddress` function performs the following computations: + - Generate a random 32-byte entropy ephemeral private key $p_{ephemeral}$. + - Derive the ephemeral public key $P_{ephemeral}$ from $p_{ephemeral}$. + - Parse the spending and viewing public keys, $P_{spend}$ and $P_{view}$, from the stealth meta-address. + - A shared secret $s$ is computed as $s = p_{ephemeral} \cdot P_{view}$. + - The secret is hashed $s_{h} = \textrm{h}(s)$. + - The view tag $v$ is extracted by taking the most significant byte $s_{h}[0]$, + - Multiply the hashed shared secret with the generator point $S_h = s_h \cdot G$. + - The recipient's stealth public key is computed as $P_{stealth} = P_{spend} + S_h$. + - The recipient's stealth address $a_{stealth}$ is computed as $\textrm{pubkeyToAddress}(P_{stealth})$. + - The function returns the stealth address $a_{stealth}$, the ephemeral public key $P_{ephemeral}$ and the view tag $v$. + + +#### Parsing - Locate one's own stealth address(es): + +- User has access to the viewing private key $p_{view}$ and the spending public key $P_{spend}$. + +- User has access to a set of `Announcement` events and applies the `checkStealthAddress` function to each of them. + +- The `checkStealthAddress` function performs the following computations: + - Shared secret $s$ is computed by multiplying the viewing private key with the ephemeral public key of the announcement $s = p_{view}$ * $P_{ephemeral}$. + - The secret is hashed $s_{h} = h(s)$. + - The view tag $v$ is extracted by taking the most significant byte $s_{h}[0]$ and can be compared to the given view tag. If the view tags do not match, this `Announcement` is not for the user and the remaining steps can be skipped. If the view tags match, continue on. + - Multiply the hashed shared secret with the generator point $S_h = s_h \cdot G$. + - The stealth public key is computed as $P_{stealth} = P_{spend} + S_h$. + - The derived stealth address $a_{stealth}$ is computed as $\textrm{pubkeyToAddress}(P_{stealth})$. + - Return `true` if the stealth address of the announcement matches the derived stealth address, else return `false`. + +#### Private key derivation - Generate the stealth address private key from the hashed shared secret and the spending private key. + +- User has access to the viewing private key $p_{view}$ and spending private key $p_{spend}$. + +- User has access to a set of `Announcement` events for which the `checkStealthAddress` function returns `true`. + +- The `computeStealthKey` function performs the following computations: + - Shared secret $s$ is computed by multiplying the viewing private key with the ephemeral public key of the announcement $s = p_{view}$ * $P_{ephemeral}$. + - The secret is hashed $s_{h} = h(s)$. + - The stealth private key is computed as $p_{stealth} = p_{spend} + s_h$. + + + +### Parsing considerations + +Usually, the recipient of a stealth address transaction has to perform the following operations to check whether he was the recipient of a certain transaction: + +- 2x ecMUL, + +- 2x HASH, + +- 1x ecADD, + +The view tags approach is introduced to reduce the parsing time by around 6x. Users only need to perform 1x ecMUL and 1x HASH (skipping 1x ecMUL, 1x ecADD and 1x HASH) for every parsed announcement. The 1-byte view tag length is based on the maximum required space to reliably filter non-matching announcements. With a 1-byte `viewTag`, the probability for users to skip the remaining computations after hashing the shared secret $h(s)$ is $255/256$. This means that users can almost certainly skip the above three operations for any announcements that do not involve them. Since the view tag reveals one byte of the shared secret, the security margin is reduced from 128 bits to 124 bits. Notably, this only affects the privacy and not the secure generation of a stealth address. + +--- + +## Rationale + +This EIP emerged from the need for privacy-preserving ways to transfer ownership without disclosing any information about the recipients' identities. Token ownership can expose sensitive personal information. While individuals may wish to donate to a specific organization or country, they might prefer not to disclose a link between themselves and the recipient simultaneously. Standardizing stealth address generation represents a significant step towards unlinkable interactions, since such privacy-enhancing solutions require standards to achieve widespread adoption. Consequently, it is crucial to focus on developing generalizable approaches for implementing related solutions. + +The stealth address specification standardizes a protocol for generating and locating stealth addresses, facilitating the transfer of assets without requiring prior interaction with the recipient. This enables recipients to verify the receipt of a transfer without the need to interact with the blockchain and query account balances. Importantly, stealth addresses enable token transfer recipients to verify receipt while maintaining their privacy, as only the recipient can recognize themselves as the recipient of the transfer. + +The authors recognize the trade-off between on- and off-chain efficiency. Although incorporating a Monero-like view tags mechanism enables recipients to parse announcements more efficiently, it adds complexity to the announcement event. + +The recipient's address and the `viewTag` MUST be included in the announcement event, allowing users to quickly verify ownership without querying the chain for positive account balances. + +## Backwards Compatibility + +This EIP is fully backward compatible. + +## Reference Implementation + +You can find an implementation of this standard in TBD. + +## Security Considerations + +### DoS Countermeasures + +There are potential denial of service (DoS) attack vectors that are not mitigated by network transaction fees. Stealth transfer senders cause an externality for recipients, as parsing announcement events consumes computational resources that are not compensated with gas. Therefore, spamming announcement events *can* be a detriment to the user experience, as it *can* lead to longer parsing times. +We consider the incentives to carry out such an attack to be low because **no monetary benefit can be obtained** +However, to tackle potential spam, parsing providers may adopt their own anti-DoS attack methods. These may include ignoring the spamming users when serving announcements to users or, less harsh, de-prioritizing them when ordering the announcements. The indexed `caller` keyword may help parsing providers to effectively filter known spammers. + +Furthermore, parsing providers have a few options to counter spam, such as introducing staking mechanisms or requiring senders to pay a `toll` before including their `Announcement`. Moreover, a Staking mechanism may allow users to stake an unslashable amount of ETH (similarly to [ERC-4337](./eip-4337)), to help mitigate potential spam through *sybil attacks* and enable parsing providers filtering spam more effectively. +Introducing a `toll`, paid by sending users, would simply put a cost on each stealth address transaction, making spamming economically unattractive. + +### Recipients' transaction costs + +The funding of the stealth address wallet represents a known issue that might breach privacy. The wallet that funds the stealth address MUST NOT have any physical connection to the stealth address owner in order to fully leverage the privacy improvements. + +Thus, the sender may attach a small amount of ETH to each stealth address transaction, thereby sponsoring subsequent transactions of the recipient. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-5568.md b/EIPS/eip-5568.md new file mode 100644 index 00000000000000..26c415392eedd7 --- /dev/null +++ b/EIPS/eip-5568.md @@ -0,0 +1,82 @@ +--- +eip: 5568 +title: Well-Known Format for Required Actions +description: Signal to wallets that an action is needed through a well-known function and revert reason +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-5568-revert-signals/10622 +status: Review +type: Standards Track +category: ERC +created: 2022-08-31 +requires: 140 +--- + +## Abstract + +This ERC 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 function and revert reason. It provides just enough data to be extendable by future ERCs 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 ERC 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 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. + +### Action Detection + +```solidity +interface IERC5568 { + function walletSignal24(bytes32 selector, bytes function_data) view returns (uint24 instruction_id, bytes instruction_data); +} +``` + +The `instruction_id` of an instruction defined by an ERC MUST be its ERC number unless there are exceptional circumstances (be reasonable). An ERC MUST define exactly zero or one `instruction_id`. The structure of the instruction data for any `instruction_id` MUST be defined by the ERC that defines the `instruction_id`. + +To indicate that an action needs to be taken, return the `instruction_id` and `instruction_data`. To indicate no actions need to be taken, set `instruction_id` to be `0` and `instruction_data` to any value. + +### Custom Revert Reason + +To signal an action was not taken, a compliant smart contract MUST revert with the following error: + +```solidity +error WalletSignal24(uint24 instruction_id, bytes instruction_data) +``` + +The `instruction_id` of an instruction defined by an ERC MUST be its ERC number unless there are exceptional circumstances (be reasonable). An ERC MUST define exactly zero or one `instruction_id`. The structure of the instruction data for any `instruction_id` MUST be defined by the ERC that defines the `instruction_id`. + +### Responding to a Revert + +Before submitting a transaction to the mempool, the `walletSignal24` function MUST be simulated locally. It MUST be treated as if it were a non-`view` function capable of making state changes (e.g. `CALLS` to non-`view` functions are allowed). If the resulting `instruction_id` is nonzero, an action needs to be taken. + +The `instruction_id`, and `instruction_data` MUST be taken from the `walletSignal24` simulation. The instruction SHOULD be evaluated as per the relevant ERC. 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 `instruction_id`, and `instruction_data` cannot be parsed, then an error MUST be displayed to the user indicating that is the case. The transaction MUST NOT be re-evaluated. + +## Rationale + +This ERC was explicitly optimized for deployment gas cost and simplicity. It is expected that libraries will eventually be developed that makes this more developer-friendly. + +[ERC-165](./eip-165.md) is not used, since the interface is simple enough that it can be detected simply by calling the function. + +## Backwards Compatibility + +### Human-Readable Revert Messages + +See [Revert Reason Collisions](#revert-reason-collisions). + +### [ERC-3668](./eip-3668.md) + +ERC-3668 can be used alongside this ERC, but it uses a different mechanism than this ERC. + +## 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-5570.md b/EIPS/eip-5570.md new file mode 100644 index 00000000000000..90e4c547d7d849 --- /dev/null +++ b/EIPS/eip-5570.md @@ -0,0 +1,291 @@ +--- +eip: 5570 +title: Digital Receipt Non-Fungible Tokens +description: Non-Fungible Tokens as digital receipts for physical purchases, where the metadata represents a JSON receipt +author: Sean Darcy (@darcys22) +discussions-to: https://ethereum-magicians.org/t/idea-standard-digital-receipts-using-erc-721/9908 +status: Final +type: Standards Track +category: ERC +created: 2022-09-01 +requires: 721 +--- + +## Abstract + +This ERC proposes a standard schema for digital receipts of transactions. Digital Receipt Non-Fungible Tokens are issued by a vendor when a customer makes a purchase from their store and contains transaction details necessary for record keeping. Digital Receipt Non-Fungible Tokens extend [ERC-721](./eip-721.md) which allows for the management and ownership of unique tokens. + +## Motivation + +Purchases from online retailers include a receipt that is emailed and/or physically provided to the customer. These receipts are critical for many reasons but are provided in an analogue form which is difficult to parse by financial systems. Digital receipts have never gained traction dispite the fact that point of sales systems are already digital and the customers often want this information in their own digital systems. So we are left with a redundant Digital -> Analogue -> Digital process which requires unnecessary data entry or the use of clunky receipt-scanning applications. + +Digital receipts are relatively simple and can be specified with a schema that can be parsed into JSON or other structured formats. In addition we can prove the receipts validity by digitally signing the receipt using the vendors private keys. + +As Ethereum scales tooling will need to be developed to provide end users with features (such as receipts) already available to fiat transactions. NFTs provide a unique opportunity to link an on chain purchase with its transaction details directly through the transaction state update. If we conceptually think of a transaction as funds provided to one participant and goods provided to another, then our real life state includes two sides of a transaction, 1) Funds changing ownership and 2) goods changing ownership. NFT receipts are first class citizens of a transaction reflecting the goods changing ownership as part of the transaction state. They will bring our on chain transaction state in line with the changes happening in the real world. + +The convenience of a direct link to the transaction receipt via the transaction state is significant, other methods of distributing receipts either off chain or through smart contracts separate to the initial transaction lose this link and force the end user to manually locate the transaction details when needed. +The benefit can be demonstrated by comparing a wallet that allows a user to click through a transaction to its receipt (available immediately after purchase without any further action) verses a user needing to search through a datastore to locate a receipt for a transaction that they can see in their wallet history. + +Digital receipt as NFTs can also conceptually include other important information such as item serial numbers and delivery tracking etc. + +One of the major roadblocks to fully automating our finance world has been the difficulty in tracking transaction details. Human beings physically tracking paper receipts is archaic and NFTs on the blockchain provide a pathway for these systems to be significantly improved. + +## Specification + +Transaction Flow: + + - A customer purchases an item from an online retailer, checking out leads the customer to an option to mint a NFT. + - The smart contract provides the user with a Digital Receipt Non-Fungible Token. + - When fulfilling the order, the retailer will upload the digital receipt specified in in the JSON schema below as the metadata to the previously minted NFT. + +### Digital Receipt JSON Schema + +The JSON schema is composed of 2 parts. The root schema contains high level details of the receipt (for example Date and Vendor) and another schema for the optionally recurring line items contained in the receipt. + +#### Root Schema + +```json +{ + "id": "receipt.json#", + "description": "Receipt Schema for Digital Receipt Non-Fungible Tokens", + "type": "object", + "required": ["name", "description", "image", "receipt"], + "properties": { + "name": { + "title": "Name", + "description": "Identifies the token as a digital receipt", + "type": "string" + }, + "description": { + "title": "Description", + "description": "Brief description of a digital receipt", + "type": "string" + }, + "receipt": { + "title": "Receipt", + "description": "Details of the receipt", + "type": "object", + "required": ["id", "date", "vendor", "items"], + "properties": { + "id": { + "title": "ID", + "description": "Unique ID for the receipt generated by the vendor", + "type": "string" + }, + "date": { + "title": "Date", + "description": "Date Receipt Issued", + "type": "string", + "format": "date" + }, + "vendor": { + "title": "Vendor", + "description": "Details of the entity issuing the receipt", + "type": "object", + "required": ["name", "website"], + "properties": { + "name": { + "title": "Name", + "description": "Name of the vendor. E.g. Acme Corp", + "type": "string" + }, + "logo": { + "title": "Logo", + "description": "URL of the issuer's logo", + "type": "string", + "format": "uri" + }, + "address": { + "title": "Address", + "description": "List of strings comprising the address of the issuer", + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 6 + }, + "website": { + "title": "Website", + "description": "URL of the issuer's website", + "type": "string", + "format": "uri" + }, + "contact": { + "title": "Contact Details", + "description": "Details of the person to contact", + "type": "object", + "required": [], + "properties": { + "name": { + "title": "Name", + "description": "Name of the contact person", + "type": "string" + }, + "position": { + "title": "Position", + "description": "Position / Role of the contact person", + "type": "string" + }, + "tel": { + "title": "Telephone Number", + "description": "Telephone number of the contact person", + "type": "string" + }, + "email": { + "title": "Email", + "description": "Email of the contact person", + "type": "string", + "format": "email" + }, + "address": { + "title": "Address", + "description": "List of strings comprising the address of the contact person", + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 6 + } + } + } + } + }, + "items": { + "title": "Items", + "description": "Items included into the receipt", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "item.json#" + } + }, + "comments": { + "title": "Comments", + "description": "Any messages/comments the issuer wishes to convey to the customer", + "type": "string" + } + } + }, + "image": { + "title": "Image", + "description": "Viewable/Printable Image of the Digital Receipt", + "type": "string" + }, + "signature": { + "title": "Signature", + "description": "Digital signature by the vendor of receipts data", + "type": "string" + }, + "extra": { + "title": "Extra", + "description": "Extra information about the business/receipt as needed", + "type": "string" + } + } +} +``` + +#### Line Items Schema + +```json +{ + "type": "object", + "id": "item.json#", + "required": ["id", "title", "date", "amount", "tax", "quantity"], + "properties": { + "id": { + "title": "ID", + "description": "Unique identifier of the goods or service", + "type": "string" + }, + "title": { + "title": "Title", + "description": "Title of the goods or service", + "type": "string" + }, + "description": { + "title": "Description", + "description": "Description of the goods or service", + "type": "string" + }, + "link": { + "title": "Link", + "description": "URL link to the web page for the product or sevice", + "type": "string", + "format": "uri" + }, + "contract": { + "title": "Contract", + "description": "URL link or hash to an external contract for this product or service", + "type": "string" + }, + "serial_number": { + "title": "Serial Number", + "description": "Serial number of the item", + "type": "string" + }, + "date": { + "title": "Supply Date", + "description": "The date the goods or service were provided", + "type": "string", + "format": "date" + }, + "amount": { + "title": "Unit Price", + "description": "Unit Price per item (excluding tax)", + "type": "number" + }, + "tax": { + "title": "Tax", + "description": "Amount of tax charged for unit", + "type": "array", + "items": { + "type": "object", + "required": ["name", "rate", "amount"], + "properties": { + "name": { + "title": "Name of Tax", + "description": "GST/PST etc", + "type": "string" + }, + "rate": { + "title": "Tax Rate", + "description": "Tax rate as a percentage", + "type": "number" + }, + "amount": { + "title": "Tax Amount", + "description": "Total amount of tax charged", + "type": "number" + } + } + } + }, + "quantity": { + "title": "Quantity", + "description": "Number of units", + "type": "integer" + } + } +} +``` + +## Rationale + +The schema introduced complies with ERC-721's metadata extension, conveniently allowing previous tools for viewing NFTs to show our receipts. The new property "receipt" contains our newly provided receipt structure and the signature property optionally allows the vendor to digitally sign the receipt structure. + +## Backwards Compatibility + +This standard is an extension of ERC-721. It is compatible with both optional extensions, Metadata and Enumerable, mentioned in ERC-721. + +## Security Considerations + +The data stored in the digital receipt includes various types of personally identifying information (PII), such as the vendor's name, contact details, and the items purchased. PII is sensitive information that can be used to identify, locate, or contact an individual. Protecting the privacy of the customer is of utmost importance, as unauthorized access to PII can lead to identity theft, fraud, or other malicious activities. + +To ensure the privacy of the customer, it is crucial to encrypt the PII contained within the digital receipt. By encrypting the PII, only authorized parties with the appropriate decryption keys can access and read the information stored in the digital receipt. This ensures that the customer's privacy is maintained, and their data is protected from potential misuse. + +While encrypting PII is essential, it is important to note that defining a specific encryption standard is beyond the scope of this ERC. + +## 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..ceb08d504b3fe0 --- /dev/null +++ b/EIPS/eip-5573.md @@ -0,0 +1,330 @@ +--- +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), Charles Cunningham (@chunningham) +discussions-to: https://ethereum-magicians.org/t/eip-5573-siwe-recap/10627 +status: Draft +type: Standards Track +category: ERC +created: 2021-07-20 +requires: 4361 +--- + +## Abstract + +[ERC-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 authorize a Relying Party to exercise certain scoped capabilities. How a Relying Party 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 (ERC-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 for the authenticated Ethereum account to authorize a relying party to interact with a third-party service (resource service) on behalf of the Ethereum account. A relying party may 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 Relying Party) 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) where SIWE ReCap implements capabilities-based authorization on top of the authentication provided by SIWE. + +## 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 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. +- Resource Service (RS) - The entity that is providing third-party services for the Ethereum account. +- SIWE Client (SC) - The entity initiating the authorization (SIWE authentication and ReCap flow). +- Relying Party (RP) - same as SC in the context of authorization. + +### 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 ERC-4361 message following a specific format that allows an Ethereum account to delegate a set of ReCap Capabilities to a Relying Party through informed consent. ReCap Capabilities MUST be represented by the final 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 Relying Party, e.g., `https://example.com`, `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK`. It is expected that the RS authenticates the Relying Party 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 the stated URI to perform the following actions on my behalf: (1) 'example': 'append', 'read' for 'https://example.com'. (2) 'other': 'action' for 'https://example.com'. (3) 'example': 'append', 'delete' for 'my:resource:uri.1'. (4) 'example': 'append' for 'my:resource:uri.2'. (5) 'example': 'append' for 'my:resource:uri.3'. + +URI: did:key:example +Version: 1 +Chain ID: 1 +Nonce: mynonce1 +Issued At: 2022-06-21T12:00:00.000Z +Resources: +- urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9leGFtcGxlLmNvbSI6eyJleGFtcGxlL2FwcGVuZCI6W10sImV4YW1wbGUvcmVhZCI6W10sIm90aGVyL2FjdGlvbiI6W119LCJteTpyZXNvdXJjZTp1cmkuMSI6eyJleGFtcGxlL2FwcGVuZCI6W10sImV4YW1wbGUvZGVsZXRlIjpbXX0sIm15OnJlc291cmNlOnVyaS4yIjp7ImV4YW1wbGUvYXBwZW5kIjpbXX0sIm15OnJlc291cmNlOnVyaS4zIjp7ImV4YW1wbGUvYXBwZW5kIjpbXX19LCJwcmYiOltdfQ +``` + +#### 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 SIWE 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 `:` and the unpadded 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. If present, a Recap URI MUST occupy the final entry of the SIWE resource list. + +The following is a non-normative example of a ReCap Capability: + +```text +urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9leGFtcGxlLmNvbS9waWN0dXJlcy8iOnsiY3J1ZC9kZWxldGUiOlt7fV0sImNydWQvdXBkYXRlIjpbe31dLCJvdGhlci9hY3Rpb24iOlt7fV19LCJtYWlsdG86dXNlcm5hbWVAZXhhbXBsZS5jb20iOnsibXNnL3JlY2VpdmUiOlt7Im1heF9jb3VudCI6NSwidGVtcGxhdGVzIjpbIm5ld3NsZXR0ZXIiLCJtYXJrZXRpbmciXX1dLCJtc2cvc2VuZCI6W3sidG8iOiJzb21lb25lQGVtYWlsLmNvbSJ9LHsidG8iOiJqb2VAZW1haWwuY29tIn1dfX0sInByZiI6WyJ6ZGo3V2o2Rk5TNHJVVWJzaUp2amp4Y3NOcVpkRENTaVlSOHNLUVhmb1BmcFNadUF3Il19 +``` + +##### Ability Strings + +Ability Strings identify an action or Ability within a Namespace. They are serialized as `/`. Namespaces and Abilities MUST contain only alphanumeric characters as well as the characters `.`, `*`, `_`, `+`, `-`, conforming to the regex `^[a-zA-Z0-9.*_+-]$`. The ability string as a whole MUST conform to `^[a-zA-Z0-9.*_+-]+\/[a-zA-z0-9.*_+-]+$`. For example, `crud/update` has an ability-namespace of `crud` and an ability-name of `update`. + +##### ReCap Details Object Schema + +The ReCap Details Object denotes which actions on which resources the Relying Party is authorized to invoke on behalf of the Ethereum account 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": { + "att": { + "type": "object", + "propertyNames": { + "format": "uri" + }, + "patternProperties": { + "^.+:.*$": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9.*_+-]+\/[a-zA-z0-9.*_+-]+$": { + "type": "array", + "items": { + "type": "object" + } + } + }, + "additionalProperties": false, + "minProperties": 1 + } + }, + "additionalProperties": false, + "minProperties": 1 + }, + "prf": { + "type": "array", + "items": { + "type": "string", + "format": "CID" + }, + "minItems": 1 + } + } +} +``` + +A ReCap Details Object defines the following properties: + +- `att`: (CONDITIONAL) If present, `att` MUST be a JSON object where each key is a URI and each value is an object containing Ability Strings as keys and a corresponding value which is an array of qualifications to the action (i.e. a restriction or requirement). The keys of the object MUST be ordered lexicographically. +- `prf`: (CONDITIONAL) If present, `prf` MUST be a JSON array of string values with at least one entry where each value is a valid Base58-encoded CID which identifies a parent capability, authorizing the Ethereum account for one or more of the entries in `att` if the SIWE `address` does not identify the controller of the `att` entries. + +Objects in the `att` field (including nested objects) MUST NOT contain duplicate keys and MUST have their keys ordered lexicographically with two steps: + +1. Sort by byte value. +2. If a string starts with another, the shorter string comes first (e.g. `msg/send` comes before `msg/send-to`) + +This is the same as the `Array.sort()` method in Javascript. In the example below, `crud/delete` must appear before `crud/update` and `other/action`, similarly `msg/receive` must appear before `msg/send`. + +The following is a non-normative example of a ReCap Capability Object with `att` and `prf`: + +```jsonc +{ + "att":{ + "https://example.com/pictures/":{ + "crud/delete": [{}], + "crud/update": [{}], + "other/action": [{}] + }, + "mailto:username@example.com":{ + "msg/receive": [{ + "max_count": 5, + "templates": ["newsletter", "marketing"] + }], + "msg/send": [{ "to": "someone@email.com" }, { "to": "joe@email.com" }] + } + }, + "prf":["bafybeigk7ly3pog6uupxku3b6bubirr434ib6tfaymvox6gotaaaaaaaaa"] +} +``` + +In the example above, the Relying Party is authorized to perform the actions `crud/update`, `crud/delete` and `other/action` on resource `https://example.com/pictures` without limitations for any. Additionally the Relying Party is authorized to perform actions `msg/send` and `msg/recieve` on resource `mailto:username@example.com`, where `msg/send` is limited to sending to `someone@email.com` or `joe@email.com` and `msg/recieve` is limited to a maximum of 5 and templates `newsletter` or `marketing`. Note, the Relying Party can invoke each action individually and independently 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. These action and restriction semantics are examples not intended to be universally understood. The Nota Bene objects appearing in the array associated with ability strings represent restrictions on use of an ability. An empty object implies that the action can be performed with no restrictions, but an empty array with no objects implies that there is no way to use this ability in a valid way. + +It is expected that RS implementers define which resources they want to expose through ReCap Details Objects and which actions they want to allow users to invoke on them. + +This example is expected to transform into the following `recap-transformed-statement` (for `URI` of `https://example.com`): + +```text +I further authorize the stated URI to perform the following actions on my behalf: (1) 'crud': 'delete', 'update' for 'https://example.com/pictures'. (2) 'other': 'action' for 'https://example.com/pictures'. (3) 'msg': 'recieve', 'send' for 'mailto:username@example.com'. +``` + +This example is also expected to transform into the following `recap-uri`: + +```text +urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9leGFtcGxlLmNvbS9waWN0dXJlcy8iOnsiY3J1ZC9kZWxldGUiOlt7fV0sImNydWQvdXBkYXRlIjpbe31dLCJvdGhlci9hY3Rpb24iOlt7fV19LCJtYWlsdG86dXNlcm5hbWVAZXhhbXBsZS5jb20iOnsibXNnL3JlY2VpdmUiOlt7Im1heF9jb3VudCI6NSwidGVtcGxhdGVzIjpbIm5ld3NsZXR0ZXIiLCJtYXJrZXRpbmciXX1dLCJtc2cvc2VuZCI6W3sidG8iOiJzb21lb25lQGVtYWlsLmNvbSJ9LHsidG8iOiJqb2VAZW1haWwuY29tIn1dfX0sInByZiI6WyJ6ZGo3V2o2Rk5TNHJVVWJzaUp2amp4Y3NOcVpkRENTaVlSOHNLUVhmb1BmcFNadUF3Il19 +``` + +##### Merging Capability Objects + +Any two Recap objects can be merged together by recursive concatenation of their field elements as long as the ordering rules of the field contents is followed. For example, two recap objects: + +```jsonc +{ + "att": { + "https://example1.com": { + "crud/read": [{}] + } + }, + "prf": ["bafyexample1"] +} + +{ + "att": { + "https://example1.com": { + "crud/update": [{ + "max_times": 1 + }] + }, + "https://example2.com": { + "crud/delete": [{}] + } + }, + "prf": ["bafyexample2"] +} +``` + +combine into: + +```jsonc +{ + "att": { + "https://example1.com": { + "crud/read": [{}], + "crud/update": [{ + "max_times": 1 + }] + }, + "https://example2.com": { + "crud/delete": [{}] + } + }, + "prf": ["bafyexample1", "bafyexample2"] +} +``` + +#### 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 ERC-4361 for definition of input-statement +recap-preamble = "I further authorize the stated URI to perform the following actions on my behalf:" +recap-statement-entry = "(" number ") " action-namespace ": " + action-name *("," action-name) "for" + recap-resource + ; see RFC8259 for definition of number +ability-namespace = string + ; see RFC8259 for definition of string +ability-name = 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 `recap-uri` be a ReCap URI, which represents the ReCap Capabilities that are to be encoded in the SIWE message, and which contains a ReCap Details Object which conforms to the ReCap Details Object Schema. +- [Optional] Let `statement` be the statement field of the input SIWE message conforming to ERC-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 the stated URI to perform the following actions on my behalf:"`. +- Let `numbering` be an integer starting with 1. +- Let `attenuations` be the `att` field of the ReCap Details Object +- For each key and value pair in `attenuations` (starting with the first entry), perform the following: + - Let `resource` be the key and `abilities` be the value + - Group the keys of the `abilities` object by their `ability-namespace` + - For each `ability-namespace`, perform the following: + - Append the string concatenation of `" ("`, `numbering`, `")"` to `recap-transformed-statement`. + - Append the string concatenation of `'`, `ability-namespace`, `':` to `recap-transformed-statement`. + - For each `ability-name` in the `ability-namespace` group, perform the following: + - Append the string concatenation of `'`, `ability-name`, `'` to `recap-transformed-statement` + - If not the final `ability-name`, append `,` to `recap-transformed-statement` + - Append `for '`, `resource`, `'.` 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 ERC-4361 and this EIP. +- Let `siwe-signature` be the output of signing `recap-siwe`, as defined in ERC-4361. +Algorithm: +- Perform ERC-4361 signature verification with `recap-siwe` and `siwe-signature` as inputs. +- Let `uri` be the uri field of `recap-siwe`. +- Let `recap-uri` be a recap URI taken from the last entry of the resources field of `recap-siwe`. +- Let `recap-transformed-statement` be the result of performing the above `ReCap Translation Algorithm` with `uri` and `recap-uri` 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 Relying Party in addition. The process of authenticating the Relying Party 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). diff --git a/EIPS/eip-5585.md b/EIPS/eip-5585.md new file mode 100644 index 00000000000000..92521bea8eb690 --- /dev/null +++ b/EIPS/eip-5585.md @@ -0,0 +1,176 @@ +--- +eip: 5585 +title: ERC-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: Review +type: Standards Track +category: ERC +created: 2022-08-15 +requires: 721 +--- + +## Abstract + +This EIP separates the [ERC-721](./eip-721.md) NFT's commercial usage rights from it's ownership 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; + uint256 expires + } + + /// @notice Get all available rights of this NFT project + /// @return All the rights that can be authorized to the user + function getRights() external view returns(string[]); + + /// @notice NFT holder authorizes all the rights of the NFT 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. It will throw exception when the rights are not defined by this NFT project + /// @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 The user of the NFT transfers his rights to the new user + /// @dev The zero address indicates there is no user + /// @param tokenId The rights of this NFT is transferred to the new user + /// @param newUser The new user + function transferUserRights(uint256 tokenId, address newUser) external; + + /// @notice NFT holder extends the duration of authorization + /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project + /// @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 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); + + /// @notice Emitted when the number of users that can be authorized per NFT is updated + /// @param userLimit The number of users set by operators only + event updateUserLimit(unit256 userLimit); +} +``` + +The `getRights()` function MAY be implemented as pure and view. + +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 `transferUserRights(uint256 tokenId, address newUser)` 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 `authorizeUser` event MUST be emitted when the user of a NFT is changed or the authorization expires time is updated. + +The `updateUserLimit` event MUST be emitted when the number of users that can be authorized per NFT 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, 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. + +Thirdly, 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 ERC-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 [ERC-721](./eip-721.md) since it is an extension of it. + +## Security Considerations + +When the resetAllowed flag is false, which means the authorization can not be revoked by NFT owner during the period of authorization, users of the EIP need to make sure the authorization fee can be fairly assigned if the NFT was sold to a new holder. + +Here is a solution for taking reference: the authorization fee paid by the users can 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 `